Documentation
¶
Overview ¶
non-sucking database
speicher v2 uses a State object for lock management to prevent deadlocks. Each goroutine should create its own State and use it for all lock operations.
Example:
package main
import (
"fmt"
"github.com/bloodmagesoftware/speicher/v2"
)
type Foo struct {
Bar string
Baz int
}
func main() {
foo, err := speicher.LoadMap[*Foo]("./data/foo.json")
if err != nil {
panic(err)
}
func() {
s := speicher.NewState()
s.Lock(foo) // use Lock to get write access
defer s.Unlock(foo) // use Unlock to release write access
foo.Set("a", &Foo{"aaa", 42})
foo.Set("b", &Foo{"abc", 69})
}()
func() {
s := speicher.NewState()
s.RLock(foo) // use RLock to get read access
defer s.RUnlock(foo) // use RUnlock to release read access
for key, value := range foo.Iterate { // use the Iterate method to create an iterator
fmt.Printf("%s => (%s, %d)\n", key, value.Bar, value.Baz)
}
}()
func() {
s := speicher.NewState()
s.Lock(foo)
defer s.Unlock(foo)
a, ok := foo.Get("a")
if ok {
a.Baz *= 10 // a.Baz only gets modified because the store uses a pointer
}
}()
func() {
s := speicher.NewState()
s.RLock(foo)
defer s.RUnlock(foo)
a, ok := foo.Get("a")
if ok {
fmt.Printf("changed a => (%s, %d)\n", a.Bar, a.Baz)
}
}()
}
Index ¶
- func Err() <-chan error
- func Read[S Store, R any](store S, f func(s S) R) R
- func ReadE[S Store, R any](store S, f func(s S) (R, error)) (R, error)
- func Write[S Store, R any](store S, f func(s S) R) R
- func WriteE[S Store, R any](store S, f func(s S) (R, error)) (R, error)
- type List
- type Map
- type MapRangeEl
- type State
- type Store
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Err ¶
func Err() <-chan error
Err returns the error channel used when saving the data stores to disk.
func Read ¶
Read acquires a read lock on the store, executes f, then releases the lock. Returns the result of f.
func ReadE ¶
ReadE acquires a read lock on the store, executes f, then releases the lock. Returns the result of f and any error.
Types ¶
type List ¶
type List[T any] interface { // Get returns the value at a given index of the List and a bool that indicates whether the index exists or not. // If no element is found, the bool result will be false. // Requires at least a read lock. Get(index int) (T, bool) // Find traverses the List and returns the first element that satisfies the provided predicate function. // If no element is found, the bool result will be false. // Requires at least a read lock. Find(func(T) bool) (value T, found bool) // FindAll returns all elements in the List that satisfy the provided predicate function. // If no elements match, it returns an empty slice. // Requires at least a read lock. FindAll(func(T) bool) (values []T) // Append adds the provided value to the end of the List. // Requires a write lock. Append(value T) // AppendUnique adds the provided value to the List only if no existing element is equal to it, // based on the supplied equality function. It returns true if the value was added, // and false otherwise. // Requires a write lock. AppendUnique(value T, equal func(a, b T) bool) bool // Set assigns the provided value to the element at the specified index. // If the index is out of bounds, it returns an error. // Requires a write lock. Set(index int, value T) error // Overwrite replaces the entire List with the data provided in the slice. // Requires a write lock. Overwrite([]T) // Len returns the number of elements currently in the List. // Requires at least a read lock. Len() int // Range returns a read-only channel through which the elements of the List can be iterated. // It also returns a cancel function to stop the iteration process if needed. // Requires at least a read lock. // // Deprecated: use Iterate if you need to iterate over the entire data store. Range() (<-chan T, func()) // Iterate iterates over the List and calls the provided function for each element. // Requires at least a read lock. Iterate(yield func(v T) bool) // Save persists the current state of the List to its underlying data store. // It returns an error if the operation fails. // This method acquires its own read lock internally. Save() error // contains filtered or unexported methods }
List is a thread-safe list data store interface that provides basic CRUD operations, predicate-based search, and iteration functionality.
All operations require appropriate locking via a State object:
s := speicher.NewState() s.Lock(myList) defer s.Unlock(myList) myList.Append(value)
type Map ¶
type Map[T any] interface { // Get retrieves an element associated with the given key. // It returns the value and a boolean indicating whether the key exists. // Requires at least a read lock. Get(key string) (T, bool) // Find searches for an element that satisfies the given predicate. // It returns the found value and a boolean indicating if a match was found. // Requires at least a read lock. Find(func(T) bool) (value T, found bool) // FindAll retrieves all elements that satisfy the given predicate. // It returns a slice containing all matching elements. // Requires at least a read lock. FindAll(func(T) bool) (values []T) // Has checks if an element with the given key exists in the data store. // It returns true if the key exists. // Requires at least a read lock. Has(key string) bool // Set adds or updates the element associated with the given key. // If the key already exists, its value is overwritten. // Requires a write lock. Set(key string, value T) // Delete removes the element associated with the given key. // Requires a write lock. Delete(key string) // Overwrite replaces the entire data store with the provided map. // Requires a write lock. Overwrite(map[string]T) // RangeKV returns a read-only channel that emits key-value pair elements // (as MapRangeEl) from the data store, along with a cancellation function // to terminate the iteration when desired. // Requires at least a read lock. // // Deprecated: use Iterate if you need to iterate over the entire data store. RangeKV() (<-chan MapRangeEl[T], func()) // RangeV returns a read-only channel that emits only the values stored in the // data store, along with a cancellation function to terminate the iteration. // Requires at least a read lock. // // Deprecated: use Iterate if you need to iterate over the entire data store. RangeV() (<-chan T, func()) // Iterate iterates over the Map and calls the provided function for each element. // Requires at least a read lock. Iterate(yield func(key string, value T) bool) // Save persists the current state of the data store. // It returns an error if the save operation fails. // This method acquires its own read lock internally. Save() error // contains filtered or unexported methods }
Map is a thread-safe key-value data store interface that provides basic CRUD operations, predicate-based search, and iteration functionality.
All operations require appropriate locking via a State object:
s := speicher.NewState()
s.Lock(myMap)
defer s.Unlock(myMap)
myMap.Set("key", value)
type MapRangeEl ¶
MapRangeEl represents a key-value pair element emitted by the Map's RangeKV method.
type State ¶
type State struct {
// contains filtered or unexported fields
}
State manages lock state for multiple stores within a single goroutine. It tracks recursive read and write locks and handles lock upgrading.
A State must not be shared across goroutines. Each goroutine that needs to access stores should create its own State via NewState().
Example usage:
s := speicher.NewState()
s.Lock(myMap)
defer s.Unlock(myMap)
myMap.Set("key", value)
func NewState ¶
func NewState() *State
NewState creates a new State for managing locks. Each goroutine should have its own State instance.
func (*State) HasReadLock ¶
HasReadLock returns true if the State holds at least one read lock on the store.
func (*State) HasWriteLock ¶
HasWriteLock returns true if the State holds at least one write lock on the store.
func (*State) Lock ¶
func (s *State) Lock(store lockable)
Lock acquires a write lock on the store. If the State already holds a write lock on this store, the lock count is incremented. If the State holds read locks, they are upgraded to a write lock.
Multiple calls to Lock must be balanced with equal calls to Unlock.
func (*State) RLock ¶
func (s *State) RLock(store lockable)
RLock acquires a read lock on the store. If the State already holds a write lock, the read is implicitly satisfied without additional mutex operations. If the State already holds read locks, the count is incremented.
Multiple calls to RLock must be balanced with equal calls to RUnlock.
func (*State) RUnlock ¶
func (s *State) RUnlock(store lockable)
RUnlock releases a read lock on the store. The actual mutex is only released when all read and write locks are released.
Panics if called without a matching RLock call.
func (*State) Unlock ¶
func (s *State) Unlock(store lockable)
Unlock releases a write lock on the store. If there are pending read locks from before the write lock was acquired, the mutex downgrades to a read lock.
When the write lock is fully released, automatic save is triggered for stores that support persistence.
Panics if called without a matching Lock call.
