Documentation
¶
Overview ¶
Package client implements the FDB client transaction lifecycle. This is the Go equivalent of NativeAPI.actor.cpp.
Index ¶
- Constants
- Variables
- func ReadEndpointFromSlot(r *wire.Reader, slot int) (types.Endpoint, error)
- type ClientMetrics
- type ClientMetricsSnapshot
- type ClusterFile
- type DBInfo
- type Database
- func (d *Database) Close() error
- func (d *Database) CreateTransaction() *Transaction
- func (d *Database) GetDBInfo() *DBInfo
- func (d *Database) HedgeEnabled() bool
- func (d *Database) InvalidateGRVCache()
- func (d *Database) Metrics() ClientMetricsSnapshot
- func (d *Database) ReadTransact(ctx context.Context, fn func(tx *Transaction) (any, error)) (any, error)
- func (d *Database) SetDefaultAccessSystemKeys()
- func (d *Database) SetDefaultReadSystemKeys()
- func (d *Database) SetDialFunc(fn transport.DialFunc)
- func (d *Database) SetHedgeEnabled(enabled bool)
- func (d *Database) SetMaxWatches(n int64) error
- func (d *Database) SetTransactionMaxRetryDelay(ms int64)
- func (d *Database) SetTransactionRetryLimit(retries int64)
- func (d *Database) SetTransactionSizeLimit(limit int64)
- func (d *Database) SetTransactionTimeout(ms int64)
- func (d *Database) Transact(ctx context.Context, fn func(tx *Transaction) (any, error)) (any, error)
- type DialFunc
- type KeyRange
- type KeySelector
- type KeyValue
- type LatencyStats
- type LocationResult
- type MappedGetRange
- type MappedGetValue
- type MappedKeyValue
- type MappedResultKind
- type Mutation
- type MutationType
- type Option
- type PendingGet
- type ProxyInfo
- type QueueModel
- type RangeMaterializationLimitError
- type ServerInfo
- type Snapshot
- func (s *Snapshot) Get(ctx context.Context, key []byte) ([]byte, error)
- func (s *Snapshot) GetKey(ctx context.Context, selectorKey []byte, orEqual bool, offset int32) ([]byte, error)
- func (s *Snapshot) GetRange(ctx context.Context, begin, end []byte, limit int) ([]KeyValue, bool, error)
- func (s *Snapshot) GetRangeReverse(ctx context.Context, begin, end []byte, limit int) ([]KeyValue, bool, error)
- func (s *Snapshot) GetRangeWithByteTarget(ctx context.Context, begin, end []byte, limit, byteTarget int, reverse bool) ([]KeyValue, bool, error)
- func (s *Snapshot) GetReadVersion(ctx context.Context) (int64, error)
- type Transaction
- func (tx *Transaction) AddReadConflictKey(key []byte)
- func (tx *Transaction) AddReadConflictRange(begin, end []byte) error
- func (tx *Transaction) AddWriteConflictKey(key []byte)
- func (tx *Transaction) AddWriteConflictRange(begin, end []byte) error
- func (tx *Transaction) Atomic(op MutationType, key, operand []byte)
- func (tx *Transaction) AwaitWatchCommit(watchCtx context.Context, act *watchActivation, readVersion int64) (int64, error)
- func (tx *Transaction) BypassUnreadable() bool
- func (tx *Transaction) Cancel()
- func (tx *Transaction) CausalReadRisky() bool
- func (tx *Transaction) Clear(key []byte)
- func (tx *Transaction) ClearRange(begin, end []byte) error
- func (tx *Transaction) Commit(ctx context.Context) error
- func (tx *Transaction) EnsureMutationCapacity(n int)
- func (tx *Transaction) Get(ctx context.Context, key []byte) ([]byte, error)
- func (tx *Transaction) GetAddressesForKey(parentCtx context.Context, key []byte) ([]string, error)
- func (tx *Transaction) GetApproximateSize() (int64, error)
- func (tx *Transaction) GetCommittedVersion() (int64, error)
- func (tx *Transaction) GetEstimatedRangeSizeBytes(parentCtx context.Context, begin, end []byte) (int64, error)
- func (tx *Transaction) GetKey(ctx context.Context, selectorKey []byte, orEqual bool, offset int32) ([]byte, error)
- func (tx *Transaction) GetLocations(parentCtx context.Context, begin, end []byte, limit int) ([]LocationResult, error)
- func (tx *Transaction) GetMappedRange(ctx context.Context, begin, end, mapper []byte, limit int, reverse bool) ([]MappedKeyValue, bool, error)
- func (tx *Transaction) GetPipelined(ctx context.Context, key []byte) (val []byte, pending *PendingGet, err error)
- func (tx *Transaction) GetRange(ctx context.Context, begin, end []byte, limit int) ([]KeyValue, bool, error)
- func (tx *Transaction) GetRangeReverse(ctx context.Context, begin, end []byte, limit int) ([]KeyValue, bool, error)
- func (tx *Transaction) GetRangeSplitPoints(parentCtx context.Context, begin, end []byte, chunkSize int64) ([][]byte, error)
- func (tx *Transaction) GetRangeWithByteTarget(ctx context.Context, begin, end []byte, limit, byteTarget int, reverse bool) ([]KeyValue, bool, error)
- func (tx *Transaction) GetReadVersion(ctx context.Context) (int64, error)
- func (tx *Transaction) GetTagThrottledDuration() float64
- func (tx *Transaction) GetVersionstamp() ([]byte, error)
- func (tx *Transaction) LockAware() bool
- func (tx *Transaction) OnError(ctx context.Context, err error) (rerr error)
- func (tx *Transaction) ReadLockAware() bool
- func (tx *Transaction) ReadTags() []string
- func (tx *Transaction) ReadVersionInstant() (time.Time, bool)
- func (tx *Transaction) ReleaseWatch()
- func (tx *Transaction) Reset()
- func (tx *Transaction) Set(key, value []byte)
- func (tx *Transaction) SetAccessSystemKeys()
- func (tx *Transaction) SetAutoThrottleTag(tag string) error
- func (tx *Transaction) SetBypassUnreadable(v bool)
- func (tx *Transaction) SetCausalReadRisky(v bool)
- func (tx *Transaction) SetLockAware(v bool)
- func (tx *Transaction) SetMaxRetryDelay(ms int64)
- func (tx *Transaction) SetNextWriteNoWriteConflictRange()
- func (tx *Transaction) SetPriority(p TransactionPriority)
- func (tx *Transaction) SetReadLockAware(v bool)
- func (tx *Transaction) SetReadSystemKeys()
- func (tx *Transaction) SetReadVersion(version int64)
- func (tx *Transaction) SetReadYourWritesDisable()
- func (tx *Transaction) SetRetryLimit(retries int64)
- func (tx *Transaction) SetSizeLimit(limit int64)
- func (tx *Transaction) SetSkipGrvCache()
- func (tx *Transaction) SetSnapshotRYWDisable()
- func (tx *Transaction) SetSnapshotRYWDisableCount(n int)
- func (tx *Transaction) SetSnapshotRYWEnable()
- func (tx *Transaction) SetSpanParent(b []byte) error
- func (tx *Transaction) SetTag(tag string) error
- func (tx *Transaction) SetTenantId(id int64)
- func (tx *Transaction) SetTimeout(ms int64)
- func (tx *Transaction) SetUseGrvCache()
- func (tx *Transaction) SetWriteConflictsDisabled()
- func (tx *Transaction) Snapshot() *Snapshot
- func (tx *Transaction) SnapshotRYWDisableCount() int
- func (tx *Transaction) Tags() []string
- func (tx *Transaction) TenantId() int64
- func (tx *Transaction) Watch(ctx context.Context, key []byte) error
- func (tx *Transaction) WatchActivation() *watchActivation
- func (tx *Transaction) WatchPoll(watchCtx context.Context, watchCancel context.CancelFunc, key, value []byte, ...) error
- func (tx *Transaction) WatchSetup(ctx context.Context, key []byte) ([]byte, int64, types.SpanContext, context.Context, context.CancelFunc, error)
- type TransactionDefaults
- type TransactionPriority
Constants ¶
const ( // ErrClientInvalidOperation is raised for a mapped read over the special // key space, which getMappedRange does not support. ErrClientInvalidOperation = 2000 // client_invalid_operation // ErrMapperBadIndex ... ErrMapperNotTuple are raised by the STORAGE SERVER, // never by this client — the mapper is not parsed here. They are named so a // caller can errors.As them and so the propagate-unchanged contract is // testable. ErrMapperBadIndex = 2030 // mapper_bad_index ErrMapperNoSuchKey = 2031 // mapper_no_such_key ErrMapperBadRangeDescrptor = 2032 // mapper_bad_range_decriptor (sic: the C++ spelling) // ErrKeyNotTuple / ErrValueNotTuple are raised when a {K[n]} / {V[n]} // placeholder forces the server to unpack a PRIMARY row that is not a packed // tuple (unpackKeyTuple / unpackValueTuple, storageserver.actor.cpp:4820-4844). // They are properties of the DATA, not of the mapper, so no amount of mapper // checking anywhere would pre-empt them. ErrKeyNotTuple = 2041 // key_not_tuple ErrValueNotTuple = 2042 // value_not_tuple // ErrGetMappedRangeReadsYourWrites is raised by THIS client when the mapped // read — primary range or any resolved secondary read — overlaps a key the // transaction has already written. ErrGetMappedRangeReadsYourWrites = 2039 // get_mapped_range_reads_your_writes ErrMapperNotTuple = 2043 // mapper_not_tuple // ErrUnsupportedOperation is what snapshot=true and read-your-writes-disabled // both raise. Neither is a MODE for getMappedRange; both are errors. ErrUnsupportedOperation = 2108 // unsupported_operation )
FDB error codes specific to getMappedRange. Source of truth: flow/include/flow/error_definitions.h at the 7.3.77 tag.
const ( ErrNotCommitted = 1020 // not_committed (conflict) ErrCommitUnknownResult = 1021 // commit_unknown_result ErrTransactionTooOld = 1007 // transaction_too_old ErrFutureVersion = 1009 // future_version ErrWrongShardServer = 1001 // wrong_shard_server (1062 is change_feed_cancelled — do not confuse) ErrTransactionTimedOut = 1031 // transaction_timed_out (NEVER retryable) ErrAccessedUnreadable = 1036 // accessed_unreadable — read of a pending versionstamped key (NOT retryable; RFC-098) ErrProcessBehind = 1037 // process_behind ErrWatchCancelled = 1029 // watch_cancelled — SS watch limit exceeded; client polls instead ErrTooManyWatches = 1032 // too_many_watches — outstanding-watch cap exceeded (client-side) ErrTimedOut = 1004 // timed_out — the SS occasionally times out a watch; re-arm ErrDatabaseLocked = 1038 // database_locked ErrClusterVersionChanged = 1039 // cluster_version_changed (MAYBE_COMMITTED) ErrProxyMemoryLimitExceeded = 1042 // commit_proxy_memory_limit_exceeded ErrBatchTransactionThrottled = 1051 // batch_transaction_throttled ErrGrvProxyMemoryLimit = 1078 // grv_proxy_memory_limit_exceeded ErrBlobGranuleRequestFailed = 1079 // blob_granule_request_failed (retryable, C++ NativeAPI.actor.cpp) ErrTagThrottled = 1213 // tag_throttled ErrProxyTagThrottled = 1223 // proxy_tag_throttled ErrThrottledHotShard = 1235 // transaction_throttled_hot_shard (FDB 7.4+, future-proof) ErrRangeLocked = 1242 // transaction_rejected_range_locked (FDB 7.4+, future-proof) ErrOperationFailed = 4 // operation_failed (endpoint not supported) ErrAllAlternativesFailed = 1006 // all_alternatives_failed (Layer 2 only) ErrRequestMaybeDelivered = 1030 // request_maybe_delivered — an in-flight RPC's connection died (transport teardown); reads retry another alternative, commit maps it to 1021 ErrBrokenPromise = 1100 // broken_promise — the peer dropped the reply promise; same maybeDelivered class as 1030 (LoadBalance.actor.h:344) ErrAllProxiesUnreachable = 1200 // Go-internal: all proxies failed at Layer 2 (NOT C++ 1200=recruitment_failed) ErrInvertedRange = 2005 // inverted_range (begin > end) ErrRangeLimitsInvalid = 2012 // range_limits_invalid (e.g. a row limit < -1) ErrInvalidMutationType = 2018 // invalid_mutation_type (a non-atomic op passed to Atomic()) )
FDB error codes. Source of truth: flow/error_definitions.h + fdb_c.cpp fdb_error_predicate().
const ( NoTenantID int64 = -1 UnlimitedBytes int32 = 0x7FFFFFFF DefaultRPCTimeout = 5 * time.Second CoordinatorTimeout = 30 * time.Second // OpenDatabaseCoordRequest + GRV batch context BootstrapMaxBackoff = 5 * time.Second // bootstrap retry backoff cap MaxWrongShardRetries = 50 // C++ is unbounded (relies on tx 5s timeout); 50×10ms = 500ms, generous safety margin )
Client constants. These mirror CLIENT_KNOBS in NativeAPI.actor.cpp.
const ( LatestVersion int64 = -2 // C++ latestVersion — used in GetKeyServerLocationsRequest.MinTenantVersion InvalidVersion int64 = -1 // C++ invalidVersion — used in GetReadVersionRequest.MaxVersion )
C++ version constants from flow/flow.h.
const ( EndpointGetValue = 0 // StorageServerInterface::getValue EndpointGetKey = 1 // StorageServerInterface::getKey EndpointGetKeyValues = 2 // StorageServerInterface::getKeyValues EndpointGetShardState = 3 // StorageServerInterface::getShardState EndpointWaitMetrics = 4 // StorageServerInterface::waitMetrics EndpointSplitMetrics = 5 // StorageServerInterface::splitMetrics EndpointGetStorageMetrics = 6 // StorageServerInterface::getStorageMetrics EndpointWaitFailure = 7 // StorageServerInterface::waitFailure EndpointGetQueuingMetrics = 8 // StorageServerInterface::getQueuingMetrics EndpointGetKeyValueStoreType = 9 // StorageServerInterface::getKeyValueStoreType EndpointWatchValue = 10 // StorageServerInterface::watchValue EndpointGetReadHotRanges = 11 // StorageServerInterface::getReadHotRanges EndpointGetRangeSplitPoints = 12 // StorageServerInterface::getRangeSplitPoints EndpointGetKeyValuesStream = 13 // StorageServerInterface::getKeyValuesStream // getMappedKeyValues is DECLARED right after getKeyValues // (StorageServerInterface.h:103) but assigned getAdjustedEndpoint(14) // (:184-185) — it was appended after getKeyValuesStream so the pre-existing // indices stayed stable. Taking the index from the declaration order would // address getShardState (3) instead. EndpointGetMappedKeyValues = 14 // StorageServerInterface::getMappedKeyValues EndpointGetKeyServerLocations = 2 // CommitProxyInterface::getKeyServerLocations )
Endpoint indices from C++ interface definitions. Indices are relative to each interface's base token via getAdjustedEndpoint().
StorageServerInterface (StorageServerInterface.h):
getValue=0, getKey=1, getKeyValues=2, getShardState=3, waitMetrics=4, splitMetrics=5, getStorageMetrics=6, waitFailure=7, getQueuingMetrics=8, getKeyValueStoreType=9, watchValue=10, getReadHotRanges=11, getRangeSplitPoints=12, getKeyValuesStream=13
CommitProxyInterface (CommitProxyInterface.h):
commit=0, ..., getKeyServerLocations=2
const ( // ByteLimitUnlimited mirrors GetRangeLimits::BYTE_LIMIT_UNLIMITED (FDBTypes.h). A range // read carrying it has no per-fetch byte TARGET — only the flat replyByteLimit ceiling // applies — so the soft byte limit in rangeScanImpl can never fire and the scan stays // bounded by rows alone. This is EXACT's value, per mode_bytes_array[EXACT] (fdb_c.cpp:1002). ByteLimitUnlimited = -1 )
Variables ¶
var ErrNeedFullRYW = errors.New("need full RYW path")
ErrNeedFullRYW is returned by GetPipelined when the key has pending atomics that require a server read + merge through the full ryw.get() path.
Functions ¶
Types ¶
type ClientMetrics ¶
type ClientMetrics struct {
// contains filtered or unexported fields
}
ClientMetrics holds the per-Database transaction counters — the operational subset of C++ DatabaseContext's CounterCollection (DatabaseContext.h:585-635), with the C++ names and the C++ increment sites (RFC-097):
- commit started/completed: commitMutations entry (NativeAPI.actor.cpp:6808; the empty/read-only fast path at :6800-6806 does NOT count) / tryCommit success (:6673). Started−Completed = failed or in-flight (intentional asymmetry, matching C++).
- the per-error-code retry counters: Transaction::onError's arms (:7749-:7772).
- read versions completed (+ per priority): extractReadVersion (:7428-7440) — i.e. per transaction served by a REAL GRV reply; cache hits do not count there in C++ and do not count here.
transactionRetries is a Go-only aggregate (total OnError-sanctioned retries); C++ tracks retries per-transaction only (trState->numErrors). It also counts codes C++ retries WITHOUT a counter (database_locked 1038, blob_granule_request_failed 1079 — :7743-7747, plus the Go-internal 1200 and future-proof 1235/1242).
Counters are monotonic; consumers poll Database.Metrics() and diff. There is no periodic trace emission (C++ logs TransactionMetrics on a timer; the Go analog is the consumer's scrape interval) — the only gap is no persisted history if nothing polls before process death.
func (*ClientMetrics) Snapshot ¶
func (m *ClientMetrics) Snapshot() ClientMetricsSnapshot
Snapshot returns a point-in-time copy of all counters.
type ClientMetricsSnapshot ¶
type ClientMetricsSnapshot struct {
TransactionsCommitStarted int64
TransactionsCommitCompleted int64
TransactionsNotCommitted int64
TransactionsMaybeCommitted int64
TransactionsResourceConstrained int64
TransactionsProcessBehind int64
TransactionsThrottled int64
TransactionsTooOld int64
TransactionsFutureVersions int64
TransactionReadVersionsCompleted int64
TransactionBatchReadVersionsCompleted int64
TransactionDefaultReadVersionsCompleted int64
TransactionImmediateReadVersionsCompleted int64
GRVCacheHits int64 // RFC-104: served from the GRV cache (opt-in)
GRVInBandMaybeDelivered int64 // GRV replies whose in-band error took the next-alternative arm
TransactionRetries int64
RecoveredPanics int64 // RFC-110: panics recovered by the goroutine backstop
RecoveredPanicsConsecutiveMax int64 // RFC-110: high-water of any loop's consecutive-panic streak
ClientConnectionFailures int64 // RFC-114: connection/dial failures (Go-only observability)
CoordinatorChanges int64 // RFC-114: followed coordinator forwards (Go-only observability)
// Latency distributions (RFC-114), seconds. Instantaneous reads, not monotonic.
ReadLatency LatencyStats // GetValue round-trip
CommitLatency LatencyStats // commit round-trip
GRVLatency LatencyStats // GRV round-trip
TransactionLatency LatencyStats // total transaction latency (C++ "latencies")
}
ClientMetricsSnapshot is a point-in-time copy of the counters. Fields are monotonic; diff two snapshots for rates.
type ClusterFile ¶
type ClusterFile struct {
Description string
ID string
Coordinators []string // "host:port" addresses for TCP connection (":tls" suffix stripped)
// UseTLS is true when the coordinators carry the ":tls" suffix (FDB
// FLAG_TLS). A real cluster is uniformly TLS, so this is a single flag;
// mixed TLS/non-TLS coordinators are rejected at parse time.
UseTLS bool
}
ClusterFile represents an fdb.cluster file. Format: "<description>:<id>@<host>:<port>[,<host>:<port>...]"
func ParseClusterFile ¶
func ParseClusterFile(path string) (*ClusterFile, error)
ParseClusterFile reads and parses an fdb.cluster file.
func ParseClusterString ¶
func ParseClusterString(s string) (*ClusterFile, error)
ParseClusterString parses a cluster connection string.
func (*ClusterFile) String ¶
func (cf *ClusterFile) String() string
String renders the connection string in C++ ClusterConnectionString::toString byte-order (MonitorLeader.actor.cpp:438-453): `description:id@`, then all IP coordinators in original order, then all hostname coordinators in original order, comma-joined, each with ":tls" re-appended when UseTLS. This is the cross-tool-shared on-disk form; it must be byte-identical to what a C++/Java client would write so they can read the file back. Equality between two ClusterFiles is defined as a.String() == b.String() (matches C++ upToDate's toString() compare) — the serialization is the single source of truth.
type DBInfo ¶
type DBInfo struct {
ID transport.UID
GRVProxies []ProxyInfo
CommitProxies []ProxyInfo
ClusterID transport.UID
// Forward, when non-empty, is the serialized new ClusterConnectionString the
// coordinators handed back instead of proxies — the C++ ClientDBInfo.forward
// field (CommitProxyInterface.h:132). Set during a `coordinators auto`/`change`
// rotation; the proxies on a forward reply are ignored (RFC-111 Path A).
Forward string
// Epoch is the cluster epoch these proxies belong to. It rides the SNAPSHOT
// rather than living beside it, so a caller that loads the proxies gets the
// epoch of the very set it is about to talk to — the epoch a request binds
// to cannot describe a proxy set the request did not use, because there is
// no second load in which the two could disagree.
//
// Set exclusively by installProxySet, which also owns the ordering against
// grvCache's fence word. See the coherence rule documented there.
Epoch int64
}
DBInfo holds the current cluster topology. Received from coordinators via OpenDatabaseCoordRequest.
type Database ¶
type Database struct {
// contains filtered or unexported fields
}
Database is the public API entry point. C++: Database is a Reference<DatabaseContext> — a handle, not state.
Safe for concurrent use by multiple goroutines.
func OpenDatabase ¶
OpenDatabase opens a database connection using a cluster file. The provided ctx is used for the initial bootstrap (coordinator connection). Background goroutines use an internal context cancelled by Close().
func OpenDatabaseFromConfig ¶
func OpenDatabaseFromConfig(ctx context.Context, cf *ClusterFile, opts ...Option) (*Database, error)
OpenDatabaseFromConfig creates and bootstraps a Database from a ClusterFile. See WithDialFunc / WithTLSConfig for options.
func (*Database) CreateTransaction ¶
func (d *Database) CreateTransaction() *Transaction
CreateTransaction creates a new transaction. Database-level defaults (timeout, retry limit, system key access) are applied.
func (*Database) GetDBInfo ¶
GetDBInfo returns the current cluster topology (proxy lists, cluster ID). Returns nil if not yet connected.
func (*Database) HedgeEnabled ¶
HedgeEnabled returns whether speculative second requests are active.
func (*Database) InvalidateGRVCache ¶
func (d *Database) InvalidateGRVCache()
InvalidateGRVCache resets the GRV cache so the next transaction fetches a fresh read version from the GRV proxy. Use after external writes.
func (*Database) Metrics ¶
func (d *Database) Metrics() ClientMetricsSnapshot
Metrics returns a point-in-time snapshot of this handle's operational counters (RFC-097) — the C++ DatabaseContext TransactionMetrics subset. Counters are monotonic; poll and diff for rates. This is the export hook: Prometheus/OTel consumers are pull-based readers of exactly this shape (see pkg/fdbgo/fdbmetrics for a ready-made scrape handler).
func (*Database) ReadTransact ¶
func (d *Database) ReadTransact(ctx context.Context, fn func(tx *Transaction) (any, error)) (any, error)
ReadTransact runs a read-only function in a transaction with automatic retry. The transaction is never committed — only reads are performed.
func (*Database) SetDefaultAccessSystemKeys ¶
func (d *Database) SetDefaultAccessSystemKeys()
SetDefaultAccessSystemKeys makes all new transactions automatically call SetAccessSystemKeys(), allowing reads AND writes of \xff-prefixed system keys.
func (*Database) SetDefaultReadSystemKeys ¶
func (d *Database) SetDefaultReadSystemKeys()
SetDefaultReadSystemKeys makes all new transactions automatically call SetReadSystemKeys(), allowing reads of \xff-prefixed system keys.
func (*Database) SetDialFunc ¶
SetDialFunc sets a custom dialer for all new connections. Must be called before OpenDatabase or any transactions.
func (*Database) SetHedgeEnabled ¶
SetHedgeEnabled controls speculative second requests (hedge) for read RPCs. When enabled (default), slow reads are rescued by sending a backup request to a second server after max(10ms, 2×latency). Disable for debugging or benchmarking the non-hedged path.
func (*Database) SetMaxWatches ¶
SetMaxWatches sets the cap on concurrently-outstanding watches for this Database (FDB_DB_OPTION_MAX_WATCHES). Once the count would exceed it, a new watch fails with too_many_watches (1032). Matches C++ DatabaseContext maxOutstandingWatches; default 10000. 0 is a REAL cap (no watches allowed — the first fails), NOT "unlimited". An out-of-range value (< 0 or > ABSOLUTE_MAX_WATCHES) is REJECTED with invalid_option_value (2006) and the cap is left UNCHANGED — C++ extractIntOption(value, 0, ABSOLUTE_MAX_WATCHES) throws on out-of-range, it does NOT clamp (NativeAPI.actor.cpp:2092-2102 / :2139).
func (*Database) SetTransactionMaxRetryDelay ¶
SetTransactionMaxRetryDelay sets the default max retry delay (ms) for all transactions. Matches FDB_DB_OPTION_TRANSACTION_MAX_RETRY_DELAY.
func (*Database) SetTransactionRetryLimit ¶
SetTransactionRetryLimit sets the default retry limit for all transactions. Matches FDB_DB_OPTION_TRANSACTION_RETRY_LIMIT.
func (*Database) SetTransactionSizeLimit ¶
SetTransactionSizeLimit sets the default size limit for all transactions. Matches FDB_DB_OPTION_TRANSACTION_SIZE_LIMIT.
func (*Database) SetTransactionTimeout ¶
SetTransactionTimeout sets the default timeout (in milliseconds) for all transactions created from this database. Matches FDB_DB_OPTION_TRANSACTION_TIMEOUT.
func (*Database) Transact ¶
func (d *Database) Transact(ctx context.Context, fn func(tx *Transaction) (any, error)) (any, error)
Transact runs a function in a transaction with automatic retry. This is the primary API for interacting with FDB. The transaction is reused across retries — retryCount and backoff escalate.
type DialFunc ¶
DialFunc is the custom-dialer signature (alias of transport.DialFunc) so callers configuring WithDialFunc don't need to import the transport package.
type KeySelector ¶
KeySelector mirrors C++ KeySelectorRef for the secondary range bounds.
type LatencyStats ¶
type LatencyStats struct {
Count int64
Sum float64 // seconds — the summary _sum (and Mean = Sum/Count)
Mean float64
Median float64 // p50
P90 float64
P99 float64
Min float64 // exact (C++ DDSketchBase::min)
Max float64 // exact (C++ DDSketchBase::max)
}
LatencyStats is a point-in-time summary of one latency distribution (RFC-114), in SECONDS. Unlike the monotonic counters, these are instantaneous distribution reads — diffing two snapshots is not meaningful. Median/P90/P99 are DDSketch quantiles (±0.5% relative error). C++ emits p90/p98 only for the aggregate transaction latency; Go exposes per-category median/p90/p99 — a local-metric superset (p99 is the conventional Prometheus tail, vs C++'s trace-only p98).
type LocationResult ¶
type LocationResult struct {
Servers []ServerInfo
ShardBegin []byte
ShardEnd []byte
}
LocationResult holds the storage servers and shard key range for a locate() result. C++ KeyRangeLocationInfo equivalent.
type MappedGetRange ¶
type MappedGetRange struct {
Begin KeySelector
End KeySelector
Rows []KeyValue
More bool
}
MappedGetRange is the range arm (C++ GetRangeReqAndResultRef, FDBTypes.h:873). Begin/End are the selectors the server actually resolved, so a range that matched nothing still reports what it probed.
type MappedGetValue ¶
MappedGetValue is the point-lookup arm (C++ GetValueReqAndResultRef, FDBTypes.h:855). Present distinguishes "the mapped key holds an empty value" from "the mapped key does not exist" — the C++ field is Optional<ValueRef> and collapsing the two would silently invent a record.
type MappedKeyValue ¶
type MappedKeyValue struct {
Key []byte
Value []byte
Kind MappedResultKind
GetValue MappedGetValue
GetRange MappedGetRange
}
MappedKeyValue is one row of a mapped range: the primary row (Key/Value — the index entry that was scanned) plus the secondary read the mapper resolved it to. Mirrors C++ MappedKeyValueRef (FDBTypes.h:895), whose KeyValueRef base carries the primary row.
type MappedResultKind ¶
type MappedResultKind uint8
MappedResultKind names which arm of the per-row union is populated. It mirrors the std::variant index in MappedReqAndResultRef, and the underlying values are the flatbuffers union tags (1-based, 0 = unset) so a decode is a direct read.
const ( // MappedResultNone means the row carried no secondary result at all. The // storage server does not produce this, but the tag is representable on the // wire (tag 0), so it is named rather than silently conflated with a // point-lookup miss — those are different facts. MappedResultNone MappedResultKind = 0 // MappedResultGetValue: the mapper resolved to a single key. MappedResultGetValue MappedResultKind = 1 // MappedResultGetRange: the mapper ended in a "{...}" range descriptor and // resolved to a key range. MappedResultGetRange MappedResultKind = 2 )
func (MappedResultKind) String ¶
func (k MappedResultKind) String() string
type Mutation ¶
type Mutation struct {
Type MutationType
Key []byte
Value []byte
}
Mutation represents a key-value mutation in a transaction.
type MutationType ¶
type MutationType uint8
MutationType is the type of mutation.
const ( MutSetValue MutationType = 0 MutClearRange MutationType = 1 MutAddValue MutationType = 2 MutAnd MutationType = 6 // C++: And (skips DebugKeyRange=3, DebugKey=4, NoOp=5) MutOr MutationType = 7 // C++: Or MutXor MutationType = 8 // C++: Xor MutAppendIfFits MutationType = 9 // C++: AppendIfFits MutMax MutationType = 12 // C++: Max (skips AvailableForReuse=10, Reserved=11) MutMin MutationType = 13 // C++: Min MutSetVersionstampedKey MutationType = 14 // C++: SetVersionstampedKey MutSetVersionstampedValue MutationType = 15 // C++: SetVersionstampedValue MutByteMin MutationType = 16 // C++: ByteMin MutByteMax MutationType = 17 // C++: ByteMax MutMinV2 MutationType = 18 // C++: MinV2 MutAndV2 MutationType = 19 // C++: AndV2 MutCompareAndClear MutationType = 20 // C++: CompareAndClear )
Mutation types — MUST match C++ MutationRef::Type enum values exactly. Wire format uses these values directly. See CommitTransaction.h.
type Option ¶
type Option func(*openOptions)
Option configures a database opened via OpenDatabase / OpenDatabaseFromConfig. Options compose and are forward-compatible — new knobs don't break callers.
func WithAPIVersion ¶
WithAPIVersion records the selected FDB API version; required — OpenDatabase rejects an unset version, mirroring fdb_select_api_version. Gates version-dependent wire behaviour, e.g. the Min→MinV2/And→AndV2 atomic upgrade at >=510.
func WithDialFunc ¶
WithDialFunc overrides the dialer used for every connection. nil (the default) uses a standard net.Dialer. Useful for fault injection, custom networking, or traffic shaping in tests.
func WithLogger ¶
WithLogger sets the per-handle logger for the client's operational events (transaction retries, commit_unknown_result — RFC-097). nil (the default) uses slog.Default(), so zero-config apps keep the standard integration point (slog.SetDefault); the per-handle option exists for multi-tenant hosts and for tests, which must never mutate process-global state.
func WithRangeByteCeiling ¶
WithRangeByteCeiling bounds how many bytes a single GetRange may materialize into memory before it fails with a *RangeMaterializationLimitError, instead of OOM-ing the process on a runaway unbounded scan. n ≤ 0 (the default) means UNLIMITED — matching libfdb_c, whose GetSliceWithError equivalent also materializes a range unbounded and never returns a "too big" error. This is a Go-only OPT-IN OOM safety valve, off by default so the default facade behavior stays oracle-matching; an operator sets a ceiling (e.g. 256<<20) as a last-resort guard. The bounded, streaming Iterator() honors StreamingMode and is the right tool for large result sets; this ceiling is the backstop for code that calls GetSliceWithError on an unexpectedly huge range. The cap bounds total materialized key+value bytes; a single read may overshoot it by at most one reply (~80 KB) before the check fires.
func WithTLSConfig ¶
WithTLSConfig connects to the cluster over TLS using the given standard *crypto/tls.Config — bring any config: in-memory certs, rotation via GetClientCertificate, custom VerifyPeerCertificate, cipher/version policy.
It takes precedence over the FDB_TLS_* environment / cluster-file ":tls" resolution and enables TLS even when the cluster string lacks ":tls". When not supplied, a ":tls" cluster string falls back to the FDB_TLS_* env convenience layer (matching the C++ client). A non-nil config is the only "use TLS" signal — there is no separate boolean, so TLS can never be silently downgraded.
WithTLSConfig(nil) is a no-op (it does NOT force plaintext): a ":tls" cluster string + FDB_TLS_* env still enables TLS. To stay plaintext, use a cluster string without ":tls".
func WithTracer ¶
WithTracer sets the OpenTelemetry tracer used to EXPORT client-side trace spans (the C++ ITracer analog — NoopTracer default, pluggable backend). The pure-Go client always GENERATES + propagates a SpanContext on the wire (RFC-115 §4 Layer 1) regardless of this; WithTracer adds the export half. Pass any go.opentelemetry.io/otel/trace.Tracer (from your OTLP/Jaeger/Datadog TracerProvider); the client emits a "Transaction" span plus per-operation child spans (getValue, getRange, commit, GRV, …), seeded with the same traceID it puts on the wire so FDB server-side spans land in the same trace. nil (the default) → an internal no-op tracer: zero telemetry, zero allocation on the hot path, no OTEL SDK pulled in. Spans are recorded only for sampled transactions (see WithTracingSampleRate).
CAVEAT (span lifetime): the per-transaction "Transaction" span ends on commit success, OnError retry, Reset, or Cancel. Database.Transact/TransactCtx always hit one of these, so they are safe. A RAW Database.CreateTransaction() handle that you read from (starting the span) and then ABANDON without committing/Reset/Cancel will LEAK that span (it is never ended) — only matters with a real tracer AND a sampled transaction. Always Reset() or Cancel() a raw handle you don't commit.
func WithTracingSampleRate ¶
WithTracingSampleRate sets the fraction (0.0–1.0) of transactions whose trace span is flagged SAMPLED. The default 0.0 matches C++ FLOW_KNOBS->TRACING_SAMPLE_RATE: every transaction still carries a real, randomly-generated SpanContext on every request (wire-faithful with C++), but flagged unsampled so collectors drop it. Raise it to emit sampled spans for a fraction of transactions. RFC-115 §4.
type PendingGet ¶
type PendingGet struct {
// contains filtered or unexported fields
}
PendingGet represents a GetValue request that has been sent but not yet resolved.
func (*PendingGet) Resolve ¶
func (p *PendingGet) Resolve() ([]byte, error)
Resolve blocks until the response arrives or timeout, then applies the SAME classify/invalidate/retry semantics as the synchronous getValue path: a wrong_shard_server or all_alternatives_failed reply (including the inline LoadBalancedReply.error, RFC-010 #1) invalidates the stale location and re-drives through the full read path; transport errors, a flush failure, or a timeout likewise fall through to the full path rather than surfacing a bare error or skipping the wrong-shard retry. Pipelining only defers the wait — it must not own a different error policy. RFC-010 #3.
Flushes the write buffer on first call to ensure the request reaches the server (batched with any other deferred frames on the same connection).
type QueueModel ¶
type QueueModel struct {
// contains filtered or unexported fields
}
QueueModel tracks estimated outstanding request depth per storage server. Used by sendGetValue/sendGetKey/sendGetRange to pick the least-loaded server from a shard's replica set.
Matches C++ QueueModel in fdbclient/QueueModel.h: - Smoother-based continuous exponential decay (eFoldingTime=2s) - Server penalty from LoadBalancedReply - failedUntil with exponential backoff for future_version errors
Selection metric: smoothOutstanding.smoothTotal() (lower = better). C++ also uses latency for speculative second requests (secondDelay), which we don't implement yet.
type RangeMaterializationLimitError ¶
type RangeMaterializationLimitError struct {
LimitBytes int64 // the configured WithRangeByteCeiling
ReachedBytes int64 // total key+value bytes materialized when the cap was exceeded
}
RangeMaterializationLimitError is returned by a GetRange that would materialize more than the opt-in WithRangeByteCeiling cap (RFC-115 §2) — an OOM safety valve, off by default. libfdb_c has no such ceiling (its GetSliceWithError equivalent also materializes unbounded and never returns a "too big" error), so this NEVER fires unless the operator opts in via WithRangeByteCeiling; the default facade behavior stays oracle-matching. Match it with errors.As. For large scans, prefer the bounded, StreamingMode-honoring Iterator() instead of raising the ceiling.
func (*RangeMaterializationLimitError) Error ¶
func (e *RangeMaterializationLimitError) Error() string
type ServerInfo ¶
type ServerInfo struct {
Address string
Token transport.UID
// TLS records whether the SS address carried the TLS flag. Address itself stays a clean
// host:port for dialing; GetAddressesForKey appends ":tls" to its output when this is set,
// matching C++ NetworkAddress::toString (flow/network.cpp:215).
TLS bool
}
ServerInfo holds a storage server's address and endpoint token.
type Snapshot ¶
type Snapshot struct {
// contains filtered or unexported fields
}
Snapshot wraps a Transaction for conflict-free reads. All reads go through the same transaction (same read version, same connection pool) but do not add read conflict ranges.
func (*Snapshot) Get ¶
Get reads a key without adding a read conflict range. Snapshot reads go through the RYW cache unless snapshot RYW is net-disabled (snapshotRYWDisableCount > 0).
func (*Snapshot) GetKey ¶
func (s *Snapshot) GetKey(ctx context.Context, selectorKey []byte, orEqual bool, offset int32) ([]byte, error)
GetKey resolves a key selector without adding a read conflict range. Snapshot reads go through the snapshot cache, and — by default — SEE the txn's own pending writes (matching Snapshot.Get/GetRange and libfdb_c, where snapshot RYW is enabled unless net-disabled via SetSnapshotRYWDisable). When net-disabled (snapshotRYWDisableCount > 0) the write map is bypassed (snapshot cache only).
func (*Snapshot) GetRange ¶
func (s *Snapshot) GetRange(ctx context.Context, begin, end []byte, limit int) ([]KeyValue, bool, error)
GetRange reads a range without adding a read conflict range. Snapshot reads go through the RYW cache unless snapshot RYW is net-disabled (snapshotRYWDisableCount > 0).
func (*Snapshot) GetRangeReverse ¶
func (s *Snapshot) GetRangeReverse(ctx context.Context, begin, end []byte, limit int) ([]KeyValue, bool, error)
GetRangeReverse reads a range in reverse without adding a read conflict range. Snapshot reads go through the RYW cache unless snapshot RYW is net-disabled (snapshotRYWDisableCount > 0).
func (*Snapshot) GetRangeWithByteTarget ¶
func (s *Snapshot) GetRangeWithByteTarget(ctx context.Context, begin, end []byte, limit, byteTarget int, reverse bool) ([]KeyValue, bool, error)
GetRangeWithByteTarget is GetRange carrying a per-fetch BYTE target, the dimension libfdb_c derives from the streaming mode (fdb_c.cpp:1002/1006). ByteLimitUnlimited means "no target", which is what the plain GetRange passes and what EXACT resolves to.
type Transaction ¶
type Transaction struct {
// contains filtered or unexported fields
}
Transaction represents an FDB transaction. Mutations are buffered locally and sent on Commit().
func (*Transaction) AddReadConflictKey ¶
func (tx *Transaction) AddReadConflictKey(key []byte)
AddReadConflictKey adds a read conflict on a single key. Like AddReadConflictRange it is filtered through the RYW write-map (C++ addReadConflictRange over [key, keyAfter(key)) → updateConflictMap, ReadYourWrites.actor.cpp:1986): a self-written independent key adds no conflict; rywDisabled adds the full single-key conflict directly (:1979). Identical to the Get-path helper, so delegate.
func (*Transaction) AddReadConflictRange ¶
func (tx *Transaction) AddReadConflictRange(begin, end []byte) error
AddReadConflictRange adds an explicit read conflict range [begin, end). If any key in this range is modified by another transaction between this transaction's read version and commit, the commit will fail. Returns inverted_range (2005) if begin > end. Matches C++ fdb_transaction_add_conflict_range.
func (*Transaction) AddWriteConflictKey ¶
func (tx *Transaction) AddWriteConflictKey(key []byte)
AddWriteConflictKey adds a write conflict on a single key.
func (*Transaction) AddWriteConflictRange ¶
func (tx *Transaction) AddWriteConflictRange(begin, end []byte) error
AddWriteConflictRange adds an explicit write conflict range [begin, end). Returns inverted_range (2005) if begin > end. Matches C++ fdb_transaction_add_conflict_range.
func (*Transaction) Atomic ¶
func (tx *Transaction) Atomic(op MutationType, key, operand []byte)
Atomic performs an atomic mutation.
func (*Transaction) AwaitWatchCommit ¶
func (tx *Transaction) AwaitWatchCommit(watchCtx context.Context, act *watchActivation, readVersion int64) (int64, error)
AwaitWatchCommit blocks until the watch's captured incarnation resolves, returning the version to register the watch at — committedVersion, or readVersion when the txn didn't commit (C++ setupWatches' `committedVersion > 0 ? committedVersion : readVersion`, NativeAPI.actor.cpp:6420). Returns an error when the watch is abandoned (reset/Cancel → 1025) or its scoped context is cancelled, so the facade drains without registering. RFC-170 (#8): the async facade calls this BEFORE WatchPoll.
func (*Transaction) BypassUnreadable ¶
func (tx *Transaction) BypassUnreadable() bool
BypassUnreadable reports whether FDB_TR_OPTION_BYPASS_UNREADABLE is set. Read-only accessor used to verify database-level option propagation.
func (*Transaction) Cancel ¶
func (tx *Transaction) Cancel()
Cancel cancels the transaction. All subsequent operations will return an error. This is irreversible — a cancelled transaction cannot be reused.
func (*Transaction) CausalReadRisky ¶
func (tx *Transaction) CausalReadRisky() bool
CausalReadRisky reports whether the GRV causal-read-risky flag is set. Read-only accessor used to verify database-level option propagation.
func (*Transaction) Clear ¶
func (tx *Transaction) Clear(key []byte)
func (*Transaction) ClearRange ¶
func (tx *Transaction) ClearRange(begin, end []byte) error
ClearRange deletes all keys in [begin, end). Returns inverted_range (2005) if begin > end. Matches C++ fdb_transaction_clear_range_impl. Zero-width ranges (begin == end) are silently ignored, matching C++.
func (*Transaction) Commit ¶
func (tx *Transaction) Commit(ctx context.Context) error
Commit sends mutations to a commit proxy. After successful commit, the transaction is automatically reset for reuse (mutations and conflict ranges cleared, read version invalidated). This matches the C client's behavior where fdb_transaction_set() can be called after commit to start building a new transaction.
func (*Transaction) EnsureMutationCapacity ¶
func (tx *Transaction) EnsureMutationCapacity(n int)
EnsureMutationCapacity pre-sizes the mutations and writeConflicts slices to avoid growth allocations during batch writes. Call before a large batch.
func (*Transaction) GetAddressesForKey ¶
GetAddressesForKey returns the addresses of storage servers responsible for the given key. Uses the location cache (queries cluster on miss).
func (*Transaction) GetApproximateSize ¶
func (tx *Transaction) GetApproximateSize() (int64, error)
GetApproximateSize returns the approximate size of the transaction's mutations and conflict ranges in bytes. Matches C++ ReadYourWritesTransaction accounting: each mutation includes sizeof(MutationRef), each conflict range includes sizeof(KeyRangeRef). For set/atomic with write conflicts, C++ also adds the key length again for the auto-generated write conflict range.
func (*Transaction) GetCommittedVersion ¶
func (tx *Transaction) GetCommittedVersion() (int64, error)
GetCommittedVersion returns the version at which this transaction committed.
func (*Transaction) GetEstimatedRangeSizeBytes ¶
func (tx *Transaction) GetEstimatedRangeSizeBytes(parentCtx context.Context, begin, end []byte) (int64, error)
GetEstimatedRangeSizeBytes returns an estimate of the byte size of the given key range. Matches C++ getStorageMetricsLargeKeyRange in NativeAPI.actor.cpp: gets all shard locations, sends WaitMetricsRequest to each with min.bytes=0, max.bytes=-1 (reversed range = immediate response), and sums the bytes.
func (*Transaction) GetKey ¶
func (tx *Transaction) GetKey(ctx context.Context, selectorKey []byte, orEqual bool, offset int32) ([]byte, error)
GetKey resolves a key selector to the actual key in the database.
func (*Transaction) GetLocations ¶
func (tx *Transaction) GetLocations(parentCtx context.Context, begin, end []byte, limit int) ([]LocationResult, error)
GetLocations returns all shard location entries overlapping [begin, end).
func (*Transaction) GetMappedRange ¶
func (tx *Transaction) GetMappedRange(ctx context.Context, begin, end, mapper []byte, limit int, reverse bool) ([]MappedKeyValue, bool, error)
GetMappedRange reads the primary range [begin, end) and, for each row, the secondary read the `mapper` tuple resolves it to — C++ ReadYourWritesTransaction::getMappedRange (ReadYourWrites.actor.cpp:1779).
The guard sequence below is C++'s, in C++'s order, and the order is load-bearing: the special-key, legal-range and limit checks all run BEFORE the snapshot / RYW-disabled guard, so a mapped read that is invalid for two reasons reports the FIRST one C++ would report.
`limit` follows the C API, not the internal GetRangeLimits: fdb_c's validate_and_update_parameters maps 0 to ROW_LIMIT_UNLIMITED before RYW ever sees it (fdb_c.cpp:990-992), and fdb_transaction_get_mapped_range runs that same validator (:1080). So 0 here means UNLIMITED, and GetRangeLimits::isReached()'s "limit 0 returns an empty result" branch (NativeAPI.actor.cpp:2856) is unreachable from any C-API caller. A limit < -1 is range_limits_invalid, matching GetRangeLimits::isValid (FDBTypes.h:753).
Isolation: this ALWAYS issues at Snapshot::True and then re-adds read conflict ranges by hand — for the primary range AND for every secondary read the server resolved (ReadYourWrites.actor.cpp:1163-1192). That is not an optimization; it is what makes a serializable mapped read correct, because the secondary keys are not known until the reply comes back and so cannot be conflict-ranged up front. Any caller building a remote-fetch cursor on top of this inherits the same obligation.
func (*Transaction) GetPipelined ¶
func (tx *Transaction) GetPipelined(ctx context.Context, key []byte) (val []byte, pending *PendingGet, err error)
GetPipelined sends a GetValue request and returns a PendingGet that can be resolved later. This enables true pipelining: send N requests without waiting, then collect all N responses. Matches C++ client pipelining.
Returns (nil, nil, nil) for RYW cache hits (value is returned in val). Returns (nil, pending, nil) for server requests (call pending.Resolve() to get value). Returns (nil, nil, err) for errors during send.
func (*Transaction) GetRange ¶
func (tx *Transaction) GetRange(ctx context.Context, begin, end []byte, limit int) ([]KeyValue, bool, error)
GetRange reads a range of keys [begin, end) in forward order.
func (*Transaction) GetRangeReverse ¶
func (tx *Transaction) GetRangeReverse(ctx context.Context, begin, end []byte, limit int) ([]KeyValue, bool, error)
GetRangeReverse reads a range of keys [begin, end) in reverse order. Matches C++ where negative limit = reverse scan.
func (*Transaction) GetRangeSplitPoints ¶
func (tx *Transaction) GetRangeSplitPoints(parentCtx context.Context, begin, end []byte, chunkSize int64) ([][]byte, error)
GetRangeSplitPoints returns suggested split points for the given key range. Matches C++ Transaction::getRangeSplitPoints in NativeAPI.actor.cpp.
func (*Transaction) GetRangeWithByteTarget ¶
func (tx *Transaction) GetRangeWithByteTarget(ctx context.Context, begin, end []byte, limit, byteTarget int, reverse bool) ([]KeyValue, bool, error)
GetRangeWithByteTarget is GetRange carrying a per-fetch BYTE target — the dimension libfdb_c derives from the streaming mode and puts on the request, where the storage server truncates against it. That truncation, not any client-side accounting, is what divides a scan into batches; see rangeScanImpl's soft byte limit for the half that ends the call. ByteLimitUnlimited means "no target", which is what plain GetRange passes and what EXACT resolves to.
func (*Transaction) GetReadVersion ¶
func (tx *Transaction) GetReadVersion(ctx context.Context) (int64, error)
GetReadVersion returns the read version for this transaction, fetching it from a GRV proxy if not already set. Matches C++ fdb_transaction_get_read_version.
func (*Transaction) GetTagThrottledDuration ¶
func (tx *Transaction) GetTagThrottledDuration() float64
GetTagThrottledDuration returns the total time this transaction was delayed by proxy tag throttling across all GRV requests. Matches C++ Transaction::getTagThrottledDuration() (NativeAPI.actor.cpp:7594).
func (*Transaction) GetVersionstamp ¶
func (tx *Transaction) GetVersionstamp() ([]byte, error)
GetVersionstamp returns the 10-byte versionstamp from the committed transaction. Format: [version 8 bytes big-endian][txnBatchId 2 bytes big-endian]. Must be called after a successful Commit.
func (*Transaction) LockAware ¶
func (tx *Transaction) LockAware() bool
LockAware reports whether the commit is lock-aware (sets FLAG_IS_LOCK_AWARE, bypassing the locked-database check). Mirrors SetLockAware.
func (*Transaction) OnError ¶
func (tx *Transaction) OnError(ctx context.Context, err error) (rerr error)
OnError handles a transaction error. Returns nil if the error is retryable (the transaction has been reset for retry). Returns the error if non-retryable or ctx.Err() if ctx fires during the backoff sleep.
func (*Transaction) ReadLockAware ¶
func (tx *Transaction) ReadLockAware() bool
ReadLockAware reports whether reads bypass the locked-database check. Mirrors SetReadLockAware.
func (*Transaction) ReadTags ¶
func (tx *Transaction) ReadTags() []string
ReadTags returns the auto-throttle (read) tag subset in insertion order.
func (*Transaction) ReadVersionInstant ¶
func (tx *Transaction) ReadVersionInstant() (time.Time, bool)
ReadVersionInstant reports when this transaction's current read version was obtained from the cluster, i.e. when FDB's 5-second MVCC window opened for it. ok is false when no read version is held, or when the version was supplied by SetReadVersion (whose window opened at an instant this client never observed).
It exists so a layer above can budget against the window that actually applies. Anchoring such a budget on "when I started working" is wrong in both directions: a transaction that idles before its first read has not yet opened a window, and a retried transaction opened a fresh one. Only the GRV instant tracks it.
Callers MUST honour ok rather than treating a zero time as an anchor.
The hasReadVersion term is the whole reset discipline, deliberately. Every path that drops the read version (reset on the OnError retry, postCommitReset on commit-reuse) retires the instant through this one gate, so a new reset path cannot forget to clear it. Explicit clears were tried at those sites and removed: they were redundant with the gate, and two mechanisms covering one rule means neither is individually detectable by a mutation — the tests kept passing with either one broken, which is worse than having only one.
func (*Transaction) ReleaseWatch ¶
func (tx *Transaction) ReleaseWatch()
ReleaseWatch releases the outstanding-watch slot WatchSetup reserved (C++ decreaseWatchCounter). It is normally released by WatchPoll on completion, but when AwaitWatchCommit aborts a watch BEFORE it reaches WatchPoll (never-committed / reset / Cancel / ctx-expiry), WatchPoll never runs — so the async facade calls this on that abort path, else the slot leaks and later watches fail with too_many_watches under MAX_WATCHES. Idempotent-safe by construction: WatchPoll runs only on the success path, this only on the abort path — never both for one watch.
func (*Transaction) Reset ¶
func (tx *Transaction) Reset()
Reset resets the transaction to a clean state, as if newly created from Database. Unlike the internal reset() used by OnError (which preserves retryCount/backoff), this clears everything including retry state. Options set via Set*() are preserved across Reset, matching C++ ReadYourWritesTransaction::reset() + applyPersistentOptions. Updates creationTime so the timeout budget restarts (matches C++ reset() behavior).
In-flight Watch() calls ARE cancelled by Reset() (via reset()→cancelWatches(), below), but they surface as context.Canceled rather than the FDBError transaction_cancelled (1025) C++ raises via resetPromise.sendError — a known divergence (TODO "watch-path divergences" D5).
func (*Transaction) Set ¶
func (tx *Transaction) Set(key, value []byte)
Set writes a key-value pair.
func (*Transaction) SetAccessSystemKeys ¶
func (tx *Transaction) SetAccessSystemKeys()
SetAccessSystemKeys allows reading AND writing \xff prefix system keys.
func (*Transaction) SetAutoThrottleTag ¶
func (tx *Transaction) SetAutoThrottleTag(tag string) error
SetAutoThrottleTag adds a tag that is additionally eligible for ratekeeper auto-throttling, matching FDBTransactionOptions::AUTO_THROTTLE_TAG (NativeAPI.actor.cpp:7120-7124): the tag goes into BOTH the transaction tag set and the read tag set. The transaction set drives the GRV request and the commit tagSet; the read set is what C++ samples onto per-storage-server read requests.
func (*Transaction) SetBypassUnreadable ¶
func (tx *Transaction) SetBypassUnreadable(v bool)
SetBypassUnreadable mirrors FDB_TR_OPTION_BYPASS_UNREADABLE (ReadYourWrites.actor.cpp:2611-2613): reads of keys with pending versionstamped writes return the write-map value with the placeholder bytes as written instead of failing with accessed_unreadable (1036); SVK's unmodified-unreadable candidate range reads through to storage. RFC-098.
func (*Transaction) SetCausalReadRisky ¶
func (tx *Transaction) SetCausalReadRisky(v bool)
SetCausalReadRisky sets the causal-read-risky flag. When set, the read version may not reflect the latest committed writes.
func (*Transaction) SetLockAware ¶
func (tx *Transaction) SetLockAware(v bool)
SetLockAware sets the lock-aware flag on the commit request.
func (*Transaction) SetMaxRetryDelay ¶
func (tx *Transaction) SetMaxRetryDelay(ms int64)
SetMaxRetryDelay caps the exponential backoff between retries. Value in milliseconds. Matches C++ FDB_TR_OPTION_MAX_RETRY_DELAY.
func (*Transaction) SetNextWriteNoWriteConflictRange ¶
func (tx *Transaction) SetNextWriteNoWriteConflictRange()
SetNextWriteNoWriteConflictRange causes the next mutation to NOT add a write conflict range. Auto-resets after one mutation. Matches C++ FDB_TR_OPTION_NEXT_WRITE_NO_WRITE_CONFLICT_RANGE.
func (*Transaction) SetPriority ¶
func (tx *Transaction) SetPriority(p TransactionPriority)
SetPriority sets the transaction priority for GRV requests.
func (*Transaction) SetReadLockAware ¶
func (tx *Transaction) SetReadLockAware(v bool)
SetReadLockAware allows reads on locked databases without granting commit access. C++: options.readLockAware — only affects read path.
func (*Transaction) SetReadSystemKeys ¶
func (tx *Transaction) SetReadSystemKeys()
SetReadSystemKeys allows reading \xff prefix system keys.
func (*Transaction) SetReadVersion ¶
func (tx *Transaction) SetReadVersion(version int64)
SetReadVersion sets the read version manually.
func (*Transaction) SetReadYourWritesDisable ¶
func (tx *Transaction) SetReadYourWritesDisable()
SetReadYourWritesDisable disables RYW for regular (non-snapshot) reads. When set, Get/GetRange always read from the server, ignoring uncommitted writes. Matches FDB_TR_OPTION_READ_YOUR_WRITES_DISABLE.
libfdb_c forbids setting this option after any read or write: RYW setOptionImpl throws client_invalid_operation BEFORE assigning the option (ReadYourWrites.actor.cpp:2534-2542), so the option does NOT take effect; the throw lands in deferredError (the option call itself succeeds) and every subsequent op returns 2000 until reset (RFC-059). Match both halves: record the deferred error AND leave rywDisabled unset. A clean (pre-op) disable applies normally.
func (*Transaction) SetRetryLimit ¶
func (tx *Transaction) SetRetryLimit(retries int64)
SetRetryLimit limits the number of retries in OnError. A value of 0 means "don't retry at all" (first error escapes). A value of -1 means "unlimited" (default behavior). Matches C++ FDB_TR_OPTION_RETRY_LIMIT.
func (*Transaction) SetSizeLimit ¶
func (tx *Transaction) SetSizeLimit(limit int64)
SetSizeLimit sets the maximum transaction size in bytes. If the transaction exceeds this size, commit returns error 2101. Valid range: [32, 10_000_000]. Out-of-range values cause error 2006 at commit. A value of 0 disables the limit.
func (*Transaction) SetSkipGrvCache ¶
func (tx *Transaction) SetSkipGrvCache()
SetSkipGrvCache forces this transaction to bypass the GRV cache even if SetUseGrvCache was also set (SKIP_GRV_CACHE, 1102 — skip wins). RFC-104.
func (*Transaction) SetSnapshotRYWDisable ¶
func (tx *Transaction) SetSnapshotRYWDisable()
SetSnapshotRYWDisable decrements the snapshot-RYW enable count (stored as the disabled-oriented inverse, so it increments here). Matches FDB_TR_OPTION_SNAPSHOT_RYW_DISABLE (libfdb_c does enabledCount--). When the net count is > 0 (more disables than enables), Snapshot.Get/GetRange/GetKey read from the server, bypassing the RYW cache.
func (*Transaction) SetSnapshotRYWDisableCount ¶
func (tx *Transaction) SetSnapshotRYWDisableCount(n int)
SetSnapshotRYWDisableCount SETS the snapshot-RYW disable counter to n (vs the ++/-- of SetSnapshotRYWDisable/Enable). It seeds the per-tx counter to a database default. Setting (not incrementing) is idempotent under the retry replay of applyTxDefaults — matching libfdb_c, whose reset() re-seeds snapshotRywEnabled = db->snapshotRywEnabled each attempt rather than accumulating.
func (*Transaction) SetSnapshotRYWEnable ¶
func (tx *Transaction) SetSnapshotRYWEnable()
SetSnapshotRYWEnable re-enables RYW for snapshot reads, undoing one prior SetSnapshotRYWDisable. Matches FDB_TR_OPTION_SNAPSHOT_RYW_ENABLE (libfdb_c does enabledCount++). The option is a counter, not a toggle: two disables require two enables to re-enable, and an enable from the default pushes the count negative (still enabled).
func (*Transaction) SetSpanParent ¶
func (tx *Transaction) SetSpanParent(b []byte) error
SetSpanParent injects a parent trace context (FDBTransactionOptions::SPAN_PARENT, NativeAPI.actor.cpp:7126): a 33-byte IncludeVersion-serialized SpanContext. The transaction's span becomes a child of it (inherit traceID + flags, fresh spanID), and the linkage persists across retries (regenerateSpan honors spanParent).
func (*Transaction) SetTag ¶
func (tx *Transaction) SetTag(tag string) error
SetTag adds a tag to this transaction for tag-based throttling, matching FDBTransactionOptions::TAG (NativeAPI.actor.cpp:7115-7118).
Check order follows TagSet::addTag (TagThrottle.actor.cpp:35-39) exactly: length is validated first, then the count — and the count is checked BEFORE the duplicate test, so adding a duplicate to an already-full set still fails with too_many_tags rather than silently succeeding.
func (*Transaction) SetTenantId ¶
func (tx *Transaction) SetTenantId(id int64)
SetTenantId sets the tenant for this transaction. All operations will be scoped to the tenant's key space. Use NoTenantID (-1) for no tenant.
func (*Transaction) SetTimeout ¶
func (tx *Transaction) SetTimeout(ms int64)
SetTimeout sets a timeout in milliseconds for this transaction. The timeout is an overall budget from creation time (or last user Reset), NOT per-retry. OnError retries share the same deadline. A value of 0 disables the timeout. Matches C++ FDB_TR_OPTION_TIMEOUT.
func (*Transaction) SetUseGrvCache ¶
func (tx *Transaction) SetUseGrvCache()
SetUseGrvCache opts this transaction in to serving its read version from the database's GRV cache (USE_GRV_CACHE, 1101). Off by default — a default transaction issues a fresh proxy GRV, matching libfdb_c. RFC-104.
func (*Transaction) SetWriteConflictsDisabled ¶
func (tx *Transaction) SetWriteConflictsDisabled()
SetWriteConflictsDisabled disables write conflict ranges for all subsequent mutations. Use for insert-only batch writes where keys are guaranteed unique and all atomic operations commute (ADD, MAX, MIN). Significantly reduces commit request size and eliminates conflict buffer allocations.
func (*Transaction) Snapshot ¶
func (tx *Transaction) Snapshot() *Snapshot
Snapshot returns a snapshot view of this transaction. Snapshot reads do not add read conflict ranges, so they don't cause conflicts with concurrent writers. Same read version, same connection.
func (*Transaction) SnapshotRYWDisableCount ¶
func (tx *Transaction) SnapshotRYWDisableCount() int
SnapshotRYWDisableCount reports the net snapshot-RYW-disable count (> 0 means snapshot reads bypass the RYW cache). Read-only accessor used to verify database-level option propagation.
func (*Transaction) Tags ¶
func (tx *Transaction) Tags() []string
Tags returns the transaction tag set in insertion order. Insertion order is the wire order for the commit tagSet, so tests can assert it directly.
func (*Transaction) TenantId ¶
func (tx *Transaction) TenantId() int64
TenantId returns the current tenant ID for this transaction. Returns NoTenantID (-1) if no tenant is set.
func (*Transaction) Watch ¶
func (tx *Transaction) Watch(ctx context.Context, key []byte) error
Watch watches a key for changes. The server holds the connection open until the watched key's value changes from the version observed by this transaction. Returns nil when the key has changed, or an error on failure.
Matches C++ NativeAPI.actor.cpp watchValueMap/watchValue flow: locate storage server, send WatchValueRequest with current read version, long-poll for WatchValueReply. Retries on wrong_shard_server with cache invalidation.
The watch is a long-poll: there is no short timeout. The context's deadline (if any) controls the maximum wait time. RFC-170 (#8) note: this SYNCHRONOUS client API registers the watch at the READ version — it blocks in WatchPoll until the watch fires, so it cannot wait for a commit that (by construction) can only happen AFTER it returns. The committed-version deferral (register post-commit) is done by the ASYNC fdb facade Watch (which returns a future and blocks that future's goroutine on the commit-completion signal). Callers of this sync API that need committed-version semantics must commit before/around the watch (C++ setupWatches' committedVersion>0?committedVersion:readVersion applies at the facade, not here).
func (*Transaction) WatchActivation ¶
func (tx *Transaction) WatchActivation() *watchActivation
WatchActivation returns this incarnation's activation, lazily creating it on first use (a fresh handle before any Watch/commit has nil watchAct). Race-safe via CAS so two concurrent Watch() calls share one.
func (*Transaction) WatchPoll ¶
func (tx *Transaction) WatchPoll(watchCtx context.Context, watchCancel context.CancelFunc, key, value []byte, readVersion int64, span types.SpanContext) error
WatchPoll performs the ASYNCHRONOUS long-poll part of a watch: locate the storage server and wait for the WatchValueReply that fires when key's value differs from `value` (captured by WatchSetup), registered at `readVersion` (also captured by WatchSetup — NOT re-read from the possibly-reset transaction). Retries on wrong_shard_server with cache invalidation. Intended to run in the watch future's goroutine.
func (*Transaction) WatchSetup ¶
func (tx *Transaction) WatchSetup(ctx context.Context, key []byte) ([]byte, int64, types.SpanContext, context.Context, context.CancelFunc, error)
WatchSetup performs the SYNCHRONOUS part of a watch: pin the read version, add the read conflict, and read the value to watch — all at the transaction's read version. It returns BOTH the value bytes and the read version the watch must be registered against.
This MUST run within the watching transaction's active window (e.g. directly from Transaction.Watch, not from a detached goroutine that races later mutations). Two things are captured here, both for the same reason:
- the VALUE: if read late — after some other transaction already changed the key — the storage server would be told to watch the *new* value, see the current value already equals it, and never fire (a silent 10s-timeout flake);
- the READ VERSION: the async WatchPoll's sendWatch must NOT read tx.readVersion later, because the common `w := tr.Watch(k)` inside Database.Transact pattern commits and postCommitReset()s the transaction (readVersion → 0) before the future goroutine runs — sending the watch at version 0, which can error or register incorrectly. So the read version is captured synchronously here and threaded through to sendWatch.
type TransactionDefaults ¶
type TransactionDefaults struct {
Timeout int64 // FDB_DB_OPTION_TRANSACTION_TIMEOUT (ms), 0 = disabled
RetryLimit int // FDB_DB_OPTION_TRANSACTION_RETRY_LIMIT
MaxRetryDelay int64 // FDB_DB_OPTION_TRANSACTION_MAX_RETRY_DELAY (ms), 0 = use default
SizeLimit int64 // FDB_DB_OPTION_TRANSACTION_SIZE_LIMIT; 0/unset → 10 MB default (C++ has no "disabled" state)
HasRetryLimit bool
ReadSystemKeys bool // read \xff/* keys by default
AccessSysKeys bool // read+write \xff/* keys by default
}
TransactionDefaults holds database-level defaults applied to every new transaction. Matches C++ DatabaseContext::transactionDefaults.
type TransactionPriority ¶
type TransactionPriority int
TransactionPriority controls the GRV request priority. C++ GetReadVersionRequest::Flags encoding.
const ( PriorityDefault TransactionPriority = iota PriorityBatch PrioritySystemImmediate )
Source Files
¶
- clientmetrics.go
- clusterfile.go
- commitpath.go
- coordinator.go
- database.go
- ddsketch.go
- endpoint.go
- failure_monitor.go
- grv.go
- hedge.go
- inband_reply_error.go
- loadbalance.go
- locality.go
- mappedrange.go
- metrics.go
- options.go
- panicbackstop.go
- readpath.go
- rpc.go
- ryw.go
- ryw_getkey.go
- ryw_snapshot_cache.go
- sizelimits.go
- span.go
- tag_throttle.go
- tls.go
- topology.go
- transaction.go