github.com/TeaOSLab/EdgeNode@v1.3.8/internal/utils/kvstore/table.go (about) 1 // Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn . 2 3 package kvstore 4 5 import ( 6 "bytes" 7 "encoding/binary" 8 "errors" 9 "fmt" 10 "github.com/cockroachdb/pebble" 11 "github.com/iwind/TeaGo/types" 12 "sync" 13 ) 14 15 const ( 16 KeyPrefix = "K$" 17 KeyMaxLength = 8 << 10 18 19 FieldPrefix = "F$" 20 21 MaxBatchKeys = 8 << 10 // TODO not implemented 22 ) 23 24 type Table[T any] struct { 25 name string 26 rawNamespace []byte 27 db *DB 28 encoder ValueEncoder[T] 29 fieldNames []string 30 isClosed bool 31 32 mu *sync.RWMutex 33 } 34 35 func NewTable[T any](tableName string, encoder ValueEncoder[T]) (*Table[T], error) { 36 if !IsValidName(tableName) { 37 return nil, errors.New("invalid table name '" + tableName + "'") 38 } 39 40 return &Table[T]{ 41 name: tableName, 42 encoder: encoder, 43 mu: &sync.RWMutex{}, 44 }, nil 45 } 46 47 func (this *Table[T]) Name() string { 48 return this.name 49 } 50 51 func (this *Table[T]) Namespace() []byte { 52 var dest = make([]byte, len(this.rawNamespace)) 53 copy(dest, this.rawNamespace) 54 return dest 55 } 56 57 func (this *Table[T]) SetNamespace(namespace []byte) { 58 this.rawNamespace = namespace 59 } 60 61 func (this *Table[T]) SetDB(db *DB) { 62 this.db = db 63 } 64 65 func (this *Table[T]) DB() *DB { 66 return this.db 67 } 68 69 func (this *Table[T]) Encoder() ValueEncoder[T] { 70 return this.encoder 71 } 72 73 func (this *Table[T]) Set(key string, value T) error { 74 if this.isClosed { 75 return NewTableClosedErr(this.name) 76 } 77 78 if len(key) > KeyMaxLength { 79 return ErrKeyTooLong 80 } 81 82 valueBytes, err := this.encoder.Encode(value) 83 if err != nil { 84 return err 85 } 86 87 return this.WriteTx(func(tx *Tx[T]) error { 88 return this.set(tx, key, valueBytes, value, false, false) 89 }) 90 } 91 92 func (this *Table[T]) SetSync(key string, value T) error { 93 if this.isClosed { 94 return NewTableClosedErr(this.name) 95 } 96 97 if len(key) > KeyMaxLength { 98 return ErrKeyTooLong 99 } 100 101 valueBytes, err := this.encoder.Encode(value) 102 if err != nil { 103 return err 104 } 105 106 return this.WriteTxSync(func(tx *Tx[T]) error { 107 return this.set(tx, key, valueBytes, value, false, true) 108 }) 109 } 110 111 func (this *Table[T]) Insert(key string, value T) error { 112 if this.isClosed { 113 return NewTableClosedErr(this.name) 114 } 115 116 if len(key) > KeyMaxLength { 117 return ErrKeyTooLong 118 } 119 120 valueBytes, err := this.encoder.Encode(value) 121 if err != nil { 122 return err 123 } 124 125 return this.WriteTx(func(tx *Tx[T]) error { 126 return this.set(tx, key, valueBytes, value, true, false) 127 }) 128 } 129 130 // ComposeFieldKey compose field key 131 // $Namespace$FieldName$FieldValueSeparatorKeyValueFieldLength[2] 132 func (this *Table[T]) ComposeFieldKey(keyBytes []byte, fieldName string, fieldValueBytes []byte) []byte { 133 // TODO use 'make()' and 'copy()' to pre-alloc memory space 134 var b = make([]byte, 2) 135 binary.BigEndian.PutUint16(b, uint16(len(fieldValueBytes))) 136 var fieldKey = append(this.FieldKey(fieldName), '$') // namespace 137 fieldKey = append(fieldKey, fieldValueBytes...) // field value 138 fieldKey = append(fieldKey, 0, 0) // separator 139 fieldKey = append(fieldKey, keyBytes...) // key value 140 fieldKey = append(fieldKey, b...) // field value length 141 return fieldKey 142 } 143 144 func (this *Table[T]) Exist(key string) (found bool, err error) { 145 if this.isClosed { 146 return false, NewTableClosedErr(this.name) 147 } 148 149 _, closer, err := this.db.store.rawDB.Get(this.FullKey(key)) 150 if err != nil { 151 if IsNotFound(err) { 152 return false, nil 153 } 154 return false, err 155 } 156 defer func() { 157 _ = closer.Close() 158 }() 159 160 return true, nil 161 } 162 163 func (this *Table[T]) Get(key string) (value T, err error) { 164 if this.isClosed { 165 err = NewTableClosedErr(this.name) 166 return 167 } 168 169 err = this.ReadTx(func(tx *Tx[T]) error { 170 resultValue, getErr := this.get(tx, key) 171 if getErr == nil { 172 value = resultValue 173 } 174 return getErr 175 }) 176 177 return 178 } 179 180 func (this *Table[T]) Delete(key ...string) error { 181 if this.isClosed { 182 return NewTableClosedErr(this.name) 183 } 184 185 if len(key) == 0 { 186 return nil 187 } 188 189 return this.WriteTx(func(tx *Tx[T]) error { 190 return this.deleteKeys(tx, key...) 191 }) 192 } 193 194 func (this *Table[T]) ReadTx(fn func(tx *Tx[T]) error) error { 195 if this.isClosed { 196 return NewTableClosedErr(this.name) 197 } 198 199 tx, err := NewTx[T](this, true) 200 if err != nil { 201 return err 202 } 203 defer func() { 204 _ = tx.Close() 205 }() 206 207 err = fn(tx) 208 if err != nil { 209 return err 210 } 211 212 return tx.Commit() 213 } 214 215 func (this *Table[T]) WriteTx(fn func(tx *Tx[T]) error) error { 216 if this.isClosed { 217 return NewTableClosedErr(this.name) 218 } 219 220 tx, err := NewTx[T](this, false) 221 if err != nil { 222 return err 223 } 224 defer func() { 225 _ = tx.Close() 226 }() 227 228 err = fn(tx) 229 if err != nil { 230 return err 231 } 232 233 return tx.Commit() 234 } 235 236 func (this *Table[T]) WriteTxSync(fn func(tx *Tx[T]) error) error { 237 if this.isClosed { 238 return NewTableClosedErr(this.name) 239 } 240 241 tx, err := NewTx[T](this, false) 242 if err != nil { 243 return err 244 } 245 defer func() { 246 _ = tx.Close() 247 }() 248 249 err = fn(tx) 250 if err != nil { 251 return err 252 } 253 254 return tx.CommitSync() 255 } 256 257 func (this *Table[T]) Truncate() error { 258 if this.isClosed { 259 return NewTableClosedErr(this.name) 260 } 261 262 this.mu.Lock() 263 defer this.mu.Unlock() 264 265 return this.db.store.rawDB.DeleteRange(this.Namespace(), append(this.Namespace(), 0xFF), DefaultWriteOptions) 266 } 267 268 func (this *Table[T]) DeleteRange(start string, end string) error { 269 if this.isClosed { 270 return NewTableClosedErr(this.name) 271 } 272 273 return this.db.store.rawDB.DeleteRange(this.FullKeyBytes([]byte(start)), this.FullKeyBytes([]byte(end)), DefaultWriteOptions) 274 } 275 276 func (this *Table[T]) Query() *Query[T] { 277 var query = NewQuery[T]() 278 query.SetTable(this) 279 return query 280 } 281 282 func (this *Table[T]) Count() (int64, error) { 283 var count int64 284 285 var begin = this.FullKeyBytes(nil) 286 it, err := this.db.store.rawDB.NewIter(&pebble.IterOptions{ 287 LowerBound: begin, 288 UpperBound: append(begin, 0xFF), 289 }) 290 if err != nil { 291 return 0, err 292 } 293 defer func() { 294 _ = it.Close() 295 }() 296 297 for it.First(); it.Valid(); it.Next() { 298 count++ 299 } 300 301 return count, err 302 } 303 304 func (this *Table[T]) FullKey(realKey string) []byte { 305 return append(this.Namespace(), KeyPrefix+realKey...) 306 } 307 308 func (this *Table[T]) FullKeyBytes(realKeyBytes []byte) []byte { 309 var k = append(this.Namespace(), KeyPrefix...) 310 k = append(k, realKeyBytes...) 311 return k 312 } 313 314 func (this *Table[T]) FieldKey(fieldName string) []byte { 315 var data = append(this.Namespace(), FieldPrefix...) 316 data = append(data, fieldName...) 317 return data 318 } 319 320 func (this *Table[T]) DecodeFieldKey(fieldName string, fieldKey []byte) (fieldValue []byte, key []byte, err error) { 321 var l = len(fieldKey) 322 var baseLen = len(this.FieldKey(fieldName)) + 1 /** $ **/ + 2 /** separator length **/ + 2 /** field length **/ 323 if l < baseLen { 324 err = errors.New("invalid field key") 325 return 326 } 327 328 var fieldValueLen = binary.BigEndian.Uint16(fieldKey[l-2:]) 329 var data = fieldKey[baseLen-4 : l-2] 330 331 fieldValue = data[:fieldValueLen] 332 key = data[fieldValueLen+2: /** separator length **/] 333 334 return 335 } 336 337 func (this *Table[T]) Close() error { 338 this.isClosed = true 339 return nil 340 } 341 342 func (this *Table[T]) deleteKeys(tx *Tx[T], key ...string) error { 343 var batch = tx.batch 344 345 for _, singleKey := range key { 346 var keyErr = func(singleKey string) error { 347 var keyBytes = this.FullKey(singleKey) 348 349 // delete field values 350 if len(this.fieldNames) > 0 { 351 valueBytes, closer, getErr := batch.Get(keyBytes) 352 if getErr != nil { 353 if IsNotFound(getErr) { 354 return nil 355 } 356 return getErr 357 } 358 defer func() { 359 _ = closer.Close() 360 }() 361 362 value, decodeErr := this.encoder.Decode(valueBytes) 363 if decodeErr != nil { 364 return fmt.Errorf("decode value failed: %w", decodeErr) 365 } 366 367 for _, fieldName := range this.fieldNames { 368 fieldValueBytes, fieldErr := this.encoder.EncodeField(value, fieldName) 369 if fieldErr != nil { 370 return fieldErr 371 } 372 373 deleteKeyErr := batch.Delete(this.ComposeFieldKey([]byte(singleKey), fieldName, fieldValueBytes), DefaultWriteOptions) 374 if deleteKeyErr != nil { 375 return deleteKeyErr 376 } 377 } 378 } 379 380 err := batch.Delete(keyBytes, DefaultWriteOptions) 381 if err != nil { 382 return err 383 } 384 385 return nil 386 }(singleKey) 387 if keyErr != nil { 388 return keyErr 389 } 390 } 391 392 return nil 393 } 394 395 func (this *Table[T]) set(tx *Tx[T], key string, valueBytes []byte, value T, insertOnly bool, syncMode bool) error { 396 var keyBytes = this.FullKey(key) 397 var writeOptions = DefaultWriteOptions 398 if syncMode { 399 writeOptions = DefaultWriteSyncOptions 400 } 401 402 var batch = tx.batch 403 404 // read old value 405 var oldValue T 406 var oldFound bool 407 var countFields = len(this.fieldNames) 408 409 if !insertOnly { 410 if countFields > 0 { 411 oldValueBytes, closer, getErr := batch.Get(keyBytes) 412 if getErr != nil { 413 if !IsNotFound(getErr) { 414 return getErr 415 } 416 } else { 417 defer func() { 418 _ = closer.Close() 419 }() 420 421 var decodeErr error 422 oldValue, decodeErr = this.encoder.Decode(oldValueBytes) 423 if decodeErr != nil { 424 return fmt.Errorf("decode value failed: %w", decodeErr) 425 } 426 oldFound = true 427 } 428 } 429 } 430 431 setErr := batch.Set(keyBytes, valueBytes, writeOptions) 432 if setErr != nil { 433 return setErr 434 } 435 436 // process fields 437 if countFields > 0 { 438 // add new field keys 439 for _, fieldName := range this.fieldNames { 440 // 把EncodeField放在TX里,是为了节约内存 441 fieldValueBytes, fieldErr := this.encoder.EncodeField(value, fieldName) 442 if fieldErr != nil { 443 return fieldErr 444 } 445 446 if len(fieldValueBytes) > 8<<10 { 447 return errors.New("field value too long: " + types.String(len(fieldValueBytes))) 448 } 449 450 var newFieldKeyBytes = this.ComposeFieldKey([]byte(key), fieldName, fieldValueBytes) 451 452 // delete old field key 453 if oldFound { 454 oldFieldValueBytes, oldFieldErr := this.encoder.EncodeField(oldValue, fieldName) 455 if oldFieldErr != nil { 456 return oldFieldErr 457 } 458 var oldFieldKeyBytes = this.ComposeFieldKey([]byte(key), fieldName, oldFieldValueBytes) 459 if bytes.Equal(oldFieldKeyBytes, newFieldKeyBytes) { 460 // skip the field 461 continue 462 } 463 deleteFieldErr := batch.Delete(oldFieldKeyBytes, writeOptions) 464 if deleteFieldErr != nil { 465 return deleteFieldErr 466 } 467 } 468 469 // set new field key 470 setFieldErr := batch.Set(newFieldKeyBytes, nil, writeOptions) 471 if setFieldErr != nil { 472 return setFieldErr 473 } 474 } 475 } 476 477 return nil 478 } 479 480 func (this *Table[T]) get(tx *Tx[T], key string) (value T, err error) { 481 return this.getWithKeyBytes(tx, this.FullKey(key)) 482 } 483 484 func (this *Table[T]) getWithKeyBytes(tx *Tx[T], keyBytes []byte) (value T, err error) { 485 valueBytes, closer, err := tx.batch.Get(keyBytes) 486 if err != nil { 487 return value, err 488 } 489 defer func() { 490 _ = closer.Close() 491 }() 492 493 resultValue, decodeErr := this.encoder.Decode(valueBytes) 494 if decodeErr != nil { 495 return value, fmt.Errorf("decode value failed: %w", decodeErr) 496 } 497 value = resultValue 498 return 499 }