syncbase: improve comments about concurrency
- Updates client-facing model.go's to mention what happens
with Scan/Exec and concurrent writes
- Updates storage engine model.go to document concurrency
semantics
- Other misc related comment tweaks
Change-Id: Iad5f50334ae942ffab06170caf5ec8c9c4b270bf
diff --git a/v23/services/syncbase/nosql/service.vdl b/v23/services/syncbase/nosql/service.vdl
index c990976..ad6b9f5 100644
--- a/v23/services/syncbase/nosql/service.vdl
+++ b/v23/services/syncbase/nosql/service.vdl
@@ -26,9 +26,8 @@
// BeginBatch creates a new batch. It returns an App-relative name for a
// Database handle bound to this batch. If this Database is already bound to a
- // batch, BeginBatch() will fail with ErrBoundToBatch.
- //
- // Concurrency semantics are documented in model.go.
+ // batch, BeginBatch() will fail with ErrBoundToBatch. Concurrency semantics
+ // are documented in model.go.
BeginBatch(bo BatchOptions) (string | error) {access.Read}
// Commit persists the pending changes to the database.
@@ -36,9 +35,8 @@
// ErrNotBoundToBatch.
Commit() error {access.Read}
- // Exec executes a syncQL query and returns all results as specified by
- // in the query's select clause. The returned stream reads
- // from a consistent snapshot taken at the time of the Exec RPC.
+ // Exec executes a syncQL query and returns all results as specified by in the
+ // query's select clause. Concurrency semantics are documented in model.go.
Exec(query string) stream<_, []any> error {access.Read}
// Abort notifies the server that any pending changes can be discarded.
@@ -71,8 +69,8 @@
DeleteRowRange(start, limit []byte) error {access.Write}
// Scan returns all rows in the given half-open range [start, limit). If limit
- // is "", all rows with keys >= start are included. The returned stream reads
- // from a consistent snapshot taken at the time of the Scan RPC.
+ // is "", all rows with keys >= start are included. Concurrency semantics are
+ // documented in model.go.
Scan(start, limit []byte) stream<_, KeyValue> error {access.Read}
// GetPermissions returns an array of (prefix, perms) pairs. The array is
diff --git a/v23/services/syncbase/nosql/service.vdl.go b/v23/services/syncbase/nosql/service.vdl.go
index 2af0a60..503415e 100644
--- a/v23/services/syncbase/nosql/service.vdl.go
+++ b/v23/services/syncbase/nosql/service.vdl.go
@@ -470,17 +470,15 @@
Delete(*context.T, ...rpc.CallOpt) error
// BeginBatch creates a new batch. It returns an App-relative name for a
// Database handle bound to this batch. If this Database is already bound to a
- // batch, BeginBatch() will fail with ErrBoundToBatch.
- //
- // Concurrency semantics are documented in model.go.
+ // batch, BeginBatch() will fail with ErrBoundToBatch. Concurrency semantics
+ // are documented in model.go.
BeginBatch(ctx *context.T, bo BatchOptions, opts ...rpc.CallOpt) (string, error)
// Commit persists the pending changes to the database.
// If this Database is not bound to a batch, Commit() will fail with
// ErrNotBoundToBatch.
Commit(*context.T, ...rpc.CallOpt) error
- // Exec executes a syncQL query and returns all results as specified by
- // in the query's select clause. The returned stream reads
- // from a consistent snapshot taken at the time of the Exec RPC.
+ // Exec executes a syncQL query and returns all results as specified by in the
+ // query's select clause. Concurrency semantics are documented in model.go.
Exec(ctx *context.T, query string, opts ...rpc.CallOpt) (DatabaseExecClientCall, error)
// Abort notifies the server that any pending changes can be discarded.
// It is not strictly required, but it may allow the server to release locks
@@ -675,17 +673,15 @@
Delete(*context.T, rpc.ServerCall) error
// BeginBatch creates a new batch. It returns an App-relative name for a
// Database handle bound to this batch. If this Database is already bound to a
- // batch, BeginBatch() will fail with ErrBoundToBatch.
- //
- // Concurrency semantics are documented in model.go.
+ // batch, BeginBatch() will fail with ErrBoundToBatch. Concurrency semantics
+ // are documented in model.go.
BeginBatch(ctx *context.T, call rpc.ServerCall, bo BatchOptions) (string, error)
// Commit persists the pending changes to the database.
// If this Database is not bound to a batch, Commit() will fail with
// ErrNotBoundToBatch.
Commit(*context.T, rpc.ServerCall) error
- // Exec executes a syncQL query and returns all results as specified by
- // in the query's select clause. The returned stream reads
- // from a consistent snapshot taken at the time of the Exec RPC.
+ // Exec executes a syncQL query and returns all results as specified by in the
+ // query's select clause. Concurrency semantics are documented in model.go.
Exec(ctx *context.T, call DatabaseExecServerCall, query string) error
// Abort notifies the server that any pending changes can be discarded.
// It is not strictly required, but it may allow the server to release locks
@@ -756,17 +752,15 @@
Delete(*context.T, rpc.ServerCall) error
// BeginBatch creates a new batch. It returns an App-relative name for a
// Database handle bound to this batch. If this Database is already bound to a
- // batch, BeginBatch() will fail with ErrBoundToBatch.
- //
- // Concurrency semantics are documented in model.go.
+ // batch, BeginBatch() will fail with ErrBoundToBatch. Concurrency semantics
+ // are documented in model.go.
BeginBatch(ctx *context.T, call rpc.ServerCall, bo BatchOptions) (string, error)
// Commit persists the pending changes to the database.
// If this Database is not bound to a batch, Commit() will fail with
// ErrNotBoundToBatch.
Commit(*context.T, rpc.ServerCall) error
- // Exec executes a syncQL query and returns all results as specified by
- // in the query's select clause. The returned stream reads
- // from a consistent snapshot taken at the time of the Exec RPC.
+ // Exec executes a syncQL query and returns all results as specified by in the
+ // query's select clause. Concurrency semantics are documented in model.go.
Exec(ctx *context.T, call *DatabaseExecServerCallStub, query string) error
// Abort notifies the server that any pending changes can be discarded.
// It is not strictly required, but it may allow the server to release locks
@@ -869,7 +863,7 @@
},
{
Name: "BeginBatch",
- Doc: "// BeginBatch creates a new batch. It returns an App-relative name for a\n// Database handle bound to this batch. If this Database is already bound to a\n// batch, BeginBatch() will fail with ErrBoundToBatch.\n//\n// Concurrency semantics are documented in model.go.",
+ Doc: "// BeginBatch creates a new batch. It returns an App-relative name for a\n// Database handle bound to this batch. If this Database is already bound to a\n// batch, BeginBatch() will fail with ErrBoundToBatch. Concurrency semantics\n// are documented in model.go.",
InArgs: []rpc.ArgDesc{
{"bo", ``}, // BatchOptions
},
@@ -885,7 +879,7 @@
},
{
Name: "Exec",
- Doc: "// Exec executes a syncQL query and returns all results as specified by\n// in the query's select clause. The returned stream reads\n// from a consistent snapshot taken at the time of the Exec RPC.",
+ Doc: "// Exec executes a syncQL query and returns all results as specified by in the\n// query's select clause. Concurrency semantics are documented in model.go.",
InArgs: []rpc.ArgDesc{
{"query", ``}, // string
},
@@ -957,8 +951,8 @@
// limit is "", all rows with keys >= start are included.
DeleteRowRange(ctx *context.T, start []byte, limit []byte, opts ...rpc.CallOpt) error
// Scan returns all rows in the given half-open range [start, limit). If limit
- // is "", all rows with keys >= start are included. The returned stream reads
- // from a consistent snapshot taken at the time of the Scan RPC.
+ // is "", all rows with keys >= start are included. Concurrency semantics are
+ // documented in model.go.
Scan(ctx *context.T, start []byte, limit []byte, opts ...rpc.CallOpt) (TableScanClientCall, error)
// GetPermissions returns an array of (prefix, perms) pairs. The array is
// sorted from longest prefix to shortest, so element zero is the one that
@@ -1118,8 +1112,8 @@
// limit is "", all rows with keys >= start are included.
DeleteRowRange(ctx *context.T, call rpc.ServerCall, start []byte, limit []byte) error
// Scan returns all rows in the given half-open range [start, limit). If limit
- // is "", all rows with keys >= start are included. The returned stream reads
- // from a consistent snapshot taken at the time of the Scan RPC.
+ // is "", all rows with keys >= start are included. Concurrency semantics are
+ // documented in model.go.
Scan(ctx *context.T, call TableScanServerCall, start []byte, limit []byte) error
// GetPermissions returns an array of (prefix, perms) pairs. The array is
// sorted from longest prefix to shortest, so element zero is the one that
@@ -1155,8 +1149,8 @@
// limit is "", all rows with keys >= start are included.
DeleteRowRange(ctx *context.T, call rpc.ServerCall, start []byte, limit []byte) error
// Scan returns all rows in the given half-open range [start, limit). If limit
- // is "", all rows with keys >= start are included. The returned stream reads
- // from a consistent snapshot taken at the time of the Scan RPC.
+ // is "", all rows with keys >= start are included. Concurrency semantics are
+ // documented in model.go.
Scan(ctx *context.T, call *TableScanServerCallStub, start []byte, limit []byte) error
// GetPermissions returns an array of (prefix, perms) pairs. The array is
// sorted from longest prefix to shortest, so element zero is the one that
@@ -1276,7 +1270,7 @@
},
{
Name: "Scan",
- Doc: "// Scan returns all rows in the given half-open range [start, limit). If limit\n// is \"\", all rows with keys >= start are included. The returned stream reads\n// from a consistent snapshot taken at the time of the Scan RPC.",
+ Doc: "// Scan returns all rows in the given half-open range [start, limit). If limit\n// is \"\", all rows with keys >= start are included. Concurrency semantics are\n// documented in model.go.",
InArgs: []rpc.ArgDesc{
{"start", ``}, // []byte
{"limit", ``}, // []byte
diff --git a/v23/syncbase/nosql/model.go b/v23/syncbase/nosql/model.go
index bd0903b..858c59a 100644
--- a/v23/syncbase/nosql/model.go
+++ b/v23/syncbase/nosql/model.go
@@ -34,12 +34,14 @@
ListTables(ctx *context.T) ([]string, error)
// Exec executes a syncQL query.
- // If no error is returned, Exec returns an array of headers (i.e., column names)
- // and a result stream which contains an array of values for each row that matches
- // the query. The number of values returned in each row of the result stream will
- // match the size of the headers string array.
- // TODO(jkline): Our storage engines don't yet reflect prior puts or deletes
- // performed inside the batch. This also applies to Scan.
+ // If no error is returned, Exec returns an array of headers (i.e., column
+ // names) and a result stream which contains an array of values for each row
+ // that matches the query. The number of values returned in each row of the
+ // result stream will match the size of the headers string array.
+ // Concurrency semantics: It is legal to perform writes concurrently with
+ // Exec. The returned stream reads from a consistent snapshot taken at the
+ // time of the RPC, and will not reflect subsequent writes to keys not yet
+ // reached by the stream.
Exec(ctx *context.T, query string) ([]string, ResultStream, error)
}
@@ -66,19 +68,21 @@
DeleteTable(ctx *context.T, relativeName string) error
// BeginBatch creates a new batch. Instead of calling this function directly,
- // clients are recommended to use the RunInBatch() helper function, which
+ // clients are encouraged to use the RunInBatch() helper function, which
// detects "concurrent batch" errors and handles retries internally.
//
// Default concurrency semantics:
- // - Reads inside a batch see a consistent snapshot, taken during
- // BeginBatch(), and will also see the effect of writes and deletes inside
- // the batch.
+ // - Reads (e.g. gets, scans) inside a batch operate over a consistent
+ // snapshot taken during BeginBatch(), and will see the effects of prior
+ // writes performed inside the batch.
// - Commit() may fail with ErrConcurrentBatch, indicating that after
// BeginBatch() but before Commit(), some concurrent routine wrote to a key
- // that matches a key or row-range read inside this batch. (Writes inside a
- // batch cannot cause that batch's Commit() to fail.)
- // - Other methods (e.g. Get) will never fail with error ErrConcurrentBatch,
- // even if it is known that Commit() will fail with this error.
+ // that matches a key or row-range read inside this batch.
+ // - Other methods will never fail with error ErrConcurrentBatch, even if it
+ // is known that Commit() will fail with this error.
+ //
+ // Once a batch has been committed or aborted, subsequent method calls will
+ // fail with no effect.
//
// Concurrency semantics can be configured using BatchOptions.
// TODO(sadovsky): Maybe use varargs for options.
@@ -156,8 +160,11 @@
Delete(ctx *context.T, r RowRange) error
// Scan returns all rows in the given half-open range [start, limit). If limit
- // is "", all rows with keys >= start are included. The returned stream reads
- // from a consistent snapshot taken at the time of the Scan RPC.
+ // is "", all rows with keys >= start are included.
+ // Concurrency semantics: It is legal to perform writes concurrently with
+ // Scan. The returned stream reads from a consistent snapshot taken at the
+ // time of the RPC, and will not reflect subsequent writes to keys not yet
+ // reached by the stream.
// See helpers nosql.Prefix(), nosql.Range(), nosql.SingleRow().
Scan(ctx *context.T, r RowRange) Stream
@@ -233,7 +240,8 @@
Cancel()
}
-// ResultStream is an interface for iterating through rows resulting from an Exec.
+// ResultStream is an interface for iterating through rows resulting from an
+// Exec.
type ResultStream interface {
// Advance stages an result so the client can retrieve it with Result.
// Advance returns true iff there is a result to retrieve. The client must
diff --git a/x/ref/services/syncbase/store/model.go b/x/ref/services/syncbase/store/model.go
index 5155cd9..a2c48f0 100644
--- a/x/ref/services/syncbase/store/model.go
+++ b/x/ref/services/syncbase/store/model.go
@@ -19,8 +19,11 @@
// fails with ErrUnknownKey.
Get(key, valbuf []byte) ([]byte, error)
- // Scan returns all rows with keys in range [start, limit).
- // TODO(sadovsky): Describe the behavior of Scan with concurrent writes.
+ // Scan returns all rows with keys in range [start, limit). If limit is "",
+ // all rows with keys >= start are included.
+ // Concurrency semantics: It is legal to perform writes concurrently with
+ // Scan. The returned stream may or may not reflect subsequent writes to keys
+ // not yet reached by the stream.
Scan(start, limit []byte) Stream
}
@@ -56,9 +59,21 @@
NewSnapshot() Snapshot
}
-// Transaction provides a mechanism for atomic reads and writes.
+// Transaction provides a mechanism for atomic reads and writes. Instead of
+// calling this function directly, clients are encouraged to use the
+// RunInTransaction() helper function, which detects "concurrent transaction"
+// errors and handles retries internally.
//
-// Reads do reflect writes and deletes performed within this transaction.
+// Default concurrency semantics:
+// - Reads (e.g. gets, scans) inside a transaction operate over a consistent
+// snapshot taken during NewTransaction(), and will see the effects of prior
+// writes performed inside the transaction.
+// - Commit() may fail with ErrConcurrentTransaction, indicating that after
+// NewTransaction() but before Commit(), some concurrent routine wrote to a
+// key that matches a key or row-range read inside this transaction.
+// - Other methods will never fail with error ErrConcurrentTransaction, even if
+// it is known that Commit() will fail with this error.
+//
// Once a transaction has been committed or aborted, subsequent method calls
// will fail with no effect.
type Transaction interface {