Documentation
¶
Overview ¶
Package pebble is the v3 storage engine for baton-sdk. It is the implementation behind c1zstore.EnginePebble and the v3 envelope.
Index ¶
- Constants
- Variables
- func AddFoldDeadBytes(w connectorstore.Writer, n int64) bool
- func AppendResourceIndexKeyRawBytes(dst []byte, parentRT []byte, parentID []byte, resourceTypeID []byte, ...) []byte
- func AssetLowerBound() []byte
- func AssetUpperBound() []byte
- func BuildManifest(encoding c1zstore.PayloadEncoding) (*c1zv3.C1ZManifestV3, error)
- func BuildManifestWithSyncRuns(ctx context.Context, e *Engine, encoding c1zstore.PayloadEncoding) (*c1zv3.C1ZManifestV3, error)
- func CachedSyncStats(ctx context.Context, e *Engine, syncID string) (*reader_v2.SyncStats, bool, error)
- func CloseEngineOnly(w connectorstore.Writer) error
- func DigestLowerBound() []byte
- func DigestUpperBound() []byte
- func EntitlementByResourceLowerBound() []byte
- func EntitlementByResourceUpperBound() []byte
- func EntitlementIndexKeys(r *v3.EntitlementRecord) [][]byte
- func EntitlementLowerBound() []byte
- func EntitlementRecordIdentityKey(r *v3.EntitlementRecord) string
- func EntitlementRecordKey(externalID string) []byte
- func EntitlementUpperBound() []byte
- func ForEachEntitlementIndexKey(r *v3.EntitlementRecord, yield func([]byte) error) error
- func ForEachEntitlementIndexKeyRaw(resourceRT string, resourceID string, externalID string, ...) error
- func ForEachGrantIndexKey(r *v3.GrantRecord, yield func([]byte) error) error
- func ForEachGrantIndexKeyRaw(entitlementRT string, entitlementResourceID string, entitlementID string, ...) error
- func ForEachResourceIndexKey(r *v3.ResourceRecord, yield func([]byte) error) error
- func ForEachResourceIndexKeyRaw(parentRT string, parentID string, resourceTypeID string, resourceID string, ...) error
- func GrantByEntPrincHashLowerBound() []byte
- func GrantByEntPrincHashUpperBound() []byte
- func GrantByEntitlementLowerBound() []byte
- func GrantByEntitlementResourceLowerBound() []byte
- func GrantByEntitlementResourceUpperBound() []byte
- func GrantByEntitlementUpperBound() []byte
- func GrantByNeedsExpansionLowerBound() []byte
- func GrantByNeedsExpansionUpperBound() []byte
- func GrantByPrincipalLowerBound() []byte
- func GrantByPrincipalResourceTypeLowerBound() []byte
- func GrantByPrincipalResourceTypeUpperBound() []byte
- func GrantByPrincipalUpperBound() []byte
- func GrantContentHash(g *v2.Grant) (uint64, error)
- func GrantIndexKeys(r *v3.GrantRecord) [][]byte
- func GrantLowerBound() []byte
- func GrantPartitionFromPrimaryKey(key []byte) (string, bool)
- func GrantRecordIdentityKey(r *v3.GrantRecord) string
- func GrantRecordKey(externalID string) []byte
- func GrantUpperBound() []byte
- func MarkStoreDirty(w connectorstore.Writer) bool
- func NormalizeForFixtureSave(ctx context.Context, w connectorstore.Writer, syncID string) error
- func ReadSyncStatsRecord(ctx context.Context, e *Engine, syncID string) (*v3.SyncStatsRecord, error)
- func ResourceByParentLowerBound() []byte
- func ResourceByParentUpperBound() []byte
- func ResourceIndexKeys(r *v3.ResourceRecord) [][]byte
- func ResourceLowerBound() []byte
- func ResourceRecordKey(resourceTypeID string, resourceID string) []byte
- func ResourceTypeLowerBound() []byte
- func ResourceTypeRecordKey(externalID string) []byte
- func ResourceTypeUpperBound() []byte
- func ResourceUpperBound() []byte
- func SyncRunLowerBound() []byte
- func SyncRunUpperBound() []byte
- func SyncStatsFromRecord(stats *v3.SyncStatsRecord) *reader_v2.SyncStats
- func SyncStatsSidecarLowerBound() []byte
- func SyncStatsSidecarUpperBound() []byte
- func V2EntitlementToV3(syncID string, e *v2.Entitlement) *v3.EntitlementRecord
- func V2GrantToV3(syncID string, g *v2.Grant) *v3.GrantRecord
- func V2ResourceToV3(syncID string, r *v2.Resource) *v3.ResourceRecord
- func V2ResourceTypeToV3(syncID string, rt *v2.ResourceType) *v3.ResourceTypeRecord
- func V3EntitlementToV2(r *v3.EntitlementRecord) *v2.Entitlement
- func V3GrantToV2(r *v3.GrantRecord) *v2.Grant
- func V3ResourceToV2(r *v3.ResourceRecord) *v2.Resource
- func V3ResourceTypeToV2(r *v3.ResourceTypeRecord) *v2.ResourceType
- type Adapter
- func (a *Adapter) CheckpointSync(ctx context.Context, syncToken string) error
- func (a *Adapter) Cleanup(ctx context.Context) error
- func (a *Adapter) CleanupCandidates(ctx context.Context) ([]c1zstore.SyncRun, error)
- func (a *Adapter) Close(ctx context.Context) error
- func (a *Adapter) CurrentDBSizeBytes() (int64, error)
- func (a *Adapter) CurrentSyncID() string
- func (a *Adapter) CurrentSyncStep(ctx context.Context) (string, error)
- func (a *Adapter) DeleteGrant(ctx context.Context, grantID string) error
- func (a *Adapter) DeleteGrantByRefs(ctx context.Context, grant *v2.Grant) error
- func (a *Adapter) EndSync(ctx context.Context) error
- func (a *Adapter) FileOps() c1zstore.FileOps
- func (a *Adapter) FileOpsWithEncoding(encoding c1zstore.PayloadEncoding) c1zstore.FileOps
- func (a *Adapter) GetAsset(ctx context.Context, req *v2.AssetServiceGetAssetRequest) (string, io.Reader, error)
- func (a *Adapter) GetEntitlement(ctx context.Context, ...) (*reader_v2.EntitlementsReaderServiceGetEntitlementResponse, error)
- func (a *Adapter) GetEntitlementGrantDigest(ctx context.Context, ent *v2.Entitlement) (connectorstore.GrantDigest, bool, error)
- func (a *Adapter) GetEntitlementGrantDigestNodes(ctx context.Context, ent *v2.Entitlement, level int) ([]connectorstore.GrantDigestNode, bool, error)
- func (a *Adapter) GetGrant(ctx context.Context, req *reader_v2.GrantsReaderServiceGetGrantRequest) (*reader_v2.GrantsReaderServiceGetGrantResponse, error)
- func (a *Adapter) GetLatestFinishedSync(ctx context.Context, ...) (*reader_v2.SyncsReaderServiceGetLatestFinishedSyncResponse, error)
- func (a *Adapter) GetResource(ctx context.Context, req *reader_v2.ResourcesReaderServiceGetResourceRequest) (*reader_v2.ResourcesReaderServiceGetResourceResponse, error)
- func (a *Adapter) GetResourceType(ctx context.Context, ...) (*reader_v2.ResourceTypesReaderServiceGetResourceTypeResponse, error)
- func (a *Adapter) GetSync(ctx context.Context, req *reader_v2.SyncsReaderServiceGetSyncRequest) (*reader_v2.SyncsReaderServiceGetSyncResponse, error)
- func (a *Adapter) GrantStats(ctx context.Context, syncType connectorstore.SyncType, syncID string) (map[string]int64, error)
- func (a *Adapter) Grants() c1zstore.GrantStore
- func (a *Adapter) GrantsForEntitlementPrincipalSorted() bool
- func (a *Adapter) InitCurrentSync(ctx context.Context) error
- func (a *Adapter) LatestFinishedSyncID(ctx context.Context, syncType connectorstore.SyncType) (string, error)
- func (a *Adapter) ListEntitlements(ctx context.Context, req *v2.EntitlementsServiceListEntitlementsRequest) (*v2.EntitlementsServiceListEntitlementsResponse, error)
- func (a *Adapter) ListEntitlementsByIds(ctx context.Context, ...) (*reader_v2.EntitlementsReaderServiceListEntitlementsByIdsResponse, error)
- func (a *Adapter) ListGrantPrincipalKeysForEntitlement(ctx context.Context, entitlement *v2.Entitlement, pageToken string, ...) ([]string, string, error)
- func (a *Adapter) ListGrants(ctx context.Context, req *v2.GrantsServiceListGrantsRequest) (*v2.GrantsServiceListGrantsResponse, error)
- func (a *Adapter) ListGrantsForEntitlement(ctx context.Context, ...) (*reader_v2.GrantsReaderServiceListGrantsForEntitlementResponse, error)
- func (a *Adapter) ListGrantsForEntitlements(ctx context.Context, ...) (*reader_v2.GrantsReaderServiceListGrantsForEntitlementsResponse, error)
- func (a *Adapter) ListGrantsForPrincipal(ctx context.Context, ...) (*reader_v2.GrantsReaderServiceListGrantsForPrincipalResponse, error)
- func (a *Adapter) ListGrantsForResourceType(ctx context.Context, ...) (*reader_v2.GrantsReaderServiceListGrantsForResourceTypeResponse, error)
- func (a *Adapter) ListResourceTypes(ctx context.Context, req *v2.ResourceTypesServiceListResourceTypesRequest) (*v2.ResourceTypesServiceListResourceTypesResponse, error)
- func (a *Adapter) ListResources(ctx context.Context, req *v2.ResourcesServiceListResourcesRequest) (*v2.ResourcesServiceListResourcesResponse, error)
- func (a *Adapter) ListResourcesByIds(ctx context.Context, ...) (*reader_v2.ResourcesReaderServiceListResourcesByIdsResponse, error)
- func (a *Adapter) ListStaticEntitlements(_ context.Context, _ *v2.EntitlementsServiceListStaticEntitlementsRequest) (*v2.EntitlementsServiceListStaticEntitlementsResponse, error)
- func (a *Adapter) ListSyncRuns(ctx context.Context, pageToken string, pageSize uint32) ([]*c1zstore.SyncRun, string, error)
- func (a *Adapter) ListSyncs(ctx context.Context, req *reader_v2.SyncsReaderServiceListSyncsRequest) (*reader_v2.SyncsReaderServiceListSyncsResponse, error)
- func (a *Adapter) Metadata() connectorstore.StoreMetadata
- func (a *Adapter) OutputFilepath() (string, error)
- func (a *Adapter) PebbleEngine() *Engine
- func (a *Adapter) PutAsset(ctx context.Context, assetRef *v2.AssetRef, contentType string, data []byte) error
- func (a *Adapter) PutEntitlements(ctx context.Context, entitlements ...*v2.Entitlement) error
- func (a *Adapter) PutGrants(ctx context.Context, grants ...*v2.Grant) error
- func (a *Adapter) PutResourceTypes(ctx context.Context, rts ...*v2.ResourceType) error
- func (a *Adapter) PutResources(ctx context.Context, resources ...*v2.Resource) error
- func (a *Adapter) ResumeSync(ctx context.Context, syncType connectorstore.SyncType, syncID string) (string, error)
- func (a *Adapter) ScanEntitlementGrantBucket(ctx context.Context, ent *v2.Entitlement, ...) error
- func (a *Adapter) SetCurrentSync(ctx context.Context, syncID string) error
- func (a *Adapter) StartNewSync(ctx context.Context, syncType connectorstore.SyncType, parentSyncID string) (string, error)
- func (a *Adapter) StartNewSyncWithID(ctx context.Context, syncType connectorstore.SyncType, ...) (string, error)
- func (a *Adapter) StartOrResumeSync(ctx context.Context, syncType connectorstore.SyncType, syncID string) (string, bool, error)
- func (a *Adapter) Stats(ctx context.Context, syncType connectorstore.SyncType, syncID string) (map[string]int64, error)
- func (a *Adapter) StreamEntitlements(ctx context.Context, syncID string) iter.Seq2[*v2.Entitlement, error]
- func (a *Adapter) StreamGrants(ctx context.Context, syncID string, opts connectorstore.StreamGrantsOptions) iter.Seq2[*v2.Grant, error]
- func (a *Adapter) StreamResources(ctx context.Context, syncID string, opts connectorstore.StreamResourcesOptions) iter.Seq2[*v2.Resource, error]
- func (a *Adapter) SyncMeta() c1zstore.SyncMeta
- func (a *Adapter) UnsafePutUniqueGrants(ctx context.Context, grants ...*v2.Grant) error
- type BulkGrantShard
- type BulkSyncImport
- func (b *BulkSyncImport) Abort()
- func (b *BulkSyncImport) AddEntitlements(ctx context.Context, ents ...*v2.Entitlement) error
- func (b *BulkSyncImport) AddEntitlementsWithDiscoveredAt(ctx context.Context, ents []*v2.Entitlement, ...) error
- func (b *BulkSyncImport) AddResourceTypes(ctx context.Context, rts ...*v2.ResourceType) error
- func (b *BulkSyncImport) AddResourceTypesWithDiscoveredAt(ctx context.Context, rts []*v2.ResourceType, ...) error
- func (b *BulkSyncImport) AddResources(ctx context.Context, resources ...*v2.Resource) error
- func (b *BulkSyncImport) AddResourcesWithDiscoveredAt(ctx context.Context, resources []*v2.Resource, ...) error
- func (b *BulkSyncImport) ComputedStats() *v3.SyncStatsRecord
- func (b *BulkSyncImport) Finish(ctx context.Context) error
- func (b *BulkSyncImport) NewGrantShard() (*BulkGrantShard, error)
- type DigestBucket
- type DigestRoot
- type Durability
- type Engine
- func (e *Engine) AbortSynthesizedGrantLayer(ctx context.Context) error
- func (e *Engine) AddSynthesizedGrantLayerContributions(ctx context.Context, records []synthesizedGrantRecord) error
- func (e *Engine) BeginSynthesizedGrantLayer(ctx context.Context) (bool, error)
- func (e *Engine) BuildDeferredGrantIndexes(ctx context.Context) error
- func (e *Engine) BuildGrantDigests(ctx context.Context) error
- func (e *Engine) CheckpointTo(ctx context.Context, destDir string) error
- func (e *Engine) Close() error
- func (e *Engine) CompactAllRanges(ctx context.Context) error
- func (e *Engine) ComputeEntitlementBucketDigest(ctx context.Context, id entitlementIdentity, bucket DigestBucket) ([]byte, int64, error)
- func (e *Engine) CurrentDBSizeBytes() (int64, error)
- func (e *Engine) DB() *pebble.DB
- func (e *Engine) DBDir() string
- func (e *Engine) DeleteAssetRecord(ctx context.Context, externalID string) error
- func (e *Engine) DeleteEntitlementRecord(ctx context.Context, externalID string) error
- func (e *Engine) DeleteGrantByIdentityRefs(ctx context.Context, r *v3.GrantRecord) error
- func (e *Engine) DeleteGrantRecord(ctx context.Context, externalID string) error
- func (e *Engine) DeleteResourceRecord(ctx context.Context, resourceTypeID, resourceID string) error
- func (e *Engine) DeleteResourceTypeRecord(ctx context.Context, externalID string) error
- func (e *Engine) DeleteSyncRunRecord(ctx context.Context, syncID string) error
- func (e *Engine) DirtyEntitlementBuckets(ctx context.Context, other *Engine, id entitlementIdentity) ([]DigestBucket, error)
- func (e *Engine) DropAllGrantDigestState(ctx context.Context) error
- func (e *Engine) DropAllGrantDigests(ctx context.Context) error
- func (e *Engine) EndFreshSync(ctx context.Context) error
- func (e *Engine) ExpandWritePathStats() ExpandWritePathStats
- func (e *Engine) FinishSynthesizedGrantLayer(ctx context.Context) error
- func (e *Engine) Flush(ctx context.Context) error
- func (e *Engine) GetAssetRecord(ctx context.Context, externalID string) (*v3.AssetRecord, error)
- func (e *Engine) GetEntitlementDigestRoot(ctx context.Context, id entitlementIdentity) (DigestRoot, bool, error)
- func (e *Engine) GetEntitlementRecord(ctx context.Context, externalID string) (*v3.EntitlementRecord, error)
- func (e *Engine) GetGrantDigestGlobalRoot(ctx context.Context) (DigestRoot, bool, error)
- func (e *Engine) GetGrantRecord(ctx context.Context, externalID string) (*v3.GrantRecord, error)
- func (e *Engine) GetResourceRecord(ctx context.Context, resourceTypeID, resourceID string) (*v3.ResourceRecord, error)
- func (e *Engine) GetResourceTypeRecord(ctx context.Context, externalID string) (*v3.ResourceTypeRecord, error)
- func (e *Engine) GetSyncRunRecord(ctx context.Context, syncID string) (*v3.SyncRunRecord, error)
- func (e *Engine) GrantDigestIndexEnabled() bool
- func (e *Engine) InvalidateBareIDLookups()
- func (e *Engine) InvalidateGrantDigestPartitions(ctx context.Context, partitions []string) error
- func (e *Engine) IsFreshSync() bool
- func (e *Engine) IsSealed() bool
- func (e *Engine) IterateAllSyncRuns(ctx context.Context, yield func(*v3.SyncRunRecord) bool) error
- func (e *Engine) IterateAssets(ctx context.Context, yield func(*v3.AssetRecord) bool) error
- func (e *Engine) IterateEntitlements(ctx context.Context, yield func(*v3.EntitlementRecord) bool) error
- func (e *Engine) IterateEntitlementsByResource(ctx context.Context, resourceTypeID, resourceID string, ...) error
- func (e *Engine) IterateGrants(ctx context.Context, yield func(*v3.GrantRecord) bool) error
- func (e *Engine) IterateGrantsByEntitlement(ctx context.Context, entitlementID string, yield func(*v3.GrantRecord) bool) error
- func (e *Engine) IterateGrantsByEntitlementBucket(ctx context.Context, id entitlementIdentity, bucket DigestBucket, ...) error
- func (e *Engine) IterateGrantsByNeedsExpansion(ctx context.Context, yield func(*v3.GrantRecord) bool) error
- func (e *Engine) IterateGrantsByPrincipal(ctx context.Context, principalRT, principalID string, ...) error
- func (e *Engine) IterateGrantsByPrincipalResourceType(ctx context.Context, principalRT string, yield func(*v3.GrantRecord) bool) error
- func (e *Engine) IterateResourceTypes(ctx context.Context, yield func(*v3.ResourceTypeRecord) bool) error
- func (e *Engine) IterateResources(ctx context.Context, yield func(*v3.ResourceRecord) bool) error
- func (e *Engine) IterateResourcesByParent(ctx context.Context, parentRT, parentID string, ...) error
- func (e *Engine) LatestFinishedSyncRecord(ctx context.Context, typeOK func(v3.SyncType) bool) (*v3.SyncRunRecord, error)
- func (e *Engine) LatestUnfinishedSyncRecord(ctx context.Context, typeOK func(v3.SyncType) bool) (*v3.SyncRunRecord, error)
- func (e *Engine) LogCompactionMetrics(ctx context.Context, phase string)
- func (e *Engine) MarkFreshSync(syncID string) error
- func (e *Engine) MigratedOnOpen() bool
- func (e *Engine) PaginateEntitlements(ctx context.Context, cursor string, limit int) ([]*v3.EntitlementRecord, string, error)
- func (e *Engine) PaginateEntitlementsByResource(ctx context.Context, resourceTypeID, resourceID, cursor string, limit int) ([]*v3.EntitlementRecord, string, error)
- func (e *Engine) PaginateGrantPrincipalKeysByEntitlement(ctx context.Context, entID entitlementIdentity, cursor string, limit int) ([]string, string, error)
- func (e *Engine) PaginateGrants(ctx context.Context, cursor string, limit int) ([]*v3.GrantRecord, string, error)
- func (e *Engine) PaginateGrantsByEntitlement(ctx context.Context, entID entitlementIdentity, cursor string, limit int) ([]*v3.GrantRecord, string, error)
- func (e *Engine) PaginateGrantsByEntitlementPrincipal(ctx context.Context, entID entitlementIdentity, ...) ([]*v3.GrantRecord, string, error)
- func (e *Engine) PaginateGrantsByEntitlementResource(ctx context.Context, entRT, entRID, cursor string, limit int) ([]*v3.GrantRecord, string, error)
- func (e *Engine) PaginateGrantsByNeedsExpansion(ctx context.Context, cursor string, limit int) ([]*v3.GrantRecord, string, error)
- func (e *Engine) PaginateGrantsByPrincipal(ctx context.Context, principalRT, principalID, cursor string, limit int) ([]*v3.GrantRecord, string, error)
- func (e *Engine) PaginateGrantsByPrincipalResourceType(ctx context.Context, principalRT, cursor string, limit int) ([]*v3.GrantRecord, string, error)
- func (e *Engine) PaginateResourceTypes(ctx context.Context, cursor string, limit int) ([]*v3.ResourceTypeRecord, string, error)
- func (e *Engine) PaginateResources(ctx context.Context, cursor string, limit int) ([]*v3.ResourceRecord, string, error)
- func (e *Engine) PaginateResourcesByParent(ctx context.Context, parentRT, parentID, cursor string, limit int) ([]*v3.ResourceRecord, string, error)
- func (e *Engine) PersistComputedSyncStats(ctx context.Context, syncID string, rec *v3.SyncStatsRecord) error
- func (e *Engine) PersistSyncStats(ctx context.Context, syncID string) error
- func (e *Engine) PutAssetRecord(ctx context.Context, r *v3.AssetRecord) error
- func (e *Engine) PutEntitlementRecord(ctx context.Context, r *v3.EntitlementRecord) error
- func (e *Engine) PutEntitlementRecords(ctx context.Context, records ...*v3.EntitlementRecord) error
- func (e *Engine) PutEntitlementRecordsIfNewer(ctx context.Context, records ...*v3.EntitlementRecord) error
- func (e *Engine) PutExpandedGrantRecords(ctx context.Context, records []*v3.GrantRecord) error
- func (e *Engine) PutGrantRecord(ctx context.Context, r *v3.GrantRecord) error
- func (e *Engine) PutGrantRecords(ctx context.Context, records ...*v3.GrantRecord) error
- func (e *Engine) PutGrantRecordsIfNewer(ctx context.Context, records ...*v3.GrantRecord) error
- func (e *Engine) PutResourceRecord(ctx context.Context, r *v3.ResourceRecord) error
- func (e *Engine) PutResourceRecords(ctx context.Context, records ...*v3.ResourceRecord) error
- func (e *Engine) PutResourceRecordsIfNewer(ctx context.Context, records ...*v3.ResourceRecord) error
- func (e *Engine) PutResourceTypeRecord(ctx context.Context, r *v3.ResourceTypeRecord) error
- func (e *Engine) PutResourceTypeRecords(ctx context.Context, records ...*v3.ResourceTypeRecord) error
- func (e *Engine) PutResourceTypeRecordsIfNewer(ctx context.Context, records ...*v3.ResourceTypeRecord) error
- func (e *Engine) PutSyncRunRecord(ctx context.Context, r *v3.SyncRunRecord) error
- func (e *Engine) PutSynthesizedGrantContributions(ctx context.Context, records []synthesizedGrantRecord) error
- func (e *Engine) PutSynthesizedGrantRecords(ctx context.Context, records []*v3.GrantRecord) error
- func (e *Engine) RepairMissingGrantDigests(ctx context.Context) error
- func (e *Engine) ResetForNewSync(ctx context.Context) error
- func (e *Engine) Save(ctx context.Context, dest string) error
- func (e *Engine) SessionClear(ctx context.Context, opt ...sessions.SessionStoreOption) error
- func (e *Engine) SessionDelete(ctx context.Context, key string, opt ...sessions.SessionStoreOption) error
- func (e *Engine) SessionGet(ctx context.Context, key string, opt ...sessions.SessionStoreOption) ([]byte, bool, error)
- func (e *Engine) SessionGetAll(ctx context.Context, pageToken string, opt ...sessions.SessionStoreOption) (map[string][]byte, string, error)
- func (e *Engine) SessionGetMany(ctx context.Context, keys []string, opt ...sessions.SessionStoreOption) (map[string][]byte, []string, error)
- func (e *Engine) SessionSet(ctx context.Context, key string, value []byte, ...) error
- func (e *Engine) SessionSetMany(ctx context.Context, values map[string][]byte, ...) error
- func (e *Engine) SetCurrentSync(syncID string) error
- func (e *Engine) StartBulkSyncImport(ctx context.Context, syncID string, tmpDir string) (*BulkSyncImport, error)
- func (e *Engine) StashComputedSyncStats(syncID string, rec *v3.SyncStatsRecord)
- func (e *Engine) UnsafePutUniqueGrantRecords(ctx context.Context, records ...*v3.GrantRecord) error
- type ExpandWritePathStats
- type GrantDigestAccumulator
- type Option
- type Options
Constants ¶
const DefaultPageSize = 10000
DefaultPageSize matches the SQLite c1file's maxPageSize (10000). Callers passing 0 or a value > MaxPageSize get clamped to this.
const GrantDigestABIVersion uint32 = 1
GrantDigestABIVersion is the version of the content-hash / bucket-hash definitions below (grantContentHash64, grantPrincipalBucketHash64). Stamped into the manifest's GrantDigestRoot.abi_version so a future ABI bump (a change to either hash's input framing) makes stored manifest roots computed under different versions incomparable by construction, rather than silently comparing unrelated hash schemes. Bump alongside any index-migration version bump that touches these hashes (see index_migrations.go).
Exported so consumers of GrantContentHash / GrantDigestAccumulator can check a stored root's abi_version before comparing.
const MaxPageSize = 10000
MaxPageSize caps a single List call's record count. Matches sql_helpers.maxPageSize for consistency with the SQLite engine — some clients (the syncer, c1 backend) assume both engines clamp identically.
const SDKPebbleFormat = pebble.FormatNewest
SDKPebbleFormat is the on-disk format version this SDK release writes. Bumping it is a deliberate SDK compatibility decision, not a transitive consequence of a go.mod upgrade. The reader compares against pebble.FormatNewest at open and refuses files newer than what the binary supports.
Variables ¶
var ( ErrEngineClosing = sentinel(codes.Unavailable, "pebble engine: closing") ErrEngineNotAvailable = sentinel(codes.Unavailable, "pebble engine: not available") ErrEngineMismatch = sentinel(codes.InvalidArgument, "pebble engine: source/dest engine mismatch") ErrManifestInvalid = sentinel(codes.DataLoss, "pebble engine: manifest unmarshal failed") ErrManifestIncompleteDescriptors = sentinel(codes.DataLoss, "pebble engine: manifest descriptor closure incomplete") ErrPebbleFormatNewer = sentinel(codes.FailedPrecondition, "pebble engine: pebble file format newer than this binary supports") ErrPebbleFormatOlder = sentinel(codes.FailedPrecondition, "pebble engine: pebble file format older than this binary can read") ErrUnknownEngine = sentinel(codes.FailedPrecondition, "pebble engine: unknown engine name in manifest") ErrUnknownRecordType = sentinel(codes.FailedPrecondition, "pebble engine: unknown record type in manifest") ErrEnvelopeTruncated = sentinel(codes.DataLoss, "pebble engine: v3 envelope truncated") ErrDiskFull = sentinel(codes.ResourceExhausted, "pebble engine: disk full (ENOSPC)") ErrNoCurrentSync = sentinel(codes.FailedPrecondition, "pebble engine: no current sync") ErrEngineSealed = sentinel(codes.FailedPrecondition, "pebble engine: sealed after EndSync; bind a sync (StartNewSync/ResumeSync/SetCurrentSync) before writing") ErrSaveDestExists = sentinel(codes.AlreadyExists, "pebble engine: save destination already exists") ErrCrossFilesystem = sentinel(codes.InvalidArgument, "pebble engine: save tmpDir and dest must be on the same filesystem") ErrInvalidPageToken = sentinel(codes.InvalidArgument, "pebble engine: invalid page token") // ErrAmbiguousExternalID is returned by the bare-id lookup edge when a // lossy public id string matches more than one record (or has too many // candidate parses to certify uniqueness). Callers that hold structured // refs never hit this; it exists so a lossy string can never silently // address the wrong record. ErrAmbiguousExternalID = sentinel(codes.FailedPrecondition, "pebble engine: external id is ambiguous") )
var ErrBulkImportOutOfOrder = errors.New("bulk sync import: keys are not strictly increasing")
ErrBulkImportOutOfOrder is returned by BulkSyncImport's ordered add methods when a primary key arrives that does not sort strictly after the previous key in the same bucket. It means the caller's sorted-source contract was violated; the import cannot continue and must be Abort()ed.
var ErrDiffUnsupported = errors.New("pebble v3 engine: GenerateSyncDiff is unsupported (single-sync contract)")
ErrDiffUnsupported is returned by the Pebble v3 engine's GenerateSyncDiff. A v3 c1z holds exactly one sync by contract, so the precondition GenerateSyncDiff needs — two ended syncs (base + applied) co-resident in one file — can never be satisfied. Diffs must be computed a layer up, across two separate c1z files.
Functions ¶
func AddFoldDeadBytes ¶ added in v0.15.0
func AddFoldDeadBytes(w connectorstore.Writer, n int64) bool
AddFoldDeadBytes bumps a registered store's cumulative fold-waste counter (persisted as the envelope manifest's fold_dead_bytes at save). Called by the fold compactor with the exact raw bytes its merge shadowed in the base keyspace; the compactor's auto cutover later reads the counter from the envelope header to force a rebuild once waste crosses its threshold. Returns false when w is not a registered Pebble store.
func AppendResourceIndexKeyRawBytes ¶ added in v0.13.2
func AppendResourceIndexKeyRawBytes(dst []byte, parentRT []byte, parentID []byte, resourceTypeID []byte, resourceID []byte) []byte
Byte-slice appenders let merge/overlay code build index keys from borrowed protobuf string bytes. Avoiding intermediate string conversion was the last large allocation drop in the same-size syncs=50 overlay profile (~2.4M to ~0.8M allocs/op, combined with scratch reuse).
func AssetLowerBound ¶ added in v0.15.0
func AssetLowerBound() []byte
func AssetUpperBound ¶ added in v0.15.0
func AssetUpperBound() []byte
func BuildManifest ¶ added in v0.13.0
func BuildManifest(encoding c1zstore.PayloadEncoding) (*c1zv3.C1ZManifestV3, error)
BuildManifest assembles the v3 envelope manifest for a Pebble-engine c1z using the given payload encoding. Used by CloneSync and by pkg/dotc1z's Pebble store when saving the envelope at Close.
func BuildManifestWithSyncRuns ¶ added in v0.13.2
func BuildManifestWithSyncRuns(ctx context.Context, e *Engine, encoding c1zstore.PayloadEncoding) (*c1zv3.C1ZManifestV3, error)
BuildManifestWithSyncRuns assembles the manifest including the sync-run projection from e. Used by pkg/dotc1z's Pebble store when saving the envelope at Close, so readers can enumerate syncs and read row counts from the envelope header without unpacking the Pebble payload (consumed by compaction source selection via formatv3.ReadManifestHeader).
func CachedSyncStats ¶ added in v0.13.2
func CloseEngineOnly ¶ added in v0.13.2
func CloseEngineOnly(w connectorstore.Writer) error
CloseEngineOnly closes the Pebble engine inside a registered store without removing that store's unpacked temp directory. This is useful when a caller owns a parent temp directory and wants one bulk cleanup after closing many read-only source stores.
func DigestLowerBound ¶ added in v0.18.3
func DigestLowerBound() []byte
DigestLowerBound / UpperBound bound the entire digest keyspace (all digested indexes). Exported for the cleanup/clone/compaction keyspace plans. typeDigest is NOT adjacent to typeIndex — typeCounter and typeSession sit between them — so an excise span must never be widened to cover both the hash index and the digests in one range.
func DigestUpperBound ¶ added in v0.18.3
func DigestUpperBound() []byte
func EntitlementByResourceLowerBound ¶ added in v0.15.0
func EntitlementByResourceLowerBound() []byte
func EntitlementByResourceUpperBound ¶ added in v0.15.0
func EntitlementByResourceUpperBound() []byte
func EntitlementIndexKeys ¶ added in v0.13.2
func EntitlementIndexKeys(r *v3.EntitlementRecord) [][]byte
func EntitlementLowerBound ¶ added in v0.15.0
func EntitlementLowerBound() []byte
func EntitlementRecordIdentityKey ¶ added in v0.18.0
func EntitlementRecordIdentityKey(r *v3.EntitlementRecord) string
func EntitlementRecordKey ¶ added in v0.13.2
func EntitlementUpperBound ¶ added in v0.15.0
func EntitlementUpperBound() []byte
func ForEachEntitlementIndexKey ¶ added in v0.13.2
func ForEachEntitlementIndexKey(r *v3.EntitlementRecord, yield func([]byte) error) error
func ForEachEntitlementIndexKeyRaw ¶ added in v0.13.2
func ForEachGrantIndexKey ¶ added in v0.13.2
func ForEachGrantIndexKey(r *v3.GrantRecord, yield func([]byte) error) error
func ForEachGrantIndexKeyRaw ¶ added in v0.13.2
func ForEachResourceIndexKey ¶ added in v0.13.2
func ForEachResourceIndexKey(r *v3.ResourceRecord, yield func([]byte) error) error
func ForEachResourceIndexKeyRaw ¶ added in v0.13.2
func GrantByEntPrincHashLowerBound ¶ added in v0.18.3
func GrantByEntPrincHashLowerBound() []byte
GrantByEntPrincHashLowerBound / UpperBound bound the entire by_entitlement_principal_hash index. Exported for the cleanup/clone/compaction keyspace plans.
func GrantByEntPrincHashUpperBound ¶ added in v0.18.3
func GrantByEntPrincHashUpperBound() []byte
func GrantByEntitlementLowerBound ¶ added in v0.15.0
func GrantByEntitlementLowerBound() []byte
GrantByEntitlementLowerBound returns the retired by_entitlement index range. Kept so cleanup/migration can delete old files' index entries.
func GrantByEntitlementResourceLowerBound ¶ added in v0.15.0
func GrantByEntitlementResourceLowerBound() []byte
GrantByEntitlementResourceLowerBound returns a retired folded index range. Entitlement-resource scans are served by primary grant key prefixes.
func GrantByEntitlementResourceUpperBound ¶ added in v0.15.0
func GrantByEntitlementResourceUpperBound() []byte
func GrantByEntitlementUpperBound ¶ added in v0.15.0
func GrantByEntitlementUpperBound() []byte
func GrantByNeedsExpansionLowerBound ¶ added in v0.15.0
func GrantByNeedsExpansionLowerBound() []byte
func GrantByNeedsExpansionUpperBound ¶ added in v0.15.0
func GrantByNeedsExpansionUpperBound() []byte
func GrantByPrincipalLowerBound ¶ added in v0.15.0
func GrantByPrincipalLowerBound() []byte
func GrantByPrincipalResourceTypeLowerBound ¶ added in v0.15.0
func GrantByPrincipalResourceTypeLowerBound() []byte
GrantByPrincipalResourceTypeLowerBound returns a retired folded index range. Principal-resource-type scans are served by idxGrantByPrincipal prefixes.
func GrantByPrincipalResourceTypeUpperBound ¶ added in v0.15.0
func GrantByPrincipalResourceTypeUpperBound() []byte
func GrantByPrincipalUpperBound ¶ added in v0.15.0
func GrantByPrincipalUpperBound() []byte
func GrantContentHash ¶ added in v0.18.4
GrantContentHash is the public form of the grant content hash (grantContentHash64) computed from a v2 proto — the uint64 the stored 8-byte big-endian hash encodes. Use it (or GrantDigestAccumulator, its fold) to digest grants not held in a pebble file — e.g. a legacy SQLite c1z — for comparison against engine-served digests.
Hash grants as stored, not as emitted: expansion populates the sources map after ingest, so raw connector output hashes differently from what a sealed file digested. A grant missing its structural identity errors — such a grant cannot exist in a pebble file. Duplicates of one identity tuple drift from the disk fold (pebble stores the tuple once), but the counts differ too, so the comparison just reports a mismatch — never a false match.
ABI: pinned to GrantDigestABIVersion alongside the internal forms.
func GrantIndexKeys ¶ added in v0.13.2
func GrantIndexKeys(r *v3.GrantRecord) [][]byte
func GrantLowerBound ¶ added in v0.15.0
func GrantLowerBound() []byte
GrantLowerBound / GrantUpperBound give the half-open [lo, hi) range covering every grant primary key. Exported for synccompactor/pebble.
func GrantPartitionFromPrimaryKey ¶ added in v0.18.3
GrantPartitionFromPrimaryKey returns the entitlement partition — the same string digestPartitionForEntitlement (and this file's repair functions) key off — that a raw grant primary key belongs to, by splicing the key rather than decoding it. For callers that mutate grants through the raw DB handle and need to report which partitions they touched without ever constructing an entitlementIdentity (the fold compactor's raw merge — see InvalidateGrantDigestPartitions). ok is false for a key that does not parse as a 6-segment grant identity.
func GrantRecordIdentityKey ¶ added in v0.18.0
func GrantRecordIdentityKey(r *v3.GrantRecord) string
func GrantRecordKey ¶ added in v0.13.2
func GrantUpperBound ¶ added in v0.15.0
func GrantUpperBound() []byte
func MarkStoreDirty ¶ added in v0.13.5
func MarkStoreDirty(w connectorstore.Writer) bool
MarkStoreDirty flips a registered store's dirty bit so Close drives the save → checkpoint → envelope path. Engine-level writes (raw batches, SST ingest, direct Put*Records) bypass the registered store's markDirty wrappers; merge tooling that mutates the engine directly calls this once so the mutations are persisted at Close. Returns false when w is not a registered Pebble store.
func NormalizeForFixtureSave ¶ added in v0.13.2
NormalizeForFixtureSave flushes and compacts one sync in a Pebble store, then marks the wrapper dirty so Close writes a fresh c1z envelope. This is intentionally narrow: benchmark fixtures should not measure WAL replay or un-compacted LSM shape left over from fixture generation.
func ReadSyncStatsRecord ¶ added in v0.13.2
func ReadSyncStatsRecord(ctx context.Context, e *Engine, syncID string) (*v3.SyncStatsRecord, error)
ReadSyncStatsRecord returns the raw stats sidecar record for a sync, or (nil, nil) when no sidecar exists. Exposed for synccompactor tests that compare merge-accumulated stats against a recompute.
func ResourceByParentLowerBound ¶ added in v0.15.0
func ResourceByParentLowerBound() []byte
func ResourceByParentUpperBound ¶ added in v0.15.0
func ResourceByParentUpperBound() []byte
func ResourceIndexKeys ¶ added in v0.13.2
func ResourceIndexKeys(r *v3.ResourceRecord) [][]byte
func ResourceLowerBound ¶ added in v0.15.0
func ResourceLowerBound() []byte
func ResourceRecordKey ¶ added in v0.13.2
func ResourceTypeLowerBound ¶ added in v0.15.0
func ResourceTypeLowerBound() []byte
func ResourceTypeRecordKey ¶ added in v0.13.2
func ResourceTypeUpperBound ¶ added in v0.15.0
func ResourceTypeUpperBound() []byte
func ResourceUpperBound ¶ added in v0.15.0
func ResourceUpperBound() []byte
func SyncRunLowerBound ¶
func SyncRunLowerBound() []byte
func SyncRunUpperBound ¶
func SyncRunUpperBound() []byte
func SyncStatsFromRecord ¶ added in v0.13.2
func SyncStatsFromRecord(stats *v3.SyncStatsRecord) *reader_v2.SyncStats
SyncStatsFromRecord converts the engine-internal stats sidecar shape into the public reader stats type used by C1ZStore APIs and compactor planning. The public type intentionally has no assets field, so assets remain available only on the sidecar record.
func SyncStatsSidecarLowerBound ¶
func SyncStatsSidecarLowerBound() []byte
SyncStatsSidecarLowerBound / UpperBound expose the bounds of the stats sidecar keyspace for synccompactor and CloneSync. The file holds one stats record, so this is a single-key range.
func SyncStatsSidecarUpperBound ¶
func SyncStatsSidecarUpperBound() []byte
func V2EntitlementToV3 ¶
func V2EntitlementToV3(syncID string, e *v2.Entitlement) *v3.EntitlementRecord
V2EntitlementToV3 maps v2.Entitlement → v3.EntitlementRecord. Captures `slug` and `grantable_to` from v2 — these were silently dropped before, breaking pkg/sync/syncer's entitlement-id construction and the access-review report's principal-type narrowing. grantable_to is stored as a list of resource_type ids; the full v2.ResourceType is rehydrated on read via the resource_types table when callers need it.
func V2GrantToV3 ¶
func V2GrantToV3(syncID string, g *v2.Grant) *v3.GrantRecord
V2GrantToV3 produces a v3.GrantRecord from a v2.Grant. The syncID parameter is retained for call-site symmetry with other translators; v3 data records no longer store sync_id in the value because sync scope is encoded in Pebble keys by the engine write path.
The GrantExpandable annotation (if any) is extracted into the dedicated Expansion field and stripped from Annotations, and NeedsExpansion is set to true. This matches the SQLite path (extractAndStripExpansion in pkg/dotc1z/grants.go). Without this extraction the grant expansion pipeline silently no-ops on Pebble — idxGrantByNeedsExpansion stays empty and PendingExpansion returns no grants.
func V2ResourceToV3 ¶
func V2ResourceToV3(syncID string, r *v2.Resource) *v3.ResourceRecord
V2ResourceToV3 maps v2.Resource → v3.ResourceRecord. syncID is not stored in the value; Pebble keys carry sync scope.
func V2ResourceTypeToV3 ¶
func V2ResourceTypeToV3(syncID string, rt *v2.ResourceType) *v3.ResourceTypeRecord
V2ResourceTypeToV3 maps v2.ResourceType → v3.ResourceTypeRecord. Traits are flattened to their string names (TRAIT_USER → "USER").
func V3EntitlementToV2 ¶
func V3EntitlementToV2(r *v3.EntitlementRecord) *v2.Entitlement
V3EntitlementToV2 reverses V2EntitlementToV3. The Resource side is hydrated as a stub (identity only); callers that need the full v2 Resource must hydrate via the engine's GetResourceRecord. The grantable_to list is hydrated as ResourceType id-stubs — callers who need the full ResourceType (display name, traits) must look it up via the resource_types table.
func V3GrantToV2 ¶
func V3GrantToV2(r *v3.GrantRecord) *v2.Grant
V3GrantToV2 hydrates a v2.Grant from a v3.GrantRecord. The output has stub Entitlement / Principal — only the identity fields are populated. Callers that need the full v2.Entitlement / v2.Resource must hydrate via separate Get on those record types.
This stub-hydration approach matches the v3 design where references are identity-only.
The expansion payload stored in the dedicated Expansion field is re-attached to the v2.Grant's Annotations list so downstream callers (the syncer's expander, ListWithAnnotations consumers) see the GrantExpandable annotation that V2GrantToV3 stripped at write time. Matches the SQLite read path.
func V3ResourceToV2 ¶
func V3ResourceToV2(r *v3.ResourceRecord) *v2.Resource
V3ResourceToV2 hydrates a v2.Resource from a v3.ResourceRecord. Only the fields that exist in both shapes are populated.
func V3ResourceTypeToV2 ¶
func V3ResourceTypeToV2(r *v3.ResourceTypeRecord) *v2.ResourceType
V3ResourceTypeToV2 reverses V2ResourceTypeToV3. Unknown trait strings become TRAIT_UNSPECIFIED — round-tripping a known trait preserves it.
Types ¶
type Adapter ¶
type Adapter struct {
// embedded Unimplemented stubs for the gRPC service surfaces we
// implement partially. Each implemented method overrides the stub.
// GrantsReaderServiceServer is deliberately NOT stubbed: the Adapter
// implements it in full, and adapter_reader.go asserts the complete
// contract — re-adding the stub would make that assertion vacuous.
v2.UnimplementedResourceTypesServiceServer
reader_v2.UnimplementedResourceTypesReaderServiceServer
v2.UnimplementedResourcesServiceServer
reader_v2.UnimplementedResourcesReaderServiceServer
v2.UnimplementedEntitlementsServiceServer
reader_v2.UnimplementedEntitlementsReaderServiceServer
v2.UnimplementedGrantsServiceServer
reader_v2.UnimplementedSyncsReaderServiceServer
// contains filtered or unexported fields
}
Adapter wraps an *Engine and implements connectorstore.Writer (which embeds connectorstore.Reader) — the surface C1 + the syncer call against the v3 Pebble engine. Translates v2 wire types ↔ v3 record types via translate_v2.go and routes Put/Get/List into the engine's per-record-type methods.
The adapter implements the common connectorstore writer and reader paths directly. gRPC methods outside that surface fall through to the embedded UnimplementedXxxServer stubs.
Adapter is goroutine-safe modulo Close — concurrent reads + writes are fine, but caller must serialize Close against other calls.
func NewAdapter ¶
NewAdapter wraps an Engine. The engine must remain alive for the lifetime of the adapter.
func (*Adapter) CheckpointSync ¶
CheckpointSync persists a step token to the open sync's record.
func (*Adapter) Cleanup ¶
Cleanup on the bare Adapter is a no-op. The real Pebble retention policy lives on pkg/dotc1z's Pebble store wrapper (pebble_store.go) — it needs access to caller-supplied options (SyncLimit, SkipCleanup) that the engine itself doesn't track, plus the dirty-flag plumbing on the wrapper.
Callers that open through dotc1z.NewStore(..., WithEngine(EnginePebble)) get the real Cleanup; callers that build a bare Adapter (unit tests, embedding) silently get retention=disabled. The method is kept on the Adapter only to satisfy the connectorstore.Writer interface contract regardless of how the adapter was constructed.
func (*Adapter) CleanupCandidates ¶ added in v0.13.0
CleanupCandidates walks every sync_run record and projects it into the engine-neutral c1zstore.SyncRun shape, sorted oldest-first. c1zstore.SelectSyncsToDelete depends on this ordering so "drop the oldest overflow" trims the right end. Used by pkg/dotc1z's Pebble store to drive the retention policy at Cleanup.
func (*Adapter) CurrentDBSizeBytes ¶
CurrentDBSizeBytes returns the current uncompressed Pebble working-set size on disk. Implements connectorstore.DBSizeProvider for progress logging parity with the SQLite-backed *dotc1z.C1File.
func (*Adapter) CurrentSyncID ¶ added in v0.13.0
CurrentSyncID returns the adapter's current sync id, or "" when no sync is active. Used by pkg/dotc1z's Pebble store to drive the retention policy at Cleanup.
func (*Adapter) CurrentSyncStep ¶
CurrentSyncStep returns the current sync's step string, or "".
func (*Adapter) DeleteGrant ¶
DeleteGrant removes a grant by its raw public id, resolved through the bare-id lookup edge. Callers holding the full grant should prefer DeleteGrantByRefs, which needs no id-string resolution.
func (*Adapter) DeleteGrantByRefs ¶ added in v0.18.0
DeleteGrantByRefs removes a grant addressed by the structured refs of the supplied v2 grant — the exact delete path, no lossy id string involved. Incomplete refs are an error, never a fallback to bare-id resolution: this is a sync-internal surface, and string resolution is reserved for interactive/CLI edges (see lookup.go). A grant whose refs cannot derive an identity could not have been stored in the first place, so there is nothing a string could correctly address here.
func (*Adapter) EndSync ¶
EndSync stamps the open sync_run's ended_at and detaches it. After EndSync, the adapter has no current sync; SetCurrentSync or StartNewSync are required for further writes.
func (*Adapter) FileOps ¶
FileOps returns the FileOps sub-store backed by the Pebble adapter. Implements c1zstore.Store.FileOps(). CloneSync materializes the single sync's data into a fresh c1z (used by `baton clone`); GenerateSyncDiff is unsupported (single-sync contract — see ErrDiffUnsupported).
func (*Adapter) FileOpsWithEncoding ¶
func (a *Adapter) FileOpsWithEncoding(encoding c1zstore.PayloadEncoding) c1zstore.FileOps
FileOpsWithEncoding returns a FileOps that emits new c1z files using the given payload encoding. Used by the registered store in pkg/dotc1z to propagate its configured encoding (e.g. PayloadEncodingTar for callers that want uncompressed envelopes) through to CloneSync's output file.
func (*Adapter) GetAsset ¶
func (a *Adapter) GetAsset(ctx context.Context, req *v2.AssetServiceGetAssetRequest) (string, io.Reader, error)
GetAsset returns the (content_type, data-reader) for the given asset. The returned reader is backed by a bytes.Reader over the fully-materialized blob.
func (*Adapter) GetEntitlement ¶
func (a *Adapter) GetEntitlement(ctx context.Context, req *reader_v2.EntitlementsReaderServiceGetEntitlementRequest) (*reader_v2.EntitlementsReaderServiceGetEntitlementResponse, error)
GetEntitlement fetches a single entitlement by ID. Implements reader_v2.EntitlementsReaderServiceServer.
func (*Adapter) GetEntitlementGrantDigest ¶ added in v0.18.3
func (a *Adapter) GetEntitlementGrantDigest(ctx context.Context, ent *v2.Entitlement) (connectorstore.GrantDigest, bool, error)
GetEntitlementGrantDigest implements connectorstore.EntitlementGrantDigestReader. It returns the stored grant-digest root (content hash + grant count) for the entitlement under the reader's active sync. found is false when no digest exists — either no active sync, an unknown bare id, or no digest was built for it (e.g. WithGrantDigestIndex(false), a file that predates the digest, or a post-seal mutation invalidated it). A nil error with found=false means "no digest".
func (*Adapter) GetEntitlementGrantDigestNodes ¶ added in v0.18.3
func (a *Adapter) GetEntitlementGrantDigestNodes(ctx context.Context, ent *v2.Entitlement, level int) ([]connectorstore.GrantDigestNode, bool, error)
GetEntitlementGrantDigestNodes implements connectorstore.EntitlementGrantDigestReader. It lists the entitlement's grant-digest rollup nodes at the requested level (2^level buckets; level 0 = the root). For 0 <= level <= the digest's native level it folds the stored leaves — one scan of the digest keyspace. For a finer level it scans the grant index directly (O(grants)) instead of erroring; the level is clamped to the bucket-hash resolution (digestMaxWidthBits).
func (*Adapter) GetGrant ¶
func (a *Adapter) GetGrant(ctx context.Context, req *reader_v2.GrantsReaderServiceGetGrantRequest) (*reader_v2.GrantsReaderServiceGetGrantResponse, error)
GetGrant fetches a single grant by ID.
func (*Adapter) GetLatestFinishedSync ¶
func (a *Adapter) GetLatestFinishedSync(ctx context.Context, req *reader_v2.SyncsReaderServiceGetLatestFinishedSyncRequest) (*reader_v2.SyncsReaderServiceGetLatestFinishedSyncResponse, error)
GetLatestFinishedSync returns the most-recently-ended sync_run, optionally filtered by sync_type. Implements reader_v2.SyncsReaderServiceServer. Delegates to Engine.LatestFinishedSyncRecord.
Future work: if this becomes hot, a "latest by type" sidecar key keyed on (typeFinishedSyncByType | type) → sync_id would let Engine.LatestFinishedSyncRecord short-circuit to O(1).
func (*Adapter) GetResource ¶
func (a *Adapter) GetResource(ctx context.Context, req *reader_v2.ResourcesReaderServiceGetResourceRequest) (*reader_v2.ResourcesReaderServiceGetResourceResponse, error)
GetResource fetches a single resource by (resource_type_id, resource_id). Implements reader_v2.ResourcesReaderServiceServer.
func (*Adapter) GetResourceType ¶
func (a *Adapter) GetResourceType(ctx context.Context, req *reader_v2.ResourceTypesReaderServiceGetResourceTypeRequest) (*reader_v2.ResourceTypesReaderServiceGetResourceTypeResponse, error)
GetResourceType fetches a single resource_type by ID. Implements reader_v2.ResourceTypesReaderServiceServer.
func (*Adapter) GetSync ¶
func (a *Adapter) GetSync(ctx context.Context, req *reader_v2.SyncsReaderServiceGetSyncRequest) (*reader_v2.SyncsReaderServiceGetSyncResponse, error)
GetSync fetches a single sync_run record by ID. Implements reader_v2.SyncsReaderServiceServer.
func (*Adapter) GrantStats ¶
func (a *Adapter) GrantStats(ctx context.Context, syncType connectorstore.SyncType, syncID string) (map[string]int64, error)
GrantStats returns just the grant count, partitioned by entitlement resource_type_id. Used by progresslog to show per-RT progress. Like Stats, this is an exact count via iteration.
syncType is validated against the sync_run's actual type to match SQLite's C1File.GrantStats: SyncTypeAny accepts any sync; otherwise we require the sync_run.Type to match exactly. A type mismatch returns (nil, nil) — the syncer treats that as "nothing to show for this view," same as the SQLite contract.
func (*Adapter) Grants ¶
func (a *Adapter) Grants() c1zstore.GrantStore
Grants returns the GrantStore implementation backed by the Pebble adapter. Implements c1zstore.Store.Grants(); used by the expander, the c1-side fileClientWrapper, and the differ.
needs_expansion is populated at PutGrants time: V2GrantToV3 extracts the GrantExpandable annotation and sets NeedsExpansion, which keys the idxGrantByNeedsExpansion index. The *Page methods walk that index and return real expandable rows (see TestExpansionAnnotationRoundtrip and the live PendingExpansion path).
func (*Adapter) GrantsForEntitlementPrincipalSorted ¶ added in v0.15.3
GrantsForEntitlementPrincipalSorted reports whether ListGrantsForEntitlement yields grants in non-decreasing principal (resource_type, resource) order. True under the structured key layout: the primary grant key ends in the tuple-encoded (principal_rt, principal_id) components and the tuple codec is order-preserving, so a per-entitlement primary prefix scan IS a principal-ordered scan. This gates the topological-merge expansion path; streamingPrincipalGroupStream additionally fails loudly if the order invariant is ever violated, and the differential/parity suites compare the sorted and unsorted evaluators against SQLite.
func (*Adapter) InitCurrentSync ¶ added in v0.13.6
func (*Adapter) LatestFinishedSyncID ¶
func (a *Adapter) LatestFinishedSyncID(ctx context.Context, syncType connectorstore.SyncType) (string, error)
LatestFinishedSyncID returns the most-recently-finished sync ID of the given type. Implements connectorstore.LatestFinishedSyncIDFetcher. Delegates to Engine.LatestFinishedSyncRecord; see that method for the predicate + tiebreaker contract.
func (*Adapter) ListEntitlements ¶
func (a *Adapter) ListEntitlements(ctx context.Context, req *v2.EntitlementsServiceListEntitlementsRequest) (*v2.EntitlementsServiceListEntitlementsResponse, error)
ListEntitlements returns up to page_size entitlements, optionally filtered by Resource (resource_type_id, resource_id). Pagination matches SQLite (see ListGrants).
func (*Adapter) ListEntitlementsByIds ¶
func (a *Adapter) ListEntitlementsByIds( ctx context.Context, req *reader_v2.EntitlementsReaderServiceListEntitlementsByIdsRequest, ) (*reader_v2.EntitlementsReaderServiceListEntitlementsByIdsResponse, error)
ListEntitlementsByIds returns entitlements for the requested external_ids. Missing rows are silently omitted.
func (*Adapter) ListGrantPrincipalKeysForEntitlement ¶ added in v0.12.3
func (a *Adapter) ListGrantPrincipalKeysForEntitlement( ctx context.Context, entitlement *v2.Entitlement, pageToken string, pageSize uint32, ) ([]string, string, error)
ListGrantPrincipalKeysForEntitlement returns the compact principal keys used by grant expansion prefetch. It avoids materializing full grant records when the caller only needs to know which principals already have a descendant entitlement grant.
func (*Adapter) ListGrants ¶
func (a *Adapter) ListGrants(ctx context.Context, req *v2.GrantsServiceListGrantsRequest) (*v2.GrantsServiceListGrantsResponse, error)
ListGrants returns up to page_size grants on the active sync. Pagination matches the SQLite engine's semantics:
- page_size == 0 || page_size > MaxPageSize → DefaultPageSize (10000)
- page_token is opaque base64; pass nextPageToken back verbatim
- filter by req.Resource — the entitlement-side resource of each grant — when set; uses primary grant entitlement-resource prefixes. This matches SQLite's `listGrantsGeneric` which filters on grants.resource_id / resource_type_id (the entitlement's resource columns). Callers who want to filter by principal should use ListGrantsForPrincipal instead.
func (*Adapter) ListGrantsForEntitlement ¶
func (a *Adapter) ListGrantsForEntitlement( ctx context.Context, req *reader_v2.GrantsReaderServiceListGrantsForEntitlementRequest, ) (*reader_v2.GrantsReaderServiceListGrantsForEntitlementResponse, error)
ListGrantsForEntitlement paginates grants on a specific entitlement, optionally narrowed by principal_id or principal_resource_type_ids. Implements reader_v2.GrantsReaderServiceServer.
func (*Adapter) ListGrantsForEntitlements ¶
func (a *Adapter) ListGrantsForEntitlements( ctx context.Context, req *reader_v2.GrantsReaderServiceListGrantsForEntitlementsRequest, ) (*reader_v2.GrantsReaderServiceListGrantsForEntitlementsResponse, error)
ListGrantsForEntitlements is the batched counterpart to ListGrantsForEntitlement (RFC §A4). It walks K entitlements in one RPC and returns a flat grant list ordered by visitation; callers group by Grant.Entitlement.Id.
Cursor encoding (Option C):
varint(entitlement_index) || varint(intra_cursor_len) || intra_cursor_bytes || crc32(list_checksum)
list_checksum is crc32 over the entitlement IDs in REQUEST ORDER — the cursor resumes by positional index, so a reorder is as fatal as a drop/add; any change to the list (including order) restarts from the beginning rather than silently mis-paginating.
func (*Adapter) ListGrantsForPrincipal ¶
func (a *Adapter) ListGrantsForPrincipal( ctx context.Context, req *reader_v2.GrantsReaderServiceListGrantsForPrincipalRequest, ) (*reader_v2.GrantsReaderServiceListGrantsForPrincipalResponse, error)
ListGrantsForPrincipal returns all grants where the given principal_id is the principal, via the O(K) PaginateGrantsByPrincipal index walk. The optional Entitlement field narrows results to a single entitlement — since entitlement + principal is the full primary grant key, that case is an O(1) point lookup. Implements reader_v2.GrantsReaderServiceServer.
func (*Adapter) ListGrantsForResourceType ¶
func (a *Adapter) ListGrantsForResourceType( ctx context.Context, req *reader_v2.GrantsReaderServiceListGrantsForResourceTypeRequest, ) (*reader_v2.GrantsReaderServiceListGrantsForResourceTypeResponse, error)
ListGrantsForResourceType paginates grants whose principal is of the given resource_type_id, via the by_principal index prefix. The cursor is the by_principal index key.
Implements reader_v2.GrantsReaderServiceServer.
func (*Adapter) ListResourceTypes ¶
func (a *Adapter) ListResourceTypes(ctx context.Context, req *v2.ResourceTypesServiceListResourceTypesRequest) (*v2.ResourceTypesServiceListResourceTypesResponse, error)
ListResourceTypes returns up to page_size resource_types. Pagination matches SQLite (see ListGrants).
func (*Adapter) ListResources ¶
func (a *Adapter) ListResources(ctx context.Context, req *v2.ResourcesServiceListResourcesRequest) (*v2.ResourcesServiceListResourcesResponse, error)
ListResources returns up to page_size resources, optionally filtered by parent resource id and/or resource_type_id. Pagination matches SQLite (see ListGrants).
Note on the resource_type_id filter: when parent is also set, the by_parent index is used and we post-filter by resource_type_id (the index doesn't carry resource_type_id in the lookup prefix). When only resource_type_id is set, we still iterate the full primary range and post-filter — adding a by_resource_type index is a future-work item if this path becomes hot.
func (*Adapter) ListResourcesByIds ¶
func (a *Adapter) ListResourcesByIds( ctx context.Context, req *reader_v2.ResourcesReaderServiceListResourcesByIdsRequest, ) (*reader_v2.ResourcesReaderServiceListResourcesByIdsResponse, error)
ListResourcesByIds returns all resources matching the supplied (resource_type_id, resource_id) pairs. Missing rows are silently omitted. Callers detect partial misses by length comparison.
func (*Adapter) ListStaticEntitlements ¶
func (a *Adapter) ListStaticEntitlements( _ context.Context, _ *v2.EntitlementsServiceListStaticEntitlementsRequest, ) (*v2.EntitlementsServiceListStaticEntitlementsResponse, error)
ListStaticEntitlements is the always-empty counterpart to ListEntitlements that some connectors expose for static (compile- time-known) entitlements. C1File returns an empty list; the Pebble adapter mirrors that contract.
func (*Adapter) ListSyncRuns ¶ added in v0.15.1
func (a *Adapter) ListSyncRuns(ctx context.Context, pageToken string, pageSize uint32) ([]*c1zstore.SyncRun, string, error)
ListSyncRuns returns every sync-run record projected into the engine-neutral c1zstore.SyncRun shape, oldest-first (parent before child for a well-formed chain), in a single page. A v3 Pebble c1z holds exactly one sync, so pagination is trivial: the whole set is returned on the first call and the next-page token is always empty. pageToken and pageSize are accepted for parity with the SQLite ListSyncRuns and are not used. It backs the c1z sanitizer's source-side sync-graph-metadata read (linked_sync_id, supports_diff), which the gRPC reader surface does not carry.
func (*Adapter) ListSyncs ¶
func (a *Adapter) ListSyncs(ctx context.Context, req *reader_v2.SyncsReaderServiceListSyncsRequest) (*reader_v2.SyncsReaderServiceListSyncsResponse, error)
ListSyncs paginates sync_run records across the engine. Order is the natural sync_id (KSUID) order — KSUIDs sort by timestamp, so this is also chronological. Implements reader_v2.SyncsReaderServiceServer.
func (*Adapter) Metadata ¶
func (a *Adapter) Metadata() connectorstore.StoreMetadata
Metadata describes the storage backing this adapter. The Pebble adapter always reports the v3 format; PayloadEncoding is set by the writer at envelope time and is not directly visible on the Adapter itself — pkg/dotc1z's Pebble store wrapper (pebble_store.go) overrides this method to fill PayloadEncoding from its configured value.
Strings are inlined rather than referencing dotc1z constants because this subpackage is imported by dotc1z, so the reverse import would cycle. The values match c1zstore.EnginePebble.String() and dotc1z.C1ZFormatV3.String() — see connectorstore.StoreMetadata docs for the canonical value list.
func (*Adapter) OutputFilepath ¶
OutputFilepath returns the on-disk path the adapter writes to. The SQLite equivalent returns the .c1z file path; for Pebble we don't have a single file, so this returns the engine's directory. Used by tooling that wants to copy or inspect the storage.
func (*Adapter) PebbleEngine ¶ added in v0.13.0
PebbleEngine returns the underlying *Engine. Nil-safe so AsEngine can probe arbitrary writers.
func (*Adapter) PutAsset ¶
func (a *Adapter) PutAsset(ctx context.Context, assetRef *v2.AssetRef, contentType string, data []byte) error
PutAsset writes a single asset row. assetRef carries the (resource_type, resource_id) pair we use as the external_id — joined with a "/" separator since the engine's AssetRecord PK is (sync_id, external_id).
func (*Adapter) PutEntitlements ¶
PutEntitlements writes a batch of entitlements in a single Pebble batch.
func (*Adapter) PutGrants ¶
PutGrants writes a batch of grants in a single Pebble batch. v2 is translated to v3 first; the engine then commits the whole batch with one fsync (or NoSync during a fresh sync — see MarkFreshSync).
The translation uses per-shard arenas (grantTranslateArena) so the 3 × N proto-struct allocations from V2GrantToV3's builder pattern collapse to 3 slice allocations per shard. For large fresh-sync writes this substantially reduces GC scan pressure during the engine's parallel build phase.
The translation itself runs in parallel across translateShards workers when the input is large enough — protobuf Get/Set methods on the underlying v2.Grant and v3 arena structs are thread-safe for read+arena-private-write access patterns. Each worker writes to a disjoint range of the records slice and uses its own arena, so no shared mutable state across workers.
func (*Adapter) PutResourceTypes ¶
PutResourceTypes writes a batch of resource types in a single Pebble batch.
func (*Adapter) PutResources ¶
PutResources writes a batch of resources in a single Pebble batch.
func (*Adapter) ResumeSync ¶
func (a *Adapter) ResumeSync(ctx context.Context, syncType connectorstore.SyncType, syncID string) (string, error)
ResumeSync attaches to an existing sync_run by id. Returns the caller-provided id if it matches an existing record.
func (*Adapter) ScanEntitlementGrantBucket ¶ added in v0.18.3
func (a *Adapter) ScanEntitlementGrantBucket(ctx context.Context, ent *v2.Entitlement, bucket connectorstore.GrantDigestBucket, yield func(*v2.Grant) bool) error
ScanEntitlementGrantBucket implements connectorstore.EntitlementGrantDigestReader. It yields every grant in the given digest bucket of the entitlement, translated to v2.Grant. Bucket Level 0 scans the whole entitlement; a finer Level is clamped to the bucket-hash resolution. Yields nothing when there is no active sync or a bare entitlement id resolves to nothing.
func (*Adapter) SetCurrentSync ¶
SetCurrentSync rebinds the engine's current sync without creating a new SyncRunRecord. Used by callers that previously called StartNewSync/ResumeSync.
func (*Adapter) StartNewSync ¶
func (a *Adapter) StartNewSync(ctx context.Context, syncType connectorstore.SyncType, parentSyncID string) (string, error)
StartNewSync creates a new sync_run record under a freshly-minted sync_id. Returns the new sync_id.
func (*Adapter) StartNewSyncWithID ¶ added in v0.15.0
func (a *Adapter) StartNewSyncWithID(ctx context.Context, syncType connectorstore.SyncType, syncID, parentSyncID string) (string, error)
StartNewSyncWithID is StartNewSync but adopts the caller-supplied syncID instead of minting one. It exists for conversion/compaction (e.g. ToPebble) that must preserve the source sync's identity so the produced file's sync_id matches the snapshot it was derived from. syncID must be non-empty.
func (*Adapter) StartOrResumeSync ¶
func (a *Adapter) StartOrResumeSync(ctx context.Context, syncType connectorstore.SyncType, syncID string) (string, bool, error)
StartOrResumeSync resumes an existing sync if one is resumable, else starts a new sync. Returns (id, started_new, err).
Resume precedence mirrors SQLite's StartOrResumeSync→ResumeSync cascade (pkg/dotc1z/sync_runs.go):
- A caller-supplied syncID that names an existing sync_run.
- With an empty syncID, the latest in-progress (not-yet-ended) sync of the requested type started within the last week — the same predicate as Engine.LatestUnfinishedSyncRecord. This is the path the syncer drives across activity windows: it calls StartOrResumeSync(ctx, syncType, "") with no id, expecting an interrupted sync to resume where it checkpointed. Without this, every window started a brand-new sync (wiping prior data via ResetForNewSync and resetting the FSM to InitOp), so any sync exceeding one window never finished.
Only when nothing is resumable do we start a new sync.
func (*Adapter) Stats ¶
func (a *Adapter) Stats(ctx context.Context, syncType connectorstore.SyncType, syncID string) (map[string]int64, error)
Stats returns a map of {table_name: row_count} for the given sync. Used by pkg/sync/progresslog to show the "syncing X resources, Y entitlements, Z grants" progress lines. The SQLite engine answers this with a fast COUNT(*) per table; for Pebble we iterate each per-record-type bucket and count keys in the [sync_lo, sync_hi) range. Counts are exact (not estimates).
Mirrors connectorstore semantics: syncType == SyncTypeAny accepts any sync; otherwise we filter by the sync_run's type.
func (*Adapter) StreamEntitlements ¶
func (a *Adapter) StreamEntitlements( ctx context.Context, syncID string, ) iter.Seq2[*v2.Entitlement, error]
StreamEntitlements yields all entitlements for syncID. Implements connectorstore.StreamingReader.
func (*Adapter) StreamGrants ¶
func (a *Adapter) StreamGrants( ctx context.Context, syncID string, opts connectorstore.StreamGrantsOptions, ) iter.Seq2[*v2.Grant, error]
StreamGrants yields grants for syncID, optionally narrowed by opts. Implements connectorstore.StreamingReader.
func (*Adapter) StreamResources ¶
func (a *Adapter) StreamResources( ctx context.Context, syncID string, opts connectorstore.StreamResourcesOptions, ) iter.Seq2[*v2.Resource, error]
StreamResources yields resources for syncID, optionally narrowed by resource_type. Implements connectorstore.StreamingReader.
func (*Adapter) SyncMeta ¶
SyncMeta returns the SyncMeta sub-store backed by the Pebble adapter. Implements c1zstore.Store.SyncMeta(). The sync-run records live in Pebble under typeSyncRun; methods here walk IterateAllSyncRuns + GetSyncRunRecord + PutSyncRunRecord on the engine.
func (*Adapter) UnsafePutUniqueGrants ¶ added in v0.12.2
UnsafePutUniqueGrants writes grants on the trusted-import path: records are encoded in parallel and written unconditionally, with no read-before-write and no dedup pass. Do not use it for live connector output. The destination sync must be fresh, and the caller MUST guarantee each external_id appears at most once across the whole sync (not just within this batch). Live connector writes should use PutGrants.
type BulkGrantShard ¶ added in v0.13.7
type BulkGrantShard struct {
// contains filtered or unexported fields
}
BulkGrantShard is one goroutine's private view of the grant import: a primary-record spill sorter and per-family index sorters. Create one per scanning goroutine via NewGrantShard; AddGrants on a shard takes no shared locks. Close the shard when its scan completes.
Grant primary keys are structural identities, which do not sort in the converter's external-id scan order, so both the primary rows and the index keys go through spill sorters; Finish k-way merges the runs of all shards per family. Arrival order therefore does not matter for correctness. Distinct legacy external ids that fold to one structural identity are merged at Finish with a warning.
func (*BulkGrantShard) AddGrants ¶ added in v0.13.7
AddGrants translates and appends grants to this shard, in ascending external-id order (see BulkGrantShard). Translation, the v3 marshal, and key encoding run in the caller's goroutine; all writers are shard-private, so concurrent shards never contend.
func (*BulkGrantShard) AddGrantsWithDiscoveredAt ¶ added in v0.17.0
func (s *BulkGrantShard) AddGrantsWithDiscoveredAt(ctx context.Context, grants []*v2.Grant, discoveredAt []*timestamppb.Timestamp) error
AddGrantsWithDiscoveredAt is AddGrants with an optional per-record discovered_at, aligned by index with grants (nil slice or nil entries fall back to now). Conversions use it to preserve the source record's original discovery time: compaction merges pick winners by newest discovered_at, so re-stamping conversion wall-clock time would make converted records override genuinely newer data.
func (*BulkGrantShard) Close ¶ added in v0.13.7
func (s *BulkGrantShard) Close()
Close finalizes the shard's primary SST and flushes its index tail chunks into the background sorters. Must be called once per shard before Finish; AddGrants is invalid afterwards.
type BulkSyncImport ¶ added in v0.13.7
type BulkSyncImport struct {
// contains filtered or unexported fields
}
BulkSyncImport is the trusted one-shot import fast path: instead of committing records through the memtable/WAL, it builds sorted SSTs on disk and ingests them all in a single pebble Ingest at Finish. Each key is written once into its final table — no WAL append, no L0 flush, no background compaction debt.
Resource types and resources stream straight into one SST per bucket and must arrive in strictly increasing encoded-key order (SQLite BINARY collation order on the key's tuple columns — the tuple key codec is order-preserving; violations fail with ErrBulkImportOutOfOrder). These add methods are single-threaded.
Entitlements and grants are keyed by structural identity, whose tuple order does NOT match the converter's external-id scan order, so they go through spill sorters instead (entitlements single-threaded on the parent; grants scale across goroutines, each scanning goroutine taking its own shard via NewGrantShard so the grant hot path acquires no shared lock at all). The secondary index families are derived internally from the translated records — the same nil-guards and key shapes as the engine's canonical writeXxxIndexes paths — and are key-only spill-sorted. Spill chunks sort and flush to disk in the background while the scans keep streaming; Finish k-way merges each family's sorted runs (across all shards) into one SST per family, in parallel, and ingests everything in a single call.
SQLite's UNIQUE(external_id, sync_id) guarantees each row appears once. Grant rows with distinct external ids but identical refs fold to one structural identity; Finish field-merges such duplicates and warns. Entitlement identities embed the raw external id and cannot fold. The destination sync must be freshly started (MarkFreshSync via StartNewSync) and empty; nothing else may write to the engine between Start and Finish. Used by C1File.ToPebble for sqlite→pebble conversion.
func (*BulkSyncImport) Abort ¶ added in v0.13.7
func (b *BulkSyncImport) Abort()
Abort closes and removes all staged files without ingesting. Safe to call after Finish (no-op) or on a partially-failed import.
func (*BulkSyncImport) AddEntitlements ¶ added in v0.13.7
func (b *BulkSyncImport) AddEntitlements(ctx context.Context, ents ...*v2.Entitlement) error
AddEntitlements translates and appends entitlements. Rows are keyed by structural identity and re-sorted through a spill sorter, so arrival order does not matter (the converter's external-id scan order does NOT match identity-tuple order). Identity embeds the raw external id, so duplicates are impossible for well-formed sources and fail the merge.
func (*BulkSyncImport) AddEntitlementsWithDiscoveredAt ¶ added in v0.17.0
func (b *BulkSyncImport) AddEntitlementsWithDiscoveredAt(ctx context.Context, ents []*v2.Entitlement, discoveredAt []*timestamppb.Timestamp) error
AddEntitlementsWithDiscoveredAt is AddEntitlements with an optional per-record discovered_at aligned by index with ents; nil entries fall back to now. See AddGrantsWithDiscoveredAt for why conversions must preserve source discovery times.
func (*BulkSyncImport) AddResourceTypes ¶ added in v0.13.7
func (b *BulkSyncImport) AddResourceTypes(ctx context.Context, rts ...*v2.ResourceType) error
AddResourceTypes translates and appends resource types, which must arrive sorted by external id.
func (*BulkSyncImport) AddResourceTypesWithDiscoveredAt ¶ added in v0.17.0
func (b *BulkSyncImport) AddResourceTypesWithDiscoveredAt(ctx context.Context, rts []*v2.ResourceType, discoveredAt []*timestamppb.Timestamp) error
AddResourceTypesWithDiscoveredAt is AddResourceTypes with an optional per-record discovered_at aligned by index with rts; nil entries fall back to now. See AddGrantsWithDiscoveredAt for why conversions must preserve source discovery times.
func (*BulkSyncImport) AddResources ¶ added in v0.13.7
AddResources translates and appends resources, which must arrive sorted by (resource_type_id, resource_id). by_parent index keys are derived and spilled with the same parent guard as writeResourceIndexes.
func (*BulkSyncImport) AddResourcesWithDiscoveredAt ¶ added in v0.17.0
func (b *BulkSyncImport) AddResourcesWithDiscoveredAt(ctx context.Context, resources []*v2.Resource, discoveredAt []*timestamppb.Timestamp) error
AddResourcesWithDiscoveredAt is AddResources with an optional per-record discovered_at aligned by index with resources; nil entries fall back to now. See AddGrantsWithDiscoveredAt for why conversions must preserve source discovery times.
func (*BulkSyncImport) ComputedStats ¶ added in v0.13.7
func (b *BulkSyncImport) ComputedStats() *v3.SyncStatsRecord
ComputedStats returns a SyncStatsRecord built from the counts the import accumulated while streaming (primary record counts, resources by resource type, entitlements by resource type, grants by entitlement resource type). Valid after all grant shards are Closed; call it after Finish so the counts reflect any duplicate-identity grant rows that folded at merge time. Assets do not flow through the importer — the caller sets that count itself before stashing the record via Engine.StashComputedSyncStats.
func (*BulkSyncImport) Finish ¶ added in v0.13.7
func (b *BulkSyncImport) Finish(ctx context.Context) error
Finish finalizes the import: every spill sorter drains its background sorts, each index family k-way merges its sorted runs (combined across all shards) into one SST in parallel, the ordered primary SSTs (including each shard's grant SST) are collected, and everything is ingested in one pebble Ingest call (the key ranges are pairwise disjoint, so a single call is legal and costs one manifest rotation). The staging directory is removed on the way out regardless of outcome.
func (*BulkSyncImport) NewGrantShard ¶ added in v0.13.7
func (b *BulkSyncImport) NewGrantShard() (*BulkGrantShard, error)
NewGrantShard registers a new grant import shard. Safe to call concurrently.
type DigestBucket ¶ added in v0.18.3
DigestBucket addresses one hash bucket of a partition: the records whose bucket hash starts with the top Bits bits of Index. The zero value (Bits 0) addresses the whole partition.
type DigestRoot ¶ added in v0.18.3
type DigestRoot struct {
Hash []byte
Bits int // leaf-level width in bits; 0 = root-only digest
Count int64
}
DigestRoot is a partition's stored root digest.
type Durability ¶
type Durability int
Durability controls how aggressively the engine fsyncs writes. The default for production is DurabilitySync; the fresh-sync fast path (which uses pebble.NoSync for in-flight grant batches) falls under DurabilityNoSync because the sync workflow can replay from the connector if the host crashes before checkpoint.
const ( DurabilityDefault Durability = iota // == DurabilitySync DurabilitySync // fsync per batch commit DurabilityNoSync // buffered by the OS; faster, weaker guarantees )
type Engine ¶
type Engine struct {
// contains filtered or unexported fields
}
Engine is the v3 Pebble-backed storage engine. Methods are goroutine-safe modulo the lifecycle rules in this file:
- Open the engine once with Open(...).
- Concurrent Reader/Writer calls are safe.
- Close() releases all resources. After Close, all methods return ErrEngineClosing.
func AsEngine ¶ added in v0.12.5
func AsEngine(w connectorstore.Writer) (*Engine, bool)
AsEngine recovers the underlying *Engine from a connectorstore.Writer produced by dotc1z.NewStore for the Pebble engine. NewStore returns a wrapper that embeds *Adapter; a bare *Adapter is also accepted for callers that construct one directly. Returns (nil, false) for any non-Pebble store, so a caller can branch on the engine without importing internal types.
func Open ¶
Open creates or opens a Pebble engine rooted at dir. If dir does not exist, Pebble creates it. The caller is responsible for providing a directory that won't be shared with another Pebble instance.
func (*Engine) AbortSynthesizedGrantLayer ¶ added in v0.18.0
AbortSynthesizedGrantLayer discards an in-flight layer session: already ingested segments remain in the DB (their rows are idempotent overwrites on retry), staged chunks are dropped. Safe to call with no open session.
Deliberately does NOT take the engine write barrier — Close calls it after setting the closing flag (withWrite would refuse), and it must stay callable as a cleanup path when a writer holding writeMu panicked. The synthLayerMu take keeps the pointer handoff race-free against Begin/Add/Finish/Close.
func (*Engine) AddSynthesizedGrantLayerContributions ¶ added in v0.18.0
func (e *Engine) AddSynthesizedGrantLayerContributions(ctx context.Context, records []synthesizedGrantRecord) error
AddSynthesizedGrantLayerContributions encodes records into the open layer session. Rows become readable as their segment is ingested; callers must not rely on visibility before FinishSynthesizedGrantLayer returns.
func (*Engine) BeginSynthesizedGrantLayer ¶ added in v0.18.0
BeginSynthesizedGrantLayer opens a layer-scoped layer session. The ingested SSTs carry primary rows only; the by_principal index is always rebuilt at EndSync, so no inline index maintenance is skipped by taking this path. The boolean is part of the store-level contract (non-Pebble stores report false and callers fall back to StoreNewExpandedGrantContributions).
func (*Engine) BuildDeferredGrantIndexes ¶ added in v0.18.0
BuildDeferredGrantIndexes rebuilds the remaining scattered expansion index family, by_principal, from entitlement-first primary grant keys. The expansion write path can skip by_principal inline because expansion reads by entitlement only; this method rewrites the whole by_principal range as one sorted SST at EndSync.
The same scan also rebuilds the primary grant keyspace itself: every raw (key, value) is teed into rolling SSTs which replace the whole grant range via IngestAndExcise. Expansion's layer ingests are SSTs whose spans interleave, so they stack in the LSM's upper levels; consolidating them via compaction rewrites the data once per level hop (a whale measured ~60s / 431 compactions), while this tee rewrites each byte exactly once on top of a scan that is already being paid for the index build. After the excise the grant range is one flat sorted run in the bottom level: compaction debt ~0 without running the compactor at all. Requires no concurrent grant writers, which EndSync guarantees.
The scan also accumulates the grant portion of the sync-stats sidecar (total count + per-entitlement-resource-type grouping) and stashes it so the subsequent PersistSyncStats call can skip its own full grant scan. The stats accumulation is best-effort: a value that fails the shallow field scan just disables the stash and PersistSyncStats falls back to scanning.
The whole build runs under the engine write barrier (withWrite): the IngestAndExcise calls destroy everything in their key ranges, so a grant row committed by a concurrent writer after the scan's iterator snapshot would be silently erased. EndSync's callers are expected to have quiesced writers already — holding writeMu for the duration converts that convention into an enforced invariant (a straggler write blocks until the build finishes instead of racing the excise), and writeWG participation means Close waits the build out instead of tearing down e.db under it.
func (*Engine) BuildGrantDigests ¶ added in v0.18.3
BuildGrantDigests is the full-file standalone digest build: grants written through PutGrantRecords / UnsafePutUniqueGrantRecords / the bulk importer maintain by_principal inline and never arm the deferred-index marker, so a sync without expansion-path writes reaches EndSync with deferredIdxPending false — and possibly millions of grants. This runs its own primary-grant scan feeding the same spill-sorter → merge+fold → ingest machinery the fused pass uses (buildGrantDigestsFromSpill). When the deferred pass DOES run, the fused build supersedes this (same output, one shared scan).
Callers: Adapter.endSyncFinalize calls RepairMissingGrantDigests rather than this directly — RepairMissingGrantDigests falls back to this exact function whenever no digest exists yet at all (grantDigestsPresent false), which is always true for a brand-new sync (ResetForNewSync excises the digest keyspace at StartNewSync), so the common case is unchanged. This function stays exported and self-sufficient for direct callers (tests, explicit repair) that want an unconditional from-scratch rebuild regardless of what already exists.
Failure semantics match the fused pass: any build error (except context cancellation, which stays fatal) downgrades to a loud drop of the digest state — absent digests are safe, half-built ones are not.
func (*Engine) CheckpointTo ¶
CheckpointTo writes a self-contained Pebble directory snapshot to destDir. destDir must not exist yet. Pebble creates it and hard-links SSTs where possible.
The source engine stays writable after CheckpointTo returns; writes are only blocked while the checkpoint is cut. This is the building block dotc1z's higher-level Save wraps with the v3 envelope format.
Read-only engines cannot call pebble.DB.Checkpoint (it copies OPTIONS via d.optionsFileNum, which Pebble never populates on read-only open). Those engines clone the on-disk tree with vfs.Clone instead.
The explicit Flush is what makes the snapshot WAL-independent: every committed write lands in SSTs before the checkpoint is cut. We deliberately do NOT pass pebble.WithFlushedWAL() — it would be redundant after the flush, and it appends a WAL record, guaranteeing the checkpoint carries a WAL file.
CheckpointTo takes the engine write barrier for the whole Flush→Checkpoint→truncate window. That prevents a write from committing between the Flush and Checkpoint — such a write would otherwise exist only in the WAL, which truncateCheckpointWALs discards. It also takes checkpointMu exclusively for the same window: the synth-layer session's background worker ingests SSTs outside writeMu (see ingestSynthLayerSegment), and a flushable ingest landing mid-window would be a WAL-only record the truncate discards.
func (*Engine) Close ¶
Close shuts down the engine. After Close, all methods return ErrEngineClosing. Close blocks until all in-flight writes complete.
func (*Engine) CompactAllRanges ¶ added in v0.15.0
CompactAllRanges runs pebble.Compact over every sync-scoped range to reclaim disk from the tombstones ResetForNewSync (or any bulk delete) leaves behind. The file holds one sync, so "all ranges" is the whole data keyspace. Without this, the next checkpoint would still include the deleted bytes.
Errors are best-effort: a Compact failure on one range doesn't block the others — pebble retries compaction in the background. ctx is honored between ranges and surfaced verbatim on cancellation.
Refuses with ErrEngineSealed after EndSync (via checkWritable): manual compactions go through the same CompactionScheduler as automatic ones, so on a sealed (paused) engine db.Compact would block forever waiting for a grant — and, because we hold writeWG, deadlock Engine.Close too. Bind a sync (SetCurrentSync) first.
KNOWN LIMITATION: the gate only refuses calls made after the seal. A CompactAllRanges already inside its loop when EndSync pauses the scheduler blocks in db.Compact indefinitely (and holds writeWG, so a later Close hangs too). Do not run this concurrently with EndSync; no in-tree caller does.
func (*Engine) ComputeEntitlementBucketDigest ¶ added in v0.18.3
func (e *Engine) ComputeEntitlementBucketDigest(ctx context.Context, id entitlementIdentity, bucket DigestBucket) ([]byte, int64, error)
ComputeEntitlementBucketDigest folds the grant hash index over a single bucket of an entitlement (the zero bucket = the whole entitlement) — the authoritative on-demand counterpart of the stored digest nodes, for verifying or subdividing a digest that EXISTS. Only meaningful while the entitlement's digest is built (its root is stored): the hash index lives and dies with the digest nodes, so against a never-built or invalidated entitlement this folds an absent index range and returns {0, 0} — "zero grants", not "unknown". Never use it as a fallback for a missing root; see GetEntitlementDigestRoot and computeBucketDigest's precondition.
func (*Engine) CurrentDBSizeBytes ¶
CurrentDBSizeBytes returns the total size of regular files in the Pebble database directory. This is the Pebble equivalent of C1File's SQLite DBSizeProvider capability: it reports the uncompressed working set on disk, including WAL/log, MANIFEST, OPTIONS, and SST files currently present.
func (*Engine) DB ¶
DB returns the underlying *pebble.DB. Exported for the synccompactor/pebble package; callers must not Close it directly (use Engine.Close) and must respect the engine's lifecycle. Returns nil after Close. Callers that write the entitlement keyspace through this handle (ingests, excises) must call InvalidateBareIDLookups afterwards.
func (*Engine) DBDir ¶
DBDir returns the on-disk path the engine writes to. Exported so the Adapter can implement OutputFilepath / CurrentDBSizeBytes.
func (*Engine) DeleteAssetRecord ¶
func (*Engine) DeleteEntitlementRecord ¶
DeleteEntitlementRecord deletes by raw public id. A missing id is a no-op; an ambiguous id is an error (a lossy string must never guess a delete).
func (*Engine) DeleteGrantByIdentityRefs ¶ added in v0.18.0
DeleteGrantByIdentityRefs removes a grant addressed by its structural refs — the exact delete path for callers that hold the full grant. No lossy id string is involved. Deleting a non-existent grant is a no-op.
func (*Engine) DeleteGrantRecord ¶
DeleteGrantRecord removes a grant and its index entries by raw public id. A missing/unresolvable id is a no-op; an ambiguous id is an error (a lossy string must never guess a delete).
func (*Engine) DeleteResourceRecord ¶
func (*Engine) DeleteResourceTypeRecord ¶
func (*Engine) DeleteSyncRunRecord ¶
func (*Engine) DirtyEntitlementBuckets ¶ added in v0.18.3
func (e *Engine) DirtyEntitlementBuckets(ctx context.Context, other *Engine, id entitlementIdentity) ([]DigestBucket, error)
DirtyEntitlementBuckets compares this engine's entitlement against other's and returns the buckets whose grants differ — see dirtyPartitionBuckets for the comparison contract (zero bucket = whole entitlement; nil = identical).
func (*Engine) DropAllGrantDigestState ¶ added in v0.18.3
DropAllGrantDigestState removes every stored digest node AND the whole by_entitlement_principal_hash index, and clears the engine's digests-present flag. For callers that are about to mutate grants through paths that bypass the engine's index maintenance entirely (the synccompactor's fold merge): after this, digests read as "missing — recalculate", never as stale-but-present, and subsequent engine-path writes skip per-entitlement invalidation tombstones. Two range tombstones: the digest keyspace and the hash-index keyspace are NOT adjacent (typeCounter/typeSession sit between them), so this must never be collapsed into one span.
func (*Engine) DropAllGrantDigests ¶ added in v0.18.3
DropAllGrantDigests removes every stored digest node. Called when a seal-time build fails partway: a partially built digest that LOOKS present would violate the present-means-exact contract, whereas absent digests just make readers re-read the grants until the next successful seal recalculates them.
func (*Engine) EndFreshSync ¶
EndFreshSync clears the fresh-sync flag and flushes the memtable + fsyncs the WAL so the data written during the sync is on disk before the caller returns. Called by Adapter.EndSync.
Uses withWrite (not a bare writeMu) so the flush participates in the closing check and writeWG: Close tears e.db down after writeWG.Wait, and a bare-mutex EndFreshSync racing Close would flush a nil db.
func (*Engine) ExpandWritePathStats ¶ added in v0.18.0
func (e *Engine) ExpandWritePathStats() ExpandWritePathStats
func (*Engine) FinishSynthesizedGrantLayer ¶ added in v0.18.0
FinishSynthesizedGrantLayer flushes the session's tail segment, waits for the background worker to merge and ingest every queued segment, and closes the session. No-op if no session is open or the session saw no rows.
func (*Engine) Flush ¶
Flush forces the engine's memtable to disk. Exported so the cleanup orchestration can ensure tombstones are durable before the next checkpoint reads the LSM.
This is a thin wrapper over pebble.DB.Flush + a WAL fsync; the EndFreshSync path uses the same combination at sync end. ctx is checked before the blocking calls so a cancelled deadline doesn't trigger a full memtable flush.
func (*Engine) GetAssetRecord ¶
func (*Engine) GetEntitlementDigestRoot ¶ added in v0.18.3
func (e *Engine) GetEntitlementDigestRoot(ctx context.Context, id entitlementIdentity) (DigestRoot, bool, error)
GetEntitlementDigestRoot returns the stored grant-digest root for an entitlement. ok is false when no digest has been built for it (or it was invalidated) — which means the caller must re-read the entitlement's grants (or treat the whole entitlement as dirty), NOT fall back to ComputeEntitlementBucketDigest: the hash index that fold reads is only ever written and dropped alongside the digest nodes, so with no root it is absent too and the fold would report "zero grants" for an entitlement that may have millions — the false-clean trap dirtyPartitionBuckets' doc comment describes.
func (*Engine) GetEntitlementRecord ¶
func (e *Engine) GetEntitlementRecord(ctx context.Context, externalID string) (*v3.EntitlementRecord, error)
GetEntitlementRecord fetches an entitlement by its raw public id via the bare-id lookup (exact string-match, exactly-one rule — see lookup.go).
func (*Engine) GetGrantDigestGlobalRoot ¶ added in v0.18.3
GetGrantDigestGlobalRoot returns the whole-file grant digest root — the XOR fold of every entitlement's grant-digest root's content-hash plus the total grant count, across the sync's single sync. ok is false when no digest has been built for this file (present-means- exact: absence means "recalculate", never "zero grants"). Written only alongside a full grant-digest build (the seal-time build, or a compaction that runs BuildGrantDigests) and dropped by the same invalidation paths that drop any per-entitlement root — see stageGrantDigestInvalidation and the Drop* functions below.
func (*Engine) GetGrantRecord ¶
GetGrantRecord fetches a grant record by its raw public id via the bare-id lookup (candidate-split probing, exactly-one rule — lookup.go).
func (*Engine) GetResourceRecord ¶
func (*Engine) GetResourceTypeRecord ¶
func (*Engine) GetSyncRunRecord ¶
func (*Engine) GrantDigestIndexEnabled ¶ added in v0.18.3
GrantDigestIndexEnabled reports whether the seal-time deferred pass builds the by_entitlement_principal_hash index and grant digests. See WithGrantDigestIndex.
func (*Engine) InvalidateBareIDLookups ¶ added in v0.18.0
func (e *Engine) InvalidateBareIDLookups()
InvalidateBareIDLookups invalidates the lazily built bare-id lookup state (see lookup.go). Engine write paths call this internally; it is exported for callers that mutate the keyspace through DB() directly.
func (*Engine) InvalidateGrantDigestPartitions ¶ added in v0.18.3
InvalidateGrantDigestPartitions drops the digest + hash-index state for exactly the given entitlement partitions, plus the whole-file global root — which folds over every partition and goes stale the moment any one of them does. Every other partition's state is left completely untouched.
For callers that mutate the primary grant keyspace directly, bypassing the engine's own per-record invalidation path (stageGrantDigestInvalidation) — the fold compactor's raw merge — to report exactly what they changed. RepairMissingGrantDigests recomputes exactly what this drops (and only that), so the pair gives compaction per-entitlement-scoped digest maintenance without an O(file) rebuild. No-op when partitions is empty or no digest has ever been built for this file.
func (*Engine) IsFreshSync ¶
IsFreshSync reports whether the engine is in the fresh-sync write path (set by MarkFreshSync).
func (*Engine) IsSealed ¶ added in v0.18.0
IsSealed reports whether the engine is in the post-EndSync sealed state.
func (*Engine) IterateAllSyncRuns ¶
IterateAllSyncRuns iterates every sync_run record in the engine. Used by callers that want "what syncs do I have available?".
func (*Engine) IterateAssets ¶ added in v0.15.0
func (*Engine) IterateEntitlements ¶ added in v0.15.0
func (*Engine) IterateEntitlementsByResource ¶
func (*Engine) IterateGrants ¶ added in v0.15.0
IterateGrants iterates all grants in primary-key order. yield returns false to stop iteration.
func (*Engine) IterateGrantsByEntitlement ¶
func (e *Engine) IterateGrantsByEntitlement(ctx context.Context, entitlementID string, yield func(*v3.GrantRecord) bool) error
IterateGrantsByEntitlement iterates the primary grant keyspace for the given raw entitlement id, yielding each grant in encoded principal-key order. The id resolves through the bare-id lookup; an id matching no entitlement iterates nothing. yield returns false to stop.
func (*Engine) IterateGrantsByEntitlementBucket ¶ added in v0.18.3
func (e *Engine) IterateGrantsByEntitlementBucket(ctx context.Context, id entitlementIdentity, bucket DigestBucket, yield func(*v3.GrantRecord) bool) error
IterateGrantsByEntitlementBucket yields the grants in one principal-hash bucket of an entitlement (the zero bucket = the whole entitlement). This is the dirty-bucket loader: after a digest comparison flags a bucket, the caller materializes only those grants. The primary key is reconstructed from each index key by byte splice (no decode); the point Get per entry is the cost of MATERIALIZING a changed grant, not of finding it. Orphan index entries are skipped.
func (*Engine) IterateGrantsByNeedsExpansion ¶
func (e *Engine) IterateGrantsByNeedsExpansion(ctx context.Context, yield func(*v3.GrantRecord) bool) error
IterateGrantsByNeedsExpansion iterates the needs_expansion index, yielding each grant whose NeedsExpansion flag is currently set. yield returns false to stop.
Pebble-equivalent of the SQLite partial index `WHERE needs_expansion = 1`. Backs PendingExpansionPage on the grant store.
func (*Engine) IterateGrantsByPrincipal ¶
func (e *Engine) IterateGrantsByPrincipal(ctx context.Context, principalRT, principalID string, yield func(*v3.GrantRecord) bool) error
IterateGrantsByPrincipal iterates the by_principal index.
func (*Engine) IterateGrantsByPrincipalResourceType ¶
func (e *Engine) IterateGrantsByPrincipalResourceType(ctx context.Context, principalRT string, yield func(*v3.GrantRecord) bool) error
IterateGrantsByPrincipalResourceType iterates the by_principal index narrowed to a principal resource type. Yields each grant whose principal carries the given resource_type. Stops when yield returns false.
func (*Engine) IterateResourceTypes ¶ added in v0.15.0
func (*Engine) IterateResources ¶ added in v0.15.0
func (*Engine) IterateResourcesByParent ¶
func (*Engine) LatestFinishedSyncRecord ¶
func (e *Engine) LatestFinishedSyncRecord(ctx context.Context, typeOK func(v3.SyncType) bool) (*v3.SyncRunRecord, error)
LatestFinishedSyncRecord returns the SyncRunRecord with the latest ended_at that matches typeOK, or (nil, nil) when no finished sync matches. typeOK may be nil to accept any sync type.
This is the single source of truth for "pick the latest finished sync" on the Pebble engine; all three external entry points (connectorstore.LatestFinishedSyncIDFetcher, c1zstore.SyncMeta.LatestFullSync / LatestFinishedSyncOfAnyType, and reader_v2.SyncsReaderService.GetLatestFinishedSync) call here so the tiebreaker and predicate semantics stay consistent.
Ties on ended_at are broken by sync_id (KSUIDs sort by time, so the lexicographically greater id is the later sync). Matches the SQLite-side `ORDER BY ended_at DESC, sync_id DESC` in pkg/dotc1z/sync_runs.go:getFinishedSync (commit 1627b047) which closes a Windows coarse-time-resolution race where adjacent EndSync calls can produce identical ended_at timestamps.
O(N) in the count of sync_runs (one record per sync; typically a few hundred over a c1z's lifetime, not millions).
func (*Engine) LatestUnfinishedSyncRecord ¶
func (e *Engine) LatestUnfinishedSyncRecord(ctx context.Context, typeOK func(v3.SyncType) bool) (*v3.SyncRunRecord, error)
LatestUnfinishedSyncRecord returns the most-recently-started sync_run that has NOT ended and was started within the last week, matching typeOK (nil = any type), or (nil, nil) when none match.
Mirrors SQLite's getLatestUnfinishedSync (pkg/dotc1z/sync_runs.go): it selects ended_at-null records started after the unfinishedSyncMaxAge cutoff, ordered by started_at (sync_id tiebreaker for coarse clock resolution). It is the read-path fallback after LatestFinishedSyncRecord, so a c1z whose only sync was interrupted before EndSync still resolves to that in-progress sync instead of returning no sync at all — matching the SQLite resolveSyncIDForRead cascade.
O(N) in the count of sync_runs (one record per sync).
func (*Engine) LogCompactionMetrics ¶ added in v0.18.0
LogCompactionMetrics logs a snapshot of the LSM's compaction and ingest counters, labeled with the caller's phase name. Diagnostic only: used to attribute wall time lost to background compactions triggered by large ingests (e.g. the synthesized-grant layer segments) during benchmark runs.
func (*Engine) MarkFreshSync ¶
MarkFreshSync sets currentSync AND flags the sync as freshly started (no prior records under this sync_id). The engine then takes the perf-fast write path: pebble.NoSync per commit and skip read-before-write index cleanup. The host crash semantics match SQLite's PRAGMA synchronous=NORMAL — the connector is the source of truth during the sync; a crash forces re-sync rather than silent data loss.
Callers should call EndFreshSync (via Flush) at sync end to harden the data with a single fsync.
func (*Engine) MigratedOnOpen ¶ added in v0.18.0
MigratedOnOpen reports whether this Open ran the in-place id-index migration (see migratedOnOpen).
func (*Engine) PaginateEntitlements ¶ added in v0.15.0
func (e *Engine) PaginateEntitlements( ctx context.Context, cursor string, limit int, ) ([]*v3.EntitlementRecord, string, error)
PaginateEntitlementsBySync returns a page of entitlements in primary key order.
func (*Engine) PaginateEntitlementsByResource ¶
func (e *Engine) PaginateEntitlementsByResource( ctx context.Context, resourceTypeID, resourceID, cursor string, limit int, ) ([]*v3.EntitlementRecord, string, error)
PaginateEntitlementsByResource uses the entitlement primary key prefix.
func (*Engine) PaginateGrantPrincipalKeysByEntitlement ¶ added in v0.12.3
func (e *Engine) PaginateGrantPrincipalKeysByEntitlement( ctx context.Context, entID entitlementIdentity, cursor string, limit int, ) ([]string, string, error)
PaginateGrantPrincipalKeysByEntitlement scans the primary grant keyspace under the entitlement identity prefix and returns only principal identity keys for each matching grant. The key format is principal_resource_type + "\x00" + principal_resource_id, matching pkg/sync/expand's descendantGrantKey.
func (*Engine) PaginateGrants ¶ added in v0.15.0
func (e *Engine) PaginateGrants( ctx context.Context, cursor string, limit int, ) ([]*v3.GrantRecord, string, error)
PaginateGrantsBySync returns up to `limit` grants from the primary-key range, starting strictly after `cursor`. Returns the next cursor (empty if no more) plus the materialized records.
Uses a per-call grantReadArena to pre-allocate the GrantRecord + nested EntitlementRef + PrincipalRef slots. The vtproto-generated UnmarshalVT detects the pre-set pointers and reuses the arena slots instead of heap-allocating a fresh nested struct per record, collapsing 2N nested allocs into 2 slice allocs per page. The arena lifetime ends with this function — callers receive pointers into the arena's backing arrays, so retention is intentional.
func (*Engine) PaginateGrantsByEntitlement ¶
func (e *Engine) PaginateGrantsByEntitlement( ctx context.Context, entID entitlementIdentity, cursor string, limit int, ) ([]*v3.GrantRecord, string, error)
PaginateGrantsByEntitlement scans the primary grant keyspace under the entitlement identity prefix. Cursor is the primary key. Callers resolve the identity from structured refs (or the bare-id lookup) — the engine never parses id strings here.
func (*Engine) PaginateGrantsByEntitlementPrincipal ¶ added in v0.12.3
func (e *Engine) PaginateGrantsByEntitlementPrincipal( ctx context.Context, entID entitlementIdentity, principalRT, principalID, cursor string, limit int, ) ([]*v3.GrantRecord, string, error)
PaginateGrantsByEntitlementPrincipal uses the structured primary key for a point lookup by entitlement + principal. This is the hot path for grant expansion, where callers repeatedly ask whether a single principal already has a grant on a descendant entitlement.
func (*Engine) PaginateGrantsByEntitlementResource ¶
func (e *Engine) PaginateGrantsByEntitlementResource( ctx context.Context, entRT, entRID, cursor string, limit int, ) ([]*v3.GrantRecord, string, error)
PaginateGrantsByEntitlementResource walks the primary grant keyspace for all grants whose entitlement's resource is (entRT, entRID). Cursor is the primary key.
Drives Adapter.ListGrants and ListWithAnnotationsForResourcePage when req.Resource is set, matching SQLite's `listGrantsGeneric` filter on grants.resource_id / resource_type_id (the entitlement-side resource columns). The pre-existing Pebble path used PaginateGrantsByPrincipal here, which returned empty for the common "grants on this group" semantic.
func (*Engine) PaginateGrantsByNeedsExpansion ¶
func (e *Engine) PaginateGrantsByNeedsExpansion( ctx context.Context, cursor string, limit int, ) ([]*v3.GrantRecord, string, error)
PaginateGrantsByNeedsExpansion returns a page of grants whose NeedsExpansion flag is set. Backs the GrantStore PendingExpansionPage path; the SQLite equivalent is a query guarded by the partial index `WHERE needs_expansion = 1`.
Cursor is the needs_expansion index key.
func (*Engine) PaginateGrantsByPrincipal ¶
func (e *Engine) PaginateGrantsByPrincipal( ctx context.Context, principalRT, principalID, cursor string, limit int, ) ([]*v3.GrantRecord, string, error)
PaginateGrantsByPrincipal uses the by_principal index. Same shape as PaginateGrantsByEntitlement.
func (*Engine) PaginateGrantsByPrincipalResourceType ¶
func (e *Engine) PaginateGrantsByPrincipalResourceType( ctx context.Context, principalRT, cursor string, limit int, ) ([]*v3.GrantRecord, string, error)
PaginateGrantsByPrincipalResourceType walks the by-principal-RT index. Cursor is the index key. Drives the new fast path for the Adapter's ListGrantsForResourceType.
func (*Engine) PaginateResourceTypes ¶ added in v0.15.0
func (e *Engine) PaginateResourceTypes( ctx context.Context, cursor string, limit int, ) ([]*v3.ResourceTypeRecord, string, error)
PaginateResourceTypesBySync returns a page of resource_types.
func (*Engine) PaginateResources ¶ added in v0.15.0
func (e *Engine) PaginateResources( ctx context.Context, cursor string, limit int, ) ([]*v3.ResourceRecord, string, error)
PaginateResourcesBySync returns a page of resources in primary key order.
func (*Engine) PaginateResourcesByParent ¶
func (e *Engine) PaginateResourcesByParent( ctx context.Context, parentRT, parentID, cursor string, limit int, ) ([]*v3.ResourceRecord, string, error)
PaginateResourcesByParent uses the by_parent index.
func (*Engine) PersistComputedSyncStats ¶ added in v0.13.2
func (e *Engine) PersistComputedSyncStats(ctx context.Context, syncID string, rec *v3.SyncStatsRecord) error
PersistComputedSyncStats writes a caller-computed stats record — e.g. one accumulated while the synccompactor wrote merge winners — without re-scanning the keyspaces. SyncId and WrittenAt are set here so callers only fill counts. Durability matches PersistSyncStats (pebble.Sync via writeSyncStats).
func (*Engine) PersistSyncStats ¶
PersistSyncStats computes and writes the stats sidecar for syncID. Exposed for the EndFreshSync caller and the on-Open migration backfill. If a caller-computed record was stashed for this sync (StashComputedSyncStats), it is persisted instead of re-scanning the keyspaces.
func (*Engine) PutAssetRecord ¶
PutAssetRecord — assets have no secondary indexes.
func (*Engine) PutEntitlementRecord ¶
PutEntitlementRecord writes an entitlement + its by_resource index.
func (*Engine) PutEntitlementRecords ¶
PutEntitlementRecords writes N entitlements by structured primary key. The identity key is a pure function of the record (it contains the raw external id), so overwrites are idempotent and no read-before-write or index cleanup is needed; a within-call dedup pre-pass keeps last-wins semantics for same-identity duplicates in one batch.
func (*Engine) PutEntitlementRecordsIfNewer ¶
func (e *Engine) PutEntitlementRecordsIfNewer(ctx context.Context, records ...*v3.EntitlementRecord) error
PutEntitlementRecordsIfNewer writes entitlements only when newer.
func (*Engine) PutExpandedGrantRecords ¶ added in v0.15.3
PutExpandedGrantRecords is the grant-expander write path — the engine side of GrantStore.StoreExpandedGrants, and its only caller.
Two properties distinguish it from PutGrantRecords:
Single read-before-write. The expander must preserve each grant's existing Expansion / NeedsExpansion / DiscoveredAt side-state, which requires reading the prior primary value. That same read also yields the bytes needed to delete the prior value's stale index entries. The old path did BOTH a GetGrantRecord in the adapter (to preserve side-state) AND a db.Get here (to clean indexes) — two point lookups per grant. This path issues one and uses it for both.
NoSync commit. Expanded grants are fully regenerable from the sync (the expander recomputes them from the entitlement graph), so a per-batch fsync buys nothing. Writes commit with pebble.NoSync and are hardened by the single Flush at sync end (EndFreshSync) or Close — the same bargain the fresh-sync fast path strikes, extended to resumed syncs where IsFreshSync() is false.
records arrive as freshly translated v3 GrantRecords with NO preservation or discovered_at stamping applied; this method performs the merge so the read it already issues does double duty.
func (*Engine) PutGrantRecord ¶
PutGrantRecord writes a grant record + its remaining secondary index entries, atomically in a single pebble.Batch.
This is the engine's canonical write path; other record types follow the same shape (read the previous primary if any → delete its index entries → write the new primary → write the new index entries → commit).
func (*Engine) PutGrantRecords ¶
PutGrantRecords writes N grants in two pebble.Batches — one for primary-key writes, one for index-key writes — and commits them sequentially. This is the bulk path the adapter's PutGrants uses.
Mutation safety. Connectors can legitimately emit the same external_id twice within a single sync (paginated sources, deduplication bugs in upstream APIs, etc.). To prevent orphan index entries from earlier duplicates, we pre-scan records to find the latest occurrence of each external_id and process only those — earlier duplicates are dropped before any batch byte is written. db.Get doesn't see in-batch writes either way, so this dedup pass is the load-bearing safety net that neither the old read-before-write path nor a pure skip-Get path provides.
Read-before-write index cleanup. On a NON-fresh sync the engine must Get the prior primary value so it can delete the index keys the previous sync wrote (entitlement/principal can change between syncs). On a FRESH sync the keyspace is empty by construction (StartNewSync excises it) so the Get is guaranteed to return ErrNotFound and we skip it — saves an LSM lookup per record on the bulk path.
Two batches. The primary keys arrive in roughly sorted order from the adapter (V2→V3 translator preserves the connector's record order, and most connectors emit grants clustered by entitlement which tends to cluster external_ids). The index keys are sorted on a totally different field (entitlement_id / principal), so interleaving them in one batch makes Pebble's flushable-batch promotion path quote sort. Splitting them lets each batch's natural order survive.
Fresh-sync still uses pebble.NoSync — EndFreshSync does one Flush+fsync at sync end to harden the data.
func (*Engine) PutGrantRecordsIfNewer ¶
PutGrantRecordsIfNewer writes records that are strictly newer than the stored copy. Records without a discovered_at are treated as "always write" (caller is asserting freshness explicitly).
func (*Engine) PutResourceRecord ¶
PutResourceRecord writes a resource record + its by_parent index entry (if the resource has a parent). Read-before-write index cleanup follows the canonical pattern from grants.go.
func (*Engine) PutResourceRecords ¶
PutResourceRecords writes N resources in two pebble.Batches — primary keys in one, by_parent index keys in the other. Mirrors the PutGrantRecords pattern (RFC §3a Tier-B/C):
- within-call dedup pre-pass keyed by (rt, res_id) drops earlier occurrences of repeated resources;
- the first PutResourceRecords call of a fresh sync skips the read-before-write Get (keyspace provably empty);
- subsequent calls fall back to read-before-write so cross-call duplicates can clean up index entries the prior call wrote.
func (*Engine) PutResourceRecordsIfNewer ¶
func (e *Engine) PutResourceRecordsIfNewer(ctx context.Context, records ...*v3.ResourceRecord) error
PutResourceRecordsIfNewer writes resources only when the incoming discovered_at is strictly newer than the stored copy.
func (*Engine) PutResourceTypeRecord ¶
PutResourceTypeRecord writes a resource_type record. No secondary indexes, so this is a single-key write.
func (*Engine) PutResourceTypeRecords ¶
func (e *Engine) PutResourceTypeRecords(ctx context.Context, records ...*v3.ResourceTypeRecord) error
PutResourceTypeRecords writes N resource_types in one batch. Fresh-sync uses pebble.NoSync (one fsync at EndFreshSync).
func (*Engine) PutResourceTypeRecordsIfNewer ¶
func (e *Engine) PutResourceTypeRecordsIfNewer(ctx context.Context, records ...*v3.ResourceTypeRecord) error
PutResourceTypeRecordsIfNewer writes resource_types only when newer.
func (*Engine) PutSyncRunRecord ¶
PutSyncRunRecord writes the file's single sync-run record at the fixed sync-run key. The sync_id lives in the record value (the only place a sync_id is stored — never in a key), and must be non-empty since this record is *defining* what counts as a valid sync.
func (*Engine) PutSynthesizedGrantContributions ¶ added in v0.18.0
func (e *Engine) PutSynthesizedGrantContributions(ctx context.Context, records []synthesizedGrantRecord) error
PutSynthesizedGrantContributions batch-writes one destination's synthesized contributions. It is the fallback for stores/engines that cannot run a layer-scoped layer session (see BeginSynthesizedGrantLayer); the layer path is preferred because it publishes sorted SSTs instead of out-of-order batch commits.
func (*Engine) PutSynthesizedGrantRecords ¶ added in v0.18.0
PutSynthesizedGrantRecords writes expander-synthesized grants that the caller guarantees are brand-new by structured grant identity. It skips the read-before-write Get in PutExpandedGrantRecords because there is no prior value whose Expansion/NeedsExpansion/DiscoveredAt or index entries must be preserved/cleaned.
func (*Engine) RepairMissingGrantDigests ¶ added in v0.18.3
RepairMissingGrantDigests rebuilds the by_entitlement_principal_hash rows and digest nodes for exactly the entitlements missing a digest root — trusting every entitlement that already has one — then recomputes the whole-file global root if anything changed. The whole operation holds the engine's write lock for its entire duration, like BuildGrantDigests/BuildDeferredGrantIndexes, so no concurrent grant write can interleave with a partition mid-repair and leave it silently wrong.
When digests were never built for this file at all (or were dropped wholesale — grantDigestsPresent false), a full BuildGrantDigests is strictly cheaper than a point-Get per entitlement that would all miss anyway, so this delegates to it instead.
Failure policy: a repair error for one partition is logged and that partition is left missing (safe — present-means-exact — and self-healing on the next repair or seal) rather than failing the whole call; only context cancellation propagates. Unlike a fused full rebuild, a partial failure here does not require dropping already-repaired partitions — each one commits its own correct, complete state independently.
Orphan grants (an entitlement identity with grants but no entitlement RECORD) are covered: the missing-partition scan discovers grant-bearing partitions from the grant primary keyspace itself, not from entitlement records, so an invalidated orphan is rebuilt like any other partition and the recomputed global root always includes it. That keeps the global root a pure function of the stored grant set — identical whether it was produced by a seal (whose fold covers orphans too) or by a fold + targeted repair — which the manifest's header-only "did the grants change?" comparison depends on. Without it, a touched orphan would go missing forever: the recomputed root's presence would satisfy the repair fast path (repairMissingGrantDigestsAttempt) on every later call.
Called from Adapter.endSyncFinalize in place of a bare BuildGrantDigests whenever the deferred (by_principal-rebuilding) pass didn't run: for a brand-new sync grantDigestsPresent is always false (ResetForNewSync excises the digest keyspace at StartNewSync), so this reduces to exactly BuildGrantDigests's existing full scan — no behavior change for the common single-EndSync-per-sync case. It only diverges — into the cheap fast path or the targeted repair — when EndSync runs a SECOND time on an already-digested, rebound sync (SetCurrentSync, not StartNewSync) without a fresh reset in between: the shape grant-expansion's own follow-up sync produces, and exactly the case that made a full rebuild here wasteful (see RFC 0003 / compactPebbleFold).
Like BuildGrantDigests, a failure that isn't context cancellation is logged and downgraded to a full state drop rather than propagated — digest problems must never fail EndSync. This only covers failures in the outer scan/orchestration (e.g. the entitlement-keyspace scan itself erroring): a failure repairing one INDIVIDUAL partition is already handled without dropping anything (see repairMissingGrantDigestsLocked).
func (*Engine) ResetForNewSync ¶ added in v0.15.0
ResetForNewSync wipes every sync-scoped keyspace (records, indexes, the sync-run record, and the stats sidecar) so a freshly started sync begins on an empty keyspace.
Why this exists: a v3 Pebble c1z holds exactly one sync, and the keys carry no sync_id. StartNewSync calls this before binding a new sync so the replacement sync never inherits orphan records from a prior sync — records the new sync doesn't happen to overwrite would otherwise linger under identical keys.
The wipe uses pebble.DB.Excise rather than DeleteRange tombstones: excise drops fully-covered SSTs from the manifest outright (O(metadata), immediate disk reclaim) instead of leaving range tombstones whose dead bytes survive until compaction. With tombstones, a replacement sync that finishes before background compaction catches up would hard-link the prior sync's dead SSTs into the CheckpointTo envelope at save — the same bloat the old multi-sync Cleanup path ran CompactSyncRanges to avoid. Excise also keeps the MarkFreshSync "empty by construction" fast path honest physically, not just logically.
Two spans cover everything sync-scoped:
- [v3|typeResourceType, v3|typeEngineMeta): every record type, the sync-run record, all secondary indexes, assets, and the reserved counter/session bytes — one contiguous span, so almost every SST is fully covered and dropped whole.
- the stats sidecar range inside engine-meta.
Engine-global metadata — the keyspace-version stamp and index-migration markers — lives elsewhere in engine-meta and is intentionally preserved.
Refuses while a fresh sync is in progress (between MarkFreshSync and EndFreshSync): wiping mid-sync would corrupt the in-flight sync.
func (*Engine) SessionClear ¶ added in v0.16.0
func (*Engine) SessionDelete ¶ added in v0.16.0
func (*Engine) SessionGet ¶ added in v0.16.0
func (*Engine) SessionGetAll ¶ added in v0.16.0
func (*Engine) SessionGetMany ¶ added in v0.16.0
func (*Engine) SessionSet ¶ added in v0.16.0
func (*Engine) SessionSetMany ¶ added in v0.16.0
func (*Engine) SetCurrentSync ¶
SetCurrentSync sets the engine's tracked current sync_id from a string KSUID. Subsequent Put*/List* calls with an empty syncID use this value. Clears the freshSync flag — a bare SetCurrentSync is conservative (treats the sync as resumable, so writes keep fsync + read-before-write).
func (*Engine) StartBulkSyncImport ¶ added in v0.13.7
func (e *Engine) StartBulkSyncImport(ctx context.Context, syncID string, tmpDir string) (*BulkSyncImport, error)
StartBulkSyncImport opens a bulk import targeting syncID, which must be the engine's current FRESH sync (see BulkSyncImport contract). Working files are staged in a fresh directory under tmpDir ("" = system temp dir) and removed by Finish/Abort.
func (*Engine) StashComputedSyncStats ¶ added in v0.13.7
func (e *Engine) StashComputedSyncStats(syncID string, rec *v3.SyncStatsRecord)
StashComputedSyncStats registers a caller-computed stats record to be persisted by the next PersistSyncStats call for syncID instead of the O(N) keyspace re-scan. Intended for trusted bulk writers (e.g. the BulkSyncImport conversion path) that counted every record they wrote; the caller owns the record's correctness. The stash is consumed by exactly one PersistSyncStats call.
func (*Engine) UnsafePutUniqueGrantRecords ¶ added in v0.12.2
UnsafePutUniqueGrantRecords is the trusted-import write path: it writes records unconditionally, with NO read-before-write and NO dedup pass. Do not use it for live connector output. The engine must currently be in fresh-sync mode, and the caller must guarantee each external_id appears at most once across the whole sync (not just within this batch). Primary + index key/value encoding — including the proto marshal — runs in parallel across GOMAXPROCS workers; a single goroutine then Sets the pre-encoded bytes into two batches and commits them (NoSync during a fresh sync).
Unlike PutGrantRecords this skips the per-record db.Get that PutGrantRecords performs on every batch after the first of a fresh sync. That read-before- write only exists to clean up stale index entries when an external_id is rewritten within a sync — impossible when the caller guarantees global uniqueness for the imported sync.
type ExpandWritePathStats ¶ added in v0.18.0
type GrantDigestAccumulator ¶ added in v0.18.4
type GrantDigestAccumulator struct {
// contains filtered or unexported fields
}
GrantDigestAccumulator XOR/count-folds GrantContentHash over a set of grants. Fold one entitlement's grants to reproduce its stored digest root (GetEntitlementDigestRoot), or every grant in a sync to reproduce the whole-file root (GetGrantDigestGlobalRoot / the manifest's GrantDigestRoot.xor_digest — per-entitlement roots XOR-combine, so the flat fold equals the fold of roots). Compare only digests computed under the same GrantDigestABIVersion; see GrantContentHash for the input contract.
The zero value is ready to use. Not safe for concurrent use.
func (*GrantDigestAccumulator) Add ¶ added in v0.18.4
func (a *GrantDigestAccumulator) Add(g *v2.Grant) error
Add folds one grant into the accumulator; a grant GrantContentHash rejects leaves it unchanged.
func (*GrantDigestAccumulator) Root ¶ added in v0.18.4
func (a *GrantDigestAccumulator) Root() DigestRoot
Root returns the fold so far as a DigestRoot: Hash is the 8-byte big-endian XOR fold, Count the number of grants added. Bits is 0 (root-only); compare Hash and Count.
type Option ¶
type Option func(*Options)
Option is a functional option passed to Open.
func WithDurability ¶
func WithDurability(d Durability) Option
WithDurability selects the fsync policy for writes. Default is DurabilitySync.
func WithGrantDigestIndex ¶ added in v0.18.3
WithGrantDigestIndex toggles the seal-time construction of the by_entitlement_principal_hash index and the per-entitlement grant digests. Default true.
Set false on files that will never be grant-diffed (e.g. local CLI syncs, connector development) to skip the derivation work in the EndSync deferred pass. Safe to toggle per Open: a file sealed with this off simply stores no digest roots, which readers and the cross-file comparison treat as "missing — recalculate / whole-entitlement dirty", never as "no grants".
func WithReadOnly ¶
WithReadOnly opens the engine in read-only mode. Save is disallowed.
func WithSharedCache ¶
WithSharedCache reuses a single *pebble.Cache across multiple engine instances. Mandatory for any caller that opens >1 engine in the same process (e.g. C1's per-app ReaderCache); without it the per-engine cache cost compounds linearly.
Cache lifecycle: the caller owns the cache and is responsible for calling cache.Unref() when no engines using it remain. If WithSharedCache is not used, the engine mints its own cache and Unrefs it in Engine.Close.
func WithSlowQueryThreshold ¶
WithSlowQueryThreshold overrides the default 5 s threshold for slow-iterator logging.
Source Files
¶
- adapter.go
- adapter_clone_sync.go
- adapter_diff.go
- adapter_file_ops.go
- adapter_grants_store.go
- adapter_reader.go
- adapter_stats.go
- adapter_streaming.go
- adapter_sync_meta.go
- assets.go
- bulk_import.go
- cleanup.go
- compaction_scheduler.go
- deferred_index.go
- digest.go
- engine.go
- engine_stub.go
- entitlements.go
- expand_write_path_stats.go
- grant_digest.go
- grant_digest_build.go
- grant_digest_repair.go
- grant_read_arena.go
- grants.go
- grants_for_entitlements.go
- grants_synth_encode.go
- id_index_format.go
- id_index_migration.go
- identity.go
- if_newer.go
- index_migrations.go
- keys.go
- keyspace_version.go
- lookup.go
- manifest.go
- merge_accessor.go
- options.go
- paginate.go
- raw_records.go
- resource_types.go
- resources.go
- session_store.go
- sync_runs.go
- sync_stats_sidecar.go
- translate_v2.go
Directories
¶
| Path | Synopsis |
|---|---|
|
Package codec implements the v3 storage engine's record codec layer.
|
Package codec implements the v3 storage engine's record codec layer. |
|
Package microtests holds the 5 risk-validation tests that were written to prove the material design choices in RFC 0004 (storage engine v4) actually hold under Pebble's real behavior.
|
Package microtests holds the 5 risk-validation tests that were written to prove the material design choices in RFC 0004 (storage engine v4) actually hold under Pebble's real behavior. |