splitable.go 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. // Copyright 2019 Yunion
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package splitable
  15. import (
  16. "database/sql"
  17. "fmt"
  18. "reflect"
  19. "sync"
  20. "time"
  21. "yunion.io/x/pkg/errors"
  22. "yunion.io/x/pkg/util/reflectutils"
  23. "yunion.io/x/sqlchemy"
  24. )
  25. type SSplitTableSpec struct {
  26. indexField string
  27. dateField string
  28. tableName string
  29. tableSpec *sqlchemy.STableSpec
  30. metaSpec *sqlchemy.STableSpec
  31. maxDuration time.Duration
  32. maxSegments int
  33. lastTableSpec *sqlchemy.STableSpec
  34. lastTableLock *sync.Mutex
  35. lastTableExpire time.Time
  36. extraOpts sqlchemy.TableExtraOptions
  37. }
  38. const (
  39. lastTableSpecExpireHours = 1 // 1 hour
  40. )
  41. func (t *SSplitTableSpec) DataType() reflect.Type {
  42. return t.tableSpec.DataType()
  43. }
  44. func (t *SSplitTableSpec) ColumnSpec(name string) sqlchemy.IColumnSpec {
  45. return t.tableSpec.ColumnSpec(name)
  46. }
  47. func (t *SSplitTableSpec) Name() string {
  48. return t.tableName
  49. }
  50. func (t *SSplitTableSpec) Database() *sqlchemy.SDatabase {
  51. return t.metaSpec.Database()
  52. }
  53. func (t *SSplitTableSpec) Columns() []sqlchemy.IColumnSpec {
  54. return t.tableSpec.Columns()
  55. }
  56. func (t *SSplitTableSpec) PrimaryColumns() []sqlchemy.IColumnSpec {
  57. return t.tableSpec.PrimaryColumns()
  58. }
  59. func (t *SSplitTableSpec) Indexes() []sqlchemy.STableIndex {
  60. return t.tableSpec.Indexes()
  61. }
  62. func (t *SSplitTableSpec) Expression() string {
  63. metas, err := t.GetTableMetas()
  64. if err != nil {
  65. return fmt.Sprintf("`%s`", t.tableName)
  66. }
  67. tss := make([]sqlchemy.IQuery, 0)
  68. for _, meta := range metas {
  69. ts := t.GetTableSpec(meta)
  70. tss = append(tss, ts.Query())
  71. }
  72. union, err := sqlchemy.UnionWithError(tss...)
  73. if err != nil {
  74. return fmt.Sprintf("`%s`", t.tableName)
  75. }
  76. return union.Expression()
  77. }
  78. func (t *SSplitTableSpec) Instance() *sqlchemy.STable {
  79. return sqlchemy.NewTableInstance(t)
  80. }
  81. func (t *SSplitTableSpec) DropForeignKeySQL() []string {
  82. return t.tableSpec.DropForeignKeySQL()
  83. }
  84. func (t *SSplitTableSpec) AddIndex(unique bool, cols ...string) bool {
  85. return t.tableSpec.AddIndex(unique, cols...)
  86. }
  87. func (t *SSplitTableSpec) Fetch(dt interface{}) error {
  88. vs := reflectutils.FetchStructFieldValueSet(reflect.Indirect(reflect.ValueOf(dt)))
  89. idxVal, ok := vs.GetValue(t.indexField)
  90. if !ok {
  91. return errors.Wrap(errors.ErrNotFound, "GetValue")
  92. }
  93. idxInt := idxVal.Int()
  94. metas, err := t.GetTableMetas()
  95. if err != nil {
  96. return errors.Wrap(err, "GetTableMetas")
  97. }
  98. for _, meta := range metas {
  99. if idxInt >= meta.Start && (meta.End == 0 || meta.End >= idxInt) {
  100. ts := t.GetTableSpec(meta)
  101. return ts.Fetch(dt)
  102. }
  103. }
  104. return sql.ErrNoRows
  105. }
  106. func (t *SSplitTableSpec) Drop() error {
  107. metas, err := t.GetTableMetas()
  108. if err != nil {
  109. return errors.Wrap(err, "GetTableMetas")
  110. }
  111. for _, meta := range metas {
  112. ts := t.GetTableSpec(meta)
  113. err := ts.Drop()
  114. if err != nil {
  115. return errors.Wrapf(err, "Drop %s", meta.Table)
  116. }
  117. }
  118. err = t.metaSpec.Drop()
  119. if err != nil {
  120. return errors.Wrap(err, "Drop Meta")
  121. }
  122. return nil
  123. }
  124. func (t *SSplitTableSpec) GetExtraOptions() sqlchemy.TableExtraOptions {
  125. return t.extraOpts
  126. }
  127. func (t *SSplitTableSpec) SetExtraOptions(opts sqlchemy.TableExtraOptions) {
  128. if t.extraOpts == nil {
  129. t.extraOpts = opts
  130. return
  131. }
  132. for k := range opts {
  133. t.extraOpts[k] = opts[k]
  134. }
  135. }
  136. func NewSplitTableSpec(s interface{}, name string, indexField string, dateField string, maxDuration time.Duration, maxSegments int, dbName sqlchemy.DBName) (*SSplitTableSpec, error) {
  137. spec := sqlchemy.NewTableSpecFromStructWithDBName(s, name, dbName)
  138. /*indexCol := spec.ColumnSpec(indexField)
  139. if indexCol == nil {
  140. return nil, errors.Wrapf(errors.ErrNotFound, "indexField %s not found", indexField)
  141. }
  142. if !indexCol.IsPrimary() {
  143. return nil, errors.Wrapf(errors.ErrInvalidStatus, "indexField %s not primary", indexField)
  144. }
  145. if !indexCol.IsAutoIncrement() {
  146. return nil, errors.Wrapf(errors.ErrInvalidStatus, "indexField %s not auto_increment", indexField)
  147. }
  148. dateCol := spec.ColumnSpec(dateField)
  149. if dateCol == nil {
  150. return nil, errors.Wrapf(errors.ErrNotFound, "dateField %s not found", dateField)
  151. }
  152. if !dateCol.IsDateTime() {
  153. return nil, errors.Wrapf(errors.ErrInvalidStatus, "dateField %s not datetime column", dateField)
  154. }*/
  155. metaSpec := sqlchemy.NewTableSpecFromStructWithDBName(&STableMetadata{}, fmt.Sprintf("%s_metadata", name), dbName)
  156. sts := &SSplitTableSpec{
  157. indexField: indexField,
  158. dateField: dateField,
  159. tableName: name,
  160. tableSpec: spec,
  161. metaSpec: metaSpec,
  162. maxDuration: maxDuration,
  163. maxSegments: maxSegments,
  164. lastTableLock: &sync.Mutex{},
  165. }
  166. registerSplitable(sts)
  167. return sts, nil
  168. }