metadata

package
v1.1.2 Latest Latest
Warning

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

Go to latest
Published: Aug 28, 2026 License: MIT Imports: 3 Imported by: 0

Documentation

Overview

Package metadata is the persistence seam for file records. The Repository interface is the only contract above this layer; the pgx implementation (Phase 1) is the sole place that issues SQL.

Index

Constants

View Source
const (
	IdempotencyPending   = "pending"
	IdempotencyCompleted = "completed"
)

Idempotency-key lifecycle states.

Variables

View Source
var ErrNotFound = errors.New("metadata: file not found")

ErrNotFound is returned when no file record matches the query.

Functions

This section is empty.

Types

type File

type File struct {
	ID             string
	StorageKey     string
	Filename       string
	ContentType    string
	SizeBytes      int64
	ChecksumSHA256 string         // hex sha-256
	Metadata       map[string]any // JSONB tags
	CreatedAt      time.Time
	UpdatedAt      *time.Time // nil until the first metadata update — a pristine, unmodified file has none
	DeletedAt      *time.Time // soft delete; nil while live
}

File is the metadata record for a stored object. It mirrors the files table.

type IdempotencyRecord

type IdempotencyRecord struct {
	Status string  // IdempotencyPending | IdempotencyCompleted
	FileID *string // the produced file id; nil while pending
}

IdempotencyRecord is the stored state of an upload idempotency key.

type ListFilter

type ListFilter struct {
	ContentType string
	Tags        map[string]any // matched against the JSONB metadata column
	Limit       int
	Cursor      string // exclusive upper-bound file id; empty means first page
}

ListFilter narrows a List query. Zero values mean "no constraint".

Pagination is keyset-based: Cursor is the id of the last file of the previous page (exclusive), and a page holds the next Limit files older than it. Ids are UUIDv7 — time-ordered — so paging by id descending walks newest-first without the deep-page cost of OFFSET.

type Repository

type Repository interface {
	// Create inserts a new record. The caller supplies the ID and StorageKey.
	Create(ctx context.Context, f *File) error

	// Get returns the record by ID, or ErrNotFound.
	Get(ctx context.Context, id string) (*File, error)

	// List returns records matching the filter, newest first.
	List(ctx context.Context, filter ListFilter) ([]*File, error)

	// UpdateMetadata writes tags into the record's JSONB metadata: merged into
	// the existing object when merge is true, replacing it wholesale otherwise.
	UpdateMetadata(ctx context.Context, id string, tags map[string]any, merge bool) (*File, error)

	// Delete soft-deletes the record by ID (sets deleted_at). The object is
	// purged asynchronously by GC. Returns ErrNotFound if no live row matched.
	Delete(ctx context.Context, id string) error

	// ListDeleted returns up to limit soft-deleted records, oldest first, so GC
	// can purge their objects and then Purge the rows.
	ListDeleted(ctx context.Context, limit int) ([]*File, error)

	// Purge hard-deletes a soft-deleted record by ID, after its object is gone.
	Purge(ctx context.Context, id string) error

	// StorageKeys returns the set of every storage key referenced by the table
	// (any state), so GC can tell which stored objects are orphans.
	StorageKeys(ctx context.Context) (map[string]struct{}, error)

	// ClaimIdempotencyKey atomically claims key as pending. It returns
	// claimed=true if this caller now owns the key; otherwise claimed=false and
	// existing describes the row already present (which may be nil if it raced
	// away). Implemented as INSERT ... ON CONFLICT DO NOTHING.
	ClaimIdempotencyKey(ctx context.Context, key string) (claimed bool, existing *IdempotencyRecord, err error)

	// CreateForKey inserts the file row and marks the (already-claimed) key
	// completed in one transaction, so the two can never disagree after a crash.
	CreateForKey(ctx context.Context, f *File, key string) error

	// ReleaseIdempotencyKey deletes a still-pending claim so a failed upload can
	// be retried under the same key.
	ReleaseIdempotencyKey(ctx context.Context, key string) error

	// PurgeIdempotencyKeys deletes keys created before the cutoff (TTL), returning
	// how many were removed. Called by GC.
	PurgeIdempotencyKeys(ctx context.Context, before time.Time) (int, error)
}

Repository persists File records. Implementations own all SQL.

Directories

Path Synopsis
Package postgres is the pgx implementation of metadata.Repository.
Package postgres is the pgx implementation of metadata.Repository.

Jump to

Keyboard shortcuts

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