shared

package
v0.7.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 3, 2026 License: Apache-2.0 Imports: 18 Imported by: 0

Documentation

Overview

Package shared provides backend-agnostic components that can be reused across different backend implementations (Docker, Kubernetes, Nomad, etc.).

What lives here

  • SKUProfile, ResourceStats, ResourceAllocator (resources.go) — the resource pool primitives used by every backend that tracks CPU/memory/disk
  • Registry helpers (registry.go) — image-registry parsing + allowlist validation (ParseRegistry, IsImageAllowed, ValidateImage) used by substrate adapters to enforce per-tenant image policy
  • CallbackSender (callback_sender.go) — HMAC-signed callback delivery with bbolt-backed persistence so failure callbacks survive restarts
  • BoltStore (bolt_store.go) — small wrapper around bbolt used by the callback, diagnostics, and release stores
  • Diagnostics (diagnostics.go) — persisted failure diagnostics with fallback for "lease no longer in memory" reads
  • Releases (releases.go) — release/deployment history with TTL cleanup
  • Types (types.go) — shared SKU and resource types

All of these are consumed by the docker backend and are usable by any future in-process backend (Kubernetes, Nomad, etc.). HTTP-only backends running in a separate process should reuse callback_sender and the HMAC types but typically maintain their own registry.

Index

Constants

View Source
const (
	// CallbackMaxAttempts is the number of times to attempt callback delivery.
	CallbackMaxAttempts = 3

	// CallbackTimeout is the per-attempt timeout for callback HTTP requests.
	CallbackTimeout = 10 * time.Second
)
View Source
const RetentionStatusActive = "active"

RetentionStatusActive is the status of an active (held) retention entry.

View Source
const RetentionStatusReaping = "reaping"

RetentionStatusReaping marks a record whose volumes are pending physical destruction: the bytes are still on disk (so the footprint must keep counting in the admission projection) but the record is NOT restore-claimable. It is a finalizer tombstone — kept until every volume is confirmed destroyed, then Delete()d. See ENG-376.

View Source
const RetentionStatusRestoring = "restoring"

RetentionStatusRestoring is the status of an entry currently being restored.

Variables

View Source
var (
	// ErrNoRetention is returned when no retained data exists for a given lease UUID.
	ErrNoRetention = errors.New("no retained data for lease")
	// ErrNotRestorable is returned when a retained lease is not in a restorable state.
	ErrNotRestorable = errors.New("retained lease not in a restorable state")
)

Functions

func IsImageAllowed

func IsImageAllowed(image string, allowedRegistries []string) bool

IsImageAllowed checks if an image is from an allowed registry.

func ParseRegistry

func ParseRegistry(image string) (string, error)

ParseRegistry extracts the registry domain from a container image reference. Uses the distribution/reference library for robust parsing with Docker Hub normalization (e.g., "nginx" -> "docker.io").

func ValidateImage

func ValidateImage(image string, allowedRegistries []string) error

ValidateImage checks if an image reference is valid and allowed.

Types

type CallbackEntry

type CallbackEntry struct {
	LeaseUUID   string                 `json:"lease_uuid"`
	CallbackURL string                 `json:"callback_url"`
	Success     bool                   `json:"success"`
	Status      backend.CallbackStatus `json:"status,omitempty"`
	Backend     string                 `json:"backend,omitempty"`
	Error       string                 `json:"error,omitempty"`
	// Retained persists the best-effort deprovision retain-success flag so a
	// restart-replayed callback keeps it. Legacy entries default to false.
	Retained  bool      `json:"retained,omitempty"`
	CreatedAt time.Time `json:"created_at"`
}

CallbackEntry represents a pending callback to be delivered.

Success is retained for backwards compatibility with entries persisted by binaries that predate the Status field. New writers populate Success AND Status (and Backend). Readers prefer Status when non-empty and fall back to Success otherwise; see callback_sender.ReplayPendingCallbacks.

type CallbackSender

type CallbackSender struct {
	// contains filtered or unexported fields
}

CallbackSender handles HMAC-signed callback delivery with retry and persistence.

func NewCallbackSender

func NewCallbackSender(cfg CallbackSenderConfig) *CallbackSender

NewCallbackSender creates a new CallbackSender. Panics if HTTPClient, Logger, or StopCtx is nil (programming error). Store may be nil to disable callback persistence (callbacks will not survive restarts).

func (*CallbackSender) DeliverCallback

func (s *CallbackSender) DeliverCallback(leaseUUID, callbackURL string, body []byte) bool

DeliverCallback attempts to deliver a callback with retries. Returns true if delivery succeeded.

func (*CallbackSender) ReplayPendingCallbacks

func (s *CallbackSender) ReplayPendingCallbacks()

ReplayPendingCallbacks replays any callbacks that were persisted but not successfully delivered before the previous shutdown.

func (*CallbackSender) SendCallback

func (s *CallbackSender) SendCallback(leaseUUID, callbackURL, backendName string, status backend.CallbackStatus, errMsg string, retained bool)

TODO(ENG-134): no per-lease ctx seam — see k3s.Deprovision doc for the residual deprovision-vs-callback TOCTOU this enables.

SendCallback sends a provision result callback with HMAC signature. It persists the callback before delivery and removes it on success. The caller must provide the callbackURL (resolved from its own state) and the backendName (so Fred can label metrics per-backend without a placement lookup, which is often already deleted for intentional deprovisions). retained is the best-effort ground-truth deprovision retain-success flag (false for all non-deprovision callbacks); it is threaded into the payload and persisted so a restart-replayed callback keeps it.

type CallbackSenderConfig

type CallbackSenderConfig struct {
	Store        *CallbackStore
	HTTPClient   *http.Client
	Secret       string
	Logger       *slog.Logger
	StopCtx      context.Context
	OnDelivery   func(outcome string)                // optional metrics callback
	OnStoreError func()                              // optional; called when bbolt persistence fails
	Backoff      *[CallbackMaxAttempts]time.Duration // retry delays; nil uses default {0, 1s, 5s}
}

CallbackSenderConfig configures a CallbackSender.

type CallbackStore

type CallbackStore struct {
	// contains filtered or unexported fields
}

CallbackStore persists pending callbacks in bbolt so they survive restarts.

func NewCallbackStore

func NewCallbackStore(cfg CallbackStoreConfig) (*CallbackStore, error)

NewCallbackStore opens or creates a bbolt database for callback persistence. If MaxAge > 0, a background cleanup loop removes expired entries periodically and an initial cleanup runs immediately to clear stale entries from previous runs.

func (CallbackStore) Close

func (s CallbackStore) Close() error

Close shuts down the store gracefully. It is idempotent: the first call closes the database and captures any error; subsequent calls return the same error.

func (CallbackStore) Healthy

func (s CallbackStore) Healthy() error

Healthy checks that the bbolt database is accessible and the bucket exists.

func (*CallbackStore) ListPending

func (s *CallbackStore) ListPending() ([]CallbackEntry, error)

ListPending returns all pending callback entries for replay on startup.

func (*CallbackStore) Remove

func (s *CallbackStore) Remove(leaseUUID string) error

Remove deletes a callback entry after successful delivery.

func (*CallbackStore) RemoveOlderThan

func (s *CallbackStore) RemoveOlderThan(maxAge time.Duration) (int, error)

RemoveOlderThan deletes callback entries older than maxAge and returns the number of entries removed.

func (*CallbackStore) Store

func (s *CallbackStore) Store(entry CallbackEntry) error

Store persists a callback entry before attempting delivery.

type CallbackStoreConfig

type CallbackStoreConfig struct {
	DBPath          string            // Path to bbolt database file
	MaxAge          time.Duration     // Max age before entries are cleaned up (0 = no expiry)
	CleanupInterval time.Duration     // How often to run cleanup (defaults to MaxAge)
	OnCleanupPanic  util.PanicHandler // Optional: invoked on cleanup-loop panic (e.g., bump a metric)
}

CallbackStoreConfig configures the callback store.

type DiagnosticEntry

type DiagnosticEntry struct {
	LeaseUUID    string            `json:"lease_uuid"`
	ProviderUUID string            `json:"provider_uuid"`
	Tenant       string            `json:"tenant"`
	Error        string            `json:"error"`
	Logs         map[string]string `json:"logs,omitempty"`
	FailCount    int               `json:"fail_count"`
	CreatedAt    time.Time         `json:"created_at"`
}

DiagnosticEntry represents a persisted failure diagnostic record.

type DiagnosticsStore

type DiagnosticsStore struct {
	// contains filtered or unexported fields
}

DiagnosticsStore persists failure diagnostics in bbolt so they survive container removal and backend restarts.

func NewDiagnosticsStore

func NewDiagnosticsStore(cfg DiagnosticsStoreConfig) (*DiagnosticsStore, error)

NewDiagnosticsStore opens or creates a bbolt database for diagnostics persistence. If MaxAge > 0, a background cleanup loop removes expired entries periodically and an initial cleanup runs immediately to clear stale entries from previous runs.

func (DiagnosticsStore) Close

func (s DiagnosticsStore) Close() error

Close shuts down the store gracefully. It is idempotent: the first call closes the database and captures any error; subsequent calls return the same error.

func (*DiagnosticsStore) Delete

func (s *DiagnosticsStore) Delete(leaseUUID string) error

Delete removes a diagnostic entry by lease UUID. It is a no-op if the entry does not exist.

func (*DiagnosticsStore) Get

func (s *DiagnosticsStore) Get(leaseUUID string) (*DiagnosticEntry, error)

Get retrieves a diagnostic entry by lease UUID. Returns nil, nil when not found.

func (DiagnosticsStore) Healthy

func (s DiagnosticsStore) Healthy() error

Healthy checks that the bbolt database is accessible and the bucket exists.

func (*DiagnosticsStore) RemoveOlderThan

func (s *DiagnosticsStore) RemoveOlderThan(maxAge time.Duration) (int, error)

RemoveOlderThan deletes diagnostic entries older than maxAge and returns the number of entries removed.

func (*DiagnosticsStore) Store

func (s *DiagnosticsStore) Store(entry DiagnosticEntry) error

Store persists a diagnostic entry, upserting by LeaseUUID.

type DiagnosticsStoreConfig

type DiagnosticsStoreConfig struct {
	DBPath          string            // Path to bbolt database file
	MaxAge          time.Duration     // Max age before entries are cleaned up (0 = no expiry)
	CleanupInterval time.Duration     // How often to run cleanup (defaults to MaxAge)
	OnCleanupPanic  util.PanicHandler // Optional: invoked on cleanup-loop panic.
}

DiagnosticsStoreConfig configures the diagnostics store.

type Release

type Release struct {
	Version   int       `json:"version"`
	Manifest  []byte    `json:"manifest"`
	Image     string    `json:"image"`
	Status    string    `json:"status"`
	CreatedAt time.Time `json:"created_at"`
	Error     string    `json:"error,omitempty"`
}

Release represents a single release (deployment) of a lease.

type ReleaseStore

type ReleaseStore struct {
	// contains filtered or unexported fields
}

ReleaseStore persists release history in bbolt so it survives backend restarts.

func NewReleaseStore

func NewReleaseStore(cfg ReleaseStoreConfig) (*ReleaseStore, error)

NewReleaseStore opens or creates a bbolt database for release persistence.

func (*ReleaseStore) ActivateLatest

func (s *ReleaseStore) ActivateLatest(leaseUUID string) error

ActivateLatest marks the most recent release as "active" and all previous "active" releases as "superseded" in a single transaction.

func (*ReleaseStore) Append

func (s *ReleaseStore) Append(leaseUUID string, r Release) error

Append adds a new release for a lease, auto-assigning Version.

func (ReleaseStore) Close

func (s ReleaseStore) Close() error

Close shuts down the store gracefully. It is idempotent: the first call closes the database and captures any error; subsequent calls return the same error.

func (*ReleaseStore) Delete

func (s *ReleaseStore) Delete(leaseUUID string) error

Delete removes all releases for a lease. No-op if not found.

func (ReleaseStore) Healthy

func (s ReleaseStore) Healthy() error

Healthy checks that the bbolt database is accessible and the bucket exists.

func (*ReleaseStore) Latest

func (s *ReleaseStore) Latest(leaseUUID string) (*Release, error)

Latest returns the most recent release for a lease. Returns nil, nil when not found.

func (*ReleaseStore) LatestActive

func (s *ReleaseStore) LatestActive(leaseUUID string) (*Release, error)

LatestActive returns the most recent release with "active" status for a lease. Returns nil, nil when no active release is found.

func (*ReleaseStore) List

func (s *ReleaseStore) List(leaseUUID string) ([]Release, error)

List returns all releases for a lease. Returns nil, nil when not found.

func (*ReleaseStore) RecordMigration

func (s *ReleaseStore) RecordMigration(leaseUUID string, manifest []byte) error

RecordMigration appends an "active" release entry for a recover-time migration so the lease's release history captures the wrap-and-rename step. Idempotent: if the most-recent active entry already carries the same wrapped manifest bytes (byte-equal), the call is a no-op. This lets the migration pipeline re-run on the next startup if it was interrupted between rename and recover-loop completion without inflating release history.

Used exclusively by the docker backend's migrate.go (Task 9).

func (*ReleaseStore) RemoveOlderThan

func (s *ReleaseStore) RemoveOlderThan(maxAge time.Duration) (int, error)

RemoveOlderThan prunes release history older than maxAge while preserving each lease's load-bearing records. For every lease it ALWAYS retains its index-latest entry — the entry the runtime mutators append to / activate, and (because Append assigns the next version to the tail) the holder of the lease's maximum version, which keeps Append's max+1 derivation collision-free — AND, when a distinct one exists, the most-recent "active" release that recoverState rehydrates the StackManifest from (see LatestActive). So it protects one entry when the newest entry is itself the active release or no active release exists, two otherwise. Only entries that are BOTH older than the cutoff AND not protected are pruned, so a lease's record is never emptied and its live manifest is never removed (ENG-440). Corrupt or empty values are removed whole-key. Returns the number of release ENTRIES removed (corrupt/empty keys count as one).

func (*ReleaseStore) UpdateLatestStatus

func (s *ReleaseStore) UpdateLatestStatus(leaseUUID, status, errMsg string) error

UpdateLatestStatus updates the status (and optionally error) of the most recent release.

type ReleaseStoreConfig

type ReleaseStoreConfig struct {
	DBPath          string
	MaxAge          time.Duration
	CleanupInterval time.Duration
	OnCleanupPanic  util.PanicHandler // Optional: invoked on cleanup-loop panic.
}

ReleaseStoreConfig configures the release store.

type ResourceAllocation

type ResourceAllocation struct {
	LeaseUUID string
	Tenant    string
	SKU       string
	CPUCores  float64
	MemoryMB  int64
	DiskMB    int64
}

ResourceAllocation tracks resources allocated to a single lease.

type ResourcePool

type ResourcePool struct {
	// contains filtered or unexported fields
}

ResourcePool manages the backend's resource capacity. It provides atomic allocation and release of resources based on SKU profiles.

func NewResourcePool

func NewResourcePool(totalCPU float64, totalMemoryMB, totalDiskMB int64, resolver SKUResolver, tenantQuota *TenantQuotaConfig) *ResourcePool

NewResourcePool creates a new resource pool with the given capacity. Panics if resolver is nil (programming error).

func (*ResourcePool) GetAllocation

func (p *ResourcePool) GetAllocation(leaseUUID string) *ResourceAllocation

GetAllocation returns the allocation for a lease, or nil if not allocated.

func (*ResourcePool) ListAllocations

func (p *ResourcePool) ListAllocations() []ResourceAllocation

ListAllocations returns a copy of all current allocations.

func (*ResourcePool) Release

func (p *ResourcePool) Release(leaseUUID string)

Release returns resources for a lease back to the pool. It is safe to call Release for a lease that has no allocation.

func (*ResourcePool) Reset

func (p *ResourcePool) Reset(allocations []ResourceAllocation)

Reset clears all allocations and rebuilds from a list of allocations.

func (*ResourcePool) SetRetainedDisk added in v0.5.0

func (p *ResourcePool) SetRetainedDisk(mb int64)

SetRetainedDisk records the aggregate disk (MB) reserved by retained (soft-deleted) volumes. The owner derives this from the retention store and pushes it here; TryAllocate subtracts it from available disk so retained volumes keep counting against the pool until reaped. Idempotent.

func (*ResourcePool) Stats

func (p *ResourcePool) Stats() ResourceStats

Stats returns current resource usage statistics.

func (*ResourcePool) TenantStats

func (p *ResourcePool) TenantStats(tenant string) ResourceStats

TenantStats returns resource usage statistics for a specific tenant. RetainedDiskMB is intentionally left 0: retained disk is a provider-level term (not attributed per tenant), so AvailableDiskMB() on a tenant snapshot intentionally excludes it.

func (*ResourcePool) TryAllocate

func (p *ResourcePool) TryAllocate(leaseUUID, sku, tenant string) error

TryAllocate attempts to reserve resources for a new provision (gates all of CPU, memory, disk, and tenant quota).

func (*ResourcePool) TryAllocateAdopt added in v0.5.0

func (p *ResourcePool) TryAllocateAdopt(leaseUUID, sku, tenant string) error

TryAllocateAdopt reserves resources for a lease being RESTORED by adopting an existing retained volume (rename, not new disk). It gates CPU, memory, and tenant quota normally but SKIPS the global disk capacity check: the adopted bytes are already committed on disk and counted in the retained projection, so re-gating them would double-count the lease's own footprint and spuriously deny a restore that physically fits. DiskMB is still added to allocatedDisk for correct live accounting; the retained projection drops it when the record flips to restoring.

type ResourceStats

type ResourceStats struct {
	TotalCPU          float64
	TotalMemoryMB     int64
	TotalDiskMB       int64
	AllocatedCPU      float64
	AllocatedMemoryMB int64
	AllocatedDiskMB   int64
	RetainedDiskMB    int64
	AllocationCount   int
}

ResourceStats contains resource usage statistics.

func (ResourceStats) AvailableCPU

func (s ResourceStats) AvailableCPU() float64

AvailableCPU returns available CPU cores.

func (ResourceStats) AvailableDiskMB

func (s ResourceStats) AvailableDiskMB() int64

AvailableDiskMB returns disk available for new allocations: total minus live allocations minus retained (soft-deleted) reservations, clamped to >= 0 (a total_disk_mb shrink or stale retained projection must not surface a negative "available" via the /stats endpoints).

func (ResourceStats) AvailableMemoryMB

func (s ResourceStats) AvailableMemoryMB() int64

AvailableMemoryMB returns available memory in MB.

type RetentionEntry added in v0.5.0

type RetentionEntry struct {
	OriginalLeaseUUID   string                  `json:"original_lease_uuid"`
	Tenant              string                  `json:"tenant"`
	ProviderUUID        string                  `json:"provider_uuid"`
	Items               []backend.LeaseItem     `json:"items"`
	StackManifest       *manifest.StackManifest `json:"stack_manifest"`
	CallbackURL         string                  `json:"callback_url"`
	RetainedVolumeNames []string                `json:"retained_volume_names"`
	Status              string                  `json:"status"`
	NewLeaseUUID        string                  `json:"new_lease_uuid,omitempty"`
	Generation          int                     `json:"generation"`
	CreatedAt           time.Time               `json:"created_at"`
	RestoringSince      time.Time               `json:"restoring_since,omitempty"`
	ReapingSince        time.Time               `json:"reaping_since,omitempty"`
}

RetentionEntry records the data needed to restore a soft-deleted lease.

type RetentionStore added in v0.5.0

type RetentionStore struct {
	// contains filtered or unexported fields
}

RetentionStore persists soft-deleted lease data in bbolt. The `retention` bucket is the single source of truth; byTenant/byStatus are a DERIVED in-memory index rebuilt from the bucket on open (never persisted, cannot drift across a restart). The bucket is INDEX-COUPLED: it may be mutated ONLY through this type's wrapped methods (each maintains the index under s.mu). Never wire boltStore.startCleanup / removeOlderThan to this store — they cursor.Delete directly on the bucket and would bypass the index.

func NewRetentionStore added in v0.5.0

func NewRetentionStore(cfg RetentionStoreConfig) (*RetentionStore, error)

NewRetentionStore opens or creates a bbolt database for retention persistence. No background cleanup loop is started; the docker backend drives reaping and eviction explicitly via the MarkReaping* / ListReaping / ListExpired methods (reapExpiredRetentions, evictRetentionsToCap, and the retryReapingRecords sweep in restore.go), plus PutReaping for deprovision give-up tombstones.

func (*RetentionStore) ClaimForRestore added in v0.5.0

func (s *RetentionStore) ClaimForRestore(orig, newLease string, maxAge time.Duration) (*RetentionEntry, error)

ClaimForRestore atomically transitions an ACTIVE, non-expired record to restoring. Returns ErrNoRetention when absent or expired, ErrNotRestorable when not in active state.

func (RetentionStore) Close added in v0.5.0

func (s RetentionStore) Close() error

Close shuts down the store gracefully. It is idempotent: the first call closes the database and captures any error; subsequent calls return the same error.

func (*RetentionStore) Delete added in v0.5.0

func (s *RetentionStore) Delete(orig string) error

Delete removes a RetentionEntry by original lease UUID. It is idempotent: no error is returned when the entry is absent. It reads the pre-image in-txn so the index can drop the deleted record's partition membership.

func (*RetentionStore) DeleteIfActive added in v0.5.0

func (s *RetentionStore) DeleteIfActive(orig string) ([]string, bool, error)

DeleteIfActive atomically removes a record ONLY if it is still ACTIVE. Returns (names, deleted, err); deleted=false (nil names) when absent or not active (e.g. concurrently claimed for restore). Used by reconcileOrphanedRetentions (ENG-370) to prune an orphaned active record whose backing volumes have already vanished out-of-band — the ACTIVE-only CAS guarantees a concurrent restore (active→restoring) is never clobbered. The returned names are unused there: the volumes are already gone, so there is nothing to destroy.

func (*RetentionStore) Get added in v0.5.0

func (s *RetentionStore) Get(orig string) (*RetentionEntry, error)

Get retrieves a RetentionEntry by original lease UUID. Returns nil, nil when absent.

func (RetentionStore) Healthy added in v0.5.0

func (s RetentionStore) Healthy() error

Healthy checks that the bbolt database is accessible and the bucket exists.

func (*RetentionStore) Keys added in v0.5.0

func (s *RetentionStore) Keys() ([]string, error)

Keys returns every retained lease UUID (the bbolt key) without unmarshalling the heavy record value (skips the dominant per-record json.Unmarshal). string(k) copies, so no cursor bytes escape the transaction.

func (*RetentionStore) KeysPage added in v0.7.0

func (s *RetentionStore) KeysPage(after string, limit int) (keys []string, next string, err error)

KeysPage returns one keyset page of retained lease UUIDs (the bbolt keys) in ascending key order, containing the keys strictly greater than `after`. It uses a bbolt cursor Seek so the read is O(limit), not a full-bucket scan, and (like Keys) skips the heavy per-record value unmarshal.

  • limit <= 0 -> returns ALL keys, next "" — the unpaginated passthrough. The cursor is ignored in this mode, matching PaginateRetentions/keysetPage.
  • limit > 0 -> returns up to limit keys strictly greater than `after`; next is the last returned key iff a full page was returned AND more keys remain, otherwise "".

The returned slice is always non-nil so callers serialize it as [] not null. Precondition: keys are canonical lease UUIDs — bbolt stores keys byte-sorted, which matches the client's keyset cursor order (canonical-lowercase UUID).

func (*RetentionStore) List added in v0.5.0

func (s *RetentionStore) List() ([]RetentionEntry, error)

List returns all RetentionEntry records in the store.

func (*RetentionStore) ListByTenant added in v0.5.0

func (s *RetentionStore) ListByTenant(tenant string) ([]RetentionEntry, error)

ListByTenant returns all entries for the given tenant (eventually-consistent; callers must re-validate via CAS before mutating — see type doc). Served from the index.

func (*RetentionStore) ListExpired added in v0.5.0

func (s *RetentionStore) ListExpired(maxAge time.Duration) ([]RetentionEntry, error)

ListExpired returns active entries whose CreatedAt is older than maxAge.

func (*RetentionStore) ListReaping added in v0.5.0

func (s *RetentionStore) ListReaping() ([]RetentionEntry, error)

ListReaping returns all entries currently in the reaping (pending-destroy) state.

func (*RetentionStore) ListRestoring added in v0.5.0

func (s *RetentionStore) ListRestoring() ([]RetentionEntry, error)

ListRestoring returns all entries currently restoring (eventually-consistent). Served from the index.

func (*RetentionStore) MarkReapingIfActive added in v0.5.0

func (s *RetentionStore) MarkReapingIfActive(orig string) ([]string, bool, error)

MarkReapingIfActive atomically transitions an ACTIVE record to reaping and returns its volume names for the caller to destroy AFTER the txn commits. ok=false (nil names) when absent or not active (e.g. concurrently claimed for restore). The record is NOT deleted — it is the finalizer tombstone that keeps the footprint counted until the volumes are confirmed gone. (ENG-376)

func (*RetentionStore) MarkReapingIfExpired added in v0.5.0

func (s *RetentionStore) MarkReapingIfExpired(orig string, maxAge time.Duration) ([]string, bool, error)

MarkReapingIfExpired atomically transitions an ACTIVE, expired record to reaping and returns its volume names for the caller to destroy AFTER the txn commits. The record is NOT deleted — it stays a counted tombstone until the volumes are confirmed gone. Returns ok=false when absent, not active, or not yet expired, and a no-op when maxAge<=0. (ENG-376)

func (*RetentionStore) Put added in v0.5.0

Put persists a RetentionEntry, upserting by OriginalLeaseUUID. It reads the pre-image in-txn so the index can drop the stale partition membership of any record being overwritten (a status/tenant change must not leave a phantom).

func (*RetentionStore) PutActiveMerged added in v0.5.0

func (s *RetentionStore) PutActiveMerged(base RetentionEntry) (bool, error)

PutActiveMerged atomically upserts the soft-delete record for a closing lease, merging mergeVolumes into any existing record's RetainedVolumeNames. Single txn, so it is safe against a concurrent ClaimForRestore (no Get→Put TOCTOU):

  • absent: writes `base` fresh (caller sets CreatedAt=now, Generation=0, Status=active).
  • existing ACTIVE: PRESERVES the stored CreatedAt and Generation, writes the UNION of stored RetainedVolumeNames and base.RetainedVolumeNames (dedup), and KEEPS the stored StackManifest when base's is nil (a close retry must never clobber a restorable manifest with a nil one); other fields come from `base`.
  • existing NON-active (restoring): writes NOTHING, returns ok=false — a restore owns the record; a blind write would corrupt the CAS. Caller defers (keeps lease Failed).

Returns (ok bool, err error): ok=false + nil err means "deferred, record is restoring".

func (*RetentionStore) PutReaping added in v0.5.0

func (s *RetentionStore) PutReaping(base RetentionEntry) (bool, error)

PutReaping writes a reaping tombstone for an ABANDONED on-disk footprint (a deprovision give-up). It is idempotent and never clobbers a still-counted record:

  • absent: writes a fresh reaping record (stamps ReapingSince=now).
  • existing reaping: unions RetainedVolumeNames and PRESERVES ReapingSince (aging).
  • existing active/restoring: writes NOTHING, returns ok=false — that record already counts the footprint (or owns it for restore); a blind reaping write would corrupt accounting/CAS. Caller treats ok=false as "already tracked".

Single txn, so it is safe against a concurrent ClaimForRestore. (ENG-376)

func (*RetentionStore) ReIndex added in v0.5.0

func (s *RetentionStore) ReIndex() error

ReIndex rebuilds the in-memory index from the primary bucket and swaps it in. Safe on a live store: it holds s.mu across the WHOLE scan+swap, which serializes it with mutators (each holds s.mu across its {db.Update + indexApply}). Holding the lock only for the swap would be unsafe — a mutator could commit a write and update the live index between the scan returning and the swap, and the swap would then overwrite that write with a pre-scan snapshot (lost update → drift until the next rebuild). fireReindex runs outside the lock. The self-heal/recovery seam (the index is never the source of truth).

func (*RetentionStore) RevertToActive added in v0.5.0

func (s *RetentionStore) RevertToActive(orig string, expectGen int) (bool, error)

RevertToActive transitions a restoring record back to active, using a compare-and-swap on Generation. Returns (true, nil) on success, (false, nil) when the record is absent, not in restoring state, or the generation does not match. On success the Generation is bumped and NewLeaseUUID/RestoringSince are cleared.

type RetentionStoreConfig added in v0.5.0

type RetentionStoreConfig struct {
	DBPath string
	// OnReindex (nil-safe) fires after each index build with record count, duration, and
	// trigger ("open"|"manual"). A callback (not a metrics import) so this package stays
	// free of internal/metrics — mirrors boltStore.startCleanup's onPanic seam.
	OnReindex func(count int, dur time.Duration, trigger string)
}

RetentionStoreConfig configures the retention store.

type SKUProfile

type SKUProfile struct {
	CPUCores float64 `yaml:"cpu_cores"`
	MemoryMB int64   `yaml:"memory_mb"`
	DiskMB   int64   `yaml:"disk_mb"`
}

SKUProfile defines resource limits for a SKU.

func (SKUProfile) Validate

func (p SKUProfile) Validate() error

Validate checks that the profile's resource values are valid.

type SKUResolver

type SKUResolver func(sku string) (SKUProfile, error)

SKUResolver resolves a SKU identifier to its resource profile. This abstracts the SKU mapping logic so it can be shared between components.

type TenantQuotaConfig

type TenantQuotaConfig struct {
	MaxCPUCores float64 `yaml:"max_cpu_cores"`
	MaxMemoryMB int64   `yaml:"max_memory_mb"`
	MaxDiskMB   int64   `yaml:"max_disk_mb"`
}

TenantQuotaConfig configures per-tenant resource limits. When set, each tenant's aggregate resource usage is capped.

func (TenantQuotaConfig) Validate

func (q TenantQuotaConfig) Validate() error

Validate checks that all quota values are positive.

Directories

Path Synopsis
Package leasesm owns the per-lease state machine and actor, plus the substrate-agnostic seams they consume.
Package leasesm owns the per-lease state machine and actor, plus the substrate-agnostic seams they consume.
Package manifest defines the tenant-facing container manifest schema (StackManifest, holding a map of named per-service Manifest entries), JSON parsing, and validation.
Package manifest defines the tenant-facing container manifest schema (StackManifest, holding a map of named per-service Manifest entries), JSON parsing, and validation.
Package workbarrier provides a reference-counted barrier whose zero signal is exposed as a real channel, so callers can wait-with-timeout using a plain select without spawning helper goroutines that would leak when the count never drops to zero.
Package workbarrier provides a reference-counted barrier whose zero signal is exposed as a real channel, so callers can wait-with-timeout using a plain select without spawning helper goroutines that would leak when the count never drops to zero.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL