Documentation
¶
Overview ¶
Package storage provides a uniform way to handle various types of centralized config storages for Tarantool.
See the github.com/tarantool/go-storage/integrity package for high-level typed storage with automatic hash and signature verification.
See the github.com/tarantool/go-storage/locker package for the distributed lock interface backed by the same drivers.
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ErrPrefixNoLeadingSlash = errors.New("storage.Prefixed: prefix must start with '/'")
ErrPrefixNoLeadingSlash is returned by Prefixed when a non-empty prefix does not start with "/". Keys are rooted at "/", so a prefix must be an absolute path segment.
var ErrPrefixTrailingSlash = errors.New("storage.Prefixed: prefix must not end with '/'")
ErrPrefixTrailingSlash is returned by Prefixed when the prefix ends with "/". The codec namer prepends "/" to every key, so a trailing slash here would produce keys like "/foo//objectLocation/name".
Functions ¶
Types ¶
type Option ¶
type Option func(*storageOptions)
Option is a function that configures storage options.
func WithRetry ¶
func WithRetry() Option
WithRetry configures retry behavior for failed operations. This is a dummy option for demonstration purposes.
func WithTimeout ¶
func WithTimeout() Option
WithTimeout configures a default timeout for storage operations. This is a dummy option for demonstration purposes.
type Prefixer ¶ added in v1.6.0
type Prefixer interface {
Prefix() []byte
}
Prefixer is implemented by storages that carry a key prefix applied by Prefixed. The base storage does not implement it. Callers that want to discover the prefix without depending on the unexported wrapper type should use StoragePrefix.
type RangeOption ¶
type RangeOption func(*rangeOptions)
RangeOption is a function that configures range operation options.
func WithLimit ¶
func WithLimit(limit int) RangeOption
WithLimit configures a range operation to limit the number of results returned.
func WithPrefix ¶
func WithPrefix(prefix string) RangeOption
WithPrefix configures a range operation to filter keys by the specified prefix.
type Storage ¶
type Storage interface {
// Watch streams changes for a specific key or prefix.
// Options:
// - WithPrefix: watch for changes on keys with the specified prefix
Watch(ctx context.Context, key []byte, opts ...watch.Option) <-chan watch.Event
// Tx creates a new transaction.
// The context manages timeouts and cancellation for the transaction.
Tx(ctx context.Context) txPkg.Tx
// TxFactory returns a tx.Factory bound to this Storage. Use it to hand
// "begin transaction" capability to components that do not need the full
// Storage interface.
TxFactory() txPkg.Factory
// Range queries a range of keys with optional filtering.
// Options:
// - WithPrefix: filter keys by prefix
// - WithLimit: limit the number of results returned
Range(ctx context.Context, opts ...RangeOption) ([]kv.KeyValue, error)
// NewLocker creates a Locker for name. ctx is the locker-lifetime context:
// cancelling it stops any keepalive goroutine and aborts a blocking Lock.
NewLocker(ctx context.Context, name string, opts ...locker.Option) (locker.Locker, error)
// LockerFactory returns a locker.Factory bound to this Storage. Use it to
// hand "create a lock" capability to components that do not need the full
// Storage interface.
LockerFactory() locker.Factory
}
Storage is the main interface for key-value storage operations. It provides methods for watching changes, transaction management, and range queries.
func NewStorage ¶
NewStorage creates a new Storage instance with the specified driver. Optional StorageOption parameters can be provided to configure the storage.
func Prefixed ¶ added in v1.3.0
Prefixed returns a Storage that scopes every operation, predicate, range, and watch under prefix. Keys returned to the caller (RequestResponse.Values, kv.KeyValue, watch.Event.Prefix) have prefix stripped — callers never see absolute keys.
Composition: Prefixed("/a", Prefixed("/b", base)) ≡ Prefixed("/a/b", base). Nested wrappers are flattened at construction so the outer prefix is the leftmost segment.
An empty prefix yields a transparent wrapper. A non-empty prefix must start with "/" (returns ErrPrefixNoLeadingSlash) and must not end with "/" (returns ErrPrefixTrailingSlash) — the codec namer prepends "/" to every key, so a trailing slash here would produce keys like "/foo//objectLocation/name". Interior "/" separators are allowed (e.g. "/foo/bar").
Example ¶
ExamplePrefixed shows how Prefixed scopes every Tx operation under a namespace. Callers work with logical keys ("/cfg/version"); the underlying driver sees absolute keys ("/ns/cfg/version"), and the namespace is stripped from any keys returned to the caller.
package main
import (
"context"
"fmt"
"log"
storage "github.com/tarantool/go-storage"
"github.com/tarantool/go-storage/driver/dummy"
"github.com/tarantool/go-storage/operation"
)
func main() {
ctx := context.Background()
base := storage.NewStorage(dummy.New())
scoped, err := storage.Prefixed("/ns", base)
if err != nil {
log.Fatalf("prefix: %v", err)
}
_, err = scoped.Tx(ctx).Then(
operation.Put([]byte("/cfg/version"), []byte("1.0.0")),
).Commit()
if err != nil {
log.Fatalf("put: %v", err)
}
resp, err := scoped.Tx(ctx).Then(
operation.Get([]byte("/cfg/version")),
).Commit()
if err != nil {
log.Fatalf("get: %v", err)
}
got := resp.Results[0].Values[0]
fmt.Printf("logical key: %s, value: %s\n", got.Key, got.Value)
}
Output: logical key: /cfg/version, value: 1.0.0
Example (Locker) ¶
ExamplePrefixed_locker shows that Prefixed scopes lock names too: Prefixed("/ns", base).NewLocker(ctx, "/lock") asks the inner Storage for a lock named "/ns/lock", so two wrappers under different namespaces cannot collide on the same logical name.
package main
import (
"context"
"fmt"
"log"
storage "github.com/tarantool/go-storage"
"github.com/tarantool/go-storage/driver/dummy"
)
func main() {
ctx := context.Background()
base := storage.NewStorage(dummy.New())
nsA, err := storage.Prefixed("/a", base)
if err != nil {
log.Fatalf("prefix /a: %v", err)
}
nsB, err := storage.Prefixed("/b", base)
if err != nil {
log.Fatalf("prefix /b: %v", err)
}
lockA, err := nsA.NewLocker(ctx, "/lock")
if err != nil {
log.Fatalf("new-locker A: %v", err)
}
lockB, err := nsB.NewLocker(ctx, "/lock")
if err != nil {
log.Fatalf("new-locker B: %v", err)
}
err = lockA.Lock(ctx)
if err != nil {
log.Fatalf("lock A: %v", err)
}
// Different namespaces, so B can acquire even while A is held.
err = lockB.Lock(ctx)
if err != nil {
log.Fatalf("lock B: %v", err)
}
fmt.Println("A:", lockA.Key())
fmt.Println("B:", lockB.Key())
err = lockB.Unlock(ctx)
if err != nil {
log.Fatalf("unlock B: %v", err)
}
err = lockA.Unlock(ctx)
if err != nil {
log.Fatalf("unlock A: %v", err)
}
}
Output: A: /a/lock B: /b/lock
Example (Nested) ¶
ExamplePrefixed_nested shows that nested wrappers are flattened at construction time: Prefixed("/a", Prefixed("/b", base)) is equivalent to Prefixed("/a/b", base). This is observable by reading the same key through the un-wrapped base storage.
package main
import (
"context"
"fmt"
"log"
storage "github.com/tarantool/go-storage"
"github.com/tarantool/go-storage/driver/dummy"
"github.com/tarantool/go-storage/operation"
)
func main() {
ctx := context.Background()
base := storage.NewStorage(dummy.New())
inner, err := storage.Prefixed("/b", base)
if err != nil {
log.Fatalf("prefix /b: %v", err)
}
outer, err := storage.Prefixed("/a", inner)
if err != nil {
log.Fatalf("prefix /a: %v", err)
}
_, err = outer.Tx(ctx).Then(
operation.Put([]byte("/k"), []byte("v")),
).Commit()
if err != nil {
log.Fatalf("put: %v", err)
}
// Read directly from base to see the absolute key the driver actually stored.
resp, err := base.Tx(ctx).Then(
operation.Get([]byte("/a/b/k")),
).Commit()
if err != nil {
log.Fatalf("get: %v", err)
}
got := resp.Results[0].Values[0]
fmt.Printf("absolute: %s = %s\n", got.Key, got.Value)
}
Output: absolute: /a/b/k = v
Example (Predicates) ¶
ExamplePrefixed_predicates shows that predicates are also rewritten under the configured prefix, so conditional transactions stay scoped.
package main
import (
"context"
"fmt"
"log"
storage "github.com/tarantool/go-storage"
"github.com/tarantool/go-storage/driver/dummy"
"github.com/tarantool/go-storage/operation"
"github.com/tarantool/go-storage/predicate"
)
func main() {
ctx := context.Background()
scoped, err := storage.Prefixed("/ns", storage.NewStorage(dummy.New()))
if err != nil {
log.Fatalf("prefix: %v", err)
}
_, err = scoped.Tx(ctx).Then(
operation.Put([]byte("/feature"), []byte("on")),
).Commit()
if err != nil {
log.Fatalf("seed: %v", err)
}
resp, err := scoped.Tx(ctx).
If(predicate.ValueEqual([]byte("/feature"), "on")).
Then(operation.Put([]byte("/feature"), []byte("off"))).
Else(operation.Put([]byte("/feature"), []byte("unchanged"))).
Commit()
if err != nil {
log.Fatalf("commit: %v", err)
}
fmt.Println("succeeded:", resp.Succeeded)
}
Output: succeeded: true
Directories
¶
| Path | Synopsis |
|---|---|
|
Package connect provides utilities for connecting to storage backends.
|
Package connect provides utilities for connecting to storage backends. |
|
Package crypto implements verification interfaces.
|
Package crypto implements verification interfaces. |
|
Package driver defines the interface for storage driver implementations.
|
Package driver defines the interface for storage driver implementations. |
|
dummy
Package dummy provides a base in-memory implementation of the storage driver interface for demonstration and tests.
|
Package dummy provides a base in-memory implementation of the storage driver interface for demonstration and tests. |
|
etcd
Package etcd provides an etcd implementation of the storage driver interface.
|
Package etcd provides an etcd implementation of the storage driver interface. |
|
tcs
Package tcs provides a Tarantool config storage driver implementation.
|
Package tcs provides a Tarantool config storage driver implementation. |
|
Package hasher provides types and interfaces for hash calculating.
|
Package hasher provides types and interfaces for hash calculating. |
|
Package integrity provides typed storage with built-in data integrity protection.
|
Package integrity provides typed storage with built-in data integrity protection. |
|
internal
|
|
|
mocks
Package mocks provides generated mock implementations for testing.
|
Package mocks provides generated mock implementations for testing. |
|
testing
Package testing provides a mock implementation of the tarantool.Doer and other interfaces.
|
Package testing provides a mock implementation of the tarantool.Doer and other interfaces. |
|
Package kv provides key-value data structures and interfaces for storage operations.
|
Package kv provides key-value data structures and interfaces for storage operations. |
|
Package locker provides the Locker interface and shared types for distributed lock drivers.
|
Package locker provides the Locker interface and shared types for distributed lock drivers. |
|
Package namer represent interface to templates creation.
|
Package namer represent interface to templates creation. |
|
Package operation provides types and interfaces for storage operations.
|
Package operation provides types and interfaces for storage operations. |
|
Package predicate provides types and interfaces for conditional operations.
|
Package predicate provides types and interfaces for conditional operations. |
|
test_helpers
|
|
|
etcd
Package etcd provides a reusable, embedded single-node etcd cluster for integration tests.
|
Package etcd provides a reusable, embedded single-node etcd cluster for integration tests. |
|
Package tx provides transactional interfaces for atomic storage operations.
|
Package tx provides transactional interfaces for atomic storage operations. |
|
Package watch provides change notification functionality for storage operations.
|
Package watch provides change notification functionality for storage operations. |