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  }