Memory
Datahoster holds a host's whole dataset in memory, laid out the same way as on disc. Every field is a column of typed arrays, chunked to match the files. A read walks these arrays and touches no disc. The one thing left on disc is the body of each text value: memory keeps only the fixed-width locator that says which strings file to open, at what offset, for what length.
Objects
The data structures this page describes, from the top down.
| Object | Holds |
|---|---|
| Database | All of one host's collections, by name. |
| Collection | One table: its columns, and the index of live rows. |
| Column | One field's value for every row, as typed arrays. |
Database
A host's data is one Database: a map from each collection name to the Collection that holds it. The server keeps one Database per hosted domain, so a request routed by host lands on exactly one of them.
Database {
collections: Map<string, Collection>
}
| Name | Type | Description |
|---|---|---|
collections | Map<string, Collection> | A map from each collection name to the Collection that holds it. |
Collection
A Collection is the columns of one table, plus the index that finds the current version of each row. Every field is a column, and so is each part of the record metadata: id, at and by are columns like any other, so every part of a record is stored the same way.
Collection {
length: number
columns: Map<string, Column>
current: Map<int128, number>
chunkRecords: number
bufferRecords: number
}
| Name | Type | Description |
|---|---|---|
length | number | How many record versions have been written. Every column has this same length, because a write appends one value to each. |
columns | Map<string, Column> | A map from column name to its Column. The id, at and by columns come first, then one per field. |
current | Map<int128, number> | A map from a live record id to the row number of its latest version. A row not named here is a past version or a deleted record. |
chunkRecords | number | Values in a full chunk, described under Chunks and the tail. |
bufferRecords | number | Values in one tail buffer, described under Chunks and the tail. |
Reading the collection is a walk over current: for each live id, read its row from every column. An insert or update appends one row to every column and points current at it. A delete drops the id from current; its rows stay in place as history, and its id joins the collection's deleted list on disc with the append index at deletion, so inserting the same id again later revives it.
Column
A Column holds one field's value for every row, in row order, as typed arrays. It is a list of full chunks, one buffer per numbered file on disc, followed by a growing tail for the newest rows.
Column {
type: string
width: number
length: number
chunks: ArrayBuffer[]
tail: ArrayBuffer[]
}
| Name | Type | Description |
|---|---|---|
type | string | The stored type, the same name as on disc. The typed array to use follows from it (see Types). |
width | number | Bytes per value at that type. |
length | number | Values written into this column, matching the collection's length. |
chunks | ArrayBuffer[] | The full chunks, one ArrayBuffer each, the same bytes as file 0000, 0001 and so on. Each holds chunkRecords values. |
tail | ArrayBuffer[] | The current chunk, still filling: a list of smaller buffers of bufferRecords values each. A new buffer is allocated only when the last one fills. |
Chunks and the tail
The last chunk grows as rows arrive. Preallocating a whole chunk, a million values, for every column would waste memory, and growing one chunk by reallocation would copy it in full on every extension. So the tail is built from small buffers: allocate one buffer of bufferRecords values, fill it, allocate the next, and so on. When the tail holds a full chunk's worth of values, its buffers are copied once into a single chunk buffer, appended to chunks, and written to the next numbered file. The tail then begins again empty for the next chunk.
The result is one layout in two places. Every completed chunk is a single buffer of chunkRecords values, matching one file byte for byte. Only the final, open chunk is split into buffers, and only in memory.
| Number | Sets |
|---|---|
chunkRecords | Values in a full chunk: in a file, and in a completed chunk in memory. Set by datahoster-collection-recordsPerChunkFile; defaults to 1,000,000. |
bufferRecords | Values in one tail buffer, the unit memory grows by. Set by datahoster-collection-recordsPerTailChunk; defaults to 65,536. |
Both are set per collection. The tail fills buffer by buffer until it reaches a full chunk; the last buffer of a chunk may be only partly used, since chunkRecords need not be a whole number of buffers.
Types
A column uses the typed array that matches its stored type, so the bytes in memory are the bytes on disc. Text is the one exception: memory holds the locator, and the strings stay on disc.
| Field type | Stored type | In memory |
|---|---|---|
number | float64 | Float64Array |
integer | int32 | Int32Array |
boolean | uint8 | Uint8Array |
int128 | int128 | 16 bytes per value, read as two BigUint64 lanes |
char8 … char1024 | fixed byte count | Uint8Array, that many bytes per value |
text | text locator | the locator array (strings file, offset, length); bodies stay on disc |
The automatic columns are held the same binary way: id as an int128 (two BigUint64 lanes), at as a number (a Float64Array of whole milliseconds), and by as a fixed-width username. Because a text column's bodies are not in memory, a text column can be returned but cannot be filtered, sorted, or used in an endpoint's logic. A char column is fixed-width and in memory, so it can; every in-memory column can.
Reading a value
To read row i of a column, find its chunk and its slot: chunk = i / chunkRecords and slot = i % chunkRecords. A completed chunk is one buffer, indexed at the slot. In the open chunk the slot sits in tail buffer slot / bufferRecords at slot % bufferRecords. Wrap the buffer in the column's typed array and read that index. For a text column the slot holds the locator, and the string is read from its strings file on disc.