Documentation
¶
Index ¶
- Constants
- Variables
- func AcquireLibraryHardDeleteLockLease(session *gocql.Session, libraryID, leaseToken uuid.UUID) (bool, error)
- func MockCanonicalStorageKey(orgID, hash string) string
- func OrgBucket(orgID uuid.UUID) int
- func PendingItemBucket(orgID, libraryID uuid.UUID, itemType ItemType, itemID string) int
- func QueueBucket(orgID uuid.UUID, itemType ItemType, itemID string) int
- func ReleaseLibraryHardDeleteLockLease(session *gocql.Session, libraryID, leaseToken uuid.UUID) error
- func RenewLibraryHardDeleteLockLease(session *gocql.Session, libraryID, leaseToken uuid.UUID) (bool, error)
- type AuditLogEntry
- type BlockClaimOutcome
- type BlockClaimReleaseOutcome
- type BlockClaimResult
- type BlockDeleteAuthority
- type BlockDeleteTarget
- type BlockGCCandidateIdentity
- type BlockGCCandidateInfo
- type BlockInfo
- type BlockReleaseOutcome
- type BlockStoreDeleter
- type CassandraStore
- func (s *CassandraStore) AcquireLibraryHardDeleteLock(libraryID, leaseToken uuid.UUID) (bool, error)
- func (s *CassandraStore) AcquireOrgHardDeleteLock(orgID, leaseToken uuid.UUID) (bool, error)
- func (s *CassandraStore) AcquireUserHardDeleteLock(userID, leaseToken uuid.UUID) (bool, error)
- func (s *CassandraStore) BeginOrgPurge(orgID uuid.UUID, identityAt time.Time) (bool, error)
- func (s *CassandraStore) BlockExists(orgID uuid.UUID, blockID string) (bool, error)
- func (s *CassandraStore) BlockHasReferences(orgID uuid.UUID, blockID string) (bool, error)
- func (s *CassandraStore) BlockHasReferencesGlobal(orgID uuid.UUID, blockID string) (bool, error)
- func (s *CassandraStore) BlockReferenceExists(orgID uuid.UUID, blockID, referrer string) (bool, error)
- func (s *CassandraStore) CanonicalLibraryExists(orgID, libraryID uuid.UUID) (bool, error)
- func (s *CassandraStore) ClaimBlockDelete(orgID uuid.UUID, blockID string, attempt BlockDeleteAuthority) (BlockClaimResult, error)
- func (s *CassandraStore) ClearDirtyOrg(orgID uuid.UUID, dirtyBefore time.Time) error
- func (s *CassandraStore) CompleteItem(orgID uuid.UUID, queuedAt time.Time, itemType ItemType, itemID string) error
- func (s *CassandraStore) DeleteAPIKeysByUser(orgID, userID uuid.UUID) error
- func (s *CassandraStore) DeleteBlockGCCandidate(orgID uuid.UUID, blockID string, candidate BlockGCCandidateIdentity) error
- func (s *CassandraStore) DeleteClaimedBlockStub(orgID uuid.UUID, blockID, claimID string) (bool, error)
- func (s *CassandraStore) DeleteCommit(libraryID uuid.UUID, commitID string) error
- func (s *CassandraStore) DeleteExpiredFailedItem(expiry GCFailedItemExpiryInfo, now time.Time) (bool, error)
- func (s *CassandraStore) DeleteExpiredShare(share ExpiredShareInfo) error
- func (s *CassandraStore) DeleteExpiredShareLink(link ExpiredShareLinkInfo) error
- func (s *CassandraStore) DeleteFSObject(libraryID uuid.UUID, fsID string) error
- func (s *CassandraStore) DeleteFailedItem(orgID uuid.UUID, failedAt time.Time, itemType ItemType, itemID string) error
- func (s *CassandraStore) DeleteFailedItemContext(ctx context.Context, orgID uuid.UUID, failedAt time.Time, itemType ItemType, ...) error
- func (s *CassandraStore) DeleteFileTag(libraryID uuid.UUID, filePath string, tagID int) error
- func (s *CassandraStore) DeleteFileTagByID(libraryID uuid.UUID, fileTagID int) error
- func (s *CassandraStore) DeleteFileTagCounters(libraryID uuid.UUID) error
- func (s *CassandraStore) DeleteGroupByMember(orgID, userID, groupID uuid.UUID) error
- func (s *CassandraStore) DeleteGroupFull(orgID, groupID uuid.UUID) error
- func (s *CassandraStore) DeleteGroupMember(groupID, userID uuid.UUID) error
- func (s *CassandraStore) DeleteLibraryStorageCounter(orgID, libraryID uuid.UUID) error
- func (s *CassandraStore) DeleteLockedFilesByLibrary(libraryID uuid.UUID) error
- func (s *CassandraStore) DeleteMonitoredReposByLibrary(libraryID uuid.UUID) error
- func (s *CassandraStore) DeleteMonitoredReposByUser(userID uuid.UUID) error
- func (s *CassandraStore) DeleteProvisionalBlockRefExpiryProjection(orgID uuid.UUID, blockID, referrer string, expiresAt time.Time) error
- func (s *CassandraStore) DeleteRepoAPIToken(libraryID uuid.UUID, appName string) error
- func (s *CassandraStore) DeleteRepoAPITokenByToken(apiToken string) error
- func (s *CassandraStore) DeleteRepoTag(libraryID uuid.UUID, tagID int) error
- func (s *CassandraStore) DeleteRepoTagCounters(libraryID uuid.UUID) error
- func (s *CassandraStore) DeleteRepoTagFileCounts(libraryID uuid.UUID) error
- func (s *CassandraStore) DeleteRestoreJob(orgID, libraryID, jobID uuid.UUID) error
- func (s *CassandraStore) DeleteRestoreJobsByLibrary(orgID, libraryID uuid.UUID) error
- func (s *CassandraStore) DeleteS3Orphan(orgID uuid.UUID, blockID string, firstSeenAt time.Time) error
- func (s *CassandraStore) DeleteShare(libraryID, shareID uuid.UUID) error
- func (s *CassandraStore) DeleteShareLink(shareToken string, fallbackOrgID uuid.UUID, fallbackLibraryID uuid.UUID) error
- func (s *CassandraStore) DeleteShareLinksByLibrary(orgID, libraryID uuid.UUID) ([]string, error)
- func (s *CassandraStore) DeleteStarredFilesByLibrary(libraryID uuid.UUID) error
- func (s *CassandraStore) DeleteStarredFilesByUser(userID uuid.UUID) error
- func (s *CassandraStore) DequeueBatch(orgID uuid.UUID, batchSize int, cutoff time.Time) ([]QueueItem, error)
- func (s *CassandraStore) EnqueueBatch(items []QueueItem) error
- func (s *CassandraStore) EnqueueItem(orgID uuid.UUID, queuedAt time.Time, itemType ItemType, itemID string, ...) error
- func (s *CassandraStore) EnsureBlockGCCandidate(orgID uuid.UUID, blockID, storageClass string, candidateAt time.Time) (time.Time, error)
- func (s *CassandraStore) FailItem(item QueueItem, failedAt time.Time, lastError, failureCode string) error
- func (s *CassandraStore) FinalizeBlockDelete(orgID uuid.UUID, blockID string, authority BlockDeleteAuthority) error
- func (s *CassandraStore) FindOrgForLibrary(libraryID uuid.UUID) (uuid.UUID, error)
- func (s *CassandraStore) GetBlockGCCandidate(orgID uuid.UUID, blockID string) (BlockGCCandidateInfo, bool, error)
- func (s *CassandraStore) GetBlockInfo(orgID uuid.UUID, blockID string) (BlockInfo, error)
- func (s *CassandraStore) GetCommit(libraryID uuid.UUID, commitID string) (CommitInfo, error)
- func (s *CassandraStore) GetFSObject(libraryID uuid.UUID, fsID string) (FSObjectInfo, error)
- func (s *CassandraStore) GetLibraryBlockRepresentationID(orgID, libraryID uuid.UUID) (string, error)
- func (s *CassandraStore) GetLibraryDeletedAt(libraryID uuid.UUID) (*time.Time, error)
- func (s *CassandraStore) GetLibraryStorageClass(orgID, libraryID uuid.UUID) (string, error)
- func (s *CassandraStore) GetOldestQueuedAt(orgID uuid.UUID) (*time.Time, error)
- func (s *CassandraStore) GetOrgDeletedAt(orgID uuid.UUID) (*time.Time, error)
- func (s *CassandraStore) GetOrgName(orgID uuid.UUID) (string, error)
- func (s *CassandraStore) GetOrgQueueStats(orgID uuid.UUID) (GCOrgStats, error)
- func (s *CassandraStore) GetProvisionalBlockRefExpiry(orgID uuid.UUID, blockID, referrer string) (ProvisionalBlockRefExpiryInfo, bool, error)
- func (s *CassandraStore) GetQueueSize(orgID uuid.UUID) (int, error)
- func (s *CassandraStore) GetS3OrphanGlobal(orgID uuid.UUID, blockID string) (S3OrphanInfo, bool, error)
- func (s *CassandraStore) GetTotalFailedItems() (int, error)
- func (s *CassandraStore) GetTotalQueueSize() (int, error)
- func (s *CassandraStore) GetUserDeletedAt(orgID, userID uuid.UUID) (*time.Time, error)
- func (s *CassandraStore) GetUserEmail(orgID, userID uuid.UUID) (string, error)
- func (s *CassandraStore) GroupExists(orgID, groupID uuid.UUID) (bool, error)
- func (s *CassandraStore) HardDeleteLibrary(orgID, libraryID uuid.UUID) error
- func (s *CassandraStore) HardDeleteOrg(orgID uuid.UUID) error
- func (s *CassandraStore) HardDeleteOrgLocked(orgID uuid.UUID) error
- func (s *CassandraStore) HardDeleteUser(orgID, userID uuid.UUID, email string) error
- func (s *CassandraStore) LibraryExists(libraryID uuid.UUID) (bool, error)
- func (s *CassandraStore) ListBlockGCCandidatesByDay(day time.Time, bucket int) ([]BlockGCCandidateInfo, error)
- func (s *CassandraStore) ListCommitIDsForLibrary(libraryID uuid.UUID) ([]string, error)
- func (s *CassandraStore) ListCommitsForLibrary(libraryID uuid.UUID) ([]CommitInfo, error)
- func (s *CassandraStore) ListCommitsWithTimestamps(libraryID uuid.UUID) ([]CommitWithTimestamp, error)
- func (s *CassandraStore) ListDeletedUsersExpired(graceDays int) ([]DeletedUserInfo, error)
- func (s *CassandraStore) ListDirtyOrgs(limit int) ([]GCDirtyOrg, error)
- func (s *CassandraStore) ListDistinctCommitLibraries() ([]uuid.UUID, error)
- func (s *CassandraStore) ListDistinctFSObjectLibraries() ([]uuid.UUID, error)
- func (s *CassandraStore) ListExpiredDeletedLibraries(retentionDays int) ([]DeletedLibraryInfo, error)
- func (s *CassandraStore) ListExpiredDeletedOrgs(graceDays int) ([]DeletedOrgInfo, error)
- func (s *CassandraStore) ListExpiredRestoreJobs() ([]ExpiredRestoreJobInfo, error)
- func (s *CassandraStore) ListExpiredShareLinks() ([]ExpiredShareLinkInfo, error)
- func (s *CassandraStore) ListExpiredShares() ([]ExpiredShareInfo, error)
- func (s *CassandraStore) ListFSObjectIDsForLibrary(libraryID uuid.UUID) ([]string, error)
- func (s *CassandraStore) ListFSObjectsForLibrary(libraryID uuid.UUID) ([]FSObjectInfo, error)
- func (s *CassandraStore) ListFailedItemExpiriesByDay(day time.Time, bucket int) ([]GCFailedItemExpiryInfo, error)
- func (s *CassandraStore) ListFailedItems(orgID uuid.UUID, limit int) ([]GCFailedItemInfo, error)
- func (s *CassandraStore) ListFileTagsByLibrary(libraryID uuid.UUID) ([]FileTagInfo, error)
- func (s *CassandraStore) ListGroupMembershipsByUser(orgID, userID uuid.UUID) ([]uuid.UUID, error)
- func (s *CassandraStore) ListGroupsByOrg(orgID uuid.UUID) ([]uuid.UUID, error)
- func (s *CassandraStore) ListLibrariesByOwner(orgID, ownerID uuid.UUID) ([]uuid.UUID, error)
- func (s *CassandraStore) ListLibrariesForOrg(orgID uuid.UUID) ([]OrgLibraryInfo, error)
- func (s *CassandraStore) ListLibrariesWithAutoDelete() ([]LibraryAutoDeleteInfo, error)
- func (s *CassandraStore) ListLibrariesWithVersionTTL() ([]LibraryTTLInfo, error)
- func (s *CassandraStore) ListOrganizations() ([]uuid.UUID, error)
- func (s *CassandraStore) ListOrgsWithFailedItems(limit int) ([]GCFailedItemOrgInfo, error)
- func (s *CassandraStore) ListOrgsWithQueuedItems() ([]uuid.UUID, error)
- func (s *CassandraStore) ListOrgsWithQueuedSnapshots(limit int) ([]uuid.UUID, error)
- func (s *CassandraStore) ListProvisionalBlockRefExpiriesByDay(day time.Time, bucket int) ([]ProvisionalBlockRefExpiryInfo, error)
- func (s *CassandraStore) ListRepoAPITokensByLibrary(libraryID uuid.UUID) ([]RepoAPITokenInfo, error)
- func (s *CassandraStore) ListRepoTagsByLibrary(libraryID uuid.UUID) ([]string, error)
- func (s *CassandraStore) ListS3OrphansByDay(day time.Time, bucket int, limit int) ([]S3OrphanDiscoveryInfo, error)
- func (s *CassandraStore) ListSharesByGroup(groupID uuid.UUID) ([]GroupShareInfo, error)
- func (s *CassandraStore) ListSharesByLibrary(libraryID uuid.UUID) ([]ShareInfo, error)
- func (s *CassandraStore) ListSharesByUser(orgID, userID uuid.UUID) ([]ShareByUserInfo, error)
- func (s *CassandraStore) ListSharesCreatedByUser(orgID, userID uuid.UUID) ([]ShareByCreatorInfo, error)
- func (s *CassandraStore) ListUsersByOrg(orgID uuid.UUID) ([]OrgUserInfo, error)
- func (s *CassandraStore) LoadGCStats(key string) (string, error)
- func (s *CassandraStore) MarkOrgActive(orgID uuid.UUID, activeAt time.Time) error
- func (s *CassandraStore) MarkOrgDirty(orgID uuid.UUID, dirtyAt time.Time) error
- func (s *CassandraStore) MarkS3OrphanMappingCleanupPending(orgID uuid.UUID, blockID, externalSHA1 string, now time.Time) error
- func (s *CassandraStore) PendingItemExists(orgID, libraryID uuid.UUID, identityAt time.Time, itemType ItemType, ...) (bool, error)
- func (s *CassandraStore) QueueItemExists(orgID uuid.UUID, queuedAt time.Time, itemType ItemType, itemID string) (bool, error)
- func (s *CassandraStore) RecalculateOrgQueueStats(orgID uuid.UUID) (GCOrgStats, error)
- func (s *CassandraStore) ReconcilePendingStorageCounters() (int, error)
- func (s *CassandraStore) ReleaseBlockClaim(orgID uuid.UUID, blockID string, authority BlockDeleteAuthority) (BlockReleaseOutcome, error)
- func (s *CassandraStore) ReleaseLibraryHardDeleteLock(libraryID, leaseToken uuid.UUID) error
- func (s *CassandraStore) ReleaseOrgHardDeleteLock(orgID, leaseToken uuid.UUID) error
- func (s *CassandraStore) ReleaseStaleBlockClaim(orgID uuid.UUID, blockID string, expectedTarget BlockDeleteTarget, ...) (BlockClaimReleaseOutcome, error)
- func (s *CassandraStore) ReleaseUserHardDeleteLock(userID, leaseToken uuid.UUID) error
- func (s *CassandraStore) RemoveBlockReference(orgID uuid.UUID, blockID, referrer string) error
- func (s *CassandraStore) RemoveOrgFromActiveSet(orgID uuid.UUID, activeBefore time.Time) error
- func (s *CassandraStore) RenewLibraryHardDeleteLock(libraryID, leaseToken uuid.UUID) (bool, error)
- func (s *CassandraStore) RenewOrgHardDeleteLock(orgID, leaseToken uuid.UUID) (bool, error)
- func (s *CassandraStore) RenewUserHardDeleteLock(userID, leaseToken uuid.UUID) (bool, error)
- func (s *CassandraStore) RequeueFailedItem(orgID uuid.UUID, failedAt time.Time, itemType ItemType, itemID string, ...) error
- func (s *CassandraStore) RequeueFailedItemContext(ctx context.Context, orgID uuid.UUID, failedAt time.Time, itemType ItemType, ...) error
- func (s *CassandraStore) RequeueItem(orgID uuid.UUID, oldQueuedAt, newQueuedAt time.Time, itemType ItemType, ...) error
- func (s *CassandraStore) ResolveBlockIDs(orgID, libraryID uuid.UUID, blockRepresentationID string, blockIDs []string) ([]string, error)
- func (s *CassandraStore) SaveGCStats(key, value string) error
- func (s *CassandraStore) SaveOrgQueueStats(stats GCOrgStats) error
- func (s *CassandraStore) ScanAllGroupShares(ctx context.Context, visit func(GroupShareInfo) error) error
- func (s *CassandraStore) SoftDeleteLibrary(orgID, libraryID, deletedBy uuid.UUID) error
- func (s *CassandraStore) StartBlockDeleteOrphan(orgID uuid.UUID, blockID, storageClass, storageKey, externalSHA1 string, ...) (time.Time, error)
- func (s *CassandraStore) SumOrgQueueStats() (int, int, error)
- func (s *CassandraStore) UpdateS3OrphanAttempt(orgID uuid.UUID, blockID string, expectedFirstSeenAt time.Time, errMsg string, ...) error
- func (s *CassandraStore) ValidateDestructiveGCTopology() error
- func (s *CassandraStore) WriteAuditLog(entry AuditLogEntry) error
- type CommitInfo
- type CommitWithTimestamp
- type DeletedLibraryInfo
- type DeletedOrgInfo
- type DeletedUserInfo
- type ExpiredRestoreJobInfo
- type ExpiredShareInfo
- type ExpiredShareLinkInfo
- type FSObjectInfo
- type FileTagInfo
- type GCAdminContextStore
- type GCDirtyOrg
- type GCFailedItemExpiryInfo
- type GCFailedItemInfo
- type GCFailedItemOrgInfo
- type GCOrgStats
- type GCStatus
- type GCStore
- type GroupShareInfo
- type ItemType
- type LibraryAutoDeleteInfo
- type LibraryGuardMode
- type LibraryTTLInfo
- type MockStorageProvider
- func (p *MockStorageProvider) BlockStoreRequests() []ScopedBlockStoreRequest
- func (p *MockStorageProvider) ClearFailures()
- func (p *MockStorageProvider) DeletedBlocks() []string
- func (p *MockStorageProvider) FailAlways(err error)
- func (p *MockStorageProvider) FailNextN(n int, err error)
- func (p *MockStorageProvider) FailResolve(err error)
- func (p *MockStorageProvider) GetBlockStoreForOrg(orgID, storageClass string) (BlockStoreDeleter, error)
- func (p *MockStorageProvider) LocatorValidations() []ScopedPhysicalLocatorValidation
- func (p *MockStorageProvider) ScopedBlockDeletes() []ScopedBlockDelete
- type MockStore
- func (m *MockStore) AcquireLibraryHardDeleteLock(libraryID, leaseToken uuid.UUID) (bool, error)
- func (m *MockStore) AcquireOrgHardDeleteLock(orgID, leaseToken uuid.UUID) (bool, error)
- func (m *MockStore) AcquireUserHardDeleteLock(userID, leaseToken uuid.UUID) (bool, error)
- func (m *MockStore) AddBlock(orgID uuid.UUID, blockID, storageClass string, refCount int)
- func (m *MockStore) AddBlockGCCandidate(orgID uuid.UUID, blockID, storageClass string, candidateAt time.Time)
- func (m *MockStore) AddBlockMapping(orgID uuid.UUID, externalID, internalID string)
- func (m *MockStore) AddBlockMappingForRepresentation(orgID uuid.UUID, representationID, externalID, internalID string)
- func (m *MockStore) AddBlockReferenceForTest(orgID uuid.UUID, blockID, referrer string)
- func (m *MockStore) AddCommit(libraryID uuid.UUID, commitID, rootFSID string)
- func (m *MockStore) AddCommitWithDetails(libraryID uuid.UUID, commitID, rootFSID, parentID string, createdAt time.Time)
- func (m *MockStore) AddDeletedLibrary(orgID, libraryID uuid.UUID, storageClass string, deletedAt time.Time)
- func (m *MockStore) AddDeletedOrg(orgID uuid.UUID, name string, deletedAt time.Time)
- func (m *MockStore) AddDeletedUser(orgID, userID uuid.UUID, email string, deletedAt time.Time)
- func (m *MockStore) AddFSObject(libraryID uuid.UUID, fsID, objType string, blockIDs []string)
- func (m *MockStore) AddFSObjectReferenceForTest(orgID uuid.UUID, blockID string, libID uuid.UUID, fsID string)
- func (m *MockStore) AddFSObjectWithEntries(libraryID uuid.UUID, fsID, objType string, blockIDs, dirEntries []string)
- func (m *MockStore) AddGroupForOrg(orgID, groupID uuid.UUID)
- func (m *MockStore) AddGroupMembership(orgID, userID, groupID uuid.UUID)
- func (m *MockStore) AddGroupShare(libraryID, shareID, groupID uuid.UUID)
- func (m *MockStore) AddLibrary(orgID, libraryID uuid.UUID, storageClass string)
- func (m *MockStore) AddLibraryWithAutoDelete(orgID, libraryID uuid.UUID, storageClass, headCommitID string, ...)
- func (m *MockStore) AddLibraryWithOwner(orgID, libraryID, ownerID uuid.UUID, storageClass string)
- func (m *MockStore) AddLibraryWithTTL(orgID, libraryID uuid.UUID, storageClass, headCommitID string, ...)
- func (m *MockStore) AddMonitoredRepo(userID uuid.UUID)
- func (m *MockStore) AddOrganization(orgID uuid.UUID)
- func (m *MockStore) AddOrganizationWithName(orgID uuid.UUID, name string)
- func (m *MockStore) AddPendingStorageCounterReconciliation(scope string, orgID, ownerID uuid.UUID)
- func (m *MockStore) AddProvisionalBlockRefExpiry(orgID uuid.UUID, blockID, referrer, storageClass string, expiresAt time.Time)
- func (m *MockStore) AddProvisionalBlockRefExpiryProjectionForTest(orgID uuid.UUID, blockID, referrer, storageClass string, expiresAt time.Time)
- func (m *MockStore) AddPurgeRequestedDeletedLibrary(orgID, libraryID uuid.UUID, storageClass string, ...)
- func (m *MockStore) AddRestoreJob(orgID, libraryID, jobID uuid.UUID, status string, expiresAt time.Time)
- func (m *MockStore) AddS3OrphanProjectionForTest(info S3OrphanDiscoveryInfo)
- func (m *MockStore) AddShare(libraryID, shareID, sharedTo uuid.UUID, expiresAt time.Time)
- func (m *MockStore) AddShareByUser(orgID, userID, libraryID uuid.UUID) uuid.UUID
- func (m *MockStore) AddShareCreatedByUser(orgID, userID, recipientID, libraryID uuid.UUID) uuid.UUID
- func (m *MockStore) AddShareLink(shareToken string, orgID uuid.UUID, expiresAt time.Time)
- func (m *MockStore) AddStarredFile(userID uuid.UUID)
- func (m *MockStore) AddStorageSnapshot(scope string, bytesUsed, fileCount int64)
- func (m *MockStore) AddStubBlockForTest(orgID uuid.UUID, blockID string)
- func (m *MockStore) AddUser(orgID, userID uuid.UUID, email string)
- func (m *MockStore) AllBlockGCCandidates() []BlockGCCandidateInfo
- func (m *MockStore) AllS3Orphans() []S3OrphanInfo
- func (m *MockStore) AuditLogEntries() []AuditLogEntry
- func (m *MockStore) BackdateBlockClaimForTest(orgID uuid.UUID, blockID string, claimedAt time.Time)
- func (m *MockStore) BeginOrgPurge(orgID uuid.UUID, identityAt time.Time) (bool, error)
- func (m *MockStore) BlockDeleteAuthorityForTest(orgID uuid.UUID, blockID, claimID string, claimedAt time.Time) BlockDeleteAuthority
- func (m *MockStore) BlockExists(orgID uuid.UUID, blockID string) (bool, error)
- func (m *MockStore) BlockExistsCallsForTest() int
- func (m *MockStore) BlockHasReferences(orgID uuid.UUID, blockID string) (bool, error)
- func (m *MockStore) BlockHasReferencesCallCountsForTest() (local, global int)
- func (m *MockStore) BlockHasReferencesGlobal(orgID uuid.UUID, blockID string) (bool, error)
- func (m *MockStore) BlockReferenceCount(orgID uuid.UUID, blockID string) int
- func (m *MockStore) BlockReferenceExists(orgID uuid.UUID, blockID, referrer string) (bool, error)
- func (m *MockStore) CanonicalLibraryExists(orgID, libraryID uuid.UUID) (bool, error)
- func (m *MockStore) ClaimAttemptsForTest() []BlockDeleteAuthority
- func (m *MockStore) ClaimBlockDelete(orgID uuid.UUID, blockID string, attempt BlockDeleteAuthority) (BlockClaimResult, error)
- func (m *MockStore) ClearDirtyOrg(orgID uuid.UUID, dirtyBefore time.Time) error
- func (m *MockStore) CompleteItem(orgID uuid.UUID, queuedAt time.Time, itemType ItemType, itemID string) error
- func (m *MockStore) DeleteAPIKeysByUser(orgID, userID uuid.UUID) error
- func (m *MockStore) DeleteBlockGCCandidate(orgID uuid.UUID, blockID string, candidate BlockGCCandidateIdentity) error
- func (m *MockStore) DeleteBlockGCCandidateProjectionForTest(orgID uuid.UUID, blockID string, candidateAt time.Time)
- func (m *MockStore) DeleteClaimedBlockStub(orgID uuid.UUID, blockID, claimID string) (bool, error)
- func (m *MockStore) DeleteCommit(libraryID uuid.UUID, commitID string) error
- func (m *MockStore) DeleteExpiredFailedItem(expiry GCFailedItemExpiryInfo, now time.Time) (bool, error)
- func (m *MockStore) DeleteExpiredShare(share ExpiredShareInfo) error
- func (m *MockStore) DeleteExpiredShareLink(link ExpiredShareLinkInfo) error
- func (m *MockStore) DeleteFSObject(libraryID uuid.UUID, fsID string) error
- func (m *MockStore) DeleteFailedItem(orgID uuid.UUID, failedAt time.Time, itemType ItemType, itemID string) error
- func (m *MockStore) DeleteFailedItemContext(ctx context.Context, orgID uuid.UUID, failedAt time.Time, itemType ItemType, ...) error
- func (m *MockStore) DeleteFileTag(libraryID uuid.UUID, filePath string, tagID int) error
- func (m *MockStore) DeleteFileTagByID(libraryID uuid.UUID, fileTagID int) error
- func (m *MockStore) DeleteFileTagCounters(libraryID uuid.UUID) error
- func (m *MockStore) DeleteGroupByMember(orgID, userID, groupID uuid.UUID) error
- func (m *MockStore) DeleteGroupFull(orgID, groupID uuid.UUID) error
- func (m *MockStore) DeleteGroupMember(groupID, userID uuid.UUID) error
- func (m *MockStore) DeleteLibraryStorageCounter(orgID, libraryID uuid.UUID) error
- func (m *MockStore) DeleteLockedFilesByLibrary(libraryID uuid.UUID) error
- func (m *MockStore) DeleteMonitoredReposByLibrary(libraryID uuid.UUID) error
- func (m *MockStore) DeleteMonitoredReposByUser(userID uuid.UUID) error
- func (m *MockStore) DeleteProvisionalBlockRefExpiryProjection(orgID uuid.UUID, blockID, referrer string, expiresAt time.Time) error
- func (m *MockStore) DeleteProvisionalBlockRefExpiryProjectionForTest(orgID uuid.UUID, blockID, referrer string, expiresAt time.Time)
- func (m *MockStore) DeleteRepoAPIToken(libraryID uuid.UUID, appName string) error
- func (m *MockStore) DeleteRepoAPITokenByToken(apiToken string) error
- func (m *MockStore) DeleteRepoTag(libraryID uuid.UUID, tagID int) error
- func (m *MockStore) DeleteRepoTagCounters(libraryID uuid.UUID) error
- func (m *MockStore) DeleteRepoTagFileCounts(libraryID uuid.UUID) error
- func (m *MockStore) DeleteRestoreJob(orgID, libraryID, jobID uuid.UUID) error
- func (m *MockStore) DeleteRestoreJobsByLibrary(orgID, libraryID uuid.UUID) error
- func (m *MockStore) DeleteS3Orphan(orgID uuid.UUID, blockID string, firstSeenAt time.Time) error
- func (m *MockStore) DeleteS3OrphanCanonicalForTest(orgID uuid.UUID, blockID string)
- func (m *MockStore) DeleteS3OrphanProjectionForTest(orgID uuid.UUID, blockID string, firstSeenAt time.Time)
- func (m *MockStore) DeleteShare(libraryID, shareID uuid.UUID) error
- func (m *MockStore) DeleteShareLink(shareToken string, orgID uuid.UUID, libraryID uuid.UUID) error
- func (m *MockStore) DeleteShareLinksByLibrary(orgID, libraryID uuid.UUID) ([]string, error)
- func (m *MockStore) DeleteStarredFilesByLibrary(libraryID uuid.UUID) error
- func (m *MockStore) DeleteStarredFilesByUser(userID uuid.UUID) error
- func (m *MockStore) DequeueBatch(orgID uuid.UUID, batchSize int, cutoff time.Time) ([]QueueItem, error)
- func (m *MockStore) EnqueueBatch(items []QueueItem) error
- func (m *MockStore) EnqueueItem(orgID uuid.UUID, queuedAt time.Time, itemType ItemType, itemID string, ...) error
- func (m *MockStore) EnsureBlockGCCandidate(orgID uuid.UUID, blockID, storageClass string, candidateAt time.Time) (time.Time, error)
- func (m *MockStore) FailItem(item QueueItem, failedAt time.Time, lastError, failureCode string) error
- func (m *MockStore) FailedItems(orgID uuid.UUID) []GCFailedItemInfo
- func (m *MockStore) FinalizeBlockDelete(orgID uuid.UUID, blockID string, authority BlockDeleteAuthority) error
- func (m *MockStore) FindOrgForLibrary(libraryID uuid.UUID) (uuid.UUID, error)
- func (m *MockStore) ForwardBlockMappingExists(orgID uuid.UUID, externalID string) bool
- func (m *MockStore) ForwardBlockMappingExistsForRepresentation(orgID uuid.UUID, representationID, externalID string) bool
- func (m *MockStore) GetBlock(orgID uuid.UUID, blockID string) *mockBlock
- func (m *MockStore) GetBlockGCCandidate(orgID uuid.UUID, blockID string) (BlockGCCandidateInfo, bool, error)
- func (m *MockStore) GetBlockGCCandidateForTest(orgID uuid.UUID, blockID string) (BlockGCCandidateInfo, bool)
- func (m *MockStore) GetBlockInfo(orgID uuid.UUID, blockID string) (BlockInfo, error)
- func (m *MockStore) GetCommit(libraryID uuid.UUID, commitID string) (CommitInfo, error)
- func (m *MockStore) GetCommitRecord(libraryID uuid.UUID, commitID string) *mockCommit
- func (m *MockStore) GetFSObj(libraryID uuid.UUID, fsID string) *mockFSObject
- func (m *MockStore) GetFSObject(libraryID uuid.UUID, fsID string) (FSObjectInfo, error)
- func (m *MockStore) GetLibraryBlockRepresentationID(orgID, libraryID uuid.UUID) (string, error)
- func (m *MockStore) GetLibraryDeletedAt(libraryID uuid.UUID) (*time.Time, error)
- func (m *MockStore) GetLibraryStorageClass(orgID, libraryID uuid.UUID) (string, error)
- func (m *MockStore) GetOldestQueuedAt(orgID uuid.UUID) (*time.Time, error)
- func (m *MockStore) GetOrgDeletedAt(orgID uuid.UUID) (*time.Time, error)
- func (m *MockStore) GetOrgName(orgID uuid.UUID) (string, error)
- func (m *MockStore) GetOrgQueueStats(orgID uuid.UUID) (GCOrgStats, error)
- func (m *MockStore) GetProvisionalBlockRefExpiry(orgID uuid.UUID, blockID, referrer string) (ProvisionalBlockRefExpiryInfo, bool, error)
- func (m *MockStore) GetQueueSize(orgID uuid.UUID) (int, error)
- func (m *MockStore) GetS3OrphanGlobal(orgID uuid.UUID, blockID string) (S3OrphanInfo, bool, error)
- func (m *MockStore) GetS3OrphanGlobalCallsForTest() int
- func (m *MockStore) GetS3OrphanProjectionForTest(orgID uuid.UUID, blockID string, firstSeenAt time.Time) (S3OrphanDiscoveryInfo, bool)
- func (m *MockStore) GetShareLink(shareToken string) *mockShareLink
- func (m *MockStore) GetTotalFailedItems() (int, error)
- func (m *MockStore) GetTotalQueueSize() (int, error)
- func (m *MockStore) GetUserDeletedAt(orgID, userID uuid.UUID) (*time.Time, error)
- func (m *MockStore) GetUserEmail(orgID, userID uuid.UUID) (string, error)
- func (m *MockStore) GroupExists(orgID, groupID uuid.UUID) (bool, error)
- func (m *MockStore) HardDeleteLibrary(orgID, libraryID uuid.UUID) error
- func (m *MockStore) HardDeleteOrg(orgID uuid.UUID) error
- func (m *MockStore) HardDeleteOrgLocked(orgID uuid.UUID) error
- func (m *MockStore) HardDeleteUser(orgID, userID uuid.UUID, email string) error
- func (m *MockStore) HasGroup(orgID, groupID uuid.UUID) bool
- func (m *MockStore) HasMonitoredRepos(userID uuid.UUID) bool
- func (m *MockStore) HasOrg(orgID uuid.UUID) bool
- func (m *MockStore) HasShare(libraryID, shareID uuid.UUID) bool
- func (m *MockStore) HasStarredFiles(userID uuid.UUID) bool
- func (m *MockStore) HasUser(orgID, userID uuid.UUID) bool
- func (m *MockStore) IsOrgActive(orgID uuid.UUID) bool
- func (m *MockStore) LibraryExists(libraryID uuid.UUID) (bool, error)
- func (m *MockStore) ListBlockGCCandidatesByDay(day time.Time, bucket int) ([]BlockGCCandidateInfo, error)
- func (m *MockStore) ListCommitIDsForLibrary(libraryID uuid.UUID) ([]string, error)
- func (m *MockStore) ListCommitsForLibrary(libraryID uuid.UUID) ([]CommitInfo, error)
- func (m *MockStore) ListCommitsWithTimestamps(libraryID uuid.UUID) ([]CommitWithTimestamp, error)
- func (m *MockStore) ListDeletedUsersExpired(graceDays int) ([]DeletedUserInfo, error)
- func (m *MockStore) ListDirtyOrgs(limit int) ([]GCDirtyOrg, error)
- func (m *MockStore) ListDistinctCommitLibraries() ([]uuid.UUID, error)
- func (m *MockStore) ListDistinctFSObjectLibraries() ([]uuid.UUID, error)
- func (m *MockStore) ListExpiredDeletedLibraries(retentionDays int) ([]DeletedLibraryInfo, error)
- func (m *MockStore) ListExpiredDeletedOrgs(graceDays int) ([]DeletedOrgInfo, error)
- func (m *MockStore) ListExpiredRestoreJobs() ([]ExpiredRestoreJobInfo, error)
- func (m *MockStore) ListExpiredShareLinks() ([]ExpiredShareLinkInfo, error)
- func (m *MockStore) ListExpiredShares() ([]ExpiredShareInfo, error)
- func (m *MockStore) ListFSObjectIDsForLibrary(libraryID uuid.UUID) ([]string, error)
- func (m *MockStore) ListFSObjectsForLibrary(libraryID uuid.UUID) ([]FSObjectInfo, error)
- func (m *MockStore) ListFailedItemExpiriesByDay(day time.Time, bucket int) ([]GCFailedItemExpiryInfo, error)
- func (m *MockStore) ListFailedItems(orgID uuid.UUID, limit int) ([]GCFailedItemInfo, error)
- func (m *MockStore) ListFileTagsByLibrary(libraryID uuid.UUID) ([]FileTagInfo, error)
- func (m *MockStore) ListGroupMembershipsByUser(orgID, userID uuid.UUID) ([]uuid.UUID, error)
- func (m *MockStore) ListGroupsByOrg(orgID uuid.UUID) ([]uuid.UUID, error)
- func (m *MockStore) ListLibrariesByOwner(orgID, ownerID uuid.UUID) ([]uuid.UUID, error)
- func (m *MockStore) ListLibrariesForOrg(orgID uuid.UUID) ([]OrgLibraryInfo, error)
- func (m *MockStore) ListLibrariesWithAutoDelete() ([]LibraryAutoDeleteInfo, error)
- func (m *MockStore) ListLibrariesWithVersionTTL() ([]LibraryTTLInfo, error)
- func (m *MockStore) ListOrganizations() ([]uuid.UUID, error)
- func (m *MockStore) ListOrgsWithFailedItems(limit int) ([]GCFailedItemOrgInfo, error)
- func (m *MockStore) ListOrgsWithQueuedItems() ([]uuid.UUID, error)
- func (m *MockStore) ListOrgsWithQueuedSnapshots(limit int) ([]uuid.UUID, error)
- func (m *MockStore) ListProvisionalBlockRefExpiriesByDay(day time.Time, bucket int) ([]ProvisionalBlockRefExpiryInfo, error)
- func (m *MockStore) ListRepoAPITokensByLibrary(libraryID uuid.UUID) ([]RepoAPITokenInfo, error)
- func (m *MockStore) ListRepoTagsByLibrary(libraryID uuid.UUID) ([]string, error)
- func (m *MockStore) ListS3OrphansByDay(day time.Time, bucket int, limit int) ([]S3OrphanDiscoveryInfo, error)
- func (m *MockStore) ListSharesByGroup(groupID uuid.UUID) ([]GroupShareInfo, error)
- func (m *MockStore) ListSharesByLibrary(libraryID uuid.UUID) ([]ShareInfo, error)
- func (m *MockStore) ListSharesByUser(orgID, userID uuid.UUID) ([]ShareByUserInfo, error)
- func (m *MockStore) ListSharesCreatedByUser(orgID, userID uuid.UUID) ([]ShareByCreatorInfo, error)
- func (m *MockStore) ListUsersByOrg(orgID uuid.UUID) ([]OrgUserInfo, error)
- func (m *MockStore) LoadGCStats(key string) (string, error)
- func (m *MockStore) MarkOrgActive(orgID uuid.UUID, activeAt time.Time) error
- func (m *MockStore) MarkOrgDirty(orgID uuid.UUID, dirtyAt time.Time) error
- func (m *MockStore) MarkS3OrphanMappingCleanupPending(orgID uuid.UUID, blockID, externalSHA1 string, now time.Time) error
- func (m *MockStore) PendingItemExists(orgID, libraryID uuid.UUID, identityAt time.Time, itemType ItemType, ...) (bool, error)
- func (m *MockStore) QueueItemExists(orgID uuid.UUID, queuedAt time.Time, itemType ItemType, itemID string) (bool, error)
- func (m *MockStore) QueueItems(orgID uuid.UUID) []QueueItem
- func (m *MockStore) QueueLen() int
- func (m *MockStore) RecalculateOrgQueueStats(orgID uuid.UUID) (GCOrgStats, error)
- func (m *MockStore) ReconcilePendingStorageCounters() (int, error)
- func (m *MockStore) ReleaseBlockClaim(orgID uuid.UUID, blockID string, authority BlockDeleteAuthority) (BlockReleaseOutcome, error)
- func (m *MockStore) ReleaseLibraryHardDeleteLock(libraryID, leaseToken uuid.UUID) error
- func (m *MockStore) ReleaseOrgHardDeleteLock(orgID, leaseToken uuid.UUID) error
- func (m *MockStore) ReleaseStaleBlockClaim(orgID uuid.UUID, blockID string, expectedTarget BlockDeleteTarget, ...) (BlockClaimReleaseOutcome, error)
- func (m *MockStore) ReleaseUserHardDeleteLock(userID, leaseToken uuid.UUID) error
- func (m *MockStore) RemoveBlockForTest(orgID uuid.UUID, blockID string)
- func (m *MockStore) RemoveBlockReference(orgID uuid.UUID, blockID, referrer string) error
- func (m *MockStore) RemoveOrgFromActiveSet(orgID uuid.UUID, activeBefore time.Time) error
- func (m *MockStore) RenewLibraryHardDeleteLock(libraryID, leaseToken uuid.UUID) (bool, error)
- func (m *MockStore) RenewOrgHardDeleteLock(orgID, leaseToken uuid.UUID) (bool, error)
- func (m *MockStore) RenewUserHardDeleteLock(userID, leaseToken uuid.UUID) (bool, error)
- func (m *MockStore) RequeueFailedItem(orgID uuid.UUID, failedAt time.Time, itemType ItemType, itemID string, ...) error
- func (m *MockStore) RequeueFailedItemContext(ctx context.Context, orgID uuid.UUID, failedAt time.Time, itemType ItemType, ...) error
- func (m *MockStore) RequeueItem(orgID uuid.UUID, oldQueuedAt, newQueuedAt time.Time, itemType ItemType, ...) error
- func (m *MockStore) ResolveBlockIDs(orgID, libraryID uuid.UUID, blockRepresentationID string, blockIDs []string) ([]string, error)
- func (m *MockStore) S3OrphanCount() int
- func (m *MockStore) SaveGCStats(key, value string) error
- func (m *MockStore) SaveOrgQueueStats(stats GCOrgStats) error
- func (m *MockStore) ScanAllGroupShares(ctx context.Context, visit func(GroupShareInfo) error) error
- func (m *MockStore) SeedBlockClaimForTest(orgID uuid.UUID, blockID, claimID string, claimedAt time.Time) BlockDeleteAuthority
- func (m *MockStore) SetBlockExistsErrForTest(err error)
- func (m *MockStore) SetBlockGCCandidateTargetForTest(orgID uuid.UUID, blockID string, target BlockDeleteTarget)
- func (m *MockStore) SetBlockGCStateForTest(orgID uuid.UUID, blockID, gcState, claimID string, claimedAt time.Time)
- func (m *MockStore) SetBlockHasReferencesGlobalErrForTest(err error)
- func (m *MockStore) SetBlockHasReferencesHookForTest(hook func(orgID uuid.UUID, blockID string, current bool) (bool, error))
- func (m *MockStore) SetBlockStorageKeyForTest(orgID uuid.UUID, blockID, storageKey string)
- func (m *MockStore) SetClaimBlockDeleteErrForTest(err error)
- func (m *MockStore) SetClaimBlockDeleteSettleErrForTest(err error)
- func (m *MockStore) SetDeleteS3OrphanErrOnceForTest(err error)
- func (m *MockStore) SetGetBlockGCCandidateErrForTest(err error)
- func (m *MockStore) SetGetBlockInfoErrorForTest(err error)
- func (m *MockStore) SetGetBlockInfoHookForTest(hook func(BlockInfo) BlockInfo)
- func (m *MockStore) SetGetFSObjectError(libraryID uuid.UUID, fsID string, err error)
- func (m *MockStore) SetGetS3OrphanGlobalErrForTest(err error)
- func (m *MockStore) SetGetS3OrphanGlobalHookForTest(...)
- func (m *MockStore) SetLibraryCanonicalStats(libraryID uuid.UUID, sizeBytes, fileCount int64)
- func (m *MockStore) SetLibraryEncrypted(libraryID uuid.UUID, encrypted bool)
- func (m *MockStore) SetMarkS3OrphanMappingCleanupPendingErrOnceForTest(err error)
- func (m *MockStore) SetReleaseBlockClaimErrForTest(err error)
- func (m *MockStore) SetReleaseStaleBlockClaimErrForTest(err error)
- func (m *MockStore) SetS3OrphanStorageKeyForTest(orgID uuid.UUID, blockID, storageKey string)
- func (m *MockStore) SetStartBlockDeleteOrphanResetRaceForTest(enabled bool)
- func (m *MockStore) SetValidateDestructiveGCTopologyErrForTest(err error)
- func (m *MockStore) SoftDeleteLibrary(orgID, libraryID, deletedBy uuid.UUID) error
- func (m *MockStore) StartBlockDeleteOrphan(orgID uuid.UUID, blockID, storageClass, storageKey, externalSHA1 string, ...) (time.Time, error)
- func (m *MockStore) StorageSnapshot(scope string) traffic.StorageSnapshot
- func (m *MockStore) SumOrgQueueStats() (int, int, error)
- func (m *MockStore) UpdateS3OrphanAttempt(orgID uuid.UUID, blockID string, expectedFirstSeenAt time.Time, errMsg string, ...) error
- func (m *MockStore) ValidateDestructiveGCTopology() error
- func (m *MockStore) WriteAuditLog(entry AuditLogEntry) error
- type OnlyOfficeReconciler
- type OrgLibraryInfo
- type OrgUserInfo
- type OrphanRecoverer
- type ProvisionalBlockRefExpiryInfo
- type Queue
- func (q *Queue) Complete(orgID uuid.UUID, queuedAt time.Time, itemType ItemType, itemID string) error
- func (q *Queue) DequeueBatch(orgID uuid.UUID, batchSize int, minAge time.Duration) ([]QueueItem, error)
- func (q *Queue) Enqueue(orgID uuid.UUID, itemType ItemType, itemID string, libraryID uuid.UUID, ...) error
- func (q *Queue) EnqueueBatch(items []QueueItem) error
- func (q *Queue) EnqueueCascade(orgID uuid.UUID, parentQueuedAt time.Time, itemType ItemType, itemID string, ...) error
- func (q *Queue) GetQueueSize(orgID uuid.UUID) (int, error)
- func (q *Queue) GetTotalQueueSize() (int, error)
- func (q *Queue) IncrementRetry(item QueueItem) error
- func (q *Queue) ListOrgsWithQueuedItems() ([]uuid.UUID, error)
- type QueueItem
- type RepoAPITokenInfo
- type S3OrphanDiscoveryInfo
- type S3OrphanInfo
- type Scanner
- func (s *Scanner) ScanExpiredDeletedLibrariesOnce(ctx context.Context) (int, error)
- func (s *Scanner) ScanExpiredProvisionalBlockRefsOnce(ctx context.Context) (int, error)
- func (s *Scanner) ScanOnce(ctx context.Context) error
- func (s *Scanner) SetOnlyOfficeReconciler(r OnlyOfficeReconciler)
- func (s *Scanner) SetOrphanRecoverer(r OrphanRecoverer)
- type ScopedBlockDelete
- type ScopedBlockStoreRequest
- type ScopedPhysicalLocatorValidation
- type Service
- func (s *Service) AcceptsManualTriggers() bool
- func (s *Service) DeleteFailedItem(orgID uuid.UUID, failedAt time.Time, itemType ItemType, itemID string) error
- func (s *Service) DeleteFailedItemContext(ctx context.Context, orgID uuid.UUID, failedAt time.Time, itemType ItemType, ...) error
- func (s *Service) EnqueueBlock(orgID uuid.UUID, blockID string, libraryID uuid.UUID, storageClass string) error
- func (s *Service) EnqueueCommits(orgID, libraryID uuid.UUID, commitIDs []string) error
- func (s *Service) EnqueueLibraryCascade(orgID, libraryID uuid.UUID, blockRepresentationID, storageClass string, ...) error
- func (s *Service) FailedItemsPageSize() int
- func (s *Service) ListFailedItemOrgs(limit int) ([]GCFailedItemOrgInfo, error)
- func (s *Service) ListFailedItems(orgID uuid.UUID, limit int) ([]GCFailedItemInfo, error)
- func (s *Service) ManualTriggerError() error
- func (s *Service) Queue() *Queue
- func (s *Service) RefreshFailedItemSnapshot()
- func (s *Service) RequeueFailedItem(orgID uuid.UUID, failedAt time.Time, itemType ItemType, itemID string) error
- func (s *Service) RequeueFailedItemContext(ctx context.Context, orgID uuid.UUID, failedAt time.Time, itemType ItemType, ...) error
- func (s *Service) SetDryRun(dryRun bool)
- func (s *Service) SetOnlyOfficeReconciler(r OnlyOfficeReconciler)
- func (s *Service) Start()
- func (s *Service) Status() GCStatus
- func (s *Service) Stop()
- func (s *Service) StopWithContext(ctx context.Context) error
- func (s *Service) TriggerScanner() bool
- func (s *Service) TriggerScannerWithDryRun(dryRun *bool) bool
- func (s *Service) TriggerWorker() bool
- func (s *Service) TriggerWorkerWithDryRun(dryRun *bool) bool
- type ShareByCreatorInfo
- type ShareByUserInfo
- type ShareInfo
- type ShareLinkInfo
- type Stats
- func (s *Stats) BlocksDeleted() int64
- func (s *Stats) IncrBlocksDeleted()
- func (s *Stats) LastScanAttempt() time.Time
- func (s *Stats) LastScanError() string
- func (s *Stats) LastScanRun() time.Time
- func (s *Stats) LastScanSuccess() time.Time
- func (s *Stats) LastWorkerRun() time.Time
- func (s *Stats) SetLastScanAttempt(t time.Time)
- func (s *Stats) SetLastScanError(v string)
- func (s *Stats) SetLastScanRun(t time.Time)
- func (s *Stats) SetLastScanSuccess(t time.Time)
- func (s *Stats) SetLastWorkerRun(t time.Time)
- type StorageManagerAdapter
- type StorageProvider
- type Worker
- func (w *Worker) EnqueueLibraryContents(orgID, libraryID uuid.UUID, storageClass string) error
- func (w *Worker) ProcessOnce(ctx context.Context) (int, error)
- func (w *Worker) ProcessOrgOnce(ctx context.Context, orgID uuid.UUID) (int, error)
- func (w *Worker) RecoverS3Orphans(ctx context.Context, perBucketLimit int) (int, error)
- func (w *Worker) SetDestructiveTopologyGate(gate func() error)
Constants ¶
const ( GCFailureCodeNone = "" GCFailureCodeLibraryHardDeleteInProgress = "library_hard_delete_in_progress" // GCFailureCodeBlockClaimNotYetStale marks a candidate that cannot be settled // yet because a delete claim on its block is too young to hand back safely. The // item is postponed, not retried and not failed. GCFailureCodeBlockClaimNotYetStale = "block_claim_not_yet_stale" // GCFailureCodeBlockAuthorityInvalid marks a candidate whose physical identity is // unusable as destructive authority. Postponed, never retried and never consumed. GCFailureCodeBlockAuthorityInvalid = "block_authority_invalid" // GCFailureCodeBlockClaimForeignOwner marks a walk that reached a settlement step // only to find its own claim already gone — taken over, or finalized elsewhere. // // The item is postponed and the candidate preserved. That is the same rule // BlockClaimFreshOwner follows at the claim, arrived at from the other side: some // other lifecycle owns the fence now, and this candidate is what will lift it if // that lifecycle dies. Consuming it here would leave the fence standing with // nothing able to take it over. GCFailureCodeBlockClaimForeignOwner = "block_claim_foreign_owner" // GCFailureCodeBlockCandidateWithinGrace marks a candidate that is younger than the // grace period, so the incarnation it names has not yet had its own window for // in-flight writers to finish. Postponed, never retried and never consumed. // // It is separate from GCFailureCodeBlockClaimNotYetStale because the two say // different things: one is "someone else holds the fence", the other is "nobody // holds anything, this work is simply not due yet". Collapsing them made every // grace postpone read as claim contention in the metrics. GCFailureCodeBlockCandidateWithinGrace = "block_candidate_within_grace" // GCFailureCodeBlockCanonicalReadUnreliable marks a post-claim canonical read that // contradicts what the claim CAS already proved in the serial domain. // // The claim names `IF storage_class = ? AND storage_key = ?`, so a successful claim // is proof the row carried that exact locator. GetBlockInfo is an ordinary read and // can land on a replica that has only the gc_* columns the claim itself just wrote, // showing an empty row. That observation says nothing about the block and // everything about the replica, so it must not spend a retry — and the fence this // attempt is holding has to come off before postponing, or a lagging replica turns // into a permanent upload refusal. GCFailureCodeBlockCanonicalReadUnreliable = "block_canonical_read_unreliable" // GCFailureCodeDestructiveFailClosed marks a delete refused because the // environment could not authorize it — an unreachable datacenter, or a // replication map that no longer carries the per-DC EACH_QUORUM argument. These // are postponed rather than retried, so the code exists mainly to make the // refusal legible; it should not normally reach the DLQ. GCFailureCodeDestructiveFailClosed = "destructive_fail_closed" // GCFailureCodeBlockClaimReleaseUnconfirmed marks a candidate whose block still // carries a stale delete claim that this pass tried and failed to hand back. // // It postpones for ANY failure reason, not only an availability one, and that // breadth is the whole point. This queue item is the only work that will ever // lift that fence: block items do not auto-recover from the DLQ, and the // scanner's day cursor has already moved past the candidate, so spending the // retry budget here strands a LIVE block behind gc_state='deleting' forever and // BlockDeleteFenceActive then refuses every future upload of that content. An // unknown column or a CQL bug in the release statement is exactly as fatal to // that fence as an unreachable datacenter is. // // The cost of that breadth is a permanently failing release postponing forever // instead of surfacing in the DLQ, which is the same trade documented on // isClusterUnavailableError's timeout codes. It is paid deliberately, and the // visibility it gives up is bought back by a dedicated // gc_errors_total{type="stale_claim_release_failed"} counter rather than left // silent. That counter is deliberately NOT seeded at registration: unlike the // destructive blocked/liveness gauge pair — where an absent series silently drops // out of a comparison — a counter that has never fired is simply absent, and // `increase(...) > 0` reads absence as "did not happen", which is true. GCFailureCodeBlockClaimReleaseUnconfirmed = "block_claim_release_unconfirmed" )
const ( S3OrphanPhasePendingS3 = "pending_s3" // Historical name. After R11a this phase means the physical S3 delete has // completed and only orphan finalization remains; it performs no mapping delete. S3OrphanPhasePendingMappingCleanup = "pending_mapping_cleanup" )
Variables ¶
ErrBlockCandidateTargetUnavailable is returned by EnsureBlockGCCandidate when the block's exact physical incarnation cannot be captured, so no candidate is written.
This is fail-closed by construction rather than by convention: with no candidate row there is no destructive authority to misuse later.
EVERY CALLER MUST TEST FOR IT WITH errors.Is AND CARRY ON. A block whose canonical row is already gone has nothing to reclaim, and processBlock itself treats that state as routine further down the walk ("missing canonical row, skipping deletion"), so failing the enqueue hard is incoherent with the rest of the lifecycle. On the fs_object path it is worse than incoherent: the delete aborts, retries, re-derives the same zero-ref block through an idempotent reference removal, hits the same missing row, and the fs_object never gets deleted at all.
var ErrGCDisabled = errors.New("gc is disabled on this node")
ErrGCDisabled is returned when GC is disabled on this node. It is distinct from ErrGCNotRunning and ErrNotLeader so callers can distinguish configuration shutdown, lifecycle shutdown, and leadership routing.
var ErrGCNotRunning = errors.New("gc is not running on this node")
ErrGCNotRunning is returned when GC is enabled in configuration but this service is not currently inside its running lifecycle.
var ErrNotLeader = errors.New("gc leadership required")
Functions ¶
func AcquireLibraryHardDeleteLockLease ¶
func AcquireLibraryHardDeleteLockLease(session *gocql.Session, libraryID, leaseToken uuid.UUID) (bool, error)
AcquireLibraryHardDeleteLockLease acquires the library hard-delete lock using the same stale-aware CAS semantics as the GC worker.
func MockCanonicalStorageKey ¶
MockCanonicalStorageKey mirrors storage.BlockStore.hashToKey for the mock backend. Tests seed and assert through it so a mock delete is only "correct" when it targets the same org-scoped locator the real store would derive.
func PendingItemBucket ¶
PendingItemBucket returns the Cassandra bucket used by gc_pending_items.
func QueueBucket ¶
QueueBucket returns the Cassandra bucket used by the live GC queue.
Types ¶
type AuditLogEntry ¶
type AuditLogEntry struct {
OrgID uuid.UUID
Action string // e.g. "delete_library", "delete_group", "gc_block_deleted"
TargetType string // e.g. "library", "group", "block", "user"
TargetID string
ActorID string // user who triggered it, or "gc_worker"/"gc_scanner"
Details string // JSON or free-text with extra context
Timestamp time.Time
}
AuditLogEntry records a deletion event for compliance/traceability.
type BlockClaimOutcome ¶
type BlockClaimOutcome int
BlockClaimOutcome classifies what ClaimBlockDelete found. A boolean cannot carry this: the four ways a claim can fail to apply demand four different responses, and collapsing them is exactly the defect R16 names — treating any non-applied CAS as "someone already handled it" and consuming the candidate.
const ( // BlockClaimAcquired: this attempt owns the row. Proceed to claim-then-verify. BlockClaimAcquired BlockClaimOutcome = iota // BlockClaimTargetChanged: the row is a DIFFERENT physical incarnation than the one // this candidate was created for. The candidate's work is finished and irrelevant; // the current incarnation was never authorized by anything and must not be touched. // Settle the candidate, mutate nothing. BlockClaimTargetChanged // BlockClaimFreshOwner: the exact incarnation is already claimed by an attempt too // young to be presumed dead. Another worker is very likely mid-delete under it. // // The caller must NOT settle: if that owner turns out to be dead, this candidate is // what will eventually take the claim over, and consuming it now leaves the fence // standing with nothing left to lift it. Postpone without spending a retry. BlockClaimFreshOwner // BlockClaimStaleOwner: the exact incarnation is claimed by an attempt old enough // that no live walk can still be running under it. Eligible for takeover — which is // still a CAS against that exact previous authority, never an unconditional clear. BlockClaimStaleOwner // BlockClaimMissing: there is no canonical row at all. Nothing to delete; settle. BlockClaimMissing // BlockClaimInvalid: the row exists but its own physical identity is unusable — a // present partition with no storage class or no storage key. Never destructive, and // never settled either: consuming the candidate would drop the only work item that // could ever revisit the block, so this postpones and asks for a human. BlockClaimInvalid // BlockClaimAmbiguous: the LWT's result could not be established, and a SERIAL // settling read could not establish it either. Retain the claim, retain the // candidate, finalize nothing, release nothing. Fail closed (R20). BlockClaimAmbiguous )
func (BlockClaimOutcome) String ¶
func (o BlockClaimOutcome) String() string
type BlockClaimReleaseOutcome ¶
type BlockClaimReleaseOutcome int
BlockClaimReleaseOutcome is what ReleaseStaleBlockClaim observed about a block's delete claim. The distinction between "absent" and "too fresh" is load-bearing: only the first means the caller may settle its candidate.
const ( // BlockClaimAbsent: the block carries no delete claim at all. Safe to settle. BlockClaimAbsent BlockClaimReleaseOutcome = iota // BlockClaimReleased: a stale claim was handed back. Safe to settle. BlockClaimReleased // BlockClaimTooFresh: a claim exists but was taken too recently to distinguish // from a live in-flight attempt, so it was left alone. Its owner is irrelevant — // a fresh claim belonging to another candidate is exactly as unsafe to lift as a // fresh claim belonging to this one. // // The caller must NOT settle. A claim younger than the staleness threshold may // belong to a worker still deleting — releasing it drops the upload fence // mid-delete — but it may equally belong to a worker that died seconds ago, in // which case the fence still has to come off eventually. This candidate is the // only work item that will ever look at that block again, so consuming it now // leaves gc_state='deleting' with nothing left to clear it: the block is fenced // against every future upload of its content, permanently. Postpone instead and // let a later pass release the claim once it has aged out. BlockClaimTooFresh )
func (BlockClaimReleaseOutcome) String ¶
func (o BlockClaimReleaseOutcome) String() string
type BlockClaimResult ¶
type BlockClaimResult struct {
Outcome BlockClaimOutcome
Owner BlockDeleteAuthority
}
BlockClaimResult is what ClaimBlockDelete observed.
Owner is populated for BlockClaimFreshOwner and BlockClaimStaleOwner and is the EXACT authority found on the row — incarnation, claim id and claimed_at. Carrying it back to the caller is what lets a stale takeover be a CAS against that authority rather than a second read that adopts whichever owner happens to be there by then. Those are not the same operation: between the two reads the row can become a different incarnation, and releasing THAT is precisely the P1-acts-on-P2 violation this package exists to prevent.
type BlockDeleteAuthority ¶
type BlockDeleteAuthority struct {
Target BlockDeleteTarget
ClaimID string
ClaimedAt time.Time
}
BlockDeleteAuthority is everything that authorizes one attempt to mutate one block row: the incarnation it may act on, plus the per-attempt ownership token.
ClaimID is a fresh UUID per ATTEMPT, never a value derived from candidate_at. A candidate-derived id is shared by every concurrent attempt on the same candidate, so the claim CAS answers "applied" to both and either one can release the other's fence.
func (BlockDeleteAuthority) IsZero ¶
func (a BlockDeleteAuthority) IsZero() bool
IsZero reports whether the authority is incomplete and therefore cannot be used for any destructive transition.
type BlockDeleteTarget ¶
BlockDeleteTarget is the exact physical incarnation P = (storage_class, storage_key) of a block. Nothing may reconstruct it from block_id: storage keys are minted, so deriving one yields a DIFFERENT incarnation's key that merely looks plausible.
func (BlockDeleteTarget) IsZero ¶
func (t BlockDeleteTarget) IsZero() bool
IsZero reports whether the target is unusable as destructive authority.
func (BlockDeleteTarget) String ¶
func (t BlockDeleteTarget) String() string
type BlockGCCandidateIdentity ¶
type BlockGCCandidateIdentity struct {
Target BlockDeleteTarget
CandidateAt time.Time
}
BlockGCCandidateIdentity is the full identity of one candidate row: which physical incarnation it was created for, and when.
candidate_at alone is NOT identity. It orders discovery and measures the grace period, and it is reused across successive lives of the same logical block, so a delete keyed on it alone can consume another incarnation's work item.
type BlockGCCandidateInfo ¶
type BlockGCCandidateInfo struct {
OrgID uuid.UUID
BlockID string
Target BlockDeleteTarget
CandidateAt time.Time
}
func (BlockGCCandidateInfo) Identity ¶
func (c BlockGCCandidateInfo) Identity() BlockGCCandidateIdentity
Identity is the exact tuple DeleteBlockGCCandidate conditions on.
func (BlockGCCandidateInfo) StorageClass ¶
func (c BlockGCCandidateInfo) StorageClass() string
StorageClass reports the candidate's captured storage class. It is an accessor so the class can only be read through the incarnation it belongs to, never as a free-floating string that has lost its key.
type BlockInfo ¶
type BlockInfo struct {
BlockID string
StorageClass string
StorageKey string
CreatedAt *time.Time
// Sha1 is the block's external Seafile SHA-1 (blocks.sha1), retained for
// orphan recovery metadata and legacy diagnostics. Empty for legacy/pre-PR2
// rows.
Sha1 string
}
BlockInfo holds data about a block needed by the scanner.
type BlockReleaseOutcome ¶
type BlockReleaseOutcome int
BlockReleaseOutcome classifies what ReleaseBlockClaim did.
Under the candidate-derived claim id this was a bare error, because a release that did not apply meant something was genuinely wrong. Per-attempt identity changes that: an attempt whose claim was taken over while it worked SHOULD fail to release, and reporting that as an error would spend the item's retry budget and — because releaseBlockClaim's error dominates the caller's original one — bury the real reason the walk was unwinding.
const ( // BlockReleaseReleased: this authority owned the row and the fence is now off. BlockReleaseReleased BlockReleaseOutcome = iota // BlockReleaseNotOwner: the row is no longer owned by this authority — taken over, // already released, already finalized, or now a different incarnation. Benign: this // attempt has no fence left to drop, so there is nothing to repair and no retry to // spend. BlockReleaseNotOwner )
func (BlockReleaseOutcome) String ¶
func (o BlockReleaseOutcome) String() string
type BlockStoreDeleter ¶
type BlockStoreDeleter interface {
// ValidatePhysicalLocator verifies that the persisted key belongs to this
// exact org-scoped store and identifies blockID. Destructive callers must call
// it before handing the persisted locator to the backing store.
//
// The persisted key is authoritative for WHICH object to destroy. Exact-key store
// operations structurally reject keys outside their configured org prefix, but
// they cannot prove that an in-prefix key belongs to blockID. This caller-level
// logical binding prevents corruption, a bad backfill, or a future key-minting
// writer from redirecting a delete to different bytes within the same org.
ValidatePhysicalLocator(blockID, storageKey string) error
DeleteBlockByStorageKey(ctx context.Context, storageKey string) error
}
BlockStoreDeleter validates and deletes physical block locators. Allows mocking the storage layer in tests.
type CassandraStore ¶
type CassandraStore struct {
// contains filtered or unexported fields
}
CassandraStore implements GCStore using a Cassandra database.
func NewCassandraStore ¶
func NewCassandraStore(database *db.DB) *CassandraStore
NewCassandraStore creates a new CassandraStore.
func (*CassandraStore) AcquireLibraryHardDeleteLock ¶
func (s *CassandraStore) AcquireLibraryHardDeleteLock(libraryID, leaseToken uuid.UUID) (bool, error)
func (*CassandraStore) AcquireOrgHardDeleteLock ¶
func (s *CassandraStore) AcquireOrgHardDeleteLock(orgID, leaseToken uuid.UUID) (bool, error)
func (*CassandraStore) AcquireUserHardDeleteLock ¶
func (s *CassandraStore) AcquireUserHardDeleteLock(userID, leaseToken uuid.UUID) (bool, error)
func (*CassandraStore) BeginOrgPurge ¶
func (*CassandraStore) BlockExists ¶
BlockExists reports whether the canonical blocks row still exists.
func (*CassandraStore) BlockHasReferences ¶
BlockHasReferences reports whether any block_references row still exists, at the session consistency. Discovery and abort-early only — see the interface contract.
func (*CassandraStore) BlockHasReferencesGlobal ¶
BlockHasReferencesGlobal is the EACH_QUORUM liveness read that authorizes physical deletion. Errors (including an unreachable DC) propagate so the caller fails closed.
func (*CassandraStore) BlockReferenceExists ¶
func (*CassandraStore) CanonicalLibraryExists ¶
func (s *CassandraStore) CanonicalLibraryExists(orgID, libraryID uuid.UUID) (bool, error)
CanonicalLibraryExists reads the authoritative `libraries` table by (org_id, library_id). A present row (even soft-deleted) means the library is live or recoverable, so its content must not be orphan-deleted. Fails closed on read errors so a Cassandra blip never masquerades as "library gone".
func (*CassandraStore) ClaimBlockDelete ¶
func (s *CassandraStore) ClaimBlockDelete(orgID uuid.UUID, blockID string, attempt BlockDeleteAuthority) (BlockClaimResult, error)
ClaimBlockDelete marks the block row gc_state='deleting' via LWT so writers back off, deferring the physical DELETE until S3-recovery state is persisted. This is the single expensive Paxos operation in the block lifecycle.
The IF names the exact incarnation AND requires the row to be unowned. See the GCStore interface for why both halves are required.
func (*CassandraStore) ClearDirtyOrg ¶
func (*CassandraStore) CompleteItem ¶
func (*CassandraStore) DeleteAPIKeysByUser ¶
func (s *CassandraStore) DeleteAPIKeysByUser(orgID, userID uuid.UUID) error
func (*CassandraStore) DeleteBlockGCCandidate ¶
func (s *CassandraStore) DeleteBlockGCCandidate(orgID uuid.UUID, blockID string, candidate BlockGCCandidateIdentity) error
DeleteBlockGCCandidate removes both the canonical row and the matching discovery row, but only while the candidate is still exactly the one the caller observed.
THE CANONICAL DELETE IS CONDITIONAL, AND THAT IS THE POINT. It used to be an unconditional `DELETE ... WHERE org_id = ? AND block_id = ?`, which is keyed on the LOGICAL block and therefore erases whatever candidate happens to be there. A lifecycle that started on P1 and finished late would consume a candidate that by then belonged to P2 — destroying the only work item authorized to reclaim P2, silently, with no fence left behind to notice. Naming (storage_class, storage_key, candidate_at) makes that a no-op instead.
A no-op is a normal outcome, not an error: it means another lifecycle already settled this candidate or replaced it. The discovery row is then left alone too, because it belongs to whatever candidate now owns the identity.
func (*CassandraStore) DeleteClaimedBlockStub ¶
func (*CassandraStore) DeleteCommit ¶
func (s *CassandraStore) DeleteCommit(libraryID uuid.UUID, commitID string) error
func (*CassandraStore) DeleteExpiredFailedItem ¶
func (s *CassandraStore) DeleteExpiredFailedItem(expiry GCFailedItemExpiryInfo, now time.Time) (bool, error)
func (*CassandraStore) DeleteExpiredShare ¶
func (s *CassandraStore) DeleteExpiredShare(share ExpiredShareInfo) error
func (*CassandraStore) DeleteExpiredShareLink ¶
func (s *CassandraStore) DeleteExpiredShareLink(link ExpiredShareLinkInfo) error
func (*CassandraStore) DeleteFSObject ¶
func (s *CassandraStore) DeleteFSObject(libraryID uuid.UUID, fsID string) error
func (*CassandraStore) DeleteFailedItem ¶
func (*CassandraStore) DeleteFailedItemContext ¶
func (s *CassandraStore) DeleteFailedItemContext(ctx context.Context, orgID uuid.UUID, failedAt time.Time, itemType ItemType, itemID string) error
DeleteFailedItemContext is cancellable up to its commit point, and no further.
The reads take ctx and there is a final ctx check immediately before the batch, so a cancelled request (client disconnect, or shutdown cancelling the in-flight DLQ operation) returns without having written anything. The LoggedBatch itself is deliberately NOT ctx-bound, and binding it would be a regression rather than hardening: Cassandra does not roll back a logged batch the coordinator has accepted, so ctx cancellation there cannot undo the mutation — it can only abort the client's wait and turn a definite outcome into an ambiguous one, with the caller unable to tell whether gc_failed_items was cleared. Shutdown safety does not depend on cancelling mid-batch either: Service.finishStop waits for the DLQ gate with an uncancellable context and only then releases the lease, so a committing mutation can never overlap a new leader's destructive work.
RequeueFailedItemContext follows the same contract.
func (*CassandraStore) DeleteFileTag ¶
func (*CassandraStore) DeleteFileTagByID ¶
func (s *CassandraStore) DeleteFileTagByID(libraryID uuid.UUID, fileTagID int) error
func (*CassandraStore) DeleteFileTagCounters ¶
func (s *CassandraStore) DeleteFileTagCounters(libraryID uuid.UUID) error
func (*CassandraStore) DeleteGroupByMember ¶
func (s *CassandraStore) DeleteGroupByMember(orgID, userID, groupID uuid.UUID) error
func (*CassandraStore) DeleteGroupFull ¶
func (s *CassandraStore) DeleteGroupFull(orgID, groupID uuid.UUID) error
func (*CassandraStore) DeleteGroupMember ¶
func (s *CassandraStore) DeleteGroupMember(groupID, userID uuid.UUID) error
func (*CassandraStore) DeleteLibraryStorageCounter ¶
func (s *CassandraStore) DeleteLibraryStorageCounter(orgID, libraryID uuid.UUID) error
func (*CassandraStore) DeleteLockedFilesByLibrary ¶
func (s *CassandraStore) DeleteLockedFilesByLibrary(libraryID uuid.UUID) error
func (*CassandraStore) DeleteMonitoredReposByLibrary ¶
func (s *CassandraStore) DeleteMonitoredReposByLibrary(libraryID uuid.UUID) error
func (*CassandraStore) DeleteMonitoredReposByUser ¶
func (s *CassandraStore) DeleteMonitoredReposByUser(userID uuid.UUID) error
func (*CassandraStore) DeleteProvisionalBlockRefExpiryProjection ¶
func (*CassandraStore) DeleteRepoAPIToken ¶
func (s *CassandraStore) DeleteRepoAPIToken(libraryID uuid.UUID, appName string) error
func (*CassandraStore) DeleteRepoAPITokenByToken ¶
func (s *CassandraStore) DeleteRepoAPITokenByToken(apiToken string) error
func (*CassandraStore) DeleteRepoTag ¶
func (s *CassandraStore) DeleteRepoTag(libraryID uuid.UUID, tagID int) error
func (*CassandraStore) DeleteRepoTagCounters ¶
func (s *CassandraStore) DeleteRepoTagCounters(libraryID uuid.UUID) error
func (*CassandraStore) DeleteRepoTagFileCounts ¶
func (s *CassandraStore) DeleteRepoTagFileCounts(libraryID uuid.UUID) error
func (*CassandraStore) DeleteRestoreJob ¶
func (s *CassandraStore) DeleteRestoreJob(orgID, libraryID, jobID uuid.UUID) error
func (*CassandraStore) DeleteRestoreJobsByLibrary ¶
func (s *CassandraStore) DeleteRestoreJobsByLibrary(orgID, libraryID uuid.UUID) error
func (*CassandraStore) DeleteS3Orphan ¶
func (s *CassandraStore) DeleteS3Orphan(orgID uuid.UUID, blockID string, firstSeenAt time.Time) error
DeleteS3Orphan removes both the canonical row and the matching discovery projection row. Callers should pass firstSeenAt when they already know it so the discovery row can still be removed if the canonical row has already been deleted. A zero firstSeenAt falls back to reading the canonical row first.
func (*CassandraStore) DeleteShare ¶
func (s *CassandraStore) DeleteShare(libraryID, shareID uuid.UUID) error
func (*CassandraStore) DeleteShareLink ¶
func (*CassandraStore) DeleteShareLinksByLibrary ¶
func (s *CassandraStore) DeleteShareLinksByLibrary(orgID, libraryID uuid.UUID) ([]string, error)
func (*CassandraStore) DeleteStarredFilesByLibrary ¶
func (s *CassandraStore) DeleteStarredFilesByLibrary(libraryID uuid.UUID) error
func (*CassandraStore) DeleteStarredFilesByUser ¶
func (s *CassandraStore) DeleteStarredFilesByUser(userID uuid.UUID) error
func (*CassandraStore) DequeueBatch ¶
func (*CassandraStore) EnqueueBatch ¶
func (s *CassandraStore) EnqueueBatch(items []QueueItem) error
func (*CassandraStore) EnqueueItem ¶
func (*CassandraStore) EnsureBlockGCCandidate ¶
func (s *CassandraStore) EnsureBlockGCCandidate(orgID uuid.UUID, blockID, storageClass string, candidateAt time.Time) (time.Time, error)
EnsureBlockGCCandidate inserts a (org_id, block_id) row into the canonical gc_block_candidates table if one does not already exist, and guarantees the matching gc_block_candidates_by_day discovery row exists for the effective candidate_at timestamp. If a row already exists with a later candidate_at, the earlier requested timestamp wins so explicit zero-ref enqueue paths are not delayed behind a provisional upload's future TTL-based candidate.
THE EARLIEST-WINS RULE IS SCOPED TO ONE INCARNATION. A stored candidate for P1 and a new observation of P2 are not two views of the same work item, they are two different lives of the same logical block, so P2 gets its OWN candidate_at rather than inheriting P1's. Inheriting it would hand the new incarnation an artificially old timestamp and let it skip the grace period that exists to let in-flight writers finish.
func (*CassandraStore) FinalizeBlockDelete ¶
func (s *CassandraStore) FinalizeBlockDelete(orgID uuid.UUID, blockID string, authority BlockDeleteAuthority) error
FinalizeBlockDelete removes a block row that was previously claimed by GC, and only while this exact authority still owns it.
func (*CassandraStore) FindOrgForLibrary ¶
func (*CassandraStore) GetBlockGCCandidate ¶
func (s *CassandraStore) GetBlockGCCandidate(orgID uuid.UUID, blockID string) (BlockGCCandidateInfo, bool, error)
GetBlockGCCandidate loads the canonical candidate row, including the exact physical incarnation it was created for.
func (*CassandraStore) GetBlockInfo ¶
func (*CassandraStore) GetCommit ¶
func (s *CassandraStore) GetCommit(libraryID uuid.UUID, commitID string) (CommitInfo, error)
func (*CassandraStore) GetFSObject ¶
func (s *CassandraStore) GetFSObject(libraryID uuid.UUID, fsID string) (FSObjectInfo, error)
func (*CassandraStore) GetLibraryBlockRepresentationID ¶
func (s *CassandraStore) GetLibraryBlockRepresentationID(orgID, libraryID uuid.UUID) (string, error)
func (*CassandraStore) GetLibraryDeletedAt ¶
func (*CassandraStore) GetLibraryStorageClass ¶
func (s *CassandraStore) GetLibraryStorageClass(orgID, libraryID uuid.UUID) (string, error)
func (*CassandraStore) GetOldestQueuedAt ¶
func (*CassandraStore) GetOrgDeletedAt ¶
func (*CassandraStore) GetOrgName ¶
func (s *CassandraStore) GetOrgName(orgID uuid.UUID) (string, error)
func (*CassandraStore) GetOrgQueueStats ¶
func (s *CassandraStore) GetOrgQueueStats(orgID uuid.UUID) (GCOrgStats, error)
func (*CassandraStore) GetProvisionalBlockRefExpiry ¶
func (s *CassandraStore) GetProvisionalBlockRefExpiry(orgID uuid.UUID, blockID, referrer string) (ProvisionalBlockRefExpiryInfo, bool, error)
func (*CassandraStore) GetQueueSize ¶
func (s *CassandraStore) GetQueueSize(orgID uuid.UUID) (int, error)
func (*CassandraStore) GetS3OrphanGlobal ¶
func (s *CassandraStore) GetS3OrphanGlobal(orgID uuid.UUID, blockID string) (S3OrphanInfo, bool, error)
GetS3OrphanGlobal reads the canonical recovery row at EACH_QUORUM. The discovery projection is deliberately not consulted here: recovery uses this row for phase, external SHA-1 characterization, and backend selection. EACH_QUORUM provides the same cross-DC visibility contract as the destructive liveness read, but this ordinary SELECT is not a Paxos settlement and does not authorize a physical delete by itself.
func (*CassandraStore) GetTotalFailedItems ¶
func (s *CassandraStore) GetTotalFailedItems() (int, error)
func (*CassandraStore) GetTotalQueueSize ¶
func (s *CassandraStore) GetTotalQueueSize() (int, error)
func (*CassandraStore) GetUserDeletedAt ¶
func (*CassandraStore) GetUserEmail ¶
func (s *CassandraStore) GetUserEmail(orgID, userID uuid.UUID) (string, error)
func (*CassandraStore) GroupExists ¶
func (s *CassandraStore) GroupExists(orgID, groupID uuid.UUID) (bool, error)
func (*CassandraStore) HardDeleteLibrary ¶
func (s *CassandraStore) HardDeleteLibrary(orgID, libraryID uuid.UUID) error
func (*CassandraStore) HardDeleteOrg ¶
func (s *CassandraStore) HardDeleteOrg(orgID uuid.UUID) error
func (*CassandraStore) HardDeleteOrgLocked ¶
func (s *CassandraStore) HardDeleteOrgLocked(orgID uuid.UUID) error
func (*CassandraStore) HardDeleteUser ¶
func (s *CassandraStore) HardDeleteUser(orgID, userID uuid.UUID, email string) error
func (*CassandraStore) LibraryExists ¶
func (s *CassandraStore) LibraryExists(libraryID uuid.UUID) (bool, error)
func (*CassandraStore) ListBlockGCCandidatesByDay ¶
func (s *CassandraStore) ListBlockGCCandidatesByDay(day time.Time, bucket int) ([]BlockGCCandidateInfo, error)
ListBlockGCCandidatesByDay enumerates candidates for one (UTC day, discovery bucket) partition. The scanner walks buckets [0, GCDiscoveryBucketCount) for each day from its persisted cursor up to today.
func (*CassandraStore) ListCommitIDsForLibrary ¶
func (s *CassandraStore) ListCommitIDsForLibrary(libraryID uuid.UUID) ([]string, error)
func (*CassandraStore) ListCommitsForLibrary ¶
func (s *CassandraStore) ListCommitsForLibrary(libraryID uuid.UUID) ([]CommitInfo, error)
func (*CassandraStore) ListCommitsWithTimestamps ¶
func (s *CassandraStore) ListCommitsWithTimestamps(libraryID uuid.UUID) ([]CommitWithTimestamp, error)
func (*CassandraStore) ListDeletedUsersExpired ¶
func (s *CassandraStore) ListDeletedUsersExpired(graceDays int) ([]DeletedUserInfo, error)
func (*CassandraStore) ListDirtyOrgs ¶
func (s *CassandraStore) ListDirtyOrgs(limit int) ([]GCDirtyOrg, error)
func (*CassandraStore) ListDistinctCommitLibraries ¶
func (s *CassandraStore) ListDistinctCommitLibraries() ([]uuid.UUID, error)
func (*CassandraStore) ListDistinctFSObjectLibraries ¶
func (s *CassandraStore) ListDistinctFSObjectLibraries() ([]uuid.UUID, error)
func (*CassandraStore) ListExpiredDeletedLibraries ¶
func (s *CassandraStore) ListExpiredDeletedLibraries(retentionDays int) ([]DeletedLibraryInfo, error)
func (*CassandraStore) ListExpiredDeletedOrgs ¶
func (s *CassandraStore) ListExpiredDeletedOrgs(graceDays int) ([]DeletedOrgInfo, error)
func (*CassandraStore) ListExpiredRestoreJobs ¶
func (s *CassandraStore) ListExpiredRestoreJobs() ([]ExpiredRestoreJobInfo, error)
func (*CassandraStore) ListExpiredShareLinks ¶
func (s *CassandraStore) ListExpiredShareLinks() ([]ExpiredShareLinkInfo, error)
func (*CassandraStore) ListExpiredShares ¶
func (s *CassandraStore) ListExpiredShares() ([]ExpiredShareInfo, error)
func (*CassandraStore) ListFSObjectIDsForLibrary ¶
func (s *CassandraStore) ListFSObjectIDsForLibrary(libraryID uuid.UUID) ([]string, error)
func (*CassandraStore) ListFSObjectsForLibrary ¶
func (s *CassandraStore) ListFSObjectsForLibrary(libraryID uuid.UUID) ([]FSObjectInfo, error)
func (*CassandraStore) ListFailedItemExpiriesByDay ¶
func (s *CassandraStore) ListFailedItemExpiriesByDay(day time.Time, bucket int) ([]GCFailedItemExpiryInfo, error)
func (*CassandraStore) ListFailedItems ¶
func (s *CassandraStore) ListFailedItems(orgID uuid.UUID, limit int) ([]GCFailedItemInfo, error)
func (*CassandraStore) ListFileTagsByLibrary ¶
func (s *CassandraStore) ListFileTagsByLibrary(libraryID uuid.UUID) ([]FileTagInfo, error)
func (*CassandraStore) ListGroupMembershipsByUser ¶
func (*CassandraStore) ListGroupsByOrg ¶
func (*CassandraStore) ListLibrariesByOwner ¶
func (*CassandraStore) ListLibrariesForOrg ¶
func (s *CassandraStore) ListLibrariesForOrg(orgID uuid.UUID) ([]OrgLibraryInfo, error)
func (*CassandraStore) ListLibrariesWithAutoDelete ¶
func (s *CassandraStore) ListLibrariesWithAutoDelete() ([]LibraryAutoDeleteInfo, error)
func (*CassandraStore) ListLibrariesWithVersionTTL ¶
func (s *CassandraStore) ListLibrariesWithVersionTTL() ([]LibraryTTLInfo, error)
func (*CassandraStore) ListOrganizations ¶
func (s *CassandraStore) ListOrganizations() ([]uuid.UUID, error)
func (*CassandraStore) ListOrgsWithFailedItems ¶
func (s *CassandraStore) ListOrgsWithFailedItems(limit int) ([]GCFailedItemOrgInfo, error)
func (*CassandraStore) ListOrgsWithQueuedItems ¶
func (s *CassandraStore) ListOrgsWithQueuedItems() ([]uuid.UUID, error)
func (*CassandraStore) ListOrgsWithQueuedSnapshots ¶
func (s *CassandraStore) ListOrgsWithQueuedSnapshots(limit int) ([]uuid.UUID, error)
func (*CassandraStore) ListProvisionalBlockRefExpiriesByDay ¶
func (s *CassandraStore) ListProvisionalBlockRefExpiriesByDay(day time.Time, bucket int) ([]ProvisionalBlockRefExpiryInfo, error)
func (*CassandraStore) ListRepoAPITokensByLibrary ¶
func (s *CassandraStore) ListRepoAPITokensByLibrary(libraryID uuid.UUID) ([]RepoAPITokenInfo, error)
func (*CassandraStore) ListRepoTagsByLibrary ¶
func (s *CassandraStore) ListRepoTagsByLibrary(libraryID uuid.UUID) ([]string, error)
func (*CassandraStore) ListS3OrphansByDay ¶
func (s *CassandraStore) ListS3OrphansByDay(day time.Time, bucket int, limit int) ([]S3OrphanDiscoveryInfo, error)
ListS3OrphansByDay enumerates discovery identities for one (UTC day, discovery bucket) partition. It intentionally does not select recovery phase, mapping identity, or storage class; the worker must reload those fields from gc_s3_orphans before taking any action.
func (*CassandraStore) ListSharesByGroup ¶
func (s *CassandraStore) ListSharesByGroup(groupID uuid.UUID) ([]GroupShareInfo, error)
func (*CassandraStore) ListSharesByLibrary ¶
func (s *CassandraStore) ListSharesByLibrary(libraryID uuid.UUID) ([]ShareInfo, error)
func (*CassandraStore) ListSharesByUser ¶
func (s *CassandraStore) ListSharesByUser(orgID, userID uuid.UUID) ([]ShareByUserInfo, error)
func (*CassandraStore) ListSharesCreatedByUser ¶
func (s *CassandraStore) ListSharesCreatedByUser(orgID, userID uuid.UUID) ([]ShareByCreatorInfo, error)
func (*CassandraStore) ListUsersByOrg ¶
func (s *CassandraStore) ListUsersByOrg(orgID uuid.UUID) ([]OrgUserInfo, error)
func (*CassandraStore) LoadGCStats ¶
func (s *CassandraStore) LoadGCStats(key string) (string, error)
func (*CassandraStore) MarkOrgActive ¶
func (*CassandraStore) MarkOrgDirty ¶
func (*CassandraStore) MarkS3OrphanMappingCleanupPending ¶
func (*CassandraStore) PendingItemExists ¶
func (*CassandraStore) QueueItemExists ¶
func (*CassandraStore) RecalculateOrgQueueStats ¶
func (s *CassandraStore) RecalculateOrgQueueStats(orgID uuid.UUID) (GCOrgStats, error)
func (*CassandraStore) ReconcilePendingStorageCounters ¶
func (s *CassandraStore) ReconcilePendingStorageCounters() (int, error)
func (*CassandraStore) ReleaseBlockClaim ¶
func (s *CassandraStore) ReleaseBlockClaim(orgID uuid.UUID, blockID string, authority BlockDeleteAuthority) (BlockReleaseOutcome, error)
ReleaseBlockClaim clears the gc_state claim when a concurrent reference appeared between the claim and the verify step, so writers stop backing off.
Not-applied is an OUTCOME, not an error: under per-attempt identity an attempt whose claim was taken over while it worked is SUPPOSED to fail here, and it has no fence left to drop.
func (*CassandraStore) ReleaseLibraryHardDeleteLock ¶
func (s *CassandraStore) ReleaseLibraryHardDeleteLock(libraryID, leaseToken uuid.UUID) error
func (*CassandraStore) ReleaseOrgHardDeleteLock ¶
func (s *CassandraStore) ReleaseOrgHardDeleteLock(orgID, leaseToken uuid.UUID) error
func (*CassandraStore) ReleaseStaleBlockClaim ¶
func (s *CassandraStore) ReleaseStaleBlockClaim(orgID uuid.UUID, blockID string, expectedTarget BlockDeleteTarget, staleBefore time.Time) (BlockClaimReleaseOutcome, error)
ReleaseStaleBlockClaim hands back a delete claim left behind by an attempt that died between claiming and releasing. It reads the claim first so the common case — no claim at all — costs one point read and reports "nothing to do" instead of a failed conditional update, and so a claim young enough to belong to a concurrent in-flight attempt is left strictly alone.
Age is the whole test; the owning claim id is read but never compared against the caller's. See the interface contract for why an owner-only release strands blocks behind a permanent fence.
A claim with no gc_claimed_at is treated as too fresh rather than as releasable. That is the fail-safe direction: the timestamp is written in the same statement as the claim, so its absence means an unexpected row shape, and guessing "old enough" there would drop a fence on no evidence.
The conditional update pins gc_claimed_at as well as the claim id it observed, so a claim that gets released and re-taken between the read and the write is not the one this call hands back.
THE OBSERVING READ IS IN THE SERIAL DOMAIN, AND THAT IS LOAD-BEARING. Every other read in this file was audited for the X2 asymmetry ("a local positive is proof, a local zero authorizes nothing"), and this one does not fit that shape: its zero DOES authorize something. BlockClaimAbsent makes processBlock fall through to DeleteBlockGCCandidate, consuming the only work item that could ever lift the fence — so a read that misses an existing claim strands the block behind gc_state='deleting' exactly as consuming the item on an error would.
This used to be an ordinary session-consistency read, filed as ISSUE-GC-STALE-CLAIM-READ-CONSISTENCY-01, and it could miss a claim two ways:
- CROSS-DATACENTER. The claim LWT commits at the regular consistency of the writing process, so a claim taken by a worker in another DC is acknowledged by a quorum THERE. With RF 1 per DC those replica sets do not intersect, and a LOCAL_QUORUM read could legitimately see no claim. Same geometry as X2 itself.
- THE PAXOS WINDOW, same DC. A LWT accepted but not yet committed when its proposer died is materialized by a SERIAL read and may be missed by an ordinary one.
The historical objection to fixing it here was that a global SERIAL read need not intersect a claim committed under LOCAL_SERIAL, and that mixing the two levels on the blocks partition is the one-serial-domain violation R12 tracks. P0/R12 SETTLED THAT: every conditional mutation on `blocks` is now pinned to SerialConsistency(gocql.Serial), so there is exactly one global serial domain and a global SERIAL read intersects it. The read is therefore correct now, and it is also cheap where it matters — it runs only after the local pre-check found the block still referenced.
The conditional update pins the exact incarnation as well as the claim id and gc_claimed_at it observed, so a claim released and re-taken between the read and the write is not the one this call hands back, and a claim belonging to a DIFFERENT incarnation is never handed back at all.
func (*CassandraStore) ReleaseUserHardDeleteLock ¶
func (s *CassandraStore) ReleaseUserHardDeleteLock(userID, leaseToken uuid.UUID) error
func (*CassandraStore) RemoveBlockReference ¶
func (s *CassandraStore) RemoveBlockReference(orgID uuid.UUID, blockID, referrer string) error
RemoveBlockReference deletes one (block, referrer) reference row (idempotent).
func (*CassandraStore) RemoveOrgFromActiveSet ¶
func (*CassandraStore) RenewLibraryHardDeleteLock ¶
func (s *CassandraStore) RenewLibraryHardDeleteLock(libraryID, leaseToken uuid.UUID) (bool, error)
func (*CassandraStore) RenewOrgHardDeleteLock ¶
func (s *CassandraStore) RenewOrgHardDeleteLock(orgID, leaseToken uuid.UUID) (bool, error)
func (*CassandraStore) RenewUserHardDeleteLock ¶
func (s *CassandraStore) RenewUserHardDeleteLock(userID, leaseToken uuid.UUID) (bool, error)
func (*CassandraStore) RequeueFailedItem ¶
func (*CassandraStore) RequeueFailedItemContext ¶
func (*CassandraStore) RequeueItem ¶
func (s *CassandraStore) RequeueItem(orgID uuid.UUID, oldQueuedAt, newQueuedAt time.Time, itemType ItemType, itemID string, libraryID uuid.UUID, blockRepresentationID, storageClass string, newRetryCount int, identityAt time.Time, requiresLibraryDeletedCheck bool, libraryGuardMode LibraryGuardMode) error
RequeueItem moves a failed item to the back of the queue to prevent head-of-line blocking. It deletes the old queue record and inserts a new one with a new queued_at timestamp and incremented retry count.
func (*CassandraStore) ResolveBlockIDs ¶
func (*CassandraStore) SaveGCStats ¶
func (s *CassandraStore) SaveGCStats(key, value string) error
func (*CassandraStore) SaveOrgQueueStats ¶
func (s *CassandraStore) SaveOrgQueueStats(stats GCOrgStats) error
func (*CassandraStore) ScanAllGroupShares ¶
func (s *CassandraStore) ScanAllGroupShares(ctx context.Context, visit func(GroupShareInfo) error) error
ScanAllGroupShares streams all group-share projection rows to the scanner. Scan the projection directly: enumerating groups first cannot discover a shares_by_group partition after its group row has already been deleted.
func (*CassandraStore) SoftDeleteLibrary ¶
func (s *CassandraStore) SoftDeleteLibrary(orgID, libraryID, deletedBy uuid.UUID) error
func (*CassandraStore) StartBlockDeleteOrphan ¶
func (s *CassandraStore) StartBlockDeleteOrphan(orgID uuid.UUID, blockID, storageClass, storageKey, externalSHA1 string, now time.Time) (time.Time, error)
StartBlockDeleteOrphan records the durable recovery row for a NEW block delete lifecycle. It always resets the phase to pending_s3, even when a stale row from an older delete already exists for the same block_id.
func (*CassandraStore) SumOrgQueueStats ¶
func (s *CassandraStore) SumOrgQueueStats() (int, int, error)
func (*CassandraStore) UpdateS3OrphanAttempt ¶
func (s *CassandraStore) UpdateS3OrphanAttempt(orgID uuid.UUID, blockID string, expectedFirstSeenAt time.Time, errMsg string, now time.Time) error
UpdateS3OrphanAttempt records a failed recovery attempt on an EXISTING orphan row. It never creates one.
Two defects this closes, both of which produced the same shape — a row whose primary key is still live, whose identity columns are gone, and which has no gc_s3_orphans_by_day entry. Under A+ any orphan row is a writer fence (ProbeBlockReuse answers BlockedByGC on mere existence, and both fence reads select only block_id, which such a row still returns), so that shape blocks every upload of the content while no sweep can enumerate it.
R19: the statement was a plain UPDATE with no IF, and in Cassandra that is an upsert. A recoverer whose S3 delete failed could write it after another path had already cleared the row, recreating it from the three diagnostic columns alone. The expected first_seen_at makes the statement non-creating and stale-token-safe when the stored token differs. This mutation is non-creating; making StartBlockDeleteOrphan the sole creator is the R21 authority boundary. Reusing a token when resetting an existing lifecycle remains a separate open issue. R28: Cassandra applies default_time_to_live per written VALUE and counts it from the WRITE, so an UPDATE that rewrites only the diagnostic columns hands them a fresh full term while storage_class, first_seen_at and recovery_phase keep the term they were inserted with. A retry late in the row's life pushed the diagnostics months past the identity columns, and the projection — never rewritten — expired with the identity. No upsert was needed to produce a partial orphan; ordinary expiry did it. Anchoring the diagnostic TTL on first_seen_at keeps this writer on the same application-derived schedule; coordinator-clock alignment remains a separate open requirement.
Rewriting the identity columns to realign them was the other candidate and is deliberately NOT what happens here: external_sha1 and recovery_phase both have other conditional writers (StartBlockDeleteOrphan's reset and the pending_mapping_cleanup transition), so echoing back values read a moment earlier would trade a TTL race for a lost-update race — including a recovery_phase regression.
Note what this does NOT do: the row still expires, and expiry still destroys the durable record that an object needs deleting. Removing the TTL outright is the documented package (R28 in docs/GC-X1-CLOSURE-OPTIONS.md) and needs the cold-start horizon and cursor semantics redefined with it, since gcS3OrphanInitialScanLookbackDays is pinned to this same 90 days.
func (*CassandraStore) ValidateDestructiveGCTopology ¶
func (s *CassandraStore) ValidateDestructiveGCTopology() error
ValidateDestructiveGCTopology gates every physical delete on the live keyspace replication still supporting EACH_QUORUM's per-datacenter semantics.
func (*CassandraStore) WriteAuditLog ¶
func (s *CassandraStore) WriteAuditLog(entry AuditLogEntry) error
type CommitInfo ¶
CommitInfo holds data about a commit needed by the worker.
type CommitWithTimestamp ¶
type CommitWithTimestamp struct {
CommitID string
ParentID string
RootFSID string
CreatedAt time.Time
}
CommitWithTimestamp holds commit data needed for version TTL enforcement.
type DeletedLibraryInfo ¶
type DeletedLibraryInfo struct {
OrgID uuid.UUID
LibraryID uuid.UUID
BlockRepresentationID string
StorageClass string
DeletedAt time.Time
// PurgeRequestedAt is set (non-zero) when a permanent-delete path asked for the
// library to be reclaimed on the retention-independent schedule rather than after
// TrashRetentionDays. When set, Phase 13 treats the row as eligible regardless of
// DeletedAt (the worker still applies the normal grace period before processing).
PurgeRequestedAt time.Time
}
DeletedLibraryInfo holds data about a soft-deleted library for trash auto-purge.
type DeletedOrgInfo ¶
DeletedOrgInfo holds data about a soft-deleted org for cascade processing.
type DeletedUserInfo ¶
DeletedUserInfo holds data about a soft-deleted user for cascade processing.
type ExpiredRestoreJobInfo ¶
type ExpiredRestoreJobInfo struct {
OrgID uuid.UUID
LibraryID uuid.UUID
JobID uuid.UUID
Status string
ExpiresAt time.Time
}
ExpiredRestoreJobInfo holds data about an expired/completed restore job.
type ExpiredShareInfo ¶
type ExpiredShareInfo struct {
}
ExpiredShareInfo holds data about an expired user-to-user share.
type ExpiredShareLinkInfo ¶
type ExpiredShareLinkInfo struct {
}
ExpiredShareLinkInfo holds the cleanup context from gc_share_links_by_expiry.
type FSObjectInfo ¶
type FSObjectInfo struct {
FSID string
ObjType string
BlockIDs []string
DirEntries []string // child fs_ids for dir objects; nil for files
}
FSObjectInfo holds data about an fs_object needed by the worker.
type FileTagInfo ¶
FileTagInfo holds data about a file tag for orphan cleanup.
type GCAdminContextStore ¶
type GCAdminContextStore interface {
DeleteFailedItemContext(ctx context.Context, orgID uuid.UUID, failedAt time.Time, itemType ItemType, itemID string) error
RequeueFailedItemContext(ctx context.Context, orgID uuid.UUID, failedAt time.Time, itemType ItemType, itemID string, queuedAt time.Time) error
}
GCAdminContextStore is implemented by stores whose synchronous admin mutations can be cancelled when service shutdown begins. The base GCStore remains context-free for the worker hot path; the service uses this optional interface only for the DLQ endpoints.
type GCDirtyOrg ¶
GCDirtyOrg identifies an org whose queue snapshot needs reconciliation.
type GCFailedItemExpiryInfo ¶
type GCFailedItemExpiryInfo struct {
OrgID uuid.UUID
FailedAt time.Time
ExpiresAt time.Time
ItemType ItemType
ItemID string
}
GCFailedItemExpiryInfo is the lightweight discovery row used by the scanner to expire DLQ rows through the store, preserving failed-depth counters.
type GCFailedItemInfo ¶
type GCFailedItemInfo struct {
OrgID uuid.UUID `json:"org_id"`
FailedAt time.Time `json:"failed_at"`
ExpiresAt time.Time `json:"expires_at"`
QueuedAt time.Time `json:"queued_at"`
IdentityAt time.Time `json:"identity_at"`
RequiresLibraryDeletedCheck bool `json:"requires_library_deleted_check"`
LibraryGuardMode LibraryGuardMode `json:"library_guard_mode"`
ItemType ItemType `json:"item_type"`
ItemID string `json:"item_id"`
LibraryID uuid.UUID `json:"library_id"`
BlockRepresentationID string `json:"block_representation_id"`
StorageClass string `json:"storage_class"`
RetryCount int `json:"retry_count"`
LastError string `json:"last_error"`
FailureCode string `json:"failure_code"`
ResolvedAt *time.Time `json:"resolved_at"`
ResolvedState string `json:"resolved_state"`
}
GCFailedItemInfo represents an item moved to the GC dead-letter queue.
type GCFailedItemOrgInfo ¶
type GCFailedItemOrgInfo struct {
OrgID uuid.UUID `json:"org_id"`
OrgName string `json:"org_name"`
FailedItemsTotal int `json:"failed_items_total"`
UpdatedAt time.Time `json:"updated_at"`
}
GCFailedItemOrgInfo summarizes one organization with items in the GC DLQ.
type GCOrgStats ¶
type GCOrgStats struct {
OrgID uuid.UUID
QueueDepth int
FailedDepth int
OldestQueuedAt *time.Time
UpdatedAt time.Time
RecalculatedAt time.Time
}
GCOrgStats stores reconciled queue state for a single org.
type GCStatus ¶
type GCStatus struct {
Enabled bool `json:"enabled"`
DryRun bool `json:"dry_run"`
LastWorkerRun string `json:"last_worker_run"`
LastScanRun string `json:"last_scan_run"`
LastScanAttempt string `json:"last_scan_attempt"`
LastScanSuccess string `json:"last_scan_success"`
LastScanError string `json:"last_scan_error"`
LastReconcileRun string `json:"last_reconcile_run"`
QueueSize int `json:"queue_size"`
FailedItemsTotal int `json:"failed_items_total"`
DirtyOrgsTotal int `json:"dirty_orgs_total"`
// SnapshotAgeSeconds reports how long ago the queue/failed snapshots were
// last reconciled. -1 means no reconciliation has run yet (e.g. cold deploy).
SnapshotAgeSeconds int64 `json:"snapshot_age_seconds"`
BlocksDeletedTotal int64 `json:"blocks_deleted_total"`
GracePeriodSeconds int64 `json:"grace_period_seconds"`
}
GCStatus is the JSON response for the admin status endpoint.
type GCStore ¶
type GCStore interface {
// Queue operations
// EnqueueItem is the low-level path for queue rows that do not need
// block_representation_id context. Callers enqueuing commits, fs_objects,
// or library cascades must use EnqueueBatch with QueueItem.
EnqueueItem(orgID uuid.UUID, queuedAt time.Time, itemType ItemType, itemID string, libraryID uuid.UUID, storageClass string, retryCount int) error
EnqueueBatch(items []QueueItem) error
QueueItemExists(orgID uuid.UUID, queuedAt time.Time, itemType ItemType, itemID string) (bool, error)
PendingItemExists(orgID, libraryID uuid.UUID, identityAt time.Time, itemType ItemType, itemID string) (bool, error)
DequeueBatch(orgID uuid.UUID, batchSize int, cutoff time.Time) ([]QueueItem, error)
CompleteItem(orgID uuid.UUID, queuedAt time.Time, itemType ItemType, itemID string) error
RequeueItem(orgID uuid.UUID, oldQueuedAt, newQueuedAt time.Time, itemType ItemType, itemID string, libraryID uuid.UUID, blockRepresentationID, storageClass string, newRetryCount int, identityAt time.Time, requiresLibraryDeletedCheck bool, libraryGuardMode LibraryGuardMode) error
FailItem(item QueueItem, failedAt time.Time, lastError, failureCode string) error
GetQueueSize(orgID uuid.UUID) (int, error)
GetTotalQueueSize() (int, error)
GetTotalFailedItems() (int, error)
ListOrgsWithQueuedItems() ([]uuid.UUID, error)
ListOrgsWithQueuedSnapshots(limit int) ([]uuid.UUID, error)
ListOrgsWithFailedItems(limit int) ([]GCFailedItemOrgInfo, error)
ListFailedItems(orgID uuid.UUID, limit int) ([]GCFailedItemInfo, error)
ListFailedItemExpiriesByDay(day time.Time, bucket int) ([]GCFailedItemExpiryInfo, error)
DeleteFailedItem(orgID uuid.UUID, failedAt time.Time, itemType ItemType, itemID string) error
DeleteExpiredFailedItem(expiry GCFailedItemExpiryInfo, now time.Time) (bool, error)
RequeueFailedItem(orgID uuid.UUID, failedAt time.Time, itemType ItemType, itemID string, queuedAt time.Time) error
MarkOrgActive(orgID uuid.UUID, activeAt time.Time) error
RemoveOrgFromActiveSet(orgID uuid.UUID, activeBefore time.Time) error
MarkOrgDirty(orgID uuid.UUID, dirtyAt time.Time) error
ListDirtyOrgs(limit int) ([]GCDirtyOrg, error)
ClearDirtyOrg(orgID uuid.UUID, dirtyBefore time.Time) error
GetOrgQueueStats(orgID uuid.UUID) (GCOrgStats, error)
SaveOrgQueueStats(stats GCOrgStats) error
RecalculateOrgQueueStats(orgID uuid.UUID) (GCOrgStats, error)
GetOldestQueuedAt(orgID uuid.UUID) (*time.Time, error)
SumOrgQueueStats() (int, int, error)
GetUserDeletedAt(orgID, userID uuid.UUID) (*time.Time, error)
GetLibraryDeletedAt(libraryID uuid.UUID) (*time.Time, error)
GetOrgDeletedAt(orgID uuid.UUID) (*time.Time, error)
GetLibraryBlockRepresentationID(orgID, libraryID uuid.UUID) (string, error)
// Block operations (worker)
//
// BlockExists reports whether the canonical `blocks` row still exists.
// RecoverS3Orphans relies on this to distinguish a block still being
// claimed/finalized by GC (row present → skip) from one whose DB row was
// already removed (absent → proceed with S3 cleanup).
BlockExists(orgID uuid.UUID, blockID string) (bool, error)
// BlockHasReferences reports whether any block_references row still exists for
// the block, at the session consistency. TRUE is proof and may abort a delete;
// FALSE proves only local absence, so it may drive discovery but MUST NOT
// authorize destroying bytes. Use BlockHasReferencesGlobal for that.
BlockHasReferences(orgID uuid.UUID, blockID string) (bool, error)
// BlockHasReferencesGlobal is the same liveness check pinned to EACH_QUORUM, so
// it intersects every DC that can acknowledge a LOCAL_QUORUM reference write.
// Its FALSE answer is the ONLY one that may authorize a physical delete
// (ISSUE-GC-CROSS-DC-REFERENCE-VISIBILITY-01). An unreachable DC makes it fail;
// callers must fail closed rather than treat the error as "no references".
BlockHasReferencesGlobal(orgID uuid.UUID, blockID string) (bool, error)
// ValidateDestructiveGCTopology reports whether the live keyspace replication
// still supports the per-datacenter EACH_QUORUM argument that authorizes
// physical deletes. It is part of this interface rather than an optional
// capability so the guarantee cannot be lost by wrapping the store: dropping it
// is a compile error, not a silently disarmed safety gate.
ValidateDestructiveGCTopology() error
GetBlockInfo(orgID uuid.UUID, blockID string) (BlockInfo, error)
// RemoveBlockReference deletes one (block, referrer) reference row. Idempotent.
RemoveBlockReference(orgID uuid.UUID, blockID, referrer string) error
ResolveBlockIDs(orgID, libraryID uuid.UUID, blockRepresentationID string, blockIDs []string) ([]string, error)
// ClaimBlockDelete atomically marks the block row gc_state='deleting' via LWT,
// but ONLY while the row is still the exact physical incarnation the candidate was
// created for and carries no owner at all.
//
// Both halves of that condition are load-bearing. Naming the incarnation is R14: a
// candidate enqueued for P1 must not claim P2 after P1 died and P2 was installed on
// the same logical block. Requiring NO owner — rather than merely "not deleting" —
// keeps GC off gc_state='repairing_stub', which belongs to the upload path, and
// stops the LWT from materializing a stub row: in Cassandra an UPDATE whose IF only
// tests columns for null applies against a MISSING partition, while an IF that names
// storage_class cannot.
//
// Callers MUST re-check BlockHasReferencesGlobal — the EACH_QUORUM form, never the
// session-consistency one — after a successful claim before deleting from S3
// (claim-then-verify). Verifying with the local read reopens
// ISSUE-GC-CROSS-DC-REFERENCE-VISIBILITY-01.
//
// The result is classified rather than boolean because a non-applied CAS is not
// completion (R16): see BlockClaimOutcome. It also carries the OWNER it observed, so
// a caller that decides to take a stale claim over can CAS against exactly that
// authority instead of re-reading and adopting whoever it finds the second time.
ClaimBlockDelete(orgID uuid.UUID, blockID string, attempt BlockDeleteAuthority) (BlockClaimResult, error)
// ReleaseBlockClaim clears gc_state only while the exact authority — incarnation,
// claim id AND claimed_at — still owns the row. This stops an attempt from releasing
// a claim it did not win, and stops an attempt that already lost the row to a stale
// takeover from dropping the new owner's fence.
//
// It returns an outcome rather than an error for the not-applied case, because under
// per-attempt identity a lost race is an EXPECTED result and not a failure. See
// BlockReleaseNotOwner.
ReleaseBlockClaim(orgID uuid.UUID, blockID string, authority BlockDeleteAuthority) (BlockReleaseOutcome, error)
// ReleaseStaleBlockClaim clears gc_state on a block whose delete claim was taken
// at or before staleBefore, WHICHEVER attempt owns it. Age is the only criterion,
// and that is the point: an unconditional release would let one worker drop the
// fence out from under another worker's in-flight delete, while an owner-only
// release cannot lift a claim whose owner will never come back.
//
// The owner-only form was the earlier design and it leaks. claimID derives from
// the candidate timestamp, so a claim left behind by candidate C1 carries C1's id;
// a later candidate C2 finds an id that is not its own, concludes "someone else's
// pass will lift it", and settles. If C1's queue item is gone — DLQ'd, and block
// items never auto-recover from there — nothing ever lifts it, and
// BlockDeleteFenceActive refuses every future upload of that content forever.
// Releasing by age closes that: the only claim this can touch is one older than
// any possible live attempt.
//
// The outcome is three-valued on purpose. "Nothing to release" and "there IS a
// claim, but it is too young to touch" demand opposite things from the caller:
// the first means the item is settled, the second means it emphatically is not,
// because that fence still has to come off later and this candidate is what will
// do it. Collapsing them into a single false is how a live block ends up fenced
// forever — see BlockClaimTooFresh.
//
// IT IS OWNER-AGNOSTIC BUT NOT INCARNATION-AGNOSTIC. expectedTarget is the physical
// incarnation the CALLER is authorized for, and a claim on any other incarnation is
// left alone however old it is. Without that binding a candidate for `P1` could hand
// back a fence belonging to `P2` — the exact authority violation R14a exists to
// prevent — and the age test would be the only thing standing between it and a live
// delete. Callers that DID observe a specific owner and want to take it over must use
// ReleaseBlockClaim with that exact authority instead; this is for the pre-check path,
// which by construction never observed one.
ReleaseStaleBlockClaim(orgID uuid.UUID, blockID string, expectedTarget BlockDeleteTarget, staleBefore time.Time) (BlockClaimReleaseOutcome, error)
// DeleteClaimedBlockStub removes only a metadata-free stub owned by claimID.
// applied=false means the row changed and callers must retry rather than
// treating the stale observation as success.
DeleteClaimedBlockStub(orgID uuid.UUID, blockID, claimID string) (bool, error)
// FinalizeBlockDelete removes the block row only while the exact authority still
// owns it — incarnation, claim id and claimed_at.
//
// It deliberately does NOT pin Consistency(EACH_QUORUM) the way ClaimBlockDelete
// does. The window this DELETE opens — a writer in another DC that has not yet seen
// the row vanish — is covered by the gc_s3_orphans row, which IS published at
// EACH_QUORUM and is written BEFORE this call. That row, not this one, is the fence
// that spans the physical delete; see db.BlockAuthorityRead for the intersection
// argument this relies on.
FinalizeBlockDelete(orgID uuid.UUID, blockID string, authority BlockDeleteAuthority) error
// EnsureBlockGCCandidate records a block as a delete candidate together with the
// EXACT physical incarnation it was observed at.
//
// Capturing P happens HERE, inside the helper, rather than at the three enqueue call
// sites: a candidate with no exact incarnation cannot authorize anything, so making
// its capture a mandatory side effect of candidate creation means no caller can
// forget it. A block with no canonical row, or one with no usable storage key,
// yields ErrBlockCandidateTargetUnavailable and writes NO candidate row.
//
// Callers MUST distinguish that sentinel with errors.Is and carry on: "this block has
// nothing reclaimable" is a normal observation, not a failure of the batch. Treating
// it as fatal aborts every sibling block in the same call and, on the fs_object path,
// is self-poisoning — the retry re-derives the same zero-ref block, finds the same
// missing row, and fails again forever.
EnsureBlockGCCandidate(orgID uuid.UUID, blockID, storageClass string, candidateAt time.Time) (time.Time, error)
// GetBlockGCCandidate loads the candidate's own record of the incarnation it was
// created for. This — never a fresh read of `blocks` — is what authorizes a
// destructive claim: re-reading `blocks` here would simply observe P2 and go on to
// delete it, which is the whole of R14.
GetBlockGCCandidate(orgID uuid.UUID, blockID string) (BlockGCCandidateInfo, bool, error)
// DeleteBlockGCCandidate removes the candidate only while it is still exactly the
// one that was observed. A delayed lifecycle for P1 must not consume a candidate
// that now belongs to P2.
DeleteBlockGCCandidate(orgID uuid.UUID, blockID string, candidate BlockGCCandidateIdentity) error
// ListBlockGCCandidatesByDay enumerates candidates whose `candidate_at`
// falls on the given UTC day for one discovery bucket. Bucket indices
// range over [0, db.GCDiscoveryBucketCount). Replaces the old per-org
// partition scan that depended on `blocks` partitioning by org.
ListBlockGCCandidatesByDay(day time.Time, bucket int) ([]BlockGCCandidateInfo, error)
// ListProvisionalBlockRefExpiriesByDay enumerates provisional upload-ref
// expiry records whose `expires_at` falls on the given UTC day for one
// discovery bucket. Each row is keyed by the specific provisional referrer,
// so concurrent uploads of the same block are expired independently.
ListProvisionalBlockRefExpiriesByDay(day time.Time, bucket int) ([]ProvisionalBlockRefExpiryInfo, error)
// GetProvisionalBlockRefExpiry loads the canonical expiry row. The scanner
// revalidates this before acting because the durable by-day table is only a
// discovery projection and can be stale.
GetProvisionalBlockRefExpiry(orgID uuid.UUID, blockID, referrer string) (ProvisionalBlockRefExpiryInfo, bool, error)
// DeleteProvisionalBlockRefExpiryProjection removes only the discovery row.
// It intentionally leaves the canonical row untouched for stale projection
// cleanup when an upload ref was renewed or already finalized elsewhere.
DeleteProvisionalBlockRefExpiryProjection(orgID uuid.UUID, blockID, referrer string, expiresAt time.Time) error
// BlockReferenceExists reports whether one specific reference row survives.
// Phase 0 uses it to confirm a provisional reference has actually been retired
// by its Cassandra TTL before drawing any conclusion about block liveness.
BlockReferenceExists(orgID uuid.UUID, blockID, referrer string) (bool, error)
// S3 orphan recovery / pending delete tracking for blocks claimed by GC.
// StartBlockDeleteOrphan records the durable recovery row for a NEW block
// deletion. It always resets recovery state to pending_s3 so a stale
// pending_mapping_cleanup row from an older delete cannot make recovery skip
// the physical object delete for this new lifecycle.
StartBlockDeleteOrphan(orgID uuid.UUID, blockID, storageClass, storageKey, externalSHA1 string, now time.Time) (time.Time, error)
// GetS3OrphanGlobal reads the canonical orphan row at EACH_QUORUM for the
// destructive recovery path. It supplies recovery state and the physical
// backend selector; it is not a Paxos settlement read and does not authorize
// deletion by itself. R22a keeps an absent or failed read fail-closed.
GetS3OrphanGlobal(orgID uuid.UUID, blockID string) (S3OrphanInfo, bool, error)
// ListS3OrphansByDay enumerates S3-orphan rows whose `first_seen_at`
// falls on the given UTC day for one discovery bucket. It returns only the
// discovery identity; callers must reload the canonical orphan before acting.
// `limit` caps the number of rows returned for a single (day, bucket) pair.
ListS3OrphansByDay(day time.Time, bucket int, limit int) ([]S3OrphanDiscoveryInfo, error)
// MarkS3OrphanMappingCleanupPending advances the recovery row after the S3
// delete has completed so restart recovery can finish the orphan lifecycle
// without touching S3 again. The phase name is historical: after R11a this
// transition performs no block-id mapping cleanup.
MarkS3OrphanMappingCleanupPending(orgID uuid.UUID, blockID, externalSHA1 string, now time.Time) error
UpdateS3OrphanAttempt(orgID uuid.UUID, blockID string, expectedFirstSeenAt time.Time, errMsg string, now time.Time) error
DeleteS3Orphan(orgID uuid.UUID, blockID string, firstSeenAt time.Time) error
// Commit operations (worker)
GetCommit(libraryID uuid.UUID, commitID string) (CommitInfo, error)
DeleteCommit(libraryID uuid.UUID, commitID string) error
// FS object operations (worker)
GetFSObject(libraryID uuid.UUID, fsID string) (FSObjectInfo, error)
DeleteFSObject(libraryID uuid.UUID, fsID string) error
// Library operations (worker + scanner)
GetLibraryStorageClass(orgID, libraryID uuid.UUID) (string, error)
ListCommitsForLibrary(libraryID uuid.UUID) ([]CommitInfo, error)
ListFSObjectsForLibrary(libraryID uuid.UUID) ([]FSObjectInfo, error)
// Scanner operations
ListOrganizations() ([]uuid.UUID, error)
ListDistinctCommitLibraries() ([]uuid.UUID, error)
ListDistinctFSObjectLibraries() ([]uuid.UUID, error)
LibraryExists(libraryID uuid.UUID) (bool, error)
// CanonicalLibraryExists reports whether the authoritative `libraries` row exists
// for (orgID, libraryID). Unlike LibraryExists (which reads the libraries_by_id
// projection), this consults the canonical table so orphan cleanup cannot act on a
// library that is still live under projection drift. Fails closed on read errors.
CanonicalLibraryExists(orgID, libraryID uuid.UUID) (bool, error)
FindOrgForLibrary(libraryID uuid.UUID) (uuid.UUID, error)
ListCommitIDsForLibrary(libraryID uuid.UUID) ([]string, error)
ListFSObjectIDsForLibrary(libraryID uuid.UUID) ([]string, error)
ReconcilePendingStorageCounters() (int, error)
// Version TTL enforcement
ListLibrariesWithVersionTTL() ([]LibraryTTLInfo, error)
ListCommitsWithTimestamps(libraryID uuid.UUID) ([]CommitWithTimestamp, error)
// Auto-delete enforcement
ListLibrariesWithAutoDelete() ([]LibraryAutoDeleteInfo, error)
// Share link deletion (defensive: attempts index cleanup even if primary record is gone)
// Expired shares (user-to-user library shares)
// Expired restore jobs
ListExpiredRestoreJobs() ([]ExpiredRestoreJobInfo, error)
DeleteRestoreJob(orgID, libraryID, jobID uuid.UUID) error
// Orphaned library artifacts cleanup
ListRepoTagsByLibrary(libraryID uuid.UUID) ([]string, error)
DeleteRepoTag(libraryID uuid.UUID, tagID int) error
ListFileTagsByLibrary(libraryID uuid.UUID) ([]FileTagInfo, error)
DeleteFileTag(libraryID uuid.UUID, filePath string, tagID int) error
DeleteFileTagByID(libraryID uuid.UUID, fileTagID int) error
ListRepoAPITokensByLibrary(libraryID uuid.UUID) ([]RepoAPITokenInfo, error)
DeleteRepoAPIToken(libraryID uuid.UUID, appName string) error
DeleteRepoAPITokenByToken(apiToken string) error
DeleteLockedFilesByLibrary(libraryID uuid.UUID) error
// Starred files and monitored repos cleanup
DeleteStarredFilesByLibrary(libraryID uuid.UUID) error
DeleteMonitoredReposByLibrary(libraryID uuid.UUID) error
// Restore jobs cleanup by library
DeleteRestoreJobsByLibrary(orgID, libraryID uuid.UUID) error
// Tag counter cleanup
DeleteRepoTagCounters(libraryID uuid.UUID) error
DeleteFileTagCounters(libraryID uuid.UUID) error
DeleteRepoTagFileCounts(libraryID uuid.UUID) error
ListSharesByGroup(groupID uuid.UUID) ([]GroupShareInfo, error)
// Scanner: orphaned group shares
GroupExists(orgID, groupID uuid.UUID) (bool, error)
// Audit log
WriteAuditLog(entry AuditLogEntry) error
// User cascade (soft-delete → hard-delete after grace period)
ListDeletedUsersExpired(graceDays int) ([]DeletedUserInfo, error)
ListLibrariesByOwner(orgID, ownerID uuid.UUID) ([]uuid.UUID, error)
SoftDeleteLibrary(orgID, libraryID, deletedBy uuid.UUID) error
ListGroupMembershipsByUser(orgID, userID uuid.UUID) ([]uuid.UUID, error)
DeleteGroupMember(groupID, userID uuid.UUID) error
DeleteGroupByMember(orgID, userID, groupID uuid.UUID) error
DeleteStarredFilesByUser(userID uuid.UUID) error
DeleteMonitoredReposByUser(userID uuid.UUID) error
DeleteAPIKeysByUser(orgID, userID uuid.UUID) error
HardDeleteUser(orgID, userID uuid.UUID, email string) error
GetUserEmail(orgID, userID uuid.UUID) (string, error)
// AcquireUserHardDeleteLock acquires a renewable lease for a user cascade
// delete. Returns (true, nil) when the lock is successfully acquired.
// activateUser checks this table to block concurrent restores.
AcquireUserHardDeleteLock(userID, leaseToken uuid.UUID) (bool, error)
RenewUserHardDeleteLock(userID, leaseToken uuid.UUID) (bool, error)
ReleaseUserHardDeleteLock(userID, leaseToken uuid.UUID) error
// AcquireLibraryHardDeleteLock acquires a renewable lease for a library
// cascade delete. Returns (true, nil) when the lock is successfully acquired.
// restoreDeletedLibrary checks this table to block concurrent restores.
AcquireLibraryHardDeleteLock(libraryID, leaseToken uuid.UUID) (bool, error)
RenewLibraryHardDeleteLock(libraryID, leaseToken uuid.UUID) (bool, error)
ReleaseLibraryHardDeleteLock(libraryID, leaseToken uuid.UUID) error
// Library trash auto-purge (soft-deleted libraries past retention period)
ListExpiredDeletedLibraries(retentionDays int) ([]DeletedLibraryInfo, error)
HardDeleteLibrary(orgID, libraryID uuid.UUID) error
// Storage counter cleanup after permanent library deletion.
// Deletes the lib-scope counter row. Aggregate scopes (org, user, platform)
// must have been adjusted earlier via SoftDeleteLibrary.
DeleteLibraryStorageCounter(orgID, libraryID uuid.UUID) error
// Org cascade (soft-deleted orgs past grace period)
ListExpiredDeletedOrgs(graceDays int) ([]DeletedOrgInfo, error)
ListUsersByOrg(orgID uuid.UUID) ([]OrgUserInfo, error)
ListGroupsByOrg(orgID uuid.UUID) ([]uuid.UUID, error)
ListLibrariesForOrg(orgID uuid.UUID) ([]OrgLibraryInfo, error)
DeleteGroupFull(orgID, groupID uuid.UUID) error
// BeginOrgPurge atomically transitions a soft-deleted org into an internal
// purge-in-progress state for the given deleted_at identity. It returns
// false when the org was restored or deleted again under a different
// identity before the transition could be claimed.
BeginOrgPurge(orgID uuid.UUID, identityAt time.Time) (bool, error)
// AcquireOrgHardDeleteLock acquires a renewable lease for an org cascade
// delete. Returns (true, nil) when the lock is successfully acquired.
// Restore/reactivation is blocked by the purging lifecycle state claimed
// with BeginOrgPurge; this lock only serializes concurrent hard-delete work.
AcquireOrgHardDeleteLock(orgID, leaseToken uuid.UUID) (bool, error)
RenewOrgHardDeleteLock(orgID, leaseToken uuid.UUID) (bool, error)
ReleaseOrgHardDeleteLock(orgID, leaseToken uuid.UUID) error
// HardDeleteOrgLocked deletes the org record after child rows have been
// removed. Caller must already hold the org hard-delete lock.
HardDeleteOrgLocked(orgID uuid.UUID) error
HardDeleteOrg(orgID uuid.UUID) error
GetOrgName(orgID uuid.UUID) (string, error)
// GC stats persistence
SaveGCStats(key, value string) error
LoadGCStats(key string) (string, error)
}
GCStore abstracts all database operations used by the GC system. This allows unit tests to use an in-memory mock instead of Cassandra.
type GroupShareInfo ¶
type GroupShareInfo struct {
}
GroupShareInfo holds data about a share where shared_to is a group.
type ItemType ¶
type ItemType string
ItemType identifies the kind of object in the GC queue
const ( ItemRestoreJob ItemType = "restore_job" )
ItemType constants for new GC item types
type LibraryAutoDeleteInfo ¶
type LibraryAutoDeleteInfo struct {
OrgID uuid.UUID
LibraryID uuid.UUID
HeadCommitID string
BlockRepresentationID string
// RepresentationDefaulted mirrors LibraryTTLInfo.RepresentationDefaulted.
RepresentationDefaulted bool
// RepresentationInvalid mirrors LibraryTTLInfo.RepresentationInvalid.
RepresentationInvalid bool
AutoDeleteDays int
}
LibraryAutoDeleteInfo holds library data needed for auto_delete_days enforcement.
type LibraryGuardMode ¶
type LibraryGuardMode string
LibraryGuardMode identifies the condition that must remain true before a queued commit or fs_object can be deleted.
const ( LibraryGuardNone LibraryGuardMode = "" LibraryGuardDeletedAtIdentity LibraryGuardMode = "deleted_at_identity" LibraryGuardCanonicalMustBeAbsent LibraryGuardMode = "canonical_absent" )
type LibraryTTLInfo ¶
type LibraryTTLInfo struct {
OrgID uuid.UUID
LibraryID uuid.UUID
HeadCommitID string
BlockRepresentationID string
// RepresentationDefaulted is true when the stored block_representation_id was
// empty and BlockRepresentationID was derived from the library's own identity
// (plain:v1 / library:<id>). The derivation is safe, but an empty stored value
// signals a writer/migration that did not stamp it, so scanners report it as
// drift rather than hiding it.
RepresentationDefaulted bool
// RepresentationInvalid is true when the stored block_representation_id cannot
// be validated against the library's identity and encrypted flag. This includes
// malformed identities/representations and cross-domain values. The scanner
// must skip the library and report drift instead of enqueuing work under an
// unsafe mapping domain. BlockRepresentationID carries the raw stored value
// only so the drift metric can classify it.
RepresentationInvalid bool
VersionTTLDays int
}
LibraryTTLInfo holds library data needed for version TTL enforcement.
type MockStorageProvider ¶
type MockStorageProvider struct {
DeletedKeys []string
ScopedDeletes []ScopedBlockDelete
ResolvedStores []ScopedBlockStoreRequest
PhysicalLocatorValidations []ScopedPhysicalLocatorValidation
// contains filtered or unexported fields
}
MockStorageProvider implements StorageProvider for testing.
func (*MockStorageProvider) BlockStoreRequests ¶
func (p *MockStorageProvider) BlockStoreRequests() []ScopedBlockStoreRequest
func (*MockStorageProvider) ClearFailures ¶
func (p *MockStorageProvider) ClearFailures()
ClearFailures stops injecting failures, resolution failures included.
func (*MockStorageProvider) DeletedBlocks ¶
func (p *MockStorageProvider) DeletedBlocks() []string
DeletedBlocks returns the list of block IDs that were deleted.
func (*MockStorageProvider) FailAlways ¶
func (p *MockStorageProvider) FailAlways(err error)
FailAlways causes every DeleteBlock call to return err until cleared.
func (*MockStorageProvider) FailNextN ¶
func (p *MockStorageProvider) FailNextN(n int, err error)
FailNextN causes the next n DeleteBlock calls to return err. After that, calls succeed as normal. Used to simulate transient S3 failures.
func (*MockStorageProvider) FailResolve ¶
func (p *MockStorageProvider) FailResolve(err error)
FailResolve makes every GetBlockStoreForOrg call return err until cleared.
func (*MockStorageProvider) GetBlockStoreForOrg ¶
func (p *MockStorageProvider) GetBlockStoreForOrg(orgID, storageClass string) (BlockStoreDeleter, error)
func (*MockStorageProvider) LocatorValidations ¶
func (p *MockStorageProvider) LocatorValidations() []ScopedPhysicalLocatorValidation
func (*MockStorageProvider) ScopedBlockDeletes ¶
func (p *MockStorageProvider) ScopedBlockDeletes() []ScopedBlockDelete
type MockStore ¶
type MockStore struct {
// contains filtered or unexported fields
}
MockStore is an in-memory implementation of GCStore for testing.
func (*MockStore) AcquireLibraryHardDeleteLock ¶
func (*MockStore) AcquireOrgHardDeleteLock ¶
func (*MockStore) AcquireUserHardDeleteLock ¶
func (*MockStore) AddBlockGCCandidate ¶
func (m *MockStore) AddBlockGCCandidate(orgID uuid.UUID, blockID, storageClass string, candidateAt time.Time)
AddBlockGCCandidate seeds a candidate. It captures the exact incarnation from the seeded canonical row when one exists, so a test that seeds a block and a candidate gets the same consistent pair production would have produced. Tests that deliberately model a candidate for a DEAD incarnation seed it first and mutate the block after.
func (*MockStore) AddBlockMapping ¶
func (*MockStore) AddBlockMappingForRepresentation ¶
func (*MockStore) AddBlockReferenceForTest ¶
AddBlockReferenceForTest registers a reference row for tests exercising the row-per-reference model directly.
func (*MockStore) AddCommitWithDetails ¶
func (m *MockStore) AddCommitWithDetails(libraryID uuid.UUID, commitID, rootFSID, parentID string, createdAt time.Time)
AddCommitWithDetails adds a commit with parent and creation time for TTL testing.
func (*MockStore) AddDeletedLibrary ¶
func (m *MockStore) AddDeletedLibrary(orgID, libraryID uuid.UUID, storageClass string, deletedAt time.Time)
AddDeletedLibrary adds a soft-deleted library (for scanner Phase 11).
func (*MockStore) AddDeletedOrg ¶
AddDeletedOrg adds an org with status='deleted' and deleted_at set (for scanner Phase 12).
func (*MockStore) AddDeletedUser ¶
AddDeletedUser adds a user with status='deleted' and deleted_at set (for scanner Phase 10).
func (*MockStore) AddFSObject ¶
func (*MockStore) AddFSObjectReferenceForTest ¶
func (m *MockStore) AddFSObjectReferenceForTest(orgID uuid.UUID, blockID string, libID uuid.UUID, fsID string)
AddFSObjectReferenceForTest registers the permanent reference an fs_object holds on a block, using the same referrer the GC worker removes when it sweeps that fs_object. Use it to seed "block referenced by fs_object" relationships.
func (*MockStore) AddFSObjectWithEntries ¶
func (m *MockStore) AddFSObjectWithEntries(libraryID uuid.UUID, fsID, objType string, blockIDs, dirEntries []string)
AddFSObjectWithEntries adds an fs_object with child dir entries for tree walking.
func (*MockStore) AddGroupForOrg ¶
AddGroupForOrg adds a group to the mock store.
func (*MockStore) AddGroupMembership ¶
AddGroupMembership adds a user to a group (both tables).
func (*MockStore) AddGroupShare ¶
func (*MockStore) AddLibrary ¶
func (*MockStore) AddLibraryWithAutoDelete ¶
func (m *MockStore) AddLibraryWithAutoDelete(orgID, libraryID uuid.UUID, storageClass, headCommitID string, autoDeleteDays int)
AddLibraryWithAutoDelete adds a library with auto_delete_days configuration.
func (*MockStore) AddLibraryWithOwner ¶
func (*MockStore) AddLibraryWithTTL ¶
func (m *MockStore) AddLibraryWithTTL(orgID, libraryID uuid.UUID, storageClass, headCommitID string, versionTTLDays int)
AddLibraryWithTTL adds a library with version TTL configuration.
func (*MockStore) AddMonitoredRepo ¶
AddMonitoredRepo adds a monitored repo entry for a user.
func (*MockStore) AddOrganization ¶
func (*MockStore) AddOrganizationWithName ¶
AddOrganizationWithName adds an org with a name (for cascade tests).
func (*MockStore) AddPendingStorageCounterReconciliation ¶
func (*MockStore) AddProvisionalBlockRefExpiry ¶
func (*MockStore) AddProvisionalBlockRefExpiryProjectionForTest ¶
func (*MockStore) AddPurgeRequestedDeletedLibrary ¶
func (m *MockStore) AddPurgeRequestedDeletedLibrary(orgID, libraryID uuid.UUID, storageClass string, deletedAt, purgeRequestedAt time.Time)
AddPurgeRequestedDeletedLibrary adds a permanently-deleted library marker whose canonical libraries row is already gone and whose purge_requested_at is set, so Phase 13 must treat it as eligible on its next scan regardless of deleted_at (the worker still grace-gates the enqueued cascade before processing).
func (*MockStore) AddRestoreJob ¶
func (*MockStore) AddS3OrphanProjectionForTest ¶
func (m *MockStore) AddS3OrphanProjectionForTest(info S3OrphanDiscoveryInfo)
AddS3OrphanProjectionForTest seeds a discovery row independently of the canonical row, so recovery tests can model a projection that outlived, or never matched, its canonical counterpart.
R22b removed the payload counterpart of this helper, SetS3OrphanProjectionForTest, which existed to poison storage_class/representation_id/external_sha1/recovery_phase on a discovery row and prove recovery ignored them. Those columns no longer exist (migration 014), so the property is now structural rather than behavioural and is gated by TestR22bProjectionSchemaIsIdentityOnly instead of by a poisoned fixture.
func (*MockStore) AddShareByUser ¶
AddShareByUser adds a share received by a user.
func (*MockStore) AddShareCreatedByUser ¶
func (m *MockStore) AddShareCreatedByUser(orgID, userID, recipientID, libraryID uuid.UUID) uuid.UUID
AddShareCreatedByUser adds a user-to-user share created by a specific user.
func (*MockStore) AddShareLink ¶
func (*MockStore) AddStarredFile ¶
AddStarredFile adds a starred file entry for a user.
func (*MockStore) AddStorageSnapshot ¶
func (*MockStore) AddStubBlockForTest ¶
AddStubBlockForTest seeds the kind of metadata-free row Cassandra can surface after a claim races with a missing canonical block.
func (*MockStore) AllBlockGCCandidates ¶
func (m *MockStore) AllBlockGCCandidates() []BlockGCCandidateInfo
AllBlockGCCandidates is a test helper returning every candidate row across all (day, bucket) partitions.
func (*MockStore) AllS3Orphans ¶
func (m *MockStore) AllS3Orphans() []S3OrphanInfo
AllS3Orphans is a test helper returning every orphan row across all (day, bucket) partitions. Replaces the old per-org ListS3Orphans path used by tests that just need to assert "what orphans exist right now".
func (*MockStore) AuditLogEntries ¶
func (m *MockStore) AuditLogEntries() []AuditLogEntry
AuditLogEntries returns all audit log entries (for test assertions).
func (*MockStore) BackdateBlockClaimForTest ¶
BackdateBlockClaimForTest ages an existing delete claim, so a test can model an abandoned fence while leaving the worker on the real clock.
The alternative — seeding a fresh claim and pushing w.clock() forward past blockDeleteClaimStaleAfter — has a trap that has already produced a misleading green. postponeItem stamps the requeued row with w.clock(), while Queue.DequeueBatch derives its cutoff from time.Now(); those are the same instant in production and only diverge under a test clock. A worker driven 15 minutes into the future therefore requeues postponed items into the future, where no later pass can dequeue them — so a multi-pass assertion would be testing an empty queue rather than repeated refusals. Backdating the claim instead keeps both clocks agreeing.
func (*MockStore) BeginOrgPurge ¶
func (*MockStore) BlockDeleteAuthorityForTest ¶
func (m *MockStore) BlockDeleteAuthorityForTest(orgID uuid.UUID, blockID, claimID string, claimedAt time.Time) BlockDeleteAuthority
BlockDeleteAuthorityForTest builds an authority bound to the block's CURRENT incarnation. Tests that need an authority for a DEAD incarnation build the struct themselves — the point of most of these tests is that the two differ.
func (*MockStore) BlockExists ¶
func (*MockStore) BlockExistsCallsForTest ¶
func (*MockStore) BlockHasReferences ¶
func (*MockStore) BlockHasReferencesCallCountsForTest ¶
BlockHasReferencesCallCountsForTest reports how many liveness reads of each kind were issued, so a test can assert which one authorized a delete.
func (*MockStore) BlockHasReferencesGlobal ¶
BlockHasReferencesGlobal mirrors the EACH_QUORUM read. It shares the reference state and the concurrency hook with BlockHasReferences so tests that inject a mid-claim reference still exercise claim-then-verify, but it counts separately and honours its own error injection, which is how the fail-closed path is driven.
func (*MockStore) BlockReferenceCount ¶
BlockReferenceCount returns how many reference rows a block currently has. Test helper that replaces the old mutable ref_count assertions.
func (*MockStore) BlockReferenceExists ¶
func (*MockStore) CanonicalLibraryExists ¶
func (*MockStore) ClaimAttemptsForTest ¶
func (m *MockStore) ClaimAttemptsForTest() []BlockDeleteAuthority
ClaimAttemptsForTest returns every authority ClaimBlockDelete was called with, in order. It is how a test can assert that each ATTEMPT minted its own identity rather than inheriting one from the candidate.
func (*MockStore) ClaimBlockDelete ¶
func (m *MockStore) ClaimBlockDelete(orgID uuid.UUID, blockID string, attempt BlockDeleteAuthority) (BlockClaimResult, error)
ClaimBlockDelete mirrors the exact-incarnation CAS.
Note what it can no longer do: materialize a row. The production IF names storage_class, which no absent partition can satisfy, so a missing block is BlockClaimMissing rather than a freshly created stub.
func (*MockStore) ClearDirtyOrg ¶
func (*MockStore) CompleteItem ¶
func (*MockStore) DeleteAPIKeysByUser ¶
func (*MockStore) DeleteBlockGCCandidate ¶
func (*MockStore) DeleteBlockGCCandidateProjectionForTest ¶
func (*MockStore) DeleteClaimedBlockStub ¶
func (*MockStore) DeleteCommit ¶
func (*MockStore) DeleteExpiredFailedItem ¶
func (*MockStore) DeleteExpiredShare ¶
func (m *MockStore) DeleteExpiredShare(share ExpiredShareInfo) error
func (*MockStore) DeleteExpiredShareLink ¶
func (m *MockStore) DeleteExpiredShareLink(link ExpiredShareLinkInfo) error
func (*MockStore) DeleteFSObject ¶
func (*MockStore) DeleteFailedItem ¶
func (*MockStore) DeleteFailedItemContext ¶
func (*MockStore) DeleteFileTag ¶
func (*MockStore) DeleteFileTagByID ¶
func (*MockStore) DeleteFileTagCounters ¶
func (*MockStore) DeleteGroupByMember ¶
func (*MockStore) DeleteGroupFull ¶
func (*MockStore) DeleteGroupMember ¶
func (*MockStore) DeleteLibraryStorageCounter ¶
func (*MockStore) DeleteLockedFilesByLibrary ¶
func (*MockStore) DeleteMonitoredReposByLibrary ¶
func (*MockStore) DeleteMonitoredReposByUser ¶
func (*MockStore) DeleteProvisionalBlockRefExpiryProjection ¶
func (*MockStore) DeleteProvisionalBlockRefExpiryProjectionForTest ¶
func (*MockStore) DeleteRepoAPIToken ¶
func (*MockStore) DeleteRepoAPITokenByToken ¶
func (*MockStore) DeleteRepoTag ¶
func (*MockStore) DeleteRepoTagCounters ¶
func (*MockStore) DeleteRepoTagFileCounts ¶
func (*MockStore) DeleteRestoreJob ¶
func (*MockStore) DeleteRestoreJobsByLibrary ¶
func (*MockStore) DeleteS3Orphan ¶
func (*MockStore) DeleteS3OrphanCanonicalForTest ¶
DeleteS3OrphanCanonicalForTest removes only the canonical row, leaving its discovery projection behind to model canonical expiry or drift.
func (*MockStore) DeleteS3OrphanProjectionForTest ¶
func (*MockStore) DeleteShareLink ¶
func (*MockStore) DeleteShareLinksByLibrary ¶
func (*MockStore) DeleteStarredFilesByLibrary ¶
func (*MockStore) DeleteStarredFilesByUser ¶
func (*MockStore) DequeueBatch ¶
func (*MockStore) EnqueueBatch ¶
func (*MockStore) EnqueueItem ¶
func (*MockStore) EnsureBlockGCCandidate ¶
func (*MockStore) FailedItems ¶
func (m *MockStore) FailedItems(orgID uuid.UUID) []GCFailedItemInfo
func (*MockStore) FinalizeBlockDelete ¶
func (*MockStore) FindOrgForLibrary ¶
func (*MockStore) ForwardBlockMappingExists ¶
ForwardBlockMappingExists reports whether the forward external->internal block mapping row still exists. Test accessor that replaces the dropped reverse-index assertions.
func (*MockStore) ForwardBlockMappingExistsForRepresentation ¶
func (*MockStore) GetBlockGCCandidate ¶
func (*MockStore) GetBlockGCCandidateForTest ¶
func (m *MockStore) GetBlockGCCandidateForTest(orgID uuid.UUID, blockID string) (BlockGCCandidateInfo, bool)
GetBlockGCCandidateForTest exposes the stored candidate for assertions.
func (*MockStore) GetBlockInfo ¶
func (*MockStore) GetCommitRecord ¶
GetCommitRecord returns a commit for test assertions.
func (*MockStore) GetFSObject ¶
func (*MockStore) GetLibraryBlockRepresentationID ¶
func (*MockStore) GetLibraryDeletedAt ¶
func (*MockStore) GetLibraryStorageClass ¶
func (*MockStore) GetOldestQueuedAt ¶
func (*MockStore) GetOrgDeletedAt ¶
func (*MockStore) GetOrgQueueStats ¶
func (m *MockStore) GetOrgQueueStats(orgID uuid.UUID) (GCOrgStats, error)
func (*MockStore) GetProvisionalBlockRefExpiry ¶
func (*MockStore) GetS3OrphanGlobal ¶
func (*MockStore) GetS3OrphanGlobalCallsForTest ¶
GetS3OrphanGlobalCallsForTest reports canonical reloads issued by recovery.
func (*MockStore) GetS3OrphanProjectionForTest ¶
func (m *MockStore) GetS3OrphanProjectionForTest(orgID uuid.UUID, blockID string, firstSeenAt time.Time) (S3OrphanDiscoveryInfo, bool)
GetS3OrphanProjectionForTest reads the raw discovery row for store tests. Production recovery cannot learn anything more from it than this: since R22b the stored row IS its key.
func (*MockStore) GetShareLink ¶
GetShareLink returns a share link for test assertions.
func (*MockStore) GetTotalFailedItems ¶
func (*MockStore) GetTotalQueueSize ¶
func (*MockStore) GetUserDeletedAt ¶
func (*MockStore) GetUserEmail ¶
func (*MockStore) GroupExists ¶
func (*MockStore) HardDeleteLibrary ¶
func (*MockStore) HardDeleteOrgLocked ¶
func (*MockStore) HardDeleteUser ¶
func (*MockStore) HasGroup ¶
HasGroup returns true if the group exists in the store (for test assertions).
func (*MockStore) HasMonitoredRepos ¶
HasMonitoredRepos returns true if monitored repos exist for user.
func (*MockStore) HasStarredFiles ¶
HasStarredFiles returns true if starred files exist for user.
func (*MockStore) HasUser ¶
HasUser returns true if the user exists in the store (for test assertions).
func (*MockStore) LibraryExists ¶
func (*MockStore) ListBlockGCCandidatesByDay ¶
func (*MockStore) ListCommitIDsForLibrary ¶
func (*MockStore) ListCommitsForLibrary ¶
func (m *MockStore) ListCommitsForLibrary(libraryID uuid.UUID) ([]CommitInfo, error)
func (*MockStore) ListCommitsWithTimestamps ¶
func (m *MockStore) ListCommitsWithTimestamps(libraryID uuid.UUID) ([]CommitWithTimestamp, error)
func (*MockStore) ListDeletedUsersExpired ¶
func (m *MockStore) ListDeletedUsersExpired(graceDays int) ([]DeletedUserInfo, error)
func (*MockStore) ListDirtyOrgs ¶
func (m *MockStore) ListDirtyOrgs(limit int) ([]GCDirtyOrg, error)
func (*MockStore) ListDistinctCommitLibraries ¶
func (*MockStore) ListDistinctFSObjectLibraries ¶
func (*MockStore) ListExpiredDeletedLibraries ¶
func (m *MockStore) ListExpiredDeletedLibraries(retentionDays int) ([]DeletedLibraryInfo, error)
func (*MockStore) ListExpiredDeletedOrgs ¶
func (m *MockStore) ListExpiredDeletedOrgs(graceDays int) ([]DeletedOrgInfo, error)
func (*MockStore) ListExpiredRestoreJobs ¶
func (m *MockStore) ListExpiredRestoreJobs() ([]ExpiredRestoreJobInfo, error)
func (*MockStore) ListExpiredShareLinks ¶
func (m *MockStore) ListExpiredShareLinks() ([]ExpiredShareLinkInfo, error)
func (*MockStore) ListExpiredShares ¶
func (m *MockStore) ListExpiredShares() ([]ExpiredShareInfo, error)
func (*MockStore) ListFSObjectIDsForLibrary ¶
func (*MockStore) ListFSObjectsForLibrary ¶
func (m *MockStore) ListFSObjectsForLibrary(libraryID uuid.UUID) ([]FSObjectInfo, error)
func (*MockStore) ListFailedItemExpiriesByDay ¶
func (*MockStore) ListFailedItems ¶
func (*MockStore) ListFileTagsByLibrary ¶
func (m *MockStore) ListFileTagsByLibrary(libraryID uuid.UUID) ([]FileTagInfo, error)
func (*MockStore) ListGroupMembershipsByUser ¶
func (*MockStore) ListGroupsByOrg ¶
func (*MockStore) ListLibrariesByOwner ¶
func (*MockStore) ListLibrariesForOrg ¶
func (m *MockStore) ListLibrariesForOrg(orgID uuid.UUID) ([]OrgLibraryInfo, error)
func (*MockStore) ListLibrariesWithAutoDelete ¶
func (m *MockStore) ListLibrariesWithAutoDelete() ([]LibraryAutoDeleteInfo, error)
func (*MockStore) ListLibrariesWithVersionTTL ¶
func (m *MockStore) ListLibrariesWithVersionTTL() ([]LibraryTTLInfo, error)
func (*MockStore) ListOrgsWithFailedItems ¶
func (m *MockStore) ListOrgsWithFailedItems(limit int) ([]GCFailedItemOrgInfo, error)
func (*MockStore) ListOrgsWithQueuedItems ¶
func (*MockStore) ListOrgsWithQueuedSnapshots ¶
func (*MockStore) ListProvisionalBlockRefExpiriesByDay ¶
func (*MockStore) ListRepoAPITokensByLibrary ¶
func (m *MockStore) ListRepoAPITokensByLibrary(libraryID uuid.UUID) ([]RepoAPITokenInfo, error)
func (*MockStore) ListRepoTagsByLibrary ¶
func (*MockStore) ListS3OrphansByDay ¶
func (*MockStore) ListSharesByGroup ¶
func (m *MockStore) ListSharesByGroup(groupID uuid.UUID) ([]GroupShareInfo, error)
func (*MockStore) ListSharesByLibrary ¶
func (*MockStore) ListSharesByUser ¶
func (m *MockStore) ListSharesByUser(orgID, userID uuid.UUID) ([]ShareByUserInfo, error)
func (*MockStore) ListSharesCreatedByUser ¶
func (m *MockStore) ListSharesCreatedByUser(orgID, userID uuid.UUID) ([]ShareByCreatorInfo, error)
func (*MockStore) ListUsersByOrg ¶
func (m *MockStore) ListUsersByOrg(orgID uuid.UUID) ([]OrgUserInfo, error)
func (*MockStore) MarkOrgActive ¶
func (*MockStore) MarkOrgDirty ¶
func (*MockStore) MarkS3OrphanMappingCleanupPending ¶
func (*MockStore) PendingItemExists ¶
func (*MockStore) QueueItemExists ¶
func (*MockStore) QueueItems ¶
QueueItems returns all queue items for an org.
func (*MockStore) RecalculateOrgQueueStats ¶
func (m *MockStore) RecalculateOrgQueueStats(orgID uuid.UUID) (GCOrgStats, error)
func (*MockStore) ReconcilePendingStorageCounters ¶
func (*MockStore) ReleaseBlockClaim ¶
func (m *MockStore) ReleaseBlockClaim(orgID uuid.UUID, blockID string, authority BlockDeleteAuthority) (BlockReleaseOutcome, error)
func (*MockStore) ReleaseLibraryHardDeleteLock ¶
func (*MockStore) ReleaseOrgHardDeleteLock ¶
func (*MockStore) ReleaseStaleBlockClaim ¶
func (m *MockStore) ReleaseStaleBlockClaim(orgID uuid.UUID, blockID string, expectedTarget BlockDeleteTarget, staleBefore time.Time) (BlockClaimReleaseOutcome, error)
ReleaseStaleBlockClaim mirrors the Cassandra semantics: silent no-op when there is nothing to release, so the common "referenced block, never claimed" path neither errors nor warns; a stale claim is released regardless of which attempt owns it; and a real failure is injectable to prove that callers refuse to settle a candidate whose fence they could not confirm gone.
func (*MockStore) ReleaseUserHardDeleteLock ¶
func (*MockStore) RemoveBlockForTest ¶
GetBlock returns a block for test assertions. RemoveBlockForTest deletes the canonical row, modelling an incarnation that died after its candidate was created. The candidate survives, which is the point: it is what carries the dead incarnation into the claim.
func (*MockStore) RemoveBlockReference ¶
func (*MockStore) RemoveOrgFromActiveSet ¶
func (*MockStore) RenewLibraryHardDeleteLock ¶
func (*MockStore) RenewOrgHardDeleteLock ¶
func (*MockStore) RenewUserHardDeleteLock ¶
func (*MockStore) RequeueFailedItem ¶
func (*MockStore) RequeueFailedItemContext ¶
func (*MockStore) RequeueItem ¶
func (*MockStore) ResolveBlockIDs ¶
func (*MockStore) S3OrphanCount ¶
S3OrphanCount is a test helper returning the total orphan count across orgs.
func (*MockStore) SaveGCStats ¶
func (*MockStore) SaveOrgQueueStats ¶
func (m *MockStore) SaveOrgQueueStats(stats GCOrgStats) error
func (*MockStore) ScanAllGroupShares ¶
func (*MockStore) SeedBlockClaimForTest ¶
func (m *MockStore) SeedBlockClaimForTest(orgID uuid.UUID, blockID, claimID string, claimedAt time.Time) BlockDeleteAuthority
SeedBlockClaimForTest installs a delete claim directly, modelling an attempt that claimed the row and then vanished. It returns the authority that owns it so a test can assert who may and may not release it.
func (*MockStore) SetBlockExistsErrForTest ¶
SetBlockExistsErrForTest makes BlockExists fail without affecting the other block reads. It is used to prove that post-S3 orphan finalization no longer depends on the resurrection guard that protected mapping deletion.
func (*MockStore) SetBlockGCCandidateTargetForTest ¶
func (m *MockStore) SetBlockGCCandidateTargetForTest(orgID uuid.UUID, blockID string, target BlockDeleteTarget)
SetBlockGCCandidateTargetForTest rewrites a candidate's captured incarnation, so a test can model "this candidate was created for P1" independently of what the canonical row holds now.
func (*MockStore) SetBlockGCStateForTest ¶
func (m *MockStore) SetBlockGCStateForTest(orgID uuid.UUID, blockID, gcState, claimID string, claimedAt time.Time)
SetBlockGCStateForTest installs an arbitrary gc_state owner, so a test can model a claim belonging to another subsystem — notably the upload path's repairing_stub.
func (*MockStore) SetBlockHasReferencesGlobalErrForTest ¶
SetBlockHasReferencesGlobalErrForTest drives the fail-closed path: an unreachable DC makes the EACH_QUORUM read fail, and GC must not delete on that uncertainty.
func (*MockStore) SetBlockHasReferencesHookForTest ¶
func (m *MockStore) SetBlockHasReferencesHookForTest(hook func(orgID uuid.UUID, blockID string, current bool) (bool, error))
SetBlockHasReferencesHookForTest installs a deterministic concurrency hook for component tests that drive the real worker against MockStore.
func (*MockStore) SetBlockStorageKeyForTest ¶
SetBlockStorageKeyForTest overwrites a seeded block's persisted locator so a test can model a row whose storage_key does not belong to its own org — the corruption/bad-backfill case the destructive paths must refuse.
func (*MockStore) SetClaimBlockDeleteErrForTest ¶
SetClaimBlockDeleteErrForTest injects a failure into the LWT claim.
An LWT can fail for availability reasons depending on its serial and regular consistency levels and on which replicas are reachable — Paxos needs its serial quorum on top of the ordinary one, so contention and a degraded cluster surface here first. This hook drives the worker's TREATMENT of such a failure (postpone, not DLQ); it deliberately does not model any rule of the form "one DC down implies the claim fails". SERIAL and LOCAL_SERIAL are consistencies of the Paxos phase; EACH_QUORUM, the level X2 turns on, is a per-datacenter requirement of an ordinary read. Conflating the two is what produced an incorrect advisory once already.
func (*MockStore) SetClaimBlockDeleteSettleErrForTest ¶
SetClaimBlockDeleteSettleErrForTest makes the serial settling read fail too, so an injected LWT failure becomes genuinely unsettleable. Both knobs together are the only way to reach BlockClaimAmbiguous — which is the point: ambiguity is rare, and a test that reaches it by accident is testing the wrong thing.
func (*MockStore) SetDeleteS3OrphanErrOnceForTest ¶
SetDeleteS3OrphanErrOnceForTest makes the next orphan clear fail without mutating state, modeling a crash or failed canonical clear after S3 succeeds.
func (*MockStore) SetGetBlockGCCandidateErrForTest ¶
SetGetBlockGCCandidateErrForTest injects a failure into the candidate authority read.
func (*MockStore) SetGetBlockInfoErrorForTest ¶
SetGetBlockInfoErrorForTest makes the post-claim canonical re-read fail.
func (*MockStore) SetGetBlockInfoHookForTest ¶
SetGetBlockInfoHookForTest rewrites what the post-claim canonical re-read returns.
It models the one thing the in-memory store cannot otherwise reach: GetBlockInfo is an ordinary read while the claim commits in the serial domain, so in production the two can legitimately disagree about which incarnation the row holds. Nothing downstream may publish or delete on the strength of that read.
func (*MockStore) SetGetFSObjectError ¶
func (*MockStore) SetGetS3OrphanGlobalErrForTest ¶
func (*MockStore) SetGetS3OrphanGlobalHookForTest ¶
func (m *MockStore) SetGetS3OrphanGlobalHookForTest(hook func(orgID uuid.UUID, blockID string, call int, info S3OrphanInfo) (S3OrphanInfo, error))
SetGetS3OrphanGlobalHookForTest injects deterministic changes into canonical reloads. The call number starts at one for the first row read.
func (*MockStore) SetLibraryCanonicalStats ¶
SetLibraryCanonicalStats sets the canonical size_bytes / file_count columns on a mock library row. Tests must use this instead of (or in addition to) AddStorageSnapshot for lib-scope when exercising the aggregate reconciliation path: production reads from these canonical columns, and a test that only seeds the lib-scope storage_counters row will not exercise the same code path.
func (*MockStore) SetLibraryEncrypted ¶
SetLibraryEncrypted flips a mock library's encrypted flag. Tests that need the "encrypted + empty stored representation -> library:<id>" derivation path must clear BlockRepresentationID separately.
func (*MockStore) SetMarkS3OrphanMappingCleanupPendingErrOnceForTest ¶
SetMarkS3OrphanMappingCleanupPendingErrOnceForTest makes the next phase advance fail without mutating the canonical row. This characterizes the window after a successful S3 delete and before the durable phase transition.
func (*MockStore) SetReleaseBlockClaimErrForTest ¶
SetReleaseBlockClaimErrForTest injects a failure into the post-claim release.
The interesting injection is a NON-availability error. Those used to surface as ordinary failures, spend the item's retry budget and reach the DLQ while gc_state='deleting' stayed on the row — a permanent upload fence on a block the walk may have just proven to be still referenced. See Worker.releaseBlockClaim.
func (*MockStore) SetReleaseStaleBlockClaimErrForTest ¶
SetReleaseStaleBlockClaimErrForTest injects a failure into the stale-claim release.
func (*MockStore) SetS3OrphanStorageKeyForTest ¶
SetGetS3OrphanGlobalErrForTest makes the canonical EACH_QUORUM read fail. SetS3OrphanStorageKeyForTest rewrites a canonical orphan's locator after the lifecycle entry point has refused to create it without one, so a test can model a row that lost or never had a usable key.
func (*MockStore) SetStartBlockDeleteOrphanResetRaceForTest ¶
SetStartBlockDeleteOrphanResetRaceForTest makes the mock model the canonical row disappearing after the CAS read and before the conditional reset update.
func (*MockStore) SetValidateDestructiveGCTopologyErrForTest ¶
SetValidateDestructiveGCTopologyErrForTest makes the mock's gate reject, so tests can drive the fail-closed path through the real wiring instead of overriding the worker's gate function.
func (*MockStore) SoftDeleteLibrary ¶
func (*MockStore) StartBlockDeleteOrphan ¶
func (*MockStore) StorageSnapshot ¶
func (m *MockStore) StorageSnapshot(scope string) traffic.StorageSnapshot
func (*MockStore) UpdateS3OrphanAttempt ¶
func (*MockStore) ValidateDestructiveGCTopology ¶
ValidateDestructiveGCTopology always passes for the mock: an in-memory store has no keyspace whose replication could invalidate the per-DC EACH_QUORUM argument. It is implemented because the gate is part of GCStore, so a store that forgets it fails to compile rather than silently disarming the destructive gate.
func (*MockStore) WriteAuditLog ¶
func (m *MockStore) WriteAuditLog(entry AuditLogEntry) error
type OnlyOfficeReconciler ¶
OnlyOfficeReconciler reconciles stale onlyoffice_pending_blocks rows for an org: it either drops rows whose publish commit is reachable from the current library head or rolls back materialized blocks for rows that were never published. Wired in from the API layer because the implementation lives there alongside the OnlyOffice handler.
type OrgLibraryInfo ¶
type OrgLibraryInfo struct {
LibraryID uuid.UUID
StorageClass string
OwnerID uuid.UUID
DeletedAt time.Time // zero means library is still active
}
OrgLibraryInfo holds basic library data for org cascade cleanup.
type OrgUserInfo ¶
OrgUserInfo holds basic user data for org cascade cleanup.
type OrphanRecoverer ¶
type OrphanRecoverer interface {
RecoverS3Orphans(ctx context.Context, perBucketLimit int) (int, error)
}
OrphanRecoverer retries S3 deletes for blocks whose DB rows are gone but whose S3 objects linger. Implemented by *Worker.
type Queue ¶
type Queue struct {
// contains filtered or unexported fields
}
Queue provides operations for the gc_queue.
func (*Queue) Complete ¶
func (q *Queue) Complete(orgID uuid.UUID, queuedAt time.Time, itemType ItemType, itemID string) error
Complete removes a processed item from the gc_queue.
func (*Queue) DequeueBatch ¶
func (q *Queue) DequeueBatch(orgID uuid.UUID, batchSize int, minAge time.Duration) ([]QueueItem, error)
DequeueBatch retrieves the oldest items from the queue for a given org that are older than minAge (grace period). Returns up to batchSize items.
func (*Queue) Enqueue ¶
func (q *Queue) Enqueue(orgID uuid.UUID, itemType ItemType, itemID string, libraryID uuid.UUID, storageClass string) error
Enqueue inserts an item into the gc_queue for later deletion.
func (*Queue) EnqueueBatch ¶
EnqueueBatch inserts multiple items into the gc_queue efficiently.
EnqueueBatch is the real choke point every producer uses, so the block representation invariant is enforced here (not only in Enqueue/EnqueueCascade): commits, fs_objects and library cascades must carry a *canonical* BlockRepresentationID. Rejecting an empty OR malformed value keeps an incomplete task — which the worker would fail to map and then retry forever, leaking references/blocks/mappings — from ever reaching gc_queue.
func (*Queue) EnqueueCascade ¶
func (q *Queue) EnqueueCascade(orgID uuid.UUID, parentQueuedAt time.Time, itemType ItemType, itemID string, libraryID uuid.UUID, storageClass string) error
EnqueueCascade inserts a cascade-generated item into the gc_queue using the parent's QueuedAt timestamp. Since the parent already passed the grace period, cascade children become immediately eligible for processing — they are known to be unreferenced (the parent object is being deleted).
func (*Queue) GetQueueSize ¶
GetQueueSize returns the approximate number of items in the queue for an org.
func (*Queue) GetTotalQueueSize ¶
GetTotalQueueSize returns the approximate total number of items across all orgs.
func (*Queue) IncrementRetry ¶
IncrementRetry updates the retry count for a failed item and requeues it at the back of the queue.
type QueueItem ¶
type QueueItem struct {
OrgID uuid.UUID
QueuedAt time.Time
IdentityAt time.Time
RequiresLibraryDeletedCheck bool
LibraryGuardMode LibraryGuardMode
ItemType ItemType
ItemID string
LibraryID uuid.UUID
BlockRepresentationID string
StorageClass string
RetryCount int
}
QueueItem represents a single item pending garbage collection
type RepoAPITokenInfo ¶
RepoAPITokenInfo holds data about a repo API token for orphan cleanup.
type S3OrphanDiscoveryInfo ¶
S3OrphanDiscoveryInfo is the non-authoritative identity emitted by the gc_s3_orphans_by_day discovery projection. Its first_seen_at value is only a correlation token; it is not a complete lifecycle identity.
type S3OrphanInfo ¶
type S3OrphanInfo struct {
OrgID uuid.UUID
BlockID string
StorageClass string
StorageKey string
ExternalSHA1 string
RecoveryPhase string
FirstSeenAt time.Time
LastAttemptAt time.Time
RetryCount int
LastError string
}
S3OrphanInfo holds canonical data about a block whose S3 deletion still needs recovery. Rows are created as soon as GC claims the block for deletion, before the DB row is physically removed, so a crash between DB and S3 phases remains recoverable after restart.
type Scanner ¶
type Scanner struct {
// contains filtered or unexported fields
}
Scanner periodically finds orphaned items that were missed by inline enqueue and adds them to the gc_queue for processing.
func NewScanner ¶
NewScanner creates a new safety scanner.
func (*Scanner) ScanExpiredDeletedLibrariesOnce ¶
ScanExpiredDeletedLibrariesOnce runs only the deleted-library expiry phase. Integration tests use this narrow entry point to avoid mutating unrelated scanner cursors/global state via a full ScanOnce pass.
func (*Scanner) ScanExpiredProvisionalBlockRefsOnce ¶
ScanExpiredProvisionalBlockRefsOnce runs only the provisional-reference recovery phase. Integration tests use this narrow entry point so they can exercise the production Cassandra store without advancing unrelated scanner cursors or mutating unrelated GC state.
func (*Scanner) SetOnlyOfficeReconciler ¶
func (s *Scanner) SetOnlyOfficeReconciler(r OnlyOfficeReconciler)
SetOnlyOfficeReconciler wires the OnlyOffice pending-blocks reconciler. Optional; if unset, the onlyoffice_pending_blocks phase is a no-op (the inline reconcile in saveEditedDocument is still the primary cleanup trigger).
func (*Scanner) SetOrphanRecoverer ¶
func (s *Scanner) SetOrphanRecoverer(r OrphanRecoverer)
SetOrphanRecoverer wires the S3 orphan recovery dependency. Optional; if unset, the s3_orphan_recovery phase is a no-op (useful for mock-only tests).
type ScopedBlockDelete ¶
ScopedBlockDelete records one physical delete the way the backend saw it: the org and class that selected the bucket, and the exact key handed to it. It deliberately does NOT carry a block id — the delete API takes a locator, and a mock that reconstructed an id would hide the very substitution these tests exist to catch.
type ScopedBlockStoreRequest ¶
type ScopedPhysicalLocatorValidation ¶
type ScopedPhysicalLocatorValidation struct {
OrgID string
StorageClass string
BlockID string
StorageKey string
}
ScopedPhysicalLocatorValidation records the exact persisted locator presented to an org-scoped store before a physical delete.
type Service ¶
type Service struct {
// contains filtered or unexported fields
}
Service is the top-level GC orchestrator. It starts and manages the worker and scanner goroutines.
func NewService ¶
func NewService(store GCStore, storage StorageProvider, cfg config.GCConfig, dbSession *gocql.Session) *Service
NewService creates a new GC service using the provided store and storage provider. dbSession is used for quota-period rollover; pass nil to disable rollover.
func (*Service) AcceptsManualTriggers ¶
AcceptsManualTriggers reports whether a manual worker/scanner trigger can actually reach a consumer goroutine that will execute it right now.
This is defence in depth for the GC kill switch, not decoration. A disabled service is still CONSTRUCTED by the API server and its superadmin endpoint (POST /api/v2.1/admin/gc/run) is still registered; Start() merely declines to launch runWorkerLoop/runScannerLoop. So before this guard existed the kill switch rested on "a disabled service has no consumer for the trigger channel", which is an accident of Start()'s control flow rather than a stated invariant. Any refactor that launched those loops unconditionally would have silently promoted that endpoint into a live bypass of GC_ENABLED=false. The check belongs where the decision is.
Two smaller consequences it also removes: the endpoint answered {"started":true} for a run that never happened, and the parked token in the size-1 buffered channel would have fired one unrequested run the moment GC was later enabled.
Reads lifecycle state atomically and checks the cached lease state without taking s.mu; a mutex here deadlocks Stop().
func (*Service) DeleteFailedItem ¶
func (*Service) DeleteFailedItemContext ¶
func (*Service) EnqueueBlock ¶
func (s *Service) EnqueueBlock(orgID uuid.UUID, blockID string, libraryID uuid.UUID, storageClass string) error
EnqueueBlock is a convenience method for enqueuing a block from application code.
func (*Service) EnqueueCommits ¶
EnqueueCommits enqueues specific commits for GC deletion (used by CleanRepoTrash).
func (*Service) EnqueueLibraryCascade ¶
func (s *Service) EnqueueLibraryCascade(orgID, libraryID uuid.UUID, blockRepresentationID, storageClass string, deletedAt time.Time) error
EnqueueLibraryCascade immediately queues the full library-cascade GC item for a permanently deleted library, so reclamation can start on the next worker tick instead of waiting up to a full ScanInterval (default 24h) for Phase 13 to discover the marker.
It mirrors scanner Phase 13 exactly — QueuedAt = deletedAt (the original trash time, so the grace gate is measured from the same event and long-trashed libraries process promptly), LibraryID nil, same representation — so this immediate enqueue and any later scan produce the IDENTICAL queue/pending row: the second write is a dedup no-op, never a second producer. If this fire-and-forget enqueue is lost (crash/restart), the durable purge_requested_at marker lets Phase 13 recover it. See ISSUE-GC-ORG-TRASH-NO-CASCADE-01.
func (*Service) FailedItemsPageSize ¶
func (*Service) ListFailedItemOrgs ¶
func (s *Service) ListFailedItemOrgs(limit int) ([]GCFailedItemOrgInfo, error)
func (*Service) ListFailedItems ¶
func (*Service) ManualTriggerError ¶
ManualTriggerError reports why a manual trigger cannot be accepted. A trigger is only honest when the service is running and this node currently owns the GC lease; a follower's loop will otherwise consume the token and immediately return without doing work.
func (*Service) RefreshFailedItemSnapshot ¶
func (s *Service) RefreshFailedItemSnapshot()
RefreshFailedItemSnapshot forces an exact refresh of failed-item-related queue snapshots before admin/status views read them. This is intentionally heavier than GetOrgQueueStats and should stay off the mutation hot path.
func (*Service) RequeueFailedItem ¶
func (*Service) RequeueFailedItemContext ¶
func (*Service) SetOnlyOfficeReconciler ¶
func (s *Service) SetOnlyOfficeReconciler(r OnlyOfficeReconciler)
SetOnlyOfficeReconciler wires the OnlyOffice pending-blocks reconciler into the scanner. Called from the API layer after the OnlyOffice handler exists.
func (*Service) Stop ¶
func (s *Service) Stop()
Stop gracefully stops the GC service using an uncancelable context for callers that need the historical blocking shutdown behavior.
func (*Service) StopWithContext ¶
StopWithContext starts shutdown and waits for the current run to drain. A timed-out caller does not make the service restartable: the lifecycle remains stopping until every worker and DLQ mutation has exited, then releases the lease and transitions to stopped.
func (*Service) TriggerScanner ¶
TriggerScanner triggers an immediate scanner run. It reports whether the trigger was accepted; false means the service is disabled, not running, or not the current leader.
func (*Service) TriggerScannerWithDryRun ¶
TriggerScannerWithDryRun atomically admits a scanner trigger and applies an optional runtime mode override. A rejected trigger never commits the override.
func (*Service) TriggerWorker ¶
TriggerWorker triggers an immediate worker run. It reports whether the trigger was accepted; false means the service is disabled, not running, or not the current leader, and nothing was queued.
func (*Service) TriggerWorkerWithDryRun ¶
TriggerWorkerWithDryRun atomically admits a worker trigger and applies an optional runtime mode override. A rejected trigger never commits the override.
type ShareByCreatorInfo ¶
type ShareByCreatorInfo struct {
}
ShareByCreatorInfo holds data about a share created by a user.
type ShareByUserInfo ¶
type ShareByUserInfo struct {
}
ShareByUserInfo holds data about a share received by a user.
type ShareInfo ¶
type ShareInfo struct {
}
ShareInfo holds data about a library share for orphan cleanup.
type ShareLinkInfo ¶
type ShareLinkInfo struct {
}
ShareLinkInfo holds data about a share link needed by the scanner.
type Stats ¶
type Stats struct {
// contains filtered or unexported fields
}
Stats tracks GC runtime statistics (thread-safe).
func (*Stats) BlocksDeleted ¶
func (*Stats) IncrBlocksDeleted ¶
func (s *Stats) IncrBlocksDeleted()
func (*Stats) LastScanAttempt ¶
func (*Stats) LastScanError ¶
func (*Stats) LastScanRun ¶
func (*Stats) LastScanSuccess ¶
func (*Stats) LastWorkerRun ¶
func (*Stats) SetLastScanAttempt ¶
func (*Stats) SetLastScanError ¶
func (*Stats) SetLastScanRun ¶
func (*Stats) SetLastScanSuccess ¶
func (*Stats) SetLastWorkerRun ¶
type StorageManagerAdapter ¶
type StorageManagerAdapter struct {
// contains filtered or unexported fields
}
StorageManagerAdapter wraps a *storage.Manager to implement StorageProvider.
func NewStorageManagerAdapter ¶
func NewStorageManagerAdapter(manager *storage.Manager) *StorageManagerAdapter
NewStorageManagerAdapter wraps a *storage.Manager as a StorageProvider.
func (*StorageManagerAdapter) GetBlockStoreForOrg ¶
func (a *StorageManagerAdapter) GetBlockStoreForOrg(orgID, storageClass string) (BlockStoreDeleter, error)
type StorageProvider ¶
type StorageProvider interface {
GetBlockStoreForOrg(orgID, storageClass string) (BlockStoreDeleter, error)
}
StorageProvider returns a BlockStoreDeleter bound to one org and exact storage class. GC must never health-failover a delete to a different physical backend.
type Worker ¶
type Worker struct {
// contains filtered or unexported fields
}
Worker drains the gc_queue and deletes items from S3 and the database.
func NewWorker ¶
func NewWorker(store GCStore, storage StorageProvider, queue *Queue, batchSize int, gracePeriod time.Duration, dryRun bool, stats *Stats) *Worker
NewWorker creates a new GC worker.
func (*Worker) EnqueueLibraryContents ¶
EnqueueLibraryContents enqueues all contents of a deleted library for GC. Only enqueues commits and fs_objects — blocks are handled in cascade when fs_objects are processed (via decrementFSObjectBlocks).
func (*Worker) ProcessOnce ¶
ProcessOnce runs a single pass of the worker: find orgs with queued items, dequeue a batch for each, and process them.
func (*Worker) ProcessOrgOnce ¶
ProcessOrgOnce processes a single org's queued items in one pass. It is the scoped counterpart to ProcessOnce (which fans out across every active org) so callers — notably integration tests that enqueue work under a synthetic org — can drive GC for exactly that org without dequeuing unrelated orgs' items. A worker wired with a nil or partial storage provider must never touch another org's real blocks (it would route their S3 deletes down the slow recovery path), and this is the entry point that guarantees that scoping.
func (*Worker) RecoverS3Orphans ¶
RecoverS3Orphans retries S3 deletes for orphan rows in gc_s3_orphans. Called by the scanner; exposed on the worker because it needs access to w.storage. Returns the number of orphans successfully recovered.
Walks the gc_s3_orphans_by_day discovery projection from a persisted UTC-day cursor up to today. On cold start (no cursor) it scans the full 90-day TTL horizon so old orphan rows cannot get stranded forever. `perBucketLimit` caps the rows pulled per (day, bucket) so a single misbehaving bucket cannot starve the worker. RecoverS3Orphans finishes physical deletes that processBlock started but could not complete (S3 error, crash, restart).
AUTHORIZATION INVARIANT: every physical delete in this codebase must trace back to an EACH_QUORUM liveness read (ISSUE-GC-CROSS-DC-REFERENCE-VISIBILITY-01). A new destructive path that does not is a silent reopening with no failing test.
This path is authorized twice over. Transitively, a gc_s3_orphans row cannot exist unless processBlock already passed its claim-then-verify, because StartBlockDeleteOrphan runs strictly after it. But that implication only holds forward in time: a row written by a pre-X2 binary was authorized by a LOCAL_QUORUM verify, and recovery would happily finish that delete after an upgrade. Rather than leave the guarantee resting on the greenfield deployment precondition — true today, unenforceable in code, and invisible when it stops being true — recovery re-reads BlockHasReferencesGlobal for itself before destroying bytes. It is the cold path; the extra WAN read costs nothing that matters.
func (*Worker) SetDestructiveTopologyGate ¶
SetDestructiveTopologyGate overrides the check that must pass before this worker may delete physical bytes. It stays a runtime check — keyspace replication can be altered while the process runs, and a topology that stops supporting the per-DC EACH_QUORUM argument must stop deletes then, not at the next restart. A passing result is reused for at most destructiveTopologyGateTTL; a failing one never is.
The gate is already armed from the store by NewWorker; this exists so a test can substitute a specific rejection. Passing nil restores the store's own gate rather than removing the constraint — an unguarded destructive path is not a state this type offers. Swapping the gate drops any cached pass, so a test that installs a rejection sees it on the very next call.