effect-tea-persistent
    Preparing search index...

    Interface Handle<T, C>

    What a caller does with one program's entities, deployed with the consumers C.

    interface Handle<T extends Types, C = {}> {
        ack: <Name extends string>(
            topic: Name,
            consumer: ConsumerOf<C, Name>,
            through: readonly Readonly<{ partition: number; seq: number }>[],
        ) => Effect<void>;
        consume: <Name extends string, E, R>(
            topic: Name,
            consumer: ConsumerOf<C, Name>,
            handler: (
                page: readonly Readonly<
                    {
                        id: string;
                        kind: "Inbound"
                        | "Internal"
                        | "Creation";
                        message: T["inbound"] | T["internal"] | null;
                        partition: number;
                        seq: number;
                        topic: Name;
                        value: Value<T, Name>;
                    },
                >[],
            ) => Effect<void, E, R>,
            options: Readonly<
                { limit: number; partitions?: ReadonlyArray<number>; wait?: true },
            >,
        ) => Effect<void, SqlError | E, R>;
        consumers: Effect<
            readonly Readonly<
                {
                    behind: number;
                    consumer: string;
                    cursors: readonly Readonly<{ partition: number; seq: number }>[];
                    declared: boolean;
                    since: Utc | null;
                    topic: string;
                },
            >[],
        >;
        count: (counted: Counted<T>) => Effect<number>;
        countBy: (
            counted: CountedBy<T>,
        ) => Effect<readonly Readonly<{ count: number; value: Scalar }>[]>;
        create: (
            id: string,
            request: Readonly<{ requestId: string } & FlagsOf<T> & SenderOf<T>>,
        ) => Effect<Readonly<{ _tag: "Reused" }> | Creation<T["rejection"]>>;
        discard: (id: string) => Effect<Operated>;
        expire: Effect<number>;
        fetch: <Name extends string>(
            topic: Name,
            consumer: ConsumerOf<C, Name>,
            limit: number,
            options?: Readonly<
                {
                    partitions?: readonly number[];
                    wait?: Readonly<{ millis: number }>;
                },
            >,
        ) => Effect<
            readonly Readonly<
                {
                    id: string;
                    kind: "Inbound"
                    | "Internal"
                    | "Creation";
                    message: T["inbound"] | T["internal"] | null;
                    partition: number;
                    seq: number;
                    topic: Name;
                    value: Value<T, Name>;
                },
            >[],
        >;
        forget: (topic: Topic<T>, consumer: string) => Effect<void>;
        inspect: (
            id: string,
        ) => Effect<
            | Readonly<
                {
                    id: string;
                    mailbox: readonly (
                        | Readonly<
                            {
                                _tag: "Inbound";
                                message: T["inbound"];
                                request: Request<(...)[(...)]>;
                            },
                        >
                        | Readonly<
                            {
                                _tag: "Internal";
                                incarnation: number;
                                key: Key;
                                message: T["internal"];
                            },
                        >
                    )[];
                    state: T["state"];
                    suspension: Suspended
                    | null;
                    work: readonly Readonly<
                        {
                            incarnation: number;
                            key: Key;
                            work: Piece<T["operation"], T["internal"]>;
                        },
                    >[];
                },
            >
            | null,
        >;
        partitions: number;
        prune: Effect<number>;
        published: <Name extends string>(
            topic: Name,
            after: readonly Readonly<{ partition: number; seq: number }>[],
            limit: number,
        ) => Effect<
            readonly Readonly<
                {
                    id: string;
                    kind: "Inbound"
                    | "Internal"
                    | "Creation";
                    message: T["inbound"] | T["internal"] | null;
                    partition: number;
                    seq: number;
                    topic: Name;
                    value: Value<T, Name>;
                },
            >[],
        >;
        query: (
            query: Query<T>,
        ) => Effect<
            readonly Readonly<
                {
                    id: string;
                    indexes: Readonly<Record<T["index"], Scalar>>;
                    projection: T["projection"];
                },
            >[],
        >;
        read: (id: string) => Effect<T["projection"] | null>;
        reindex: Effect<
            Readonly<
                {
                    failed: readonly Readonly<{ cause: string; id: string }>[];
                    written: number;
                },
            >,
        >;
        resume: (id: string) => Effect<Operated>;
        send: (
            id: string,
            message: T["inbound"],
            request: Readonly<{ keep: Keep; requestId: string } & SenderOf<T>>,
        ) => Effect<Answer<T["rejection"]>>;
        sum: (summed: Summed<T>) => Effect<number>;
        suspended: Effect<readonly Suspended[]>;
    }

    Type Parameters

    Index
    ack: <Name extends string>(
        topic: Name,
        consumer: ConsumerOf<C, Name>,
        through: readonly Readonly<{ partition: number; seq: number }>[],
    ) => Effect<void>

    Moves a consumer's cursor in each partition through names, and no other. The rows fetch gave are such a list. A cursor never moves back, since what is behind it may be gone, and never past the last seq written in its partition, so that no row written later is behind it (decisions 0039, 0137). The consumer is one the deploy declares for the topic (decision 0152). A topic is one the program publishes, and a partition is an integer from 1 to the number of partitions of the log. A seq and a limit are non-negative integers, here and in query, published and fetch, and a limit left out is no limit at all but the caller's defect, as is anything else (decisions 0053, 0138, 0141). An ack joins a transaction the caller left open on the client the runtime was launched with, so a store the caller writes in it commits with the cursors. Within it the caller waits for nothing a step does, since a step's commit waits for the transaction's connection (decision 0150).

    consume: <Name extends string, E, R>(
        topic: Name,
        consumer: ConsumerOf<C, Name>,
        handler: (
            page: readonly Readonly<
                {
                    id: string;
                    kind: "Inbound"
                    | "Internal"
                    | "Creation";
                    message: T["inbound"] | T["internal"] | null;
                    partition: number;
                    seq: number;
                    topic: Name;
                    value: Value<T, Name>;
                },
            >[],
        ) => Effect<void, E, R>,
        options: Readonly<
            { limit: number; partitions?: ReadonlyArray<number>; wait?: true },
        >,
    ) => Effect<void, SqlError | E, R>

    Runs a reader of the consumer consumer of topic: it reads a page of at most limit rows with fetch, runs handler on it and acks it in one transaction of the client the runtime was launched with, and reads the next. So a store that handler writes on that client commits with the cursors, and a page whose transaction fails is read again. handler is given no empty page. Without wait, the reader ends at the first page that is empty, or once the runtime stops, with the rows it did not read left for the next reader: what a test or a batch runs. Given wait: true, a reading that finds no row waits for a commit in the topic, and the reader ends only when the runtime stops: what a deployment runs. Given partitions, it reads those only, as fetch does. A failure of handler or of the transaction rolls the page back and fails the reader, whose retries are the caller's. The limit is a positive integer. A reader run within a transaction of the client would hold its connection while it waits, so it is the caller's defect, as is anything fetch refuses (decision 0184). handler runs within the page's transaction, so a call there of create, send, resume, discard, deploy or a fetch that waits is the caller's defect, and fails the reader. A handler that must create or send stores the request, and a fiber outside the transaction makes the call (decision 0256).

    consumers: Effect<
        readonly Readonly<
            {
                behind: number;
                consumer: string;
                cursors: readonly Readonly<{ partition: number; seq: number }>[];
                declared: boolean;
                since: Utc | null;
                topic: string;
            },
        >[],
    >

    Each consumer stored, with its topic, whether the deploy declares it, how many rows are past its cursors, and its cursors in the partitions it has rows to read in: what keeping it costs the log (decision 0152). since is the time of the step that wrote the oldest of the rows it reads next, from which a caller reads its age (decision 0224).

    count: (counted: Counted<T>) => Effect<number>

    How many entities of the program have indexes that match where, compared as a query compares them, and every entity where there is no criterion. It is one statement, which reads the rows as one transaction sees them and reads no projection. A row an older version wrote is counted by the indexes it holds, until a step or reindex writes it again (decision 0148).

    countBy: (
        counted: CountedBy<T>,
    ) => Effect<readonly Readonly<{ count: number; value: Scalar }>[]>

    For each value index holds among the entities whose indexes match where, as count finds them, the value and how many entities hold it, in one statement that reads no projection. A value no entity holds has no entry. The values come in the order a query orders the index by, null first, at most limit of them: a caller that must know whether they ended asks for one more. true and 1 are two values, and an index a row lacks is null, as a query reads it. A limit that is no non-negative integer is the caller's defect (decision 0150).

    create: (
        id: string,
        request: Readonly<{ requestId: string } & FlagsOf<T> & SenderOf<T>>,
    ) => Effect<Readonly<{ _tag: "Reused" }> | Creation<T["rejection"]>>

    Creates the entity id with the request's flags, left out where the program's flags are null (decision 0245). A retry of the request that created it is answered Created (decision 0017), and a request under the same sender and request id with other flags is answered Reused, and changes nothing (decision 0269). Flags the program's init rejects are answered Rejected, and nothing is stored: a retry runs init again (decision 0182). A request id that is no text a database holds is the caller's defect (decision 0204). A call within a transaction of the client of the runtime is the caller's defect too, since the step it waits for commits on a connection of its own (decision 0256).

    discard: (id: string) => Effect<Operated>

    An operator discards the inbound message a suspended entity died on (decision 0014). An internal one is not discarded, and the entity stays suspended (decisions 0041, 0044). It gives Discarded, NotDiscarded, StillSuspended, NotSuspended or NotFound (decision 0114). A call within a transaction of the client of the runtime is the caller's defect, as for create (decision 0256).

    expire: Effect<number>

    Deletes the callers' replies of the program past their time, and the replies and the answers kept for another entity's work that has ended, and gives how many went. Neither is ever read again, so this frees only their rows: the replies of an entity that answers no message, since a step that stores a reply deletes its own entity's (decision 0125), and the rows two steps at once on a pool leave (decision 0289). The replies go in batches, each a transaction of its own, so the count is the sum of the batches, and an expire that fails keeps the batches it committed before (decision 0245).

    fetch: <Name extends string>(
        topic: Name,
        consumer: ConsumerOf<C, Name>,
        limit: number,
        options?: Readonly<
            { partitions?: readonly number[]; wait?: Readonly<{ millis: number }> },
        >,
    ) => Effect<
        readonly Readonly<
            {
                id: string;
                kind: "Inbound"
                | "Internal"
                | "Creation";
                message: T["inbound"] | T["internal"] | null;
                partition: number;
                seq: number;
                topic: Name;
                value: Value<T, Name>;
            },
        >[],
    >

    The same from the stored cursors of the consumer consumer of topic: a consumer is a name within its topic, declared where the program is deployed, and ack moves its cursors once the rows are acted on. From the deploy that first declares it, its topic of the log is kept for it, so a consumer holds no state of its own, and a reading writes nothing. A name the deploy does not declare for the topic is the caller's defect (decisions 0039, 0138, 0152). Given wait, a reading that finds no row waits until a step commits a row in topic, and reads again, for at most wait.millis from the call: it gives the first page with a row, or none once the time is over or the runtime stops. A limit of 0 gives none at once. wait.millis is a non-negative integer, and anything else is the caller's defect (decision 0142). A fetch that waits, within a transaction of the client of the runtime, is the caller's defect, as create is, and one that does not wait reads within the transaction (decision 0256). Given partitions, a reading reads those partitions only, so that the readers of one name divide its partitions among them. The division is the caller's: the runtime holds no reader, and two readers that name one partition are given the same rows. A list that is empty, or holds what is no partition of the log, is the caller's defect (decision 0153).

    forget: (topic: Topic<T>, consumer: string) => Effect<void>

    Drops a consumer of topic and its cursors, so that the next prune passes what it never read: how a consumer the deploy no longer declares stops holding the log. A consumer the deploy declares is not forgotten, which is the caller's defect: a deploy without it lets it go (decisions 0039, 0152).

    inspect: (
        id: string,
    ) => Effect<
        | Readonly<
            {
                id: string;
                mailbox: readonly (
                    | Readonly<
                        {
                            _tag: "Inbound";
                            message: T["inbound"];
                            request: Request<(...)[(...)]>;
                        },
                    >
                    | Readonly<
                        {
                            _tag: "Internal";
                            incarnation: number;
                            key: Key;
                            message: T["internal"];
                        },
                    >
                )[];
                state: T["state"];
                suspension: Suspended
                | null;
                work: readonly Readonly<
                    {
                        incarnation: number;
                        key: Key;
                        work: Piece<T["operation"], T["internal"]>;
                    },
                >[];
            },
        >
        | null,
    >

    What is stored of the entity id, or null when there is none: its state, its mailbox, its work under way and its suspension, as suspended lists it. An operator reads it to decide between resume and discard. It is read from what is stored, so an entity this process does not hold is inspected too, and nothing is activated. The rows are read from one snapshot, so a step that commits meanwhile is read whole or not at all. Within a transaction the caller left open, the rows are read at the caller's level, and on PostgreSQL at READ COMMITTED each statement reads a snapshot of its own. A row that no migration reads is a defect here, as it is at the entity's activation (decision 0185).

    partitions: number

    How many partitions the log has, as the first launch of its database stored it: what the readers of one consumer divide among them (decisions 0137, 0153).

    prune: Effect<number>

    Drops what every consumer of a topic has read, in each partition through the lowest cursor there, and gives how many rows went. A consumer that has acked nothing of a partition holds the whole partition of its topic. A topic no consumer is stored for is dropped whole, since no reader that keeps cursors needs its rows: a reading with published has no say in a prune (decisions 0039, 0137, 0152). The rows go in batches, each a transaction of its own, so the count is the sum of the batches, and a prune that fails keeps the batches it committed before (decision 0245).

    published: <Name extends string>(
        topic: Name,
        after: readonly Readonly<{ partition: number; seq: number }>[],
        limit: number,
    ) => Effect<
        readonly Readonly<
            {
                id: string;
                kind: "Inbound"
                | "Internal"
                | "Creation";
                message: T["inbound"] | T["internal"] | null;
                partition: number;
                seq: number;
                topic: Name;
                value: Value<T, Name>;
            },
        >[],
    >

    What the entities of the program published in topic, past the cursors of after, at most limit rows, the oldest first. Each row is the message an entity handled and the value its topic selects of what callers read of it afterwards, written in that step's transaction. A topic selects a part of the projection, and a step is written in the topics whose selection it changed (decisions 0038, 0138, 0140). A program that declares no topics publishes its projection as the topic projection, at each step that changes it. The rows of one entity come in the order its steps committed, and each carries its partition and its seq within it. The rows of one step share its seq, so a topic's seqs have gaps. The log promises no order across entities: a row of one entity written before a row of another may come after it (decision 0137). A partition with no cursor in after is read from 0, so [] is the whole topic. The cursors are the caller's, and nothing of them is stored: a reading here, by a test or an operator looking, has no say in a prune. A consumer that keeps its cursors reads with fetch instead (decision 0039).

    query: (
        query: Query<T>,
    ) => Effect<
        readonly Readonly<
            {
                id: string;
                indexes: Readonly<Record<T["index"], Scalar>>;
                projection: T["projection"];
            },
        >[],
    >

    What callers read of the entities whose indexes match, across the program, at most query.limit of them: the read model is written in each entity's own transaction, so nothing found here is a projection its entity has moved past (decision 0032). The limit is required, so that no reading holds the whole read model in one array (decision 0141). Given after, the last row of a page, the query gives the rows past it in its order, by the index ordered by and then by the id; each row carries the indexes it stores for that. Nothing is held between two pages, so a row no step changes is given once, and a row whose index changes between them is given where it stands when the next page is read (decision 0143). A descending order is the ascending one reversed, the ids of the rows that tie included, and a page reads only the rows it gives (decision 0145).

    read: (id: string) => Effect<T["projection"] | null>

    What callers read of the entity, or null when there is none: the row written by its last event that changed its state, which a query reads too. project is not called, so a new project that dies on the stored state is no reader's defect. A new project reaches the row at the entity's next step that changes it, or at reindex. The row is read through the projection's migrations and schema, as a query reads it, and a reader is given the defect where they die (decision 0121).

    reindex: Effect<
        Readonly<
            {
                failed: readonly Readonly<{ cause: string; id: string }>[];
                written: number;
            },
        >,
    >

    Writes the read model of every entity of the program again, from its stored state, and gives how many rows it wrote and which entities it passed over. An index a new version of the program added or changed needs it, since a row is written only when its entity takes a step. An operator runs it after such a deploy (decision 0032). An entity that takes a step while it runs keeps the row its step wrote (decision 0070). An entity whose row the program cannot compute, since its state is one no version reads, or a migration, a schema, project or an index dies on it, keeps the row it had, and is listed in failed with the cause. The rest are written all the same. What the runtime throws, a corrupted row's included, fails the whole call (decision 0122).

    resume: (id: string) => Effect<Operated>

    An operator resumes a suspended entity (decision 0006), with the program deployed: a fixed program is a deploy of it, which the resume follows (decision 0064). It gives Resumed, StillSuspended, NotSuspended or NotFound (decision 0114). A call within a transaction of the client of the runtime is the caller's defect, as for create (decision 0256).

    send: (
        id: string,
        message: T["inbound"],
        request: Readonly<{ keep: Keep; requestId: string } & SenderOf<T>>,
    ) => Effect<Answer<T["rejection"]>>

    Sends an inbound message, and waits for the reply. A retry under the same request id gets the reply stored for it. A request under the same sender and request id that carries another message is answered Reused, and nothing of it is applied or stored, since a program whose sender is null shares its request ids among every caller (decision 0269). The boundary gives keep, the time its protocol retries for. The reply is then kept for keep.millis from the step that answers, and a retry after that is a new request, applied again. 'forever' keeps the reply for ever. keep is required, since only the boundary knows the window, and keep.millis is a non-negative integer: anything else is the caller's defect (decisions 0125, 0245). A request id that is no text a database holds is the caller's defect too (decision 0204). An entity that holds as many callers' messages as callers.messages answers Full at once, and nothing of the message is offered or stored: the caller retries later, under the same request id or another (decision 0147). On Cluster a caller is never answered Full, and its message waits for the entity. There, until a step of the entity or expire deletes Cluster's rows of a reply past its time, a retry after keep is given the reply Cluster saved, and a message kept 'forever' keeps Cluster's rows for ever (decisions 0165, 0171). Cluster's rows are kept for at least two of Cluster's polls of replies and a second, whatever the keep, so that another client of the request on another runner reads the answer first (decision 0248). A call within a transaction of the client of the runtime is the caller's defect, as for create (decision 0256).

    sum: (summed: Summed<T>) => Effect<number>

    The total of index over the entities whose indexes match where, as count finds them: a double, 0 over no entity. A row whose index holds null, or no value at all, adds nothing. A row whose index holds a string or a boolean is the caller's defect: the call dies, naming the index and the least id of such a row, and gives no total (decision 0148). So is a sum whose numbers, taken without their sign, may add up past the largest double: each is counted in units of 2^980, cut to an integer, and the sum is refused once those units and the count of numbers reach 2^44, alike on both dialects (decision 0277). PostgreSQL adds exactly and SQLite with a compensated sum of doubles, so the two may differ in the last digits, by at most a rounding of that total without signs (decision 0209).

    suspended: Effect<readonly Suspended[]>

    The suspended entities of the program, the longest suspended first: each with what it died on, the cause, since when, and the operations that wait for its resumption. Read from what is stored, so an entity this process does not hold is listed too, with the operations of every process and of a process before a restart. An operation is listed from the step that answered it Suspended while its run holds, until the entity is taken out of its suspension. The list is read from one snapshot outside a transaction the caller left open, and at the caller's level within one (decisions 0114, 0265).