blob: a9f6826deaa1b92788a6af969c9ecfe0053dc1b9 [file] [log] [blame]
// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package syncbase
import (
"time"
"v.io/v23/security/access"
)
// Id is a {blessing, name} pair, used to identify a database or a collection.
type Id struct {
Blessing string
Name string
}
// DevModeUpdateVClockOpts specifies what DevModeUpdateVClock should do, as
// described below.
type DevModeUpdateVClockOpts struct {
// If specified, sets the NTP host to talk to for subsequent NTP requests.
NtpHost string
// If Now is specified, the fake system clock is updated to the given values
// of Now and ElapsedTime. If Now is not specified (i.e. takes the zero
// value), the system clock is not touched by DevModeUpdateVClock.
Now time.Time
ElapsedTime time.Duration
// If specified, the clock daemon's local and/or NTP update code is triggered
// after applying the updates specified by the fields above. (Helpful because
// otherwise these only run periodically.) These functions work even if the
// clock daemon hasn't been started.
DoNtpUpdate bool
DoLocalUpdate bool
}
// BatchOptions configures a batch.
// TODO(sadovsky): Add more options, e.g. to configure isolation, timeouts,
// whether to track the read set and/or write set, etc.
// TODO(sadovsky): Maybe add a DefaultBatchOptions() function that initializes
// BatchOptions with our desired defaults. Clients would be encouraged to
// initialize their BatchOptions object using that function and then modify it
// to their liking.
type BatchOptions struct {
// Arbitrary string, typically used to describe the intent behind a batch.
// Hints are surfaced to clients during conflict resolution.
// TODO(sadovsky): Use "any" here?
Hint string
// ReadOnly specifies whether the batch should allow writes.
// If ReadOnly is set to true, Abort() should be used to release any resources
// associated with this batch (though it is not strictly required), and
// Commit() will always fail.
ReadOnly bool
}
// BatchHandle is a reference to a batch.
type BatchHandle string
// KeyValue is a key-value pair.
type KeyValue struct {
Key string
Value any
}
// SyncgroupSpec contains the specification for a syncgroup.
type SyncgroupSpec struct {
// Human-readable description of this syncgroup.
Description string
// Optional. If present, any syncbase that is the admin of this syncgroup
// is responsible for ensuring that the syncgroup is published to this
// syncbase instance.
PublishSyncbaseName string
// Permissions governing access to this syncgroup. Must include at least one
// admin.
Perms access.Permissions
// Data (set of collectionIds) covered by this syncgroup.
Collections []Id
// Mount tables at which to advertise this syncgroup, for rendezvous purposes.
// (Note that in addition to these mount tables, Syncbase also uses
// network-neighborhood-based discovery for rendezvous.)
// We expect most clients to specify a single mount table, but we accept an
// array of mount tables to permit the mount table to be changed over time
// without disruption.
// TODO(hpucha): Figure out a convention for advertising syncgroups in the
// mount table.
MountTables []string
// Specifies the privacy of this syncgroup. More specifically, specifies
// whether blobs in this syncgroup can be served to clients presenting
// blobrefs obtained from other syncgroups.
IsPrivate bool
}
// SyncgroupMemberInfo contains per-member metadata.
type SyncgroupMemberInfo struct {
SyncPriority byte
BlobDevType byte // See BlobDevType* constants.
}
// Types of device for blob ownership priority. Blobs tend to migrate to lower BlobDevType* values.
const (
BlobDevTypeNormal = int32(0) // Ordinary devices (example: laptop); uses 0 value because that's the default.
BlobDevTypeServer = int32(1) // Blobs migrate toward servers, which store them. (example: server in cloud)
BlobDevTypeLeaf = int32(2) // Blobs migrate from leaves, which have less storage (examples: a camera, phone)
)
// ResolverType defines the possible conflict resolution policies.
// A Conflict is defined as presence of two independent sets of updates
// originating from the same version of an object. Syncbase
// uses version vectors to determine sequence of changes to a given row. Hence
// if device A updates a row with key "foo" from version V3 to V4, then syncs
// with device B which further updates the same row from version V4 to V5 and
// then V5 is synced back to device A, device A will see V5 as a forward
// progression of "foo" and not a conflict with V3 of "foo". But in the
// meantime if device A had already updated "foo" again from version V4 to
// version V6 then there is a conflict between V5 and V6 with V4 being the
// common ancestor.
type ResolverType enum {
// LastWins is a policy which resolves a conflict between two writes by
// choosing the version which has the greatest timestamp.
// For example, if device A created V6 at time T1 and device B created V5 at
// time T2 where timestamp T2 > T1, then V5 will be accepted as the
// resolution of the conflict.
// Syncbase maintains an internal clock for creating write timestamps. This
// clock is kept up to date via regular synchronization with NTP and with
// other syncbases. As long as the syncbase has access to an NTP server
// directly or indirectly via another syncbase, the internal clock will
// track NTP time and correct any skews suffered by the local system clock.
LastWins
// AppResolves is a policy which allows an app to handle resolution of a
// conflict on its own. In order to receive the conflicts, the app needs to
// register a conflict resolution stream using
// Database.StartConflictResolver().
AppResolves
// Defer is a policy that allows an instance of an app to outsource its
// conflict resolution to some other instance, typically a more capable
// instance (in terms of resources, knowledge or permissions) such as a
// cloud or admin instance.
Defer
}
// ConflictInfo contains information to fully specify a conflict
// for a key, providing the (local, remote, ancestor) tuple.
// A key under conflict can be a part of a batch in local, remote or both
// updates. Since the batches can have more than one key, all ConflictInfos
// for the keys within the batches are grouped together into a single conflict
// batch and sent as a stream with the Continued field representing conflict
// batch boundaries.
type ConflictInfo struct {
// Data is a unit chunk of ConflictInfo which can be sent over the conflict
// stream.
Data ConflictData
// Continued represents whether the batch of ConflictInfos has ended.
Continued bool
}
// ConflictData represents a unit of conflict data sent over the stream. It
// can either contain information about a Batch or about an operation done
// on a row.
type ConflictData union {
Batch BatchInfo
Row RowInfo
}
type BatchInfo struct {
// Id is an identifier for a batch contained in a conflict. It is
// unique only in the context of a given conflict. Its purpose is solely to
// group one or more RowInfo objects together to represent a batch that
// was committed by the client.
Id uint64
// Hint is the hint provided by the client when this batch was committed.
Hint string
// Source states where the batch comes from.
Source BatchSource
}
// BatchSource represents where the batch was committed.
type BatchSource enum {
// Local represents that the batch was committed on local syncbase.
Local
// Remote represents that the batch was committed on remote syncbase.
Remote
}
// RowInfo contains a single operation performed on a row (in case of read or
// write) or a range or rows (in case of scan) along with a mapping to each
// of the batches that this operation belongs to.
// For example, if Row1 was updated on local syncbase conflicting with a write
// on remote syncbase as part of two separate batches, then it will be
// represented by a single RowInfo with Write Operation containing the
// respective local and remote values along with the batch id for both batches
// stored in the BatchIds field.
type RowInfo struct {
// Op is a specific operation represented by RowInfo
Op Operation
// BatchIds contains ids of all batches that this RowInfo is a part of.
BatchIds []uint64
}
// Operation represents a specific operation on a row or a set of rows that is
// a part of the conflict.
type Operation union {
// Read represents a read operation performed on a specific row. For a given
// row key there can only be at max one Read operation within a conflict.
Read RowOp
// Write represents a write operation performed on a specific row. For a
// given row key there can only be at max one Write operation within a
// conflict.
Write RowOp
// Scan represents a scan operation performed over a specific range of keys.
// For a given key range there can be at max one ScanOp within the Conflict.
Scan ScanOp
}
// RowOp represents a read or write operation on a row corresponding to the
// given key.
type RowOp struct {
// The key under conflict.
Key string
// LocalValue contains the value read or written by local syncbase or nil.
LocalValue ?Value
// RemoteValue contains the value read or written by remote syncbase or nil.
RemoteValue ?Value
// AncestorValue contains the value for the key which is the lowest common
// ancestor of the two values represented by LocalValue and RemoteValue or
// nil if no ancestor exists or if the operation was read.
AncestorValue ?Value
}
// ScanOp provides details of a scan operation.
type ScanOp struct {
// Start contains the starting key for a range scan.
Start string
// Limit contains the end key for a range scan.
Limit string
}
// Value contains the encoded bytes for a row's value stored in syncbase.
type Value struct {
// State provides information about whether the field Bytes is empty or
// not and if it is empty then why.
State ValueState
// VOM encoded bytes for a row's value or nil if the row was deleted.
Bytes any
// Write timestamp for this value
WriteTs time.Time
}
// ValueState represents the state for Value object providing information about
// whether the Value object's Byte field is empty or not.
type ValueState enum {
// Exists means that the value can be obtained from Bytes field.
Exists
// NoExists means that the value is known not to exist and Bytes field is
// empty.
NoExists
// Deleted means that this value was deleted and the Bytes field is empty.
Deleted
// Unknown means that the Bytes field is empty but there may or may not be
// an associated value. This can happen if a local row, which does
// not have a conflict, was pulled into a conflict due to a batch. In such
// a case we don't have the corresponding value for remote and the
// RemoteValue will have "Unknown" state and empty Bytes field.
Unknown
}
// ValueSelection represents the value that was selected as the final resolution
// for a conflict.
type ValueSelection enum {
// Local should be used if local value is picked as the final result.
Local
// Remote should be used if remote value is picked as the final result.
Remote
// Other should be used if a new value different from local and remote is
// used as the final result.
Other
}
// ResolutionInfo contains the application’s reply to a conflict for a key,
// providing the resolution value. The resolution may be over a group of keys
// in which case the application must send a stream of ResolutionInfos with
// the Continued field for the last ResolutionInfo representing the end of the
// batch with a value false. ResolutionInfos sent as part of a batch will be
// committed as a batch. If the commit fails, the Conflict will be re-sent.
type ResolutionInfo struct {
// Key is the key under conflict.
Key string
// Selection represents the value that was selected as resolution.
Selection ValueSelection
// Result is the resolved value for the key. This field should be used only
// if value of Selection field is 'Other'. If the result of a resolution is
// delete for this key then add Value with nil Bytes.
Result ?Value
// Continued represents whether the batch of ResolutionInfos has ended.
Continued bool
}
// SchemaMetadata maintains metadata related to the schema of a given database.
// There is one SchemaMetadata per database.
type SchemaMetadata struct {
// Non negative Schema version number. Should be increased with every schema
// change (e.g. adding fields to structs) that cannot be handled by previous
// versions of the app.
// TODO(jlodhia,ivanpi): Deprecated, needs update to multiple parallel version
// semantics.
Version int32
Policy CrPolicy
}
// For a given row with a conflict, all rules are matched against the row.
// If no rules match the row, we default to "LastWins". If multiple
// rules match the row, ties are broken as follows:
// 1. If one match has a longer prefix than the other, take that one.
// 2. Else, if only one match specifies a type, take that one.
// 3. Else, the two matches are identical; take the last one in the Rules array.
type CrPolicy struct {
Rules []CrRule
}
// CrRule provides a filter and the type of resolution to perform for a row
// under conflict that passes the filter.
type CrRule struct {
// CollectionId is the id of the collection that this rule applies to.
CollectionId Id
// KeyPrefix represents the set of keys within the given collection for which
// this policy applies. CollectionId must not be empty if this field is set.
KeyPrefix string
// Type includes the full package path for the value type for which this
// policy applies.
Type string
// Policy for resolving conflict.
Resolver ResolverType
}
// BlobRef is a reference to a blob.
type BlobRef string
const (
NullBlobRef = BlobRef("")
)
// BlobFetchState represents the state transitions of a blob fetch.
type BlobFetchState enum {
Pending // Fetch request is queued.
Locating // Blob discovery is in progress to find a source for the blob.
Fetching // Blob transfer is in progress.
Done // Blob is locally cached.
}
// BlobFetchStatus describes the progress of an asynchronous blob fetch.
type BlobFetchStatus struct {
State BlobFetchState // State of the blob fetch request.
Received int64 // Total number of bytes received.
Total int64 // Blob size.
}
// CollectionRowPattern contains SQL LIKE-style glob patterns ('%' and '_'
// wildcards, '\' as escape character) for matching rows and collections by
// name components.
// Collection blessing and name patterns are not allowed to be empty, but the
// row key pattern is (for matching only collections and no rows).
type CollectionRowPattern struct {
CollectionBlessing string
CollectionName string
RowKey string
}
// StoreChange is the new value for a watched entity.
type StoreChange struct {
// Value is the new value for the entity if the Change state equals to Exists,
// otherwise the Value is nil. The Value type is determined by the entity
// type:
// - for row updates, Value is the actual row value.
// - for collection updates, Value is a StoreChangeCollectionInfo.
// - for the initial root entity update, Value is nil.
Value any
// FromSync indicates whether the change came from sync. If FromSync is
// false, then the change originated from the local device.
// Note: FromSync is always false for initial state Changes.
FromSync bool
}
// StoreChangeCollectionInfo represents collection metadata in a StoreChange.
type StoreChangeCollectionInfo struct {
// Allowed lists all permissions that the client has on the collection. It is
// separate from Perms to allow clients lacking Admin permission, who are not
// allowed to read the full Perms, to find out what permissions they have on
// the Collection. If the client has no Read permission (Allowed does not
// contain Read), row updates on that collection will be silently skipped.
// TODO(ivanpi): Row updates are currently checked against the most recently
// committed collection permissions, which may be out of sync with the last
// seen permissions on the watch stream (that were in effect when the row was
// written). This can result in the watch stream skipping or failing to skip
// rows if Read access has changed. Destroying the collection may also result
// in skipped rows since there are no permissions to check against.
Allowed set[access.Tag]
// Perms contains the full collection permissions only if the client has Admin
// permissions on the collection (Allowed contains Admin). Otherwise, Perms is
// nil.
// TODO(ivanpi): Update when Admin tag is split into AdminRead and AdminWrite.
Perms access.Permissions
}
// Types of discovery service attributes.
const (
// DiscoveryAttrPeer is the globally unique identifier of the advertised syncbase.
DiscoveryAttrPeer = "p"
// DiscoveryAttrSyncgroupName is the name of the advertised syncgroup.
DiscoveryAttrSyncgroupName = "s"
// DiscoveryAttrSyncgroupBlessing is the blessing of the creator of the syncgroup.
DiscoveryAttrSyncgroupBlessing = "sb"
// DiscoveryAttrDatabaseName is the name component of a database ID, that this syncgroup is a part of.
DiscoveryAttrDatabaseName = "d"
// DiscoveryAttrDatabaseBlessing is the app blessing component of a database ID,
// that this syncgroup is a part of.
DiscoveryAttrDatabaseBlessing = "db"
)