Furth FortuneEngineering reference
TECHNICAL DOCUMENTATION

2027 · MerTek technology

On this page

Core system / System overview

MerDB

Typed records, read only along declared paths

MerDB is the database engine behind MTK applications. It stores typed records in troves, and a program reads them by key, through an index the trove file declares, or from an ordered change feed. Every write to a record passes one commit point, and the store checks the declared schema when a program launches.

Worked example

A parts board that stays fast at 40,000 records

The Tulsa depot tracks 40,000 part records across 60 vehicles. The 2nd-shift lead keeps a board open that asks one question every minute: which records changed since 14:00? The reorder desk, a separate service, has to act on every status change, in order, and continue after a restart without skipping one.

  1. The team declares the part trove in its own file. Each index row is an access path the store keeps up to date on every write. bySerial stores a lowercased key, so P-204 and p-204 can never both exist.

    # _db/troves/part.tri
    trove part {
        text<class=ascii> serial = ""
        text unitId = ""
        text status = "ready"
        timepoint<unix> changedAt = now()
        unique index bySerial = t.text.ascii.lower(serial)
        index byChangedAt = changedAt
        index byUnitStatus = [unitId, status]
    }
  2. The board asks its question along byChangedAt. The call names the index, two bounds and a limit, and the store reads only the key range between them. The answer is newest first and never longer than 500 ids.

    u64[] changedSince(timepoint<unix> shiftStart) {
        chest lower = { timepoint<unix> inclusive = shiftStart }
        chest upper = { timepoint<unix> inclusive = t.clock.now() }
        return t.db.range("part", "byChangedAt", lower, upper, true, 500)
    }
    
    text board() {
        timepoint<unix> shiftStart = t.time.unix.add(t.clock.now(), 0s - 8h)
        text lines = ""
        foreach (id in changedSince(shiftStart)) {
            chest row = t.db.get("part", id)
            chest doc = row.document
            text serial = doc.serial
            text status = doc.status
            lines = lines + serial + " " + status + "\n"
        }
        return lines
    }
  3. A technician marks P-204 installed. The update is conditional on the fields it names. If another technician changed status after this one read it, the volume primary refuses the write and the program takes its else arm.

    text install(part p) {
        transaction {
            part.update(p, { status = "installed", changedAt = t.clock.now() })
        } else (f) {
            fault reason = f.reason
            if (reason == fault.store_conflict) {
                return "changed by another technician; reload and retry"
            }
            return "not saved"
        }
        return "installed"
    }
  4. The reorder desk follows the change feed. poll reads a page of events without moving anything, and ack moves the desk’s cursor past what it handled. An event names a record and its version, never its contents, so the desk reads each record under its own read access.

    u64 drainOnce(u64 savedThrough) {
        resource<t.db.subscription> feed = t.db.subscribe("part", savedThrough)
        chest page = t.db.poll("part", feed, 64)
        chest[] changes = page.events
        foreach (change in changes) {
            u64 id = change.id
            u8 deleted = change.deleted
            if (deleted == 0) {
                chest row = t.db.get("part", id)
                chest doc = row.document
                text status = doc.status
                if (status == "installed") {
                    t.log.info("reorder stock for part " + text(id))
                }
            }
        }
        u64 through = page.through
        t.db.ack("part", feed, through)
        t.db.unsubscribe("part", feed)
        return through
    }
  5. At 16:40 a rollout restarts the desk. It subscribes again from the position drainOnce returned last, and the one page it had read but not acknowledged comes back. A volume primary that takes over after a failure keeps every sequence number, so the saved position still lines up. A position the store can no longer serve is refused by name, store_resync_required, and never skipped past.

What happened

The board reads the few hundred records that changed during the shift, at 40,000 records or ten times that. The reorder desk lost no status change across its restart. When a planner asked for every part awaiting inspection depot-wide, the team published a new index row, and no query ever read the whole table.

System diagram

A declared readiness query

The application names the access path before deployment. A query outside that path is refused instead of becoming an unplanned full scan.

  1. Schema asset_id • unit_id • status
  2. Index unit + update time
  3. Query Unit 12 • last 24 h
  4. Result 17 assets • ordered

Inside the system

How the parts work together

Follow the data, decisions, and access boundaries. Each diagram explains a concept used by applications.

Map 01

Three admitted reads

Reads use a key, a declared index, or an ordered stream.

  1. Read request known question
  2. Primary key one record
  3. Declared index bounded set
  4. Stream cursor ordered changes
Map 02

One keyspace, declared kinds

A kind tag separates records without creating a second storage model.

  1. Logical stream declared record kind
  2. Kind discriminator stable tag
  3. Shared keyspace ordered storage
  4. Typed result kind checked
Map 03

Write policy

Several update forms meet at one commit point.

  1. Insert • update typed delta
  2. Delete • merge declared operation
  3. Validation schema + policy
  4. Commit one accepted state
Map 04

Change-feed cursor

A consumer resumes from one ordered position without a silent gap.

  1. Committed write sequence assigned
  2. Feed ordered event
  3. Consumer cursor last accepted sequence
  4. Resume next event only
Map 05

Schema and open

The durable declaration controls how stored data is trusted on startup.

  1. Schema declaration durable truth
  2. Stored metadata recorded identity
  3. Open check compatible?
  4. Ready or refuse named mismatch

Key parts

What the system does

These are the main boundaries, inputs, outputs, and failure rules. The examples show a specific use of each part.

Three ways to read

A program reads a record by its key, through an index its trove file declares, or from the change feed. An index answers a key range or a text search. A scan exists for maintenance and must state a row ceiling and a byte ceiling. Past either one it refuses instead of returning a partial list.

For example

The parts board reads byChangedAt for the last 8 hours. A request for every part awaiting inspection depot-wide waits for an index row; it never becomes a full scan.

One commit point

Each record belongs to one volume primary, which checks every write against the record’s latest version, commits it in a batch and acknowledges it. A write that spans several primaries commits in two phases under one decision record. A conflicting write faults into the program instead of overwriting.

For example

Two technicians mark P-204 at 14:02. The first update commits; the second takes its else arm and reloads the record.

Schema checked at launch

The trove files are the schema. At launch the store compares their digest with the ones it has published. A match runs, an older known schema reconciles and keeps writing, and an unknown one refuses until a release publishes it. Every stored record carries the schema version it was written under.

For example

A node still on last month’s build keeps writing during a rollout. A laptop build with an unpublished column refuses at launch and names the digest it brought.

Code

MerDB in code

Each example is short and complete enough to read on its own. Hover a dotted word for its definition.

Trident

Read one record by its key

A key read returns the document and its version. The version is what a later conditional write compares against.

text statusOf(u64 id) {
    chest row = t.db.get("part", id)
    chest doc = row.document
    u64 version = row.version
    text status = doc.status
    return status + " at version " + text(version)
}
Trident

A scan states its ceilings

Both ceilings are required. On a 40,000-record table this call faults when the 501st row arrives; it never hands back the first 500 as if they were everything.

chest[] everyPart() {
    # at most 500 rows and 1 MiB in one answer
    return t.db.scan("part", 500, 1048576)
}
Trident

Write only if nobody else did

The untyped t.db surface spells the condition as a version. If the record moved after the read, putIf refuses with store_conflict and writes nothing.

text markInstalled(u64 id) {
    chest row = t.db.get("part", id)
    chest doc = row.document
    u64 readVersion = row.version
    doc.status = "installed"
    chest written = t.db.putIf("part", id, doc, readVersion)
    u64 newVersion = written.version
    return "version " + text(newVersion)
}
Trident

Rename a column with a migration step

A migration is one typed step per file. The store fills a shadow column in the background and switches with one publish. A program still on the old release faults only if it writes the renamed column. At launch the store compares each release’s steps with the ones it has applied.

# _db/troves/part.tri, after the change
trove part {
    text<class=ascii> serial = ""
    text bin = ""
    text status = "ready"
    unique index bySerial = t.text.ascii.lower(serial)
}

# _db/migrations/part/<stamp>-rename-bin.tri
step rename { from = binCode, to = bin }
Chest

The access a feed consumer asks for

The compiler lists the store access a program needs. The reorder desk asks to read part and follow its feed, and nothing else, so a grant that matches it cannot write a single part record.

u16 schemaVersion = 1
chest[] uncovered = [
    {
        text kind = "read"
        text name = "part"
        text scope = "a"
    },
    {
        text kind = "feed"
        text name = "part"
        text scope = "a"
    }
]

Uses

Example uses

  • Readiness and parts boards
  • Services that react to committed changes
  • Inventory and work-order records shared by many runtimes

Review questions

What the team must decide

Which questions must answer fast, and which declared index serves each one

Which consumers need every change, and where each one keeps its feed position

Which writes can collide, and what the program does when one is refused

Contact us
FURTH FORTUNE / ENGINEERING REFERENCEBack to top ↑