UNPKG

@pgxsinkit/pgwasm

Version:
119 lines • 6.32 kB
/** * `MountedRepackedVfs` — one {@link RepackedFileSystem} made of several stores, joined at path * prefixes. * * ## Why * * A store is a single durable object: four exclusive handles, one arena, one metadata generation. * That is exactly right for a datadir and exactly wrong for the parts of a datadir that must NOT be * durable — a Postgres temp tablespace, an ephemeral relation, a scratch area whose whole point is * that it never reaches the platform. A mount serves one path prefix from a SECOND store, so the * durable root and the volatile subtree are one tree to the guest and two stores to the host. * * ## The shape * * / -> root store (OPFS: durable) * /pgdata/... -> root store * /scratch -> mount store (memory: nothing durable) * /scratch/anything/... -> mount store, as "/anything/..." * * A mount's prefix is its root: `/scratch/a/b` reaches the mount as `/a/b`, and the prefix itself as * `/`. The prefix directory is also created in the ROOT store, so a `readdir` of its parent lists * it; every operation AT or BELOW the prefix is answered by the mount, so that root-side directory * is a name and nothing more. * * ## What the composite owns, and what it delegates * * - **Symlink resolution is the composite's**, because a link is the one thing that can move a path * from one store to another: `pg_tblspc/<oid>` lives in the root and points at `/scratch`, and * neither store alone can resolve it (the root would resolve it against its own placeholder * directory and find nothing there). The fast path is one `resolvePath` call on the store the * path already routes to — if that store says the path holds no link, no link exists on it at * all, because every entry on the path lives in that store. Only a path that really does traverse * a link pays the component-by-component walk. * - **Descriptors are the composite's.** Each store numbers its own fds from 3, so the composite * hands out its own numbers and remembers which store each belongs to. * - **`rename` across a boundary is `EXDEV`**, exactly as it is between two real filesystems: a * rename is a metadata move inside one store and cannot span two. * - **`strictSync()` skips a mount declared non-durable.** A memory-backed mount has nothing to make * durable, and a store-wide sync that walked it would charge the guest for flushes that cannot * protect anything. Durability is a property the STORAGE OWNER declares per mount; nothing here * infers it from the port. * - Everything else is a route plus a delegation. */ import type { RepackReason, RepackedFileSystem, RepackedStat, RepackedVfsMetrics } from "./repacked-vfs"; /** One store served at one path prefix. */ export interface RepackedMount { /** Absolute, canonical, and never `/`: the path the mount's own root appears at. */ readonly prefix: string; readonly vfs: RepackedFileSystem; /** * Whether a store-wide `strictSync()` reaches this mount. Defaults to `true`. A volatile mount * (memory-backed scratch) declares `false`: there is nothing to flush, and pretending otherwise * charges every guest `fsync` for it. */ readonly durable?: boolean; } export interface MountedRepackedVfsOptions { /** The store that answers for every path no mount claims. */ readonly root: RepackedFileSystem; readonly mounts: readonly RepackedMount[]; /** The clock used to create each mount's placeholder directory in the root. */ readonly nowMs?: () => bigint; } export declare class MountedRepackedVfs implements RepackedFileSystem { #private; constructor(options: MountedRepackedVfsOptions); /** The mounts this composite serves, in declaration order. */ get mounts(): readonly RepackedMount[]; /** The store that answers for everything no mount claims. */ get root(): RepackedFileSystem; /** * The link-free composite path `path` names. * * The fast path asks the routed store to resolve its own view: a store that reports the path * unchanged proves there is no link on it, because every entry on that path lives in that store. * Anything else falls through to the careful walk, which resolves one component at a time and can * therefore cross a boundary in either direction. */ resolvePath(path: string, follow?: boolean): string; /** Flushes the root and every mount the owner declared durable; a volatile mount is skipped. */ strictSync(): void; assertHealthy(): void; fail(cause: unknown): never; /** The ROOT store's metrics. A mount's own are read through {@link MountedRepackedVfs.mounts}. */ metrics(): RepackedVfsMetrics; repack(reason?: RepackReason): void; runScheduledRepack(nowMs?: number): boolean; close(): void; close(fd: number): void; stat(path: string): RepackedStat; lstat(path: string): RepackedStat; symlink(target: string, path: string, nowMs: bigint): void; readlink(path: string): string; readdir(path: string): string[]; mkdir(path: string, options: { recursive?: boolean; mode?: number; nowMs: bigint; }): void; writeFile(path: string, data: string | Uint8Array, options: { encoding?: string; mode?: number; flag?: string; nowMs: bigint; }): void; readFile(path: string): Uint8Array; truncate(path: string, size: bigint, nowMs: bigint): void; chmod(path: string, mode: number, nowMs: bigint): void; utimes(path: string, atimeMs: bigint, mtimeMs: bigint, ctimeMs: bigint): void; unlink(path: string, nowMs: bigint): void; rmdir(path: string, nowMs: bigint): void; /** A rename is one store's metadata move; across a boundary it is `EXDEV`, as on any host. */ rename(oldPath: string, newPath: string, nowMs: bigint): void; open(path: string, flags?: string, mode?: number, nowMs?: bigint): number; fstat(fd: number): RepackedStat; read(fd: number, buffer: Uint8Array, offset: number, length: number, position?: bigint): number; write(fd: number, buffer: Uint8Array, offset: number, length: number, position: bigint | undefined, nowMs: bigint): number; } //# sourceMappingURL=mounted-vfs.d.ts.map