-
Notifications
You must be signed in to change notification settings - Fork 193
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
TTL + table store #811
TTL + table store #811
Changes from 20 commits
6d899ae
f4c867a
0e639f4
2392cb2
f03bf05
c12f76a
6708f47
a152101
d02d5e7
c74290e
4ddfa37
91e8ff8
10380e7
cdc173e
4d8a5fe
56e3f65
3cf4385
f2a3b71
cd4d725
4510669
4b4547a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package kvstore | ||
|
||
import "time" | ||
|
||
// Batch is a collection of key / value pairs that will be written atomically to a database. | ||
// Although it is thread safe to modify different batches in parallel or to modify a batch while | ||
// the store is being modified, it is not thread safe to concurrently modify the same batch. | ||
type Batch[K any] interface { | ||
// Put stores the given key / value pair in the batch, overwriting any existing value for that key. | ||
// If nil is passed as the value, a byte slice of length 0 will be stored. | ||
Put(key K, value []byte) | ||
// Delete removes the key from the batch. | ||
Delete(key K) | ||
// Apply atomically writes all the key / value pairs in the batch to the database. | ||
Apply() error | ||
// Size returns the number of operations in the batch. | ||
Size() uint32 | ||
} | ||
|
||
// TTLBatch is a collection of key / value pairs that will be written atomically to a database with | ||
// time-to-live (TTL) or expiration times. Although it is thread safe to modify different batches in | ||
// parallel or to modify a batch while the store is being modified, it is not thread safe to concurrently | ||
// modify the same batch. | ||
type TTLBatch[K any] interface { | ||
Batch[K] | ||
// PutWithTTL stores the given key / value pair in the batch with a time-to-live (TTL) or expiration time. | ||
// If nil is passed as the value, a byte slice of length 0 will be stored. | ||
PutWithTTL(key K, value []byte, ttl time.Duration) | ||
// PutWithExpiration stores the given key / value pair in the batch with an expiration time. | ||
// If nil is passed as the value, a byte slice of length 0 will be stored. | ||
PutWithExpiration(key K, value []byte, expiryTime time.Time) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
package kvstore | ||
|
||
import "errors" | ||
|
||
// ErrInvalidKey is returned when a key cannot be interpreted as the requested type. | ||
var ErrInvalidKey = errors.New("invalid key") | ||
|
||
// Key represents a key in a TableStore. Each key is scoped to a specific table. | ||
type Key interface { | ||
// AsString interprets the key as a string and returns it. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These are a lot of types, can we start with what we know we need? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've removed all of the methods except for the ones that deal with bytes. The other methods were not strictly necessary, just syntactic sugar. |
||
AsString() string | ||
// AsBytes interprets the key as a byte slice and returns it. | ||
AsBytes() []byte | ||
// AsUint64 interprets the key as a uint64 and returns it. | ||
// Returns an error if the data cannot be interpreted as a uint64. | ||
AsUint64() (uint64, error) | ||
// AsInt64 interprets the key as an int64 and returns it. | ||
// Returns an error if the data cannot be interpreted as an int64. | ||
AsInt64() (int64, error) | ||
// AsUint32 interprets the key as a uint32 and returns it. | ||
// Returns an error if the data cannot be interpreted as a uint32. | ||
AsUint32() (uint32, error) | ||
// AsInt32 interprets the key as an int32 and returns it. | ||
// Returns an error if the data cannot be interpreted as an int32. | ||
AsInt32() (int32, error) | ||
// Raw returns the raw byte slice that represents the key. This value | ||
// may not be equal to the byte slice that was used to create the key, and | ||
// should be treated as an opaque value. | ||
Raw() []byte | ||
// Builder returns the KeyBuilder that created this key. | ||
Builder() KeyBuilder | ||
} | ||
|
||
// KeyBuilder is used to create keys for a TableStore. Each KeyBuilder is scoped to a particular table, | ||
// and can be used to create keys that are within that table. | ||
type KeyBuilder interface { | ||
// TableName returns the name of the table that this KeyBuilder is scoped to. | ||
TableName() string | ||
// Key creates a key from a byte slice. | ||
Key(key []byte) Key | ||
// StringKey creates a key from a string. Equivalent to Key([]byte(key)). | ||
StringKey(key string) Key | ||
// Uint64Key creates a key from a uint64. Resulting key is an 8-byte big-endian representation of the uint64. | ||
Uint64Key(key uint64) Key | ||
// Int64Key creates a key from an int64. Resulting key is an 8-byte big-endian representation of the int64. | ||
Int64Key(key int64) Key | ||
// Uint32Key creates a key from a uint32. Resulting key is a 4-byte big-endian representation of the uint32. | ||
Uint32Key(key uint32) Key | ||
// Int32Key creates a key from an int32. Resulting key is a 4-byte big-endian representation of the int32. | ||
Int32Key(key int32) Key | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
package tablestore | ||
|
||
import "time" | ||
|
||
// StoreType describes the underlying store implementation. | ||
type StoreType int | ||
|
||
const ( | ||
// LevelDB is a LevelDB-backed store. | ||
LevelDB StoreType = iota | ||
// MapStore is an in-memory store. This store does not preserve data across restarts. | ||
MapStore | ||
) | ||
|
||
// Config is the configuration for a TableStore. | ||
type Config struct { | ||
// The type of the base store. Default is LevelDB. | ||
Type StoreType | ||
// The path to the file system directory where the store will write its data. Default is nil. | ||
// Some store implementations may ignore this field (e.g. MapStore). Other store implementations may require | ||
// this field to be set (e.g. LevelDB). | ||
Path *string | ||
// If true, the store will perform garbage collection on a background goroutine. Default is true. | ||
GarbageCollectionEnabled bool | ||
// If garbage collection is enabled, this is the interval at which it will run. Default is 5 minutes. | ||
GarbageCollectionInterval time.Duration | ||
// If garbage collection is enabled, this is the maximum number of entries to delete in a single batch during | ||
// garbage collection. Default is 1024. | ||
GarbageCollectionBatchSize uint32 | ||
// The list of tables to create on startup. Any pre-existing table not in this list will be deleted. If | ||
// this list is nil, the previous schema will be carried forward with no modifications. Default is nil. | ||
Schema []string | ||
} | ||
|
||
// DefaultConfig returns a Config with default values. | ||
func DefaultConfig() *Config { | ||
return &Config{ | ||
Type: LevelDB, | ||
Path: nil, | ||
GarbageCollectionEnabled: true, | ||
GarbageCollectionInterval: 5 * time.Minute, | ||
GarbageCollectionBatchSize: 1024, | ||
Schema: nil, | ||
} | ||
} | ||
|
||
// DefaultLevelDBConfig returns a Config with default values for a LevelDB store. | ||
func DefaultLevelDBConfig(path string) *Config { | ||
config := DefaultConfig() | ||
config.Type = LevelDB | ||
config.Path = &path | ||
return config | ||
} | ||
|
||
// DefaultMapStoreConfig returns a Config with default values for a MapStore. | ||
func DefaultMapStoreConfig() *Config { | ||
config := DefaultConfig() | ||
config.Type = MapStore | ||
return config | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks this is not needed?
Table
, there isTableBatch
, operating on the raw keysTableStore
, there isTableStoreBatch
, operating on the namespaced keysAnd the ttl related interfaces are always there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The batch APIs have now been simplified so that they don't have generics. Are there additional simplifications you'd like to see?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that we still need two batch interfaces if we want to support batches that can set different TTLs for operations within the same batch. The core reason is that the basic
Store
API does not have a concept of a TTL, meaning we need a batch interface that doesn't have TTLs.