summaryrefslogtreecommitdiff
path: root/vendor/codeberg.org/gruf/go-store/v2/kv
diff options
context:
space:
mode:
authorLibravatar tobi <31960611+tsmethurst@users.noreply.github.com>2022-11-05 12:10:19 +0100
committerLibravatar GitHub <noreply@github.com>2022-11-05 11:10:19 +0000
commitbcb80d3ff4a669d52d63950c8830427646c05884 (patch)
tree4aa95a83545b3f87a80fe4b625cb6f2ad9c4427f /vendor/codeberg.org/gruf/go-store/v2/kv
parent[bugfix] Increase field size limits when registering apps (#958) (diff)
downloadgotosocial-bcb80d3ff4a669d52d63950c8830427646c05884.tar.xz
[chore] bump gruf/go-store to v2 (#953)
* [chore] bump gruf/go-store to v2 * no more boobs
Diffstat (limited to 'vendor/codeberg.org/gruf/go-store/v2/kv')
-rw-r--r--vendor/codeberg.org/gruf/go-store/v2/kv/iterator.go63
-rw-r--r--vendor/codeberg.org/gruf/go-store/v2/kv/state.go116
-rw-r--r--vendor/codeberg.org/gruf/go-store/v2/kv/store.go253
3 files changed, 432 insertions, 0 deletions
diff --git a/vendor/codeberg.org/gruf/go-store/v2/kv/iterator.go b/vendor/codeberg.org/gruf/go-store/v2/kv/iterator.go
new file mode 100644
index 000000000..736edddaa
--- /dev/null
+++ b/vendor/codeberg.org/gruf/go-store/v2/kv/iterator.go
@@ -0,0 +1,63 @@
+package kv
+
+import (
+ "context"
+ "errors"
+
+ "codeberg.org/gruf/go-mutexes"
+ "codeberg.org/gruf/go-store/v2/storage"
+)
+
+var ErrIteratorClosed = errors.New("store/kv: iterator closed")
+
+// Iterator provides a read-only iterator to all the key-value
+// pairs in a KVStore. While the iterator is open the store is read
+// locked, you MUST release the iterator when you are finished with
+// it.
+//
+// Please note:
+// individual iterators are NOT concurrency safe, though it is safe to
+// have multiple iterators running concurrently.
+type Iterator struct {
+ store *KVStore // store is the linked KVStore
+ state *mutexes.LockState
+ entries []storage.Entry
+ index int
+ key string
+}
+
+// Next attempts to fetch the next key-value pair, the
+// return value indicates whether another pair remains.
+func (i *Iterator) Next() bool {
+ next := i.index + 1
+ if next >= len(i.entries) {
+ i.key = ""
+ return false
+ }
+ i.key = i.entries[next].Key
+ i.index = next
+ return true
+}
+
+// Key returns the current iterator key.
+func (i *Iterator) Key() string {
+ return i.key
+}
+
+// Value returns the current iterator value at key.
+func (i *Iterator) Value(ctx context.Context) ([]byte, error) {
+ if i.store == nil {
+ return nil, ErrIteratorClosed
+ }
+ return i.store.get(i.state.RLock, ctx, i.key)
+}
+
+// Release will release the store read-lock, and close this iterator.
+func (i *Iterator) Release() {
+ i.state.UnlockMap()
+ i.state = nil
+ i.store = nil
+ i.key = ""
+ i.entries = nil
+ i.index = 0
+}
diff --git a/vendor/codeberg.org/gruf/go-store/v2/kv/state.go b/vendor/codeberg.org/gruf/go-store/v2/kv/state.go
new file mode 100644
index 000000000..9ac8ab1bf
--- /dev/null
+++ b/vendor/codeberg.org/gruf/go-store/v2/kv/state.go
@@ -0,0 +1,116 @@
+package kv
+
+import (
+ "context"
+ "errors"
+ "io"
+
+ "codeberg.org/gruf/go-mutexes"
+)
+
+// ErrStateClosed is returned on further calls to states after calling Release().
+var ErrStateClosed = errors.New("store/kv: state closed")
+
+// StateRO provides a read-only window to the store. While this
+// state is active during the Read() function window, the entire
+// store will be read-locked. The state is thread-safe for concurrent
+// use UNTIL the moment that your supplied function to Read() returns.
+type StateRO struct {
+ store *KVStore
+ state *mutexes.LockState
+}
+
+// Get: see KVStore.Get(). Returns error if state already closed.
+func (st *StateRO) Get(ctx context.Context, key string) ([]byte, error) {
+ if st.store == nil {
+ return nil, ErrStateClosed
+ }
+ return st.store.get(st.state.RLock, ctx, key)
+}
+
+// GetStream: see KVStore.GetStream(). Returns error if state already closed.
+func (st *StateRO) GetStream(ctx context.Context, key string) (io.ReadCloser, error) {
+ if st.store == nil {
+ return nil, ErrStateClosed
+ }
+ return st.store.getStream(st.state.RLock, ctx, key)
+}
+
+// Has: see KVStore.Has(). Returns error if state already closed.
+func (st *StateRO) Has(ctx context.Context, key string) (bool, error) {
+ if st.store == nil {
+ return false, ErrStateClosed
+ }
+ return st.store.has(st.state.RLock, ctx, key)
+}
+
+// Release will release the store read-lock, and close this state.
+func (st *StateRO) Release() {
+ st.state.UnlockMap()
+ st.state = nil
+ st.store = nil
+}
+
+// StateRW provides a read-write window to the store. While this
+// state is active during the Update() function window, the entire
+// store will be locked. The state is thread-safe for concurrent
+// use UNTIL the moment that your supplied function to Update() returns.
+type StateRW struct {
+ store *KVStore
+ state *mutexes.LockState
+}
+
+// Get: see KVStore.Get(). Returns error if state already closed.
+func (st *StateRW) Get(ctx context.Context, key string) ([]byte, error) {
+ if st.store == nil {
+ return nil, ErrStateClosed
+ }
+ return st.store.get(st.state.RLock, ctx, key)
+}
+
+// GetStream: see KVStore.GetStream(). Returns error if state already closed.
+func (st *StateRW) GetStream(ctx context.Context, key string) (io.ReadCloser, error) {
+ if st.store == nil {
+ return nil, ErrStateClosed
+ }
+ return st.store.getStream(st.state.RLock, ctx, key)
+}
+
+// Put: see KVStore.Put(). Returns error if state already closed.
+func (st *StateRW) Put(ctx context.Context, key string, value []byte) error {
+ if st.store == nil {
+ return ErrStateClosed
+ }
+ return st.store.put(st.state.Lock, ctx, key, value)
+}
+
+// PutStream: see KVStore.PutStream(). Returns error if state already closed.
+func (st *StateRW) PutStream(ctx context.Context, key string, r io.Reader) error {
+ if st.store == nil {
+ return ErrStateClosed
+ }
+ return st.store.putStream(st.state.Lock, ctx, key, r)
+}
+
+// Has: see KVStore.Has(). Returns error if state already closed.
+func (st *StateRW) Has(ctx context.Context, key string) (bool, error) {
+ if st.store == nil {
+ return false, ErrStateClosed
+ }
+ return st.store.has(st.state.RLock, ctx, key)
+}
+
+// Delete: see KVStore.Delete(). Returns error if state already closed.
+func (st *StateRW) Delete(ctx context.Context, key string) error {
+ if st.store == nil {
+ return ErrStateClosed
+ }
+ return st.store.delete(st.state.Lock, ctx, key)
+}
+
+// Release will release the store lock, and close this state.
+func (st *StateRW) Release() {
+ st.state.UnlockMap()
+ st.state = nil
+ st.store = nil
+}
diff --git a/vendor/codeberg.org/gruf/go-store/v2/kv/store.go b/vendor/codeberg.org/gruf/go-store/v2/kv/store.go
new file mode 100644
index 000000000..86ba73f67
--- /dev/null
+++ b/vendor/codeberg.org/gruf/go-store/v2/kv/store.go
@@ -0,0 +1,253 @@
+package kv
+
+import (
+ "context"
+ "io"
+
+ "codeberg.org/gruf/go-mutexes"
+ "codeberg.org/gruf/go-store/v2/storage"
+ "codeberg.org/gruf/go-store/v2/util"
+)
+
+// KVStore is a very simple, yet performant key-value store
+type KVStore struct {
+ mu mutexes.MutexMap // map of keys to mutexes to protect key access
+ st storage.Storage // underlying storage implementation
+}
+
+func OpenDisk(path string, cfg *storage.DiskConfig) (*KVStore, error) {
+ // Attempt to open disk storage
+ storage, err := storage.OpenDisk(path, cfg)
+ if err != nil {
+ return nil, err
+ }
+
+ // Return new KVStore
+ return OpenStorage(storage)
+}
+
+func OpenBlock(path string, cfg *storage.BlockConfig) (*KVStore, error) {
+ // Attempt to open block storage
+ storage, err := storage.OpenBlock(path, cfg)
+ if err != nil {
+ return nil, err
+ }
+
+ // Return new KVStore
+ return OpenStorage(storage)
+}
+
+func OpenMemory(overwrites bool) *KVStore {
+ return &KVStore{
+ mu: mutexes.NewMap(-1, -1),
+ st: storage.OpenMemory(100, overwrites),
+ }
+}
+
+func OpenS3(endpoint string, bucket string, cfg *storage.S3Config) (*KVStore, error) {
+ // Attempt to open S3 storage
+ storage, err := storage.OpenS3(endpoint, bucket, cfg)
+ if err != nil {
+ return nil, err
+ }
+
+ // Return new KVStore
+ return OpenStorage(storage)
+}
+
+func OpenStorage(storage storage.Storage) (*KVStore, error) {
+ // Perform initial storage clean
+ err := storage.Clean(context.Background())
+ if err != nil {
+ return nil, err
+ }
+
+ // Return new KVStore
+ return &KVStore{
+ mu: mutexes.NewMap(-1, -1),
+ st: storage,
+ }, nil
+}
+
+// RLock acquires a read-lock on supplied key, returning unlock function.
+func (st *KVStore) RLock(key string) (runlock func()) {
+ return st.mu.RLock(key)
+}
+
+// Lock acquires a write-lock on supplied key, returning unlock function.
+func (st *KVStore) Lock(key string) (unlock func()) {
+ return st.mu.Lock(key)
+}
+
+// Get fetches the bytes for supplied key in the store.
+func (st *KVStore) Get(ctx context.Context, key string) ([]byte, error) {
+ return st.get(st.RLock, ctx, key)
+}
+
+// get performs the underlying logic for KVStore.Get(), using supplied read lock func to allow use with states.
+func (st *KVStore) get(rlock func(string) func(), ctx context.Context, key string) ([]byte, error) {
+ // Acquire read lock for key
+ runlock := rlock(key)
+ defer runlock()
+
+ // Read file bytes from storage
+ return st.st.ReadBytes(ctx, key)
+}
+
+// GetStream fetches a ReadCloser for the bytes at the supplied key in the store.
+func (st *KVStore) GetStream(ctx context.Context, key string) (io.ReadCloser, error) {
+ return st.getStream(st.RLock, ctx, key)
+}
+
+// getStream performs the underlying logic for KVStore.GetStream(), using supplied read lock func to allow use with states.
+func (st *KVStore) getStream(rlock func(string) func(), ctx context.Context, key string) (io.ReadCloser, error) {
+ // Acquire read lock for key
+ runlock := rlock(key)
+
+ // Attempt to open stream for read
+ rd, err := st.st.ReadStream(ctx, key)
+ if err != nil {
+ runlock()
+ return nil, err
+ }
+
+ // Wrap readcloser in our own callback closer
+ return util.ReadCloserWithCallback(rd, runlock), nil
+}
+
+// Put places the bytes at the supplied key in the store.
+func (st *KVStore) Put(ctx context.Context, key string, value []byte) error {
+ return st.put(st.Lock, ctx, key, value)
+}
+
+// put performs the underlying logic for KVStore.Put(), using supplied lock func to allow use with states.
+func (st *KVStore) put(lock func(string) func(), ctx context.Context, key string, value []byte) error {
+ // Acquire write lock for key
+ unlock := lock(key)
+ defer unlock()
+
+ // Write file bytes to storage
+ return st.st.WriteBytes(ctx, key, value)
+}
+
+// PutStream writes the bytes from the supplied Reader at the supplied key in the store.
+func (st *KVStore) PutStream(ctx context.Context, key string, r io.Reader) error {
+ return st.putStream(st.Lock, ctx, key, r)
+}
+
+// putStream performs the underlying logic for KVStore.PutStream(), using supplied lock func to allow use with states.
+func (st *KVStore) putStream(lock func(string) func(), ctx context.Context, key string, r io.Reader) error {
+ // Acquire write lock for key
+ unlock := lock(key)
+ defer unlock()
+
+ // Write file stream to storage
+ return st.st.WriteStream(ctx, key, r)
+}
+
+// Has checks whether the supplied key exists in the store.
+func (st *KVStore) Has(ctx context.Context, key string) (bool, error) {
+ return st.has(st.RLock, ctx, key)
+}
+
+// has performs the underlying logic for KVStore.Has(), using supplied read lock func to allow use with states.
+func (st *KVStore) has(rlock func(string) func(), ctx context.Context, key string) (bool, error) {
+ // Acquire read lock for key
+ runlock := rlock(key)
+ defer runlock()
+
+ // Stat file in storage
+ return st.st.Stat(ctx, key)
+}
+
+// Delete removes value at supplied key from the store.
+func (st *KVStore) Delete(ctx context.Context, key string) error {
+ return st.delete(st.Lock, ctx, key)
+}
+
+// delete performs the underlying logic for KVStore.Delete(), using supplied lock func to allow use with states.
+func (st *KVStore) delete(lock func(string) func(), ctx context.Context, key string) error {
+ // Acquire write lock for key
+ unlock := lock(key)
+ defer unlock()
+
+ // Remove file from storage
+ return st.st.Remove(ctx, key)
+}
+
+// Iterator returns an Iterator for key-value pairs in the store, using supplied match function
+func (st *KVStore) Iterator(ctx context.Context, matchFn func(string) bool) (*Iterator, error) {
+ if matchFn == nil {
+ // By default simply match all keys
+ matchFn = func(string) bool { return true }
+ }
+
+ // Get store read lock state
+ state := st.mu.RLockMap()
+
+ var entries []storage.Entry
+
+ walkFn := func(ctx context.Context, entry storage.Entry) error {
+ // Ignore unmatched entries
+ if !matchFn(entry.Key) {
+ return nil
+ }
+
+ // Add to entries
+ entries = append(entries, entry)
+ return nil
+ }
+
+ // Collate keys in storage with our walk function
+ err := st.st.WalkKeys(ctx, storage.WalkKeysOptions{WalkFn: walkFn})
+ if err != nil {
+ state.UnlockMap()
+ return nil, err
+ }
+
+ // Return new iterator
+ return &Iterator{
+ store: st,
+ state: state,
+ entries: entries,
+ index: -1,
+ key: "",
+ }, nil
+}
+
+// Read provides a read-only window to the store, holding it in a read-locked state until release.
+func (st *KVStore) Read() *StateRO {
+ state := st.mu.RLockMap()
+ return &StateRO{store: st, state: state}
+}
+
+// ReadFn provides a read-only window to the store, holding it in a read-locked state until fn return..
+func (st *KVStore) ReadFn(fn func(*StateRO)) {
+ // Acquire read-only state
+ state := st.Read()
+ defer state.Release()
+
+ // Pass to fn
+ fn(state)
+}
+
+// Update provides a read-write window to the store, holding it in a write-locked state until release.
+func (st *KVStore) Update() *StateRW {
+ state := st.mu.LockMap()
+ return &StateRW{store: st, state: state}
+}
+
+// UpdateFn provides a read-write window to the store, holding it in a write-locked state until fn return.
+func (st *KVStore) UpdateFn(fn func(*StateRW)) {
+ // Acquire read-write state
+ state := st.Update()
+ defer state.Release()
+
+ // Pass to fn
+ fn(state)
+}
+
+// Close will close the underlying storage, the mutex map locking (e.g. RLock(), Lock()) will continue to function.
+func (st *KVStore) Close() error {
+ return st.st.Close()
+}