iClaustron training for C developers · module 3 of 8 · 20 min

Object architecture

The objects a program makes, the ones the library keeps for it, which thread owns each, and how a reply from a data node finds its object.

The map · ownership as nesting

The object map: global, connection, executor

Global IC_APID_GLOBAL · one per cluster configuration · fetched at connect NodeShared per data node · send buffer thread table · block number → inbox dictionary cache · definitions by name send pool · finishes others' writes threads · receive, heartbeat, connect Connection IC_APID_CONNECTION · one per user thread block 0x8000 + thread id · inbox queries · IC_QUERY_ID transactions · IC_TRANS_ID scans · IC_SCAN_ID · receivers coordinator records per node executed list · ready list Executor IC_APID_EXECUTOR · a library thread its own connection · a block submitters · one per thread op shapes · scan shapes requests · their rows readers · a scan's rows queries, scans kept per shape Handles IC_TABLE_DEF · IC_INDEX_DEF · IC_RECORD · IC_PROGRAM: made by the program, used by any thread

The global is shared by every thread of the process: one per cluster, with its per-node state, its thread table, its dictionary cache and its threads.

Each user thread makes a connection from the global: its own block number, inbox and objects, used by one thread at a time, with no lock on its objects.

An executor is a thread of the library's with a connection of its own; application threads reach it through submitters.

Table and index definitions, records and programs are handles: made once, used by any thread. A definition or a record may outlive the connection that bound it.

iclaustron: crates/ic_apid/src/apid_global.rs (ApidShared, ApidGlobal::start_with_options); apid_conn.rs (ApidConnection); executor.rs (Runner, Submitter); include/iclaustron/ic_apid.h (Handles, Threads)

Global · IC_APID_GLOBAL

The global: one per cluster per process, shared by every thread

Global IC_APID_GLOBAL · made by ic_apid_global_connect NodeShared · data node 1link state: atomics, one writer NodeShared · data node 2send buffer, spare, writer flag thread table4 711 slots: block → inbox dictionary cachetable, index definitions by name configurationfrom the management server send poolwrites what a writer left behind threadsreceive · heartbeat · connect · send AUTO_INCREMENT countersa connection of its own
typeIC_APID_GLOBAL * any thread may use it
makeic_apid_global_connect
useic_apid_global_wait_for_startedic_apid_global_create_connectionic_apid_executor_start
freeic_apid_global_free after its connections and executors
  • One per cluster: a program that talks to two clusters starts two.
  • One NodeShared per data node, fixed at start. Each node is dealt to one receive thread; never more receive threads than nodes.

iclaustron: crates/ic_apid/src/apid_global.rs (NodeShared, Sender, ApidShared, ApidGlobal::start_with_options); thread_conn.rs (ThreadTable); auto_increment.rs (CounterSlot); include/iclaustron/ic_apid.h (ic_apid_global_connect, ic_apid_global_free); doc/user-guide.md §2

Global · made first, freed last

Lifetime: the global is made first and freed last

ic_global_options_init(&options, sizeof options);
ic_apid_global_connect(connect_string, name, &options, &global);
ic_apid_global_create_connection(global, &conn);
ic_apid_conn_table_bind(conn, EX_DB, EX_TABLE, &table);
rec = ex_t1_record(table, 0);       /* ic_record_create */
ic_apid_executor_start(global, NULL, &executor);
ic_apid_executor_submitter(executor, &submitter);
/* ... */
ic_apid_submitter_free(submitter);
ic_apid_executor_free(executor);
ic_record_free(rec);
ic_table_def_free(table);
ic_apid_conn_free(conn);
ic_apid_global_free(global);
  • Free in the reverse order: submitters and executors, then connections, then the global.
  • ic_apid_global_free stops every thread of the library and waits for each; no other thread may use the global meanwhile.
  • ic_apid_conn_free first waits for the answers to the coordinator records it asked for: well under a millisecond as a rule, 5 s at most. A transaction left open does not commit.
  • Table, index and record handles may outlive the connection that bound them.

iclaustron: examples/c/t1.h (ex_connect); doc/user-guide.md §2; examples/c/scan_executor.c (main; EX_CHECK left out, the scan shape, index and val_rec lines elided); include/iclaustron/ic_apid.h (Handles; ic_apid_global_free, ic_apid_conn_free)

Connection · IC_APID_CONNECTION

A connection is one user thread's view of the cluster

Connection IC_APID_CONNECTION · user thread 7 block 0x80070x8000 + thread id: every reply's address · its inbox queriesIC_QUERY_ID transactionsIC_TRANS_ID scansIC_SCAN_ID · receivers coordinator recordsper node · active[place] → transaction expected repliesrequest number → its request executed list · ready listcompleted queries and batches staged requests per nodepacked at define, sent at send
typeIC_APID_CONNECTION * one thread at a time
makeic_apid_global_create_connection from any thread
useevery ic_apid_conn_* call, and the ic_apid_query_*, ic_apid_transaction_*, ic_apid_scan_* calls on its ids
freeic_apid_conn_free its queries, transactions and scans with it
  • It may change threads between calls; only one thread uses it at a time.
  • At most 4 711 per process, executors' own included (7139).

iclaustron: crates/ic_apid/src/apid_conn.rs (ApidConnection, CoordRecord, Expectations); thread_conn.rs (ThreadConnection::block_number, ThreadTable::allocate); include/iclaustron/ic_apid.h (Threads; ic_apid_global_create_connection, ic_apid_conn_free); doc/user-guide.md §2

Connection · ids, not pointers

Queries, transactions and scans are named by generational uint64_t ids

IC_QUERY_ID · IC_TRANS_ID · IC_SCAN_ID: typedef uint64_t generation · 40 bits place in the object map · 24 bits 6324230 in a signal, a 32-bit word of it: the place and eight bits of the generation; bit 23 marks a scan receiver's 0x1000005a query: generation 1, place 5 freed 0x2000005place 5 again: generation 2 old id 0x1000005refused, never followed IC_SCAN_BATCH_ID · uint64_t: each batch an id of its own, naming nothing once handed back

An id is a place in the connection's object map with a generation over it. No pointer crosses the wire, and a valid id is never 0.

A freed query's place is reused under the next generation, and a place whose generation would wrap is retired: every call refuses or passes over the old id, never follows it; a late reply naming it is dropped.

iclaustron: crates/ic_util/src/ptr_array.rs (PtrId, wire_word, PtrArray::insert, remove, id_of_wire); crates/ic_apid/src/scan.rs (IC_RECEIVER_TAG, fits_below_tag, ScanBatchId, deliver); include/iclaustron/ic_apid.h (Ids)

Handles · table and index definitions

Table and index definitions are bound once and shared through a cache

thread Aic_apid_conn_table_bind dictionary cache in the global, by name ic_ex.t1id, version, fieldsshared by handles data nodeDICTIONARY TABLE_DESC_REQ TABLE_DESC_CONF thread Bthe same bind found: no round trip receive threadTABLE_CHANGED_REP let go: the next bind fetches it again or a query's schema error: 241, 283, 284, 709, 710, 723
typeIC_TABLE_DEF *IC_INDEX_DEF * any thread
makeic_apid_conn_table_bindic_apid_conn_index_bindic_apid_submitter_table_bind
useic_table_def_field_idic_table_def_field into IC_FIELD_INFOic_index_def_table
freeic_table_def_freeic_index_def_free; ic_apid_conn_table_unbind lets the connection's go

The first bind fetches the table's description from a data node, once for every connection of the global, and keeps it in the cache by name.

Every later bind, from any thread, finds it there and shares the same definition: no round trip.

When a data node says the table changed, or a query fails with a schema error, the definition is let go of: bind again and retry.

iclaustron: crates/ic_apid/src/dict_cache.rs (DictCache, TableDef, IndexDef); rec_thread.rs (TABLE_CHANGED_REP); apid_conn.rs (table_changed); transaction.rs (schema_error); include/iclaustron/ic_apid.h (ic_apid_conn_table_bind, ic_apid_conn_table_unbind, ic_table_def_field); doc/user-guide.md §3

Handles · records

A record lays a row out over a C struct: offset and null bit per field

struct t1_row {
  uint32_t id;        /* INT UNSIGNED NOT NULL, the primary key */
  int32_t  val;       /* INT, nullable: null bit 0 */
  uint32_t code;      /* INT UNSIGNED NOT NULL, the unique key */
  uint8_t  name_len;  /* VARCHAR(32) latin1, nullable: null bit 1 */
  char     name[32];
  uint8_t  null_bits;
};
IC_FIELD_SPEC spec[4] = {
  { id_of_id,   offsetof(struct t1_row, id),       IC_NO_NULL_BIT, 0 },
  { id_of_val,  offsetof(struct t1_row, val),
                offsetof(struct t1_row, null_bits), 0 },
  /* ... code, name ... */
};
ic_record_create(table, spec, 4, sizeof(struct t1_row), &rec);
typeIC_RECORD *IC_FIELD_SPEC
makeic_record_createic_record_create_default
useic_record_fieldic_record_row_sizeic_record_set_nullic_record_is_null
freeic_record_free a query keeps its own copy
  • ic_record_create_default(table, NULL, 0, &rec) lays the row out for you; ic_record_field says where each field went.
  • Checked once, when made: every field there, nothing overlapping. Any thread may use it.

iclaustron: doc/user-guide.md §3 (the C record; ret = left out); examples/c/t1.h (struct t1_row, ex_t1_record); crates/ic_apid/src/record.rs (Record::new); include/iclaustron/ic_apid.h (ic_record_create, ic_record_create_default, ic_record_free)

Handles · values in a row

A row holds each value as the data nodes keep it: nothing is converted

TypeIn the row
TINYINT, SMALLINT, MEDIUMINT, INT, BIGINT1, 2, 3, 4, 8 bytes, little-endian, signed or not
FLOAT, DOUBLE4, 8 bytes, IEEE, little-endian
CHAR(n), BINARY(n)the field's bytes, padded: CHAR with the character set's spaces, BINARY with zeros
VARCHAR, VARBINARYone length byte up to 255 bytes, else two, little-endian; then the bytes
DECIMALMySQL's packed binary decimal
DATE, TIME, DATETIME, TIMESTAMP, YEARas the data nodes keep them, fractional forms included
BIT(n)n bits, in 32-bit words
NULLa null bit, set for NULL; a NOT NULL field has none

A field takes the room of its longest value with its length bytes: VARCHAR(32) is 33 bytes in latin1, 129 in utf8mb4. Blob columns stay out of records.

iclaustron: doc/user-guide.md §3 (How values are kept; Blob columns); crates/ic_apid/src/record.rs (module notes)

Connection · queries

A query is made once, owns its key and attribute rows, and is reused

/* once, for each slot of the pipeline */
ic_apid_query_create(conn, p.table, p.rec, p.rec, &slots[i].query);

/* each write: the query owns its rows; the struct is copied in */
key = ic_apid_query_key_row(conn, s->query, &key_len);
attr = ic_apid_query_attr_row(conn, s->query, &attr_len);
memcpy(key, &row, sizeof row);
memcpy(attr, &row, sizeof row);
/* ... start a transaction, define the write, commit ... */

/* at the end */
ic_apid_query_free(conn, slots[i].query);
typeIC_QUERY_ID a uint64_t in its connection
makeic_apid_query_createic_apid_query_create_unique
useic_apid_query_key_rowic_apid_query_attr_rowic_apid_conn_read_keyic_apid_conn_write_keyic_apid_query_error
freeic_apid_query_free when idle or completed
  • Two records: the key record holds every primary key field, or every indexed field; one record over every field serves as both, as p.rec here.

iclaustron: examples/c/pipeline.c (main, start_write; EX_CHECK and the NULL check left out); crates/ic_apid/src/query.rs (ApidQuery, check_records); include/iclaustron/ic_apid.h (Rows; ic_apid_query_create, ic_apid_query_free); doc/user-guide.md §3, §4

Connection · a query's life

A query goes Idle, Defined, Sent, Completed, and back to Idle

the rows are the library's IdleIC_QUERY_IDLE read_key DefinedIC_QUERY_DEFINED send SentIC_QUERY_SENT replies CompletedIC_QUERY_COMPLETED taken from the executed list, or its callback returned

A query starts idle: the program fills its key row, and for a write its attribute row, through the pointers the query gives.

ic_apid_conn_read_key or _write_key defines it on a transaction: KEY_OP_REQ is packed at once into its node's staged buffer.

ic_apid_conn_send, or a flush, writes every node's staged requests, one write per data node: the query is sent.

In a poll, KEY_OP_CONF and the row's ROW_DATA_REP, or a KEY_OP_REF, complete it: its row is in the attribute row, or its error is set.

Taken from the executed list, or once its callback returns, it is idle and can be defined again. From define to completion its rows are the library's.

iclaustron: crates/ic_apid/src/query.rs (QueryState, Execution); transaction.rs (read_key, take_row, get_next_executed_query); include/iclaustron/ic_apid.h (IC_QUERY_*, Rows); doc/user-guide.md §4, §8

Connection · transactions

A transaction is local until its first send; the hint picks its coordinator

key rowby the key record hint · partition pic_apid_conn_hint_for_key: hashed as the data nodes do coordinator record · at a node holding pa free one of this connection's, or COORD_ALLOC_REQ ic_apid_conn_start_transaction waits only to allocate, ≤ 5 s transaction · IC_TRANS_ID transaction id, 64 bitsits coordinator recorddefined and sent queries data nodeCOORDINATOR first KEY_OP_REQ: start flag
typeIC_TRANS_IDIC_TRANSACTION_HINT
makeic_apid_conn_start_transactionic_apid_conn_start_transaction_on_index
useic_apid_conn_hint_for_keyic_apid_conn_commit_transactionic_apid_conn_rollback_transactionic_apid_transaction_is_done
freeic_apid_conn_close_transaction once done

ic_apid_conn_hint_for_key hashes the key as the data nodes do, character columns in their collation, and names the partition that holds the row.

Starting takes a coordinator record at a started node holding that partition: a free one of this connection's, or a new one, a round trip.

The transaction holds its 64-bit transaction id, its coordinator record, and its defined and sent queries. The data node knows nothing yet.

Its first KEY_OP_REQ carries the start flag: only then does the coordinator hear of it. A hint only chooses a node; any row may be touched.

iclaustron: crates/ic_apid/src/transaction.rs (Transaction, start_transaction, choose_start_node, hint_for_key); apid_conn.rs (coord_record, next_transaction_id); include/iclaustron/ic_apid.h (ic_apid_conn_hint_for_key, ic_apid_conn_start_transaction); doc/user-guide.md §4

Connection · ending a transaction

A transaction is done when it has ended and every query has completed

done: ic_apid_transaction_is_done Started…_STARTED Commit requested…_COMMIT_REQUESTED Committed…_COMMITTED Rollback requested…_ROLLBACK_REQUESTED Rolled back…_ROLLED_BACK
stateic_apid_transaction_state one of IC_COMMIT_STATE_*
erroric_apid_transaction_erroric_apid_transaction_error_data
gciic_apid_transaction_gci the global checkpoint of a commit
closeic_apid_conn_close_transaction its query lists kept for the next

A started transaction takes queries. Commit or rollback is asked for, and goes with the next send, on its last query or alone.

The coordinator commits it and says so, in KEY_OP_CONF or COMMIT_CONF, with the global checkpoint the commit went into.

A rollback asked for, a failed write that aborts on error, or the coordinator's TRANS_ABORTED_REP ends it rolled back; its error says why.

Done is ended with every query completed: its coordinator record goes back to the free list. Close it then.

iclaustron: crates/ic_apid/src/transaction.rs (CommitState, Transaction::is_done, take_key_conf, end_transaction, release_if_done, close_transaction); include/iclaustron/ic_apid.h (IC_COMMIT_STATE_*, ic_apid_transaction_is_done); doc/user-guide.md §4

Connection · scans

A scan holds its row and, while it runs, a receiver per fragment

Scan · IC_SCAN_IDits table, its record, its rowIdle → Starting → Running → Closing → Done coordinator recordits own, from a pool per nodeSCAN_CONF names it: one index receiverfragment 1 receiverfragment 2 receiverfragment 3 receiverfragment 4 a batch a batch a batch a batch IC_SCAN_BATCH_ID: an id of its own per batch → ready list or callback
typeIC_SCAN_IDIC_SCAN_BATCH_ID
makeic_apid_scan_createic_apid_index_scan_create
useic_apid_conn_start_scanic_apid_conn_next_rowic_apid_conn_next_batchic_apid_conn_release_scan_batch
freeic_apid_scan_free not while it runs

A scan is made once over a table and a record, and holds the row read last. Each run takes a coordinator record of its own.

While a run lasts it has one receiver per fragment scanned at once. A receiver's id has bit 23 set, which no query's id has.

A receiver brings a batch at a time, to the ready list or a callback; handed back, it asks for more. Module 07 teaches the use.

iclaustron: crates/ic_apid/src/scan.rs (Scan, Receiver, ScanState, ScanBatchId, IC_RECEIVER_TAG, deliver); apid_conn.rs (scan_pools, receivers, batches, scan_of_record); include/iclaustron/ic_apid.h (Scans; ic_apid_scan_create, ic_apid_scan_free); doc/rust/02-architecture.md (Data flow for a scan)

Executor · executors and submitters

An executor runs other threads' requests on a connection of its own

app threadsubmitter, its slot app threadsubmitter, its slot app threadsubmitter, its slot inboxone mutex requestsbounded: 4 000 signal pagesfrom receive threads executor its connectiona block like others querieskept per op shape transactionsone per request scanskept per scan shape result → the submitter's slot, a wake-up
typeIC_APID_EXECUTOR *IC_APID_SUBMITTER *
makeic_apid_executor_startic_apid_executor_submitter one per thread
useic_apid_submitter_table_bindic_apid_submitter_executeic_apid_submitter_scanic_apid_executor_stats
freeic_apid_submitter_freeic_apid_executor_free before the global

Any number of application threads, each with its own submitter, put requests into the executor's inbox, beside the signal pages of the receive threads.

The executor runs each request as a transaction on its connection, with queries kept per shape, and leaves the result in the submitter's slot.

iclaustron: crates/ic_apid/src/executor.rs (Executor::start, Submitter, Slot, Runner, Pool, ScanPool, IC_EXECUTOR_QUEUE); thread_conn.rs (Inbox); include/iclaustron/ic_apid.h (Executors; ic_apid_executor_start, ic_apid_executor_free, ic_apid_executor_submitter)

Executor · shapes, requests, readers

Shapes describe an operation once; a request carries one transaction

IC_REQUEST *req;
uint32_t op;
size_t len;

ic_request_create(&req);
ic_request_add_read(req, shape, IC_READ_KIND_COMMITTED, &op);
memcpy(ic_request_key_row(req, op, &len), &key, sizeof key);
if ((ret = ic_apid_submitter_execute(sub, req)) != 0)
  goto error;
ret = ic_request_op_error(req, op);
if (ret == IC_NDB_ERROR_NO_SUCH_ROW)
  /* no such row */;
else if (ret != 0)
  goto error;
else
  memcpy(&row, ic_request_attr_row(req, op, &len), sizeof row);
op shapeIC_OP_SHAPE *ic_op_shape_createic_op_shape_create_uniqueic_op_shape_free
requestIC_REQUEST *ic_request_createic_request_add_readic_request_add_writeic_request_free
scan shapeIC_SCAN_SHAPE *ic_scan_shape_createic_scan_shape_create_indexic_scan_shape_free
readerIC_SCAN_READER *ic_scan_reader_createic_scan_reader_next_rowic_scan_reader_free
  • A shape is made once and shared by every thread; a request owns its rows and is run again with new keys. Module 06 runs them, module 07 the readers.

iclaustron: doc/user-guide.md §5 (Through an executor, the C request); crates/ic_apid/src/executor.rs (OpShape, KeyOp, Request); scan_reader.rs (ScanShape, ScanReader); include/iclaustron/ic_apid.h (ic_op_shape_create, ic_request_create, ic_scan_shape_create, ic_scan_reader_create)

Handles · programs, run in the data node

A program is made once over a table and run by any number of operations

ic_program_builder_create(table, &b);
same = ic_program_compare(b, IC_CMP_EQ, ic_program_column(b, val),
                          ic_program_input(b, 0));
ic_program_builder_finish(b, same, &still);
ic_program_builder_free(b);
ic_apid_query_create(conn, table, rec, name_rec, &guarded);
ic_apid_query_set_program(conn, guarded, still);
/* ... */
ic_apid_query_set_input(conn, guarded, 0, 0);
/* ... define the update, commit, wait ... */
ic_apid_query_set_input(conn, guarded, 0, 5);
/* ... */
ic_program_free(still);
typeIC_PROGRAM *IC_PROGRAM_BUILDER *
makeic_program_builder_createic_program_builder_finishic_program_builder_finish_no_condition
useic_apid_query_set_programic_apid_conn_set_scan_programic_op_shape_with_programic_scan_shape_with_program
freeic_program_builder_freeic_program_free what runs it keeps it
  • Only the inputs change from one operation to the next: up to 16 integers each. Module 08 uses programs.

iclaustron: examples/c/conditional_update.c (main, part 1; EX_CHECK left out); crates/ic_apid/src/program.rs (Program, ProgramBuilder); include/iclaustron/ic_apid.h (Programs; IC_PROGRAM_INPUTS; ic_program_builder_finish, ic_program_free); doc/user-guide.md §6

Replies · finding the object

A reply finds its object: the block number, then one index

to block 0x8007 word 0: 0x80000006 data nodeKEY_OP_CONF receive threadroutes by block only inbox · thread 70x8007 − 0x8000 = 7 its pollreads word 0 expected repliesCOORD_ALLOC_CONF … active[3]→ its transaction queries[id]query id → the query receivers[id]bit 23 set → a scan

Each request names our block, 0x8000 plus the thread id, as the address for its reply; the reply's first word is a number of ours.

A receive thread reads the block number only, and posts the signal to that thread's inbox, a page per round. The user thread decodes it.

In its poll, a single request's reply, such as COORD_ALLOC_CONF, matches an expected reply: its request number, signal and node.

KEY_OP_CONF, COMMIT_CONF and ROLLBACK_CONF carry our pointer for the coordinator record: its place indexes active[], which holds the transaction.

ROW_DATA_REP and KEY_OP_REF name the query by its id; a scan's rows name a receiver, bit 23 set. Each is one index, no search.

iclaustron: crates/ic_apid/src/thread_conn.rs (Router, ThreadConnection::block_number); apid_conn.rs (receive_view, Expectations::offer, coord_index_of); transaction.rs (route_reply, trans_named, take_row); scan.rs (scan_named, receiver_named); doc/rust/architecture.html §2

Replies · the bits

Two numbers in a reply: our record pointer and the transaction id

our pointer for a coordinator record · 32 bits · given in COORD_ALLOC_REQ, named by KEY_OP_CONF, COMMIT_CONF, ROLLBACK_CONF 1 place in coord_records and active[]: one index finds the transaction ack 313010 transaction id · 64 bits · in every request and every reply about the transaction block index, low 12 node id, low 12 counter · 38 bits, across the block's connections 635139370 bit 39: bit 12 of the node id · bit 38: bit 12 of the block index

A late reply, left from an earlier use of the same place, carries another transaction id: it matches nothing and is dropped, and counted.

iclaustron: crates/ic_apid/src/apid_conn.rs (IC_COORD_PTR_TAG, coord_ptr_of, coord_index_of, transaction_id, trans_count_after, next_transaction_id, note_stray); thread_conn.rs (ThreadTable::trans_counter); transaction.rs (trans_named); doc/rust/architecture.html §2

Memory · once warm

Memory: once warm, nothing is allocated per transaction or per signal

Queries own their rows

Made with the query, sized by its records. From define to completion the library holds no pointer into the program's memory; C gets pointers into the query's own rows.

Kept, not freed

A closed transaction's query lists, up to 8 192 spare; coordinator records on free lists; send buffers swapped with a spare; signal pages at their largest size.

Copies at the edges

The synchronous calls copy the caller's rows in and out. An executor copies a request's rows into its query and back. A batch moves to a reader without a copy.

On the define, send and poll path, a thread with hundreds of transactions in flight starts each in constant time and allocates nothing for it.

iclaustron: doc/rust/architecture.html §2 (Memory rules); crates/ic_apid/src/transaction.rs (IC_SPARE_QUERY_LISTS, close_transaction); apid_conn.rs (coord_record); apid_global.rs (Sender, take_waiting); signal_page.rs; executor.rs (admit, completion copies); scan_reader.rs (module notes); include/iclaustron/ic_apid.h (Rows)

Summary · objects by model

Objects in the five programming models

ModelThe program makesThe library uses for itOwned by
Synchronousa connection, definitions, records; a scanper key call a coordinator record and an expected reply; a scan's receiversthe calling thread
Batched synchronousqueries, transactions; scans and their batchescoordinator records, requests staged per node, receiversthe calling thread
Asynchronousqueries with a callback and user_ref, transactions; scansthe executed list and the ready list; callbacks run inside the pollthe thread that polls
Synchronous through an executoran executor, a submitter per thread, op shapes, a request; scan shapes, readersthe executor's connection: a kept query per operation, a transaction per request, a kept scanthe executor's thread; each caller its submitter, request, reader
Batched synchronous through an executora request of many operations; several readersthe same, with one send per data node for every caller's requeststhe same

Modules 04 to 08 teach each model; this table says which objects each one stands on.

iclaustron: crates/ic_apid/src/key_op.rs (read_committed, write_key, run_op, read_unique_committed); transaction.rs; scan.rs; executor.rs (Runner, Flight, Pool, ScanPool); scan_reader.rs (Feed); include/iclaustron/ic_apid.h (Executors, Scans); doc/user-guide.md §5

Recap · module 03

Object architecture: what to take away

  • One global per cluster, shared by every thread and freed last.
  • A connection per user thread holds its queries, transactions and scans, named by generational uint64_t ids.
  • Table and index definitions, records and programs are handles any thread may use.
  • A query owns its rows; a transaction is local until its first send, and its hint picks the coordinator.
  • An executor runs other threads' requests on its own connection: submitters, shapes, requests, readers.
  • A reply finds its object by the block number, then one index: no search, no lock.

Next · 04 · Key operations, synchronous