sandboxalloc

package
v0.9.0 Latest Latest
Warning

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

Go to latest
Published: Oct 29, 2026 License: Apache-2.0, MIT Imports: 15 Imported by: 0

Documentation

Overview

Package sandboxalloc decides and records how sessions are placed on remote sandboxes. It is the only writer of session_sandbox_assignments and, once wired, of the session's sandbox pin.

Index

Constants

View Source
const (
	// UserMachineGCLockKey makes one replica scan at a time.
	UserMachineGCLockKey = "weknora:sandbox:user-gc:lock"
	// UserMachineGCInterval is how often the container runs the scan.
	UserMachineGCInterval = time.Hour
	// UserMachineGCCursorKey remembers where a scan stopped, so the next one
	// continues instead of rereading the same oldest candidates.
	UserMachineGCCursorKey = "weknora:sandbox:user-gc:cursor"
)

Variables

View Source
var ErrMachineUnregistered = errors.New("sandboxalloc: shared machine is not registered")

ErrMachineUnregistered is returned when a user-mode assignment names a machine whose registry row is gone (its config was deleted). The session cannot run, and must never be mistaken for one with a machine of its own.

Functions

func FromEntity

FromEntity converts a row back. machine is the registry row its digest names; it is required for a user-mode row and ignored otherwise.

func ToEntity

ToEntity converts an assignment into its row, and for a user-mode one the registry row of its machine (nil otherwise). digest names that machine.

Types

type ConfigReader

type ConfigReader interface {
	GetByID(ctx context.Context, tenantID uint64, id string) (*types.TenantSandboxConfigEntity, error)
}

ConfigReader loads a sandbox config by its owning workspace.

type GCReport

type GCReport struct {
	Scanned, Reclaimed, Busy, Failed int
	CleanupsDone, CleanupsFailed     int
	Skipped                          bool
}

GCReport is one scan's outcome, logged as metrics.

type PlaceRequest

type PlaceRequest struct {
	SessionTenantID uint64
	SessionID       string
	ConfigTenantID  uint64
	ConfigID        string
	Purpose         allocation.Purpose
}

PlaceRequest names the session and the config address the caller resolved as its candidate.

type Service

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

Service is the allocation service.

func New

func New(
	repo interfaces.SandboxAllocationRepository, configs ConfigReader,
	sessions SessionReader, namespace string,
) *Service

New wires the service. namespace must be the Redis binding namespace.

func (*Service) Digest

func (s *Service) Digest(m allocation.UserMachine) string

Digest is the shared-machine digest of m in this deployment.

func (*Service) ForkAssignment

func (s *Service) ForkAssignment(source allocation.Assignment, targetSessionID string) allocation.Assignment

ForkAssignment is the child's assignment: same mode, machine and config, fresh session id. The caller persists it with the forked session.

func (*Service) Lookup

func (s *Service) Lookup(ctx context.Context, sessionID string) (allocation.Assignment, bool, error)

Lookup returns the stored assignment without creating one. It always reads the database: callers use it to verify validity. A session retired before it was ever placed has no assignment.

func (*Service) Namespace

func (s *Service) Namespace() string

Namespace is the deployment namespace machine digests use.

func (*Service) Now

func (s *Service) Now() time.Time

Now is the clock placements and releases use.

func (*Service) Place

Place returns the session's placement, recording it on first use together with the session's pin. The stored row always wins: neither the config switch, the candidate, nor the request identity can move a placed session.

func (*Service) Release

func (s *Service) Release(ctx context.Context, sessionID string) (interfaces.RetiredSession, error)

Release retires one session.

func (*Service) Repo

Repo is the allocation repository.

func (*Service) Retire

func (s *Service) Retire(ctx context.Context, sessionIDs []string) ([]interfaces.RetiredSession, error)

Retire is the first step of deleting sessions (C5): one transaction, under the lock first placement takes, releases each session's assignment and schedules its workspace cleanup. A session without an assignment gets a tombstone, so no placement can land after the deletion began.

func (*Service) SessionAllocation

func (s *Service) SessionAllocation(ctx context.Context, sessionID string) (allocation.Assignment, bool, error)

SessionAllocation implements sandbox.AllocationDirectory.

func (*Service) UserMachinesByConfig

func (s *Service) UserMachinesByConfig(
	ctx context.Context, configTenantID uint64, configID string,
) ([]allocation.UserMachine, error)

UserMachinesByConfig implements sandbox.AllocationDirectory.

func (*Service) VerifySessionAllocation

func (s *Service) VerifySessionAllocation(
	ctx context.Context, sessionTenantID uint64, sessionID string, expected allocation.Assignment,
) error

VerifySessionAllocation implements sandbox.AllocationDirectory (C4, C15, C17). The row must still be live and name the expected address. For a shared machine, the digest recomputed from the session's CURRENT owner must be the stored one — the digest binds the owner, so this is stricter than comparing a snapshot — and the session's current auth source must still qualify. A retired session's row is released (or a tombstone), so the release check covers retirement.

type SessionReader

type SessionReader interface {
	GetByID(ctx context.Context, tenantID uint64, id string) (*types.Session, error)
}

SessionReader loads a session row.

type UserMachineGC

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

UserMachineGC reclaims user machines that have had no live session for their config's retention window, and retries queued directory deletes.

func NewUserMachineGC

func NewUserMachineGC(
	svc *Service, cleaner *WorkspaceCleaner, configs ConfigReader, client redis.UniversalClient,
) *UserMachineGC

NewUserMachineGC wires the GC. A nil redis client runs unlocked, which is only correct for a single process (user mode is never enabled without Redis).

func (*UserMachineGC) RunOnce

func (g *UserMachineGC) RunOnce(ctx context.Context) (GCReport, error)

RunOnce performs one scan unless another replica holds the lock. The lock is renewed for the scan's duration; a lost lock ends the scan.

type UserMachineOps

type UserMachineOps interface {
	DeleteSessionWorkspaces(ctx context.Context, machine allocation.UserMachine, sessionIDs []string) ([]string, error)
	ReclaimUserMachine(ctx context.Context, machine allocation.UserMachine,
		check func(context.Context) (bool, error)) (bool, error)
}

UserMachineOps is the slice of *sandbox.SessionBoundManager the cleaner and the GC drive.

type WorkspaceCleaner

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

WorkspaceCleaner gives a deleted user-mode session's directory back.

func NewWorkspaceCleaner

func NewWorkspaceCleaner(svc *Service, resolver sandbox.TenantSandboxResolver) *WorkspaceCleaner

NewWorkspaceCleaner wires the cleaner. resolver resolves managers without the workspace kill switch: teardown must work while scripts are disabled.

func (*WorkspaceCleaner) DeleteSessions

func (c *WorkspaceCleaner) DeleteSessions(
	ctx context.Context, tenantID uint64, idsByOwner map[string][]string,
) (map[string]bool, int, error)

DeleteSessions atomically removes the authorized session rows and records retirement and cleanup intent. Only committed retirements may trigger cleanup.

func (*WorkspaceCleaner) OwnerMachines

func (c *WorkspaceCleaner) OwnerMachines(
	ctx context.Context, ownerTenantID uint64, ownerID string,
) ([]allocation.UserMachine, error)

OwnerMachines lists ownerID's machines in one tenant (0 = every tenant).

func (*WorkspaceCleaner) ReclaimOwner

func (c *WorkspaceCleaner) ReclaimOwner(ctx context.Context, ownerID string)

ReclaimOwner gives back the deleted account's machines now; any it cannot reclaim yet are left to the GC's retention window.

func (*WorkspaceCleaner) ReclaimOwnerMachines

func (c *WorkspaceCleaner) ReclaimOwnerMachines(ctx context.Context, machines []allocation.UserMachine)

ReclaimOwnerMachines deletes machines whose sessions were all just deleted by "delete all" or a user deletion, without waiting for the retention window. A machine with a live session, a cleared empty clock, or registered work is left to the GC.

func (*WorkspaceCleaner) ReleaseMachine

func (c *WorkspaceCleaner) ReleaseMachine(ctx context.Context, m allocation.UserMachine) (bool, error)

ReleaseMachine deletes m now, when an admin chose to. Registered work and a live session still block it (false, nil): the same locked check ReclaimOwnerMachines uses decides, so a terminal is never cut and there is no second destroy path. A cleared empty_since is a session that joined, not an empty machine. Sessions on a refused machine keep their assignments.

func (*WorkspaceCleaner) ReleaseOwnerAssignments

func (c *WorkspaceCleaner) ReleaseOwnerAssignments(ctx context.Context, ownerID string) error

ReleaseOwnerAssignments retires every session of a deleted account and releases their assignments (C5). It must succeed before the account is deleted: afterwards nothing could release them, and their machines would never become empty.

func (*WorkspaceCleaner) ReleaseSessions

func (c *WorkspaceCleaner) ReleaseSessions(ctx context.Context, sessionIDs []string) (map[string]bool, error)

ReleaseSessions retires every session in sessionIDs in one transaction (C5): placement is closed for all of them, and each user-mode assignment is released with its cleanup task. handled names the sessions on a shared machine; the caller tears the others down as before. An error means nothing was retired; the caller must not delete the sessions.

func (*WorkspaceCleaner) RetryDue

func (c *WorkspaceCleaner) RetryDue(ctx context.Context, limit int) (int, int, error)

RetryDue retries queued directory deletes whose backoff has elapsed.

func (*WorkspaceCleaner) RunCleanups

func (c *WorkspaceCleaner) RunCleanups(ctx context.Context, sessionIDs []string)

RunCleanups runs the due cleanup of these sessions now, one command per machine. Anything not done stays due for RetryDue.

func (*WorkspaceCleaner) UserSessions

func (c *WorkspaceCleaner) UserSessions(ctx context.Context, ids []string) (map[string]bool, error)

UserSessions classifies existing placements without retiring them. Retirement must commit with the session deletion, not with this pre-delete lookup.

Jump to

Keyboard shortcuts

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