-
Notifications
You must be signed in to change notification settings - Fork 165
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
Table store #774
base: master
Are you sure you want to change the base?
Table store #774
Conversation
Signed-off-by: Cody Littley <[email protected]>
Signed-off-by: Cody Littley <[email protected]>
Signed-off-by: Cody Littley <[email protected]>
Signed-off-by: Cody Littley <[email protected]>
Signed-off-by: Cody Littley <[email protected]>
Signed-off-by: Cody Littley <[email protected]>
Signed-off-by: Cody Littley <[email protected]>
Signed-off-by: Cody Littley <[email protected]>
Signed-off-by: Cody Littley <[email protected]>
Signed-off-by: Cody Littley <[email protected]>
Signed-off-by: Cody Littley <[email protected]>
Signed-off-by: Cody Littley <[email protected]>
Signed-off-by: Cody Littley <[email protected]>
Signed-off-by: Cody Littley <[email protected]>
Signed-off-by: Cody Littley <[email protected]>
Signed-off-by: Cody Littley <[email protected]>
Signed-off-by: Cody Littley <[email protected]>
Signed-off-by: Cody Littley <[email protected]>
Signed-off-by: Cody Littley <[email protected]>
Signed-off-by: Cody Littley <[email protected]>
Signed-off-by: Cody Littley <[email protected]>
Signed-off-by: Cody Littley <[email protected]>
Signed-off-by: Cody Littley <[email protected]>
Signed-off-by: Cody Littley <[email protected]>
package kvstore | ||
|
||
// Batch is a collection of operations that can be applied atomically to a TableStore. | ||
type Batch[T any] interface { |
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.
Not sure this needs template, Batch may just work with table key
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.
Base Store
objects also need to support operations using Batch
, but the type of these keys is []byte
. I think if we want to get rid of the generics, we will need to stop using type TableKey []byte
and instead use the []byte
type for batch operations on a table. Let's discuss.
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 issue is this interface intends to be used for TableStore
(like it comments out), but it's now got used to batch operations for Store
as well.
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.
One idea is to subclass this interface with two types, like (demo not real code):
type StoreBatch struct {
// key type is []byte
deletes map[[]byte]struct{}
...
}
type TableStoreBatch struct {
// key type is TableKey
deletes map[TableKey]struct{}
...
}
It establishes common interface but users doesn't need to use template. Wdyt?
common/kvstore/batch.go
Outdated
} | ||
|
||
// BatchOperator is an interface for creating new batches. | ||
type BatchOperator[T any] interface { |
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.
For simplicity, it looks Batch creation can be a function of TableStore
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.
NewBatch
is also a function on the base Store
object, but I agree this interface isn't really necessary. Removed.
"sort" | ||
) | ||
|
||
// Table ID 0 is reserved for use internal use by the metadata table. |
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.
Comment doesn't match code
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.
fixed
// Table ID 0 is reserved for use internal use by the metadata table. | ||
const metadataTableID uint32 = math.MaxUint32 | ||
|
||
// Table ID 1 is reserved for use by the namespace table. This stores a mapping between IDs and table names. |
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.
Comment doesn't match code
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.
fixed
// WARNING: it is not safe to access the wrapped store directly while the TableStore is in use. The TableStore uses | ||
// special key formatting, and direct access to the wrapped store may violate the TableStore's invariants, resulting | ||
// in undefined behavior. | ||
func Wrapper(logger logging.Logger, base kvstore.Store) (kvstore.TableStore, error) { |
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.
I'd create a TableStore, instead of asking for an external Store and wrapping it. This way the Store can be internally created and no risk of breaking invariants by accessing it directly outside the TableStore.
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 purpose of requiring the caller to provide the Store
implementation is to support multiple Store
implementations. Currently this is used in unit tests, where I sometimes use a store implementation that is built on top of an in memory map. Let's discuss this.
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.
Changed based on our slack discussion.
} | ||
|
||
// GetTable gets the table with the given name. If the table does not exist, it is first created. | ||
func (t *tableStore) GetTable(name string) (kvstore.Table, error) { |
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.
I don't see this TableStore is thread-safe (which is required per interface)
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.
In the interface, this method is specifically documented not to be thread safe (although the godoc in the implementation doesn't copy the warning). The code to make this thread simple to write, but I'm concerned about adding locks that could slow other operations. Do you think it's worth adding locks so that these methods are safe to call concurrently?
// GetTable gets the table with the given name. If the table does not exist, it is first created.
//
// WARNING: this method is not thread safe with respect to any other methods in this interface or
// any methods on any Table objects associated with this store.
GetTable(name string) (Table, error)
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.
I think it needs to, otherwise other methods like GetTableCount (which is supposed to be thread safe) won't be safe as they share the states.
Signed-off-by: Cody Littley <[email protected]>
Signed-off-by: Cody Littley <[email protected]>
Why are these changes needed?
This PR adds the ability to use tables on top of a store. A very nice to have feature for use cases that want to use multiple logical tables.
Checks