Documentation
¶
Overview ¶
Package qortoo is the Go binding of the Qortoo CRDT SDK (qortoo-rs).
It statically links the qortoo-ffi native library. This package carries no default include or library search path: point CGO_CFLAGS at the SDK's include directory and CGO_LDFLAGS at the directory holding libqortoo_ffi.a before building or testing, e.g.
export CGO_CFLAGS="-I/path/to/native-sdk/include" export CGO_LDFLAGS="-L/path/to/native-sdk/lib" go test ./...
Inside this repository, `make test` stages a matching SDK from the qortoo-rs checkout selected by QORTOO_RS_DIR.
A Client provides two datatypes: Counter, a conflict-free counter, and Variable, a last-writer-wins holder of a single JSON value. Variable values cross the boundary as encoding/json output and are stored verbatim, so they must be JSON-representable and every language reading the same variable must agree on the schema.
Every object returned by this package owns a native handle and must be released with its Close method. As a backstop, an unreachable wrapper frees its native handle via a runtime.AddCleanup cleanup, but cleanups are not guaranteed to run — treat Close as the contract, not the cleanup. A datatype keeps its Client reachable, so an otherwise unreferenced Client is not cleaned up while any of its datatypes is still alive.
Close must not be called concurrently with other methods on the same object; all other methods are safe for concurrent use.
Handler callbacks (Handler.OnStateChange / Handler.OnError) are invoked on Qortoo-owned tokio worker threads, asynchronously to the triggering operation. They must be safe for concurrent use and must not block for long. A blocking handler also delays Client.Close, which shuts the worker threads down, and ShutdownObservability, which flushes the exporters.
The Rust core emits tracing spans and metrics but installs no subscriber, recorder, or exporter on its own: call InitObservability once at startup to choose where they go, and the *Context methods (Counter.SyncContext, Variable.TransactionContext, and their siblings) to keep them in the trace of the calling span.
Index ¶
- Constants
- func InitObservability(opts ObservabilityOptions) error
- func ShutdownObservability(ctx context.Context) error
- type Client
- func (c *Client) Alias() string
- func (c *Client) Close()
- func (c *Client) Collection() string
- func (c *Client) CreateCounter(key string, opts *DatatypeOptions) (*Counter, error)
- func (c *Client) CreateVariable(key string, opts *DatatypeOptions) (*Variable, error)
- func (c *Client) SubscribeCounter(key string, opts *DatatypeOptions) (*Counter, error)
- func (c *Client) SubscribeOrCreateCounter(key string, opts *DatatypeOptions) (*Counter, error)
- func (c *Client) SubscribeOrCreateVariable(key string, opts *DatatypeOptions) (*Variable, error)
- func (c *Client) SubscribeVariable(key string, opts *DatatypeOptions) (*Variable, error)
- func (c *Client) UnsubscribeDatatype(key string) error
- type ClientOption
- type Counter
- func (d *Counter) ClientVersion() uint64
- func (c *Counter) Close()
- func (c *Counter) Increase() (int64, error)
- func (c *Counter) IncreaseBy(delta int64) (int64, error)
- func (d *Counter) Key() string
- func (d *Counter) ServerVersion() uint64
- func (d *Counter) SetHandler(priority uint, h *Handler)
- func (d *Counter) State() DatatypeState
- func (d *Counter) Sync() error
- func (d *Counter) SyncContext(ctx context.Context) error
- func (d *Counter) SyncedClientVersion() uint64
- func (c *Counter) Transaction(tag string, fn func(tx *Counter) error) error
- func (c *Counter) TransactionContext(ctx context.Context, tag string, fn func(tx *Counter) error) error
- func (d *Counter) Type() DataType
- func (d *Counter) UnsetHandler(priority uint) bool
- func (d *Counter) Unsubscribe() error
- func (c *Counter) Value() int64
- type DataType
- type DatatypeOptions
- type DatatypeState
- type Error
- type Handler
- type LocalConnectivity
- type LogFormat
- type ObservabilityOptions
- type Variable
- func (d *Variable) ClientVersion() uint64
- func (v *Variable) Close()
- func (v *Variable) Get(dst any) error
- func (d *Variable) Key() string
- func (d *Variable) ServerVersion() uint64
- func (v *Variable) Set(value any) (any, error)
- func (d *Variable) SetHandler(priority uint, h *Handler)
- func (d *Variable) State() DatatypeState
- func (d *Variable) Sync() error
- func (d *Variable) SyncContext(ctx context.Context) error
- func (d *Variable) SyncedClientVersion() uint64
- func (v *Variable) Transaction(tag string, fn func(tx *Variable) error) error
- func (v *Variable) TransactionContext(ctx context.Context, tag string, fn func(tx *Variable) error) error
- func (d *Variable) Type() DataType
- func (d *Variable) UnsetHandler(priority uint) bool
- func (d *Variable) Unsubscribe() error
Constants ¶
const ( // ClientError (100–) ErrCodeInvalidCollectionName int32 = 100 ErrCodeFailedToSubscribeOrCreateDatatype int32 = 101 // DatatypeError (200–) ErrCodeTransactionFailed int32 = 201 ErrCodeInternal int32 = 202 ErrCodeDisallowed int32 = 205 ErrCodeNotWritable int32 = 206 ErrCodeReadonlyViolation int32 = 207 ErrCodeSyncFailed int32 = 210 ErrCodePushBufferExceededMaxMemSize int32 = 211 ErrCodeServerRejected int32 = 213 ErrCodeValueConversion int32 = 214 // Observability (900–), only from InitObservability/ShutdownObservability ErrCodeObservabilityAlreadyInitialized int32 = 900 ErrCodeObservabilityShutDown int32 = 901 ErrCodeObservabilitySubscriber int32 = 902 ErrCodeObservabilityExporter int32 = 903 ErrCodeObservabilityRecorder int32 = 904 ErrCodeObservabilityInvalidConfig int32 = 905 ErrCodeObservabilityPartiallyInitialized int32 = 906 // FFI boundary (see qortoo-ffi) ErrCodeInvalidArgument int32 = 998 ErrCodeInternalFFI int32 = 999 )
Error codes mirroring the #[repr(i32)] discriminants of the Rust error enums.
Variables ¶
This section is empty.
Functions ¶
func InitObservability ¶
func InitObservability(opts ObservabilityOptions) error
InitObservability installs the SDK's logging, tracing, and metrics pipelines.
Call it once per process, before creating clients. It returns an error when it is called twice, when it is called after ShutdownObservability, when another library already installed a Rust global subscriber or metrics recorder, when an exporter cannot start, or when the options are invalid — never silently, because losing configured telemetry is worse than failing loudly at startup.
func ShutdownObservability ¶
ShutdownObservability flushes pending telemetry and stops the exporters, using the deadline remaining in ctx as its shutdown budget (5s when it has none). A context already canceled or expired is returned before crossing the FFI boundary. It is a no-op when nothing was initialized, so deferring it is always safe.
This is terminal: a later InitObservability fails instead of pretending to restore the pipelines. The recommended order at exit is Counter.Close → Client.Close → ShutdownObservability → the application's own telemetry shutdown.
Types ¶
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client is the entry point of the SDK, scoped by a collection name and alias.
func NewClient ¶
func NewClient(collection, alias string, opts ...ClientOption) (*Client, error)
NewClient builds a client. Without a connectivity option it uses the SDK-default no-op backend.
func (*Client) Close ¶
func (c *Client) Close()
Close releases the client and shuts down its internal runtime. Counters created from it remain safe to use until their own Close, but can no longer synchronize. Close must not be called concurrently with other methods on the same object.
func (*Client) Collection ¶
Collection returns the collection name of this client.
func (*Client) CreateCounter ¶
func (c *Client) CreateCounter(key string, opts *DatatypeOptions) (*Counter, error)
CreateCounter builds a counter in StateCreating (writable).
func (*Client) CreateVariable ¶
func (c *Client) CreateVariable(key string, opts *DatatypeOptions) (*Variable, error)
CreateVariable builds a variable in StateCreating (writable).
func (*Client) SubscribeCounter ¶
func (c *Client) SubscribeCounter(key string, opts *DatatypeOptions) (*Counter, error)
SubscribeCounter builds a counter in StateSubscribing (read-only until synced).
func (*Client) SubscribeOrCreateCounter ¶
func (c *Client) SubscribeOrCreateCounter(key string, opts *DatatypeOptions) (*Counter, error)
SubscribeOrCreateCounter builds a counter in StateSubscribingOrCreating (writable).
func (*Client) SubscribeOrCreateVariable ¶
func (c *Client) SubscribeOrCreateVariable(key string, opts *DatatypeOptions) (*Variable, error)
SubscribeOrCreateVariable builds a variable in StateSubscribingOrCreating (writable).
func (*Client) SubscribeVariable ¶
func (c *Client) SubscribeVariable(key string, opts *DatatypeOptions) (*Variable, error)
SubscribeVariable builds a variable in StateSubscribing (read-only until synced).
func (*Client) UnsubscribeDatatype ¶
UnsubscribeDatatype marks the datatype identified by key as unsubscribing. With manual connectivity, a following Counter.Sync drives it to StateDisabled.
type ClientOption ¶
type ClientOption func(*clientConfig)
ClientOption configures NewClient.
func WithLocalConnectivity ¶
func WithLocalConnectivity(conn *LocalConnectivity) ClientOption
WithLocalConnectivity attaches a shared in-memory backend to the client.
type Counter ¶
type Counter struct {
// contains filtered or unexported fields
}
Counter is a conflict-free counter datatype.
func (*Counter) ClientVersion ¶
func (d *Counter) ClientVersion() uint64
ClientVersion returns the number of local operations.
func (*Counter) Close ¶
func (c *Counter) Close()
Close releases this handle. The underlying datatype stays registered in its client. No-op for transaction-scoped handles. Close must not be called concurrently with other methods on the same object.
func (*Counter) IncreaseBy ¶
IncreaseBy adds delta (which may be negative) and returns the new value.
func (*Counter) ServerVersion ¶
func (d *Counter) ServerVersion() uint64
ServerVersion returns the server-side version (0 before the first sync).
func (*Counter) SetHandler ¶
SetHandler registers (or replaces) a handler at the given priority (lower priority runs first).
func (*Counter) State ¶
func (d *Counter) State() DatatypeState
State returns the current lifecycle state.
func (*Counter) Sync ¶
func (d *Counter) Sync() error
Sync performs a blocking push/pull with the connectivity backend. Use SyncContext to keep the sync inside the trace of a calling span.
func (*Counter) SyncContext ¶
SyncContext is Sync continuing the trace of ctx.
The Rust spans of this sync — including the push/pull that runs on a worker thread and the handler callbacks it dispatches — become children of the span in ctx. Without a span in ctx it behaves exactly like Sync. Cancellation of ctx is not honoured: the underlying sync is a blocking call.
func (*Counter) SyncedClientVersion ¶
func (d *Counter) SyncedClientVersion() uint64
SyncedClientVersion returns the last client version acknowledged by the server.
func (*Counter) Transaction ¶
Transaction executes fn atomically: if fn returns an error (or panics), every operation performed through tx is rolled back. fn runs inline on the calling goroutine; tx is only valid during the call. Use TransactionContext to keep the commit inside the trace of a calling span.
func (*Counter) TransactionContext ¶
func (c *Counter) TransactionContext(ctx context.Context, tag string, fn func(tx *Counter) error) error
TransactionContext is Transaction continuing the trace of ctx: the commit and any sync it triggers become children of the span in ctx. Without a span in ctx it behaves exactly like Transaction. Cancellation of ctx is not honoured — fn runs inline on the calling goroutine.
func (*Counter) Type ¶
func (d *Counter) Type() DataType
Type returns the datatype kind, which is fixed by the concrete datatype (TypeCounter for a Counter).
func (*Counter) UnsetHandler ¶
UnsetHandler removes the handler at the given priority. Returns true if one was removed.
func (*Counter) Unsubscribe ¶
func (d *Counter) Unsubscribe() error
Unsubscribe marks this datatype as unsubscribing (see Client.UnsubscribeDatatype).
type DataType ¶
type DataType int32
DataType identifies the kind of CRDT datatype (mirrors qortoo::DataType).
type DatatypeOptions ¶
type DatatypeOptions struct {
// Readonly rejects all local writes regardless of state.
Readonly bool
// MaxPushBufferSize in bytes; 0 keeps the SDK default (clamped by the SDK).
MaxPushBufferSize uint64
// Handler registered at build time (needed to observe the first auto-sync
// with realtime connectivity). HandlerPriority orders multiple handlers.
Handler *Handler
HandlerPriority uint
}
DatatypeOptions configures datatype construction. The zero value (or nil pointer) selects the SDK defaults.
type DatatypeState ¶
type DatatypeState int32
DatatypeState mirrors qortoo::DatatypeState (lifecycle and write access).
const ( StateCreating DatatypeState = 0 StateSubscribing DatatypeState = 1 StateSubscribingOrCreating DatatypeState = 2 StateSubscribed DatatypeState = 3 StateUnsubscribing DatatypeState = 4 StateDeleting DatatypeState = 5 StateDisabled DatatypeState = 6 )
func (DatatypeState) String ¶
func (s DatatypeState) String() string
type Error ¶
Error is an error surfaced from the Qortoo SDK across the FFI boundary.
Two errors with the same Code represent the same error variant; Msg is informational only (matching the variant-only equality of the Rust enums).
type Handler ¶
type Handler struct {
// OnStateChange is called as (oldState, newState) after a lifecycle transition.
OnStateChange func(oldState, newState DatatypeState)
// OnError is called with errors surfaced from the sync path.
OnError func(err *Error)
}
Handler receives datatype notifications. Both fields are optional. Callbacks run on Qortoo-owned worker threads (see package docs).
type LocalConnectivity ¶
type LocalConnectivity struct {
// contains filtered or unexported fields
}
LocalConnectivity is an in-memory synchronization backend for testing and development, mirroring the Rust core type of the same name. Share one instance between clients to synchronize them.
func NewLocalConnectivity ¶
func NewLocalConnectivity() *LocalConnectivity
NewLocalConnectivity creates an in-memory backend in realtime mode.
func (*LocalConnectivity) Close ¶
func (l *LocalConnectivity) Close()
Close releases the native handle. Clients built with this connectivity keep their own reference and stay functional. Close must not be called concurrently with other methods on the same object.
func (*LocalConnectivity) SetRealtime ¶
func (l *LocalConnectivity) SetRealtime(realtime bool)
SetRealtime toggles between realtime (auto-sync) and manual (explicit Sync) mode.
type LogFormat ¶
type LogFormat int32
LogFormat selects the stdout log stream written by the Rust side.
const ( // LogFormatOff writes no logs. Combined with TraceEnabled=false it leaves the // Rust global subscriber untouched for other libraries in the process. LogFormatOff LogFormat = 0 // LogFormatJSON writes one structured JSON object per event. LogFormatJSON LogFormat = 1 // LogFormatText writes Qortoo's compact human-readable format. Colors are enabled // only when stdout is connected to a terminal. LogFormatText LogFormat = 2 )
type ObservabilityOptions ¶
type ObservabilityOptions struct {
// ServiceName is reported as service.name to the trace backend. Empty uses
// "qortoo"; set it to the same name the application uses for its own telemetry so
// Go and Rust spans land in one service.
ServiceName string
// LogFilter holds tracing filter directives (e.g. "qortoo=debug"). Empty falls
// back to RUST_LOG, then to "qortoo=info".
LogFilter string
// LogFormat selects the stdout log stream (default: none).
LogFormat LogFormat
// TraceEnabled exports spans over OTLP/gRPC.
TraceEnabled bool
// OTLPEndpoint overrides the trace endpoint. Empty falls back to
// OTEL_EXPORTER_OTLP_TRACES_ENDPOINT, then OTEL_EXPORTER_OTLP_ENDPOINT, then
// http://localhost:4317.
OTLPEndpoint string
// MetricsEnabled installs the metrics recorder and serves a Prometheus scrape
// endpoint.
MetricsEnabled bool
// MetricsListenAddr is the address of that endpoint. Empty uses 0.0.0.0:9000.
MetricsListenAddr string
}
ObservabilityOptions configures the SDK's telemetry pipelines. The zero value is valid and installs nothing.
type Variable ¶
type Variable struct {
// contains filtered or unexported fields
}
Variable is a last-writer-wins datatype holding a single JSON value.
A variable always holds a value; before the first Set that value is JSON null, which is indistinguishable from an explicitly stored nil. Concurrent writes are resolved by the logical timestamp of each Set, so every replica converges on the same winner regardless of the order in which the writes arrive.
Values cross the boundary as JSON produced by encoding/json, which is also what a Rust or any other binding reads back — so the value must be JSON-representable and both sides must agree on the schema. Map keys follow encoding/json: strings, integer types, and types implementing encoding.TextMarshaler are supported; other key types are not. The stored bytes are kept verbatim: they are never reparsed, reordered, or re-encoded by the SDK.
func (*Variable) ClientVersion ¶
func (d *Variable) ClientVersion() uint64
ClientVersion returns the number of local operations.
func (*Variable) Close ¶
func (v *Variable) Close()
Close releases this handle. The underlying datatype stays registered in its client. No-op for transaction-scoped handles. Close must not be called concurrently with other methods on the same object.
func (*Variable) Get ¶
Get decodes the current value into dst, which must be a non-nil pointer, exactly as encoding/json would. JSON numbers reaching an any destination are kept as json.Number.
The initial and an explicitly stored null both decode as JSON null, which leaves a non-nullable destination unchanged; decode into a pointer, an any, or a type with a custom unmarshaller to tell null apart from a stored zero.
Get reads local state only: it produces no operation and changes neither the datatype version nor the push buffer, so it is allowed in every state.
func (*Variable) ServerVersion ¶
func (d *Variable) ServerVersion() uint64
ServerVersion returns the server-side version (0 before the first sync).
func (*Variable) Set ¶
Set stores value as the new JSON value and returns the value held just before this call — nil for the first Set and for a stored JSON null.
The previous value has no destination type to decode into (a variable may hold a different shape on every Set), so it is returned dynamically, with JSON numbers kept as json.Number.
value is marshalled with encoding/json before any native call: a marshalling error is returned as-is and leaves the variable untouched.
func (*Variable) SetHandler ¶
SetHandler registers (or replaces) a handler at the given priority (lower priority runs first).
func (*Variable) State ¶
func (d *Variable) State() DatatypeState
State returns the current lifecycle state.
func (*Variable) Sync ¶
func (d *Variable) Sync() error
Sync performs a blocking push/pull with the connectivity backend. Use SyncContext to keep the sync inside the trace of a calling span.
func (*Variable) SyncContext ¶
SyncContext is Sync continuing the trace of ctx.
The Rust spans of this sync — including the push/pull that runs on a worker thread and the handler callbacks it dispatches — become children of the span in ctx. Without a span in ctx it behaves exactly like Sync. Cancellation of ctx is not honoured: the underlying sync is a blocking call.
func (*Variable) SyncedClientVersion ¶
func (d *Variable) SyncedClientVersion() uint64
SyncedClientVersion returns the last client version acknowledged by the server.
func (*Variable) Transaction ¶
Transaction executes fn atomically: if fn returns an error (or panics), every operation performed through tx is rolled back — restoring both the value and the timestamp the variable held before the transaction. fn runs inline on the calling goroutine; tx is only valid during the call. Use TransactionContext to keep the commit inside the trace of a calling span.
func (*Variable) TransactionContext ¶
func (v *Variable) TransactionContext(ctx context.Context, tag string, fn func(tx *Variable) error) error
TransactionContext is Transaction continuing the trace of ctx: the commit and any sync it triggers become children of the span in ctx. Without a span in ctx it behaves exactly like Transaction. Cancellation of ctx is not honoured — fn runs inline on the calling goroutine.
func (*Variable) Type ¶
func (d *Variable) Type() DataType
Type returns the datatype kind, which is fixed by the concrete datatype (TypeCounter for a Counter).
func (*Variable) UnsetHandler ¶
UnsetHandler removes the handler at the given priority. Returns true if one was removed.
func (*Variable) Unsubscribe ¶
func (d *Variable) Unsubscribe() error
Unsubscribe marks this datatype as unsubscribing (see Client.UnsubscribeDatatype).
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
examples
|
|
|
counter
command
Command counter is the "Create and Update a Counter" walkthrough from docs/getting-started.md, kept runnable so an API change that breaks it is caught by CI's example-build step rather than silently going stale in the doc.
|
Command counter is the "Create and Update a Counter" walkthrough from docs/getting-started.md, kept runnable so an API change that breaks it is caught by CI's example-build step rather than silently going stale in the doc. |
|
observability/log
command
Command log demonstrates Qortoo's managed stdout logging from Go.
|
Command log demonstrates Qortoo's managed stdout logging from Go. |
|
observability/metrics
command
Command metrics demonstrates Qortoo metrics exported to Prometheus.
|
Command metrics demonstrates Qortoo metrics exported to Prometheus. |
|
observability/profile
command
Command profile demonstrates CPU profile export to Pyroscope.
|
Command profile demonstrates CPU profile export to Pyroscope. |
|
observability/trace
command
Command trace demonstrates a Go OpenTelemetry span continued by Qortoo's Rust core.
|
Command trace demonstrates a Go OpenTelemetry span continued by Qortoo's Rust core. |
|
variable
command
Command variable is the "Store a JSON Value in a Variable" walkthrough from docs/getting-started.md, kept runnable so an API change that breaks it is caught by CI's example-build step rather than silently going stale in the doc.
|
Command variable is the "Store a JSON Value in a Variable" walkthrough from docs/getting-started.md, kept runnable so an API change that breaks it is caught by CI's example-build step rather than silently going stale in the doc. |