CUBRID Boot — Server Startup, First-Time Creation, Restart-Recovery Dispatch, and Client Connect
Contents:
- Theoretical Background
- Common DBMS Design
- CUBRID’s Approach
- Source Walkthrough
- Cross-check Notes
- Open Questions
- Sources
Theoretical Background
Section titled “Theoretical Background”A relational DBMS at process start is a strict topological sort of subsystem dependencies. Each subsystem requires the previous one in exactly the way an operating-system kernel requires its predecessors during boot — and getting the order wrong is fatal in the same way: the symptoms cascade, and the root cause is invisible to anyone who hasn’t already read the boot code.
The dependency graph that every disk-resident engine reproduces:
- Error reporting and message catalogue. Nothing else can report a failure intelligibly until these are alive. They depend on nothing.
- System parameter loader. Reads the configuration file. The error reporter must be alive first, otherwise parse failures are silent.
- Memory and area allocators. Bound to the parameter loader because pool sizes are configurable.
- Locale, timezone, charset. Mostly inert; need the message catalogue to report failures.
- Thread / worker-pool manager. Everything below this point may
need a
THREAD_ENTRYfor diagnostics or for system transactions. - File I/O layer. Mounts volumes by descriptor. Depends only on the OS layer plus error reporting.
- Page buffer pool. Caches disk pages, depends on file I/O and on parameters (cache sizes, replacement policy).
- Log manager. Reads and writes the WAL. Needs the page buffer for the data side and direct file I/O for the log itself.
- Recovery manager. Walks the log to bring the database to crash-consistent state. Needs the log manager, the page buffer, and the disk manager.
- Catalog (system tables). Depends on heap files, B+Trees, file tracker — none of which can answer queries until recovery has restored their pages.
- Locking and MVCC. Lock manager needs catalog OIDs to identify resources; MVCC needs the active transaction table the recovery manager rebuilt.
- Query optimizer / plan cache. Depends on the catalog.
- Network listener. Last to come up because accepting a client before the catalog is loaded would mean the first SQL request sees an empty schema.
This graph splits into two execution flows depending on what is on
disk. The bootstrap flow runs once in a database’s lifetime — at
createdb time — and is the only path that creates the catalog
tables, the root-class object, and the database header. The restart
flow runs every other time the server starts; it skips the format-
and-bootstrap steps and instead hands off to recovery for crash
healing.
The boot subsystem is the contract holder for both flows. It owns:
- The order in which the dependency graph is traversed.
- The decision of which flow (bootstrap vs. restart) to execute.
- The persistent header that distinguishes a fresh database from an existing one.
- The translation of subsystem failures into clean process exits (the symmetric tear-down).
The reason the orchestrator has to be one module rather than dozens
of main()-time calls is that the dependency graph has cycles once
you look closely. The recovery manager needs the page buffer; the
page buffer needs the disk manager; the disk manager wants the
catalog to find tablespace metadata; the catalog needs recovery to
be over. The boot module breaks the cycle by staging the
initialisation: a subsystem comes up in two phases, an early phase
where it has just enough state to participate in recovery and a late
phase where it consumes catalog data. Without this staging the
graph would be unsatisfiable.
The textbook treatment of startup orchestration is Database Internals (Petrov), which addresses the WAL-and-recovery side explicitly but treats the broader subsystem ordering only by implication. The OS literature is more direct: Linux Kernel Development (Love) §“The Boot Process” and Bovet & Cesati, Understanding the Linux Kernel, both describe a graph nearly isomorphic to the DBMS one — early text, then console, then memory, then VFS, then init.
Common DBMS Design
Section titled “Common DBMS Design”Every disk-resident relational engine adopts the same handful of conventions for boot. They are not prescriptive in the literature; they are emergent from the dependency graph above.
Bootstrap split between two binaries
Section titled “Bootstrap split between two binaries”Postgres ships two binaries: postgres (the normal server) and
bootstrap mode, the latter invoked by initdb. The bootstrap
binary takes a different code path through BootStrapXLOG,
BootstrapModeMain, and BuildBootstrapping — it formats pg_xlog,
writes the initial control file, and bootstraps the system catalogs
(pg_class, pg_attribute) before normal operation can take over.
InnoDB has the same split, with srv_start branching on
srv_force_recovery and on the presence of the system tablespace.
Oracle exposes the split as user-facing phases: STARTUP NOMOUNT
(shared memory + background processes only), STARTUP MOUNT (control
file open), STARTUP OPEN (data files open, recovery dispatched).
CUBRID does not have a separate bootstrap binary. Both flows live in
boot_sr.c, dispatched by which entry point the executable calls:
xboot_initialize_server for create, boot_restart_server for
restart. This is a deliberate design choice — the cost is that
boot_sr.c is 6 178 lines, the benefit is that there is exactly one
copy of the subsystem-init order to maintain.
Recovery dispatch via a separate entry point
Section titled “Recovery dispatch via a separate entry point”Postgres’ StartupXLOG is the recovery driver, called by
PostmasterMain if and only if the control file says the previous
shutdown was unclean. The same factoring exists in InnoDB
(recv_recovery_from_checkpoint_start) and in CUBRID
(log_initialize calls log_recovery if hdr.is_shutdown == false).
The boot module’s responsibility is to call the recovery driver at
the right point in the startup sequence — late enough that page
buffer and disk manager are alive, early enough that the catalog
hasn’t been opened yet. CUBRID achieves this by splitting
boot_restart_server into a long sequence of subsystem inits with
log_initialize placed exactly between the page-buffer and the
catalog modules. See the source walkthrough for the precise
position.
Server-side credential exchange at register
Section titled “Server-side credential exchange at register”When a client first connects, the server has been up for a while,
its catalog is loaded, its HA state is decided. The client knows
none of this. The register-client RPC therefore returns a packed
server credential — page size, log page size, root-class OID,
disk-compatibility number, HA state, charset, language, session
key. The client uses these values to align its in-memory data
shapes with the server’s. CUBRID’s BOOT_SERVER_CREDENTIAL
(boot.h) is the explicit struct; Postgres encodes the same data
implicitly in the ParameterStatus messages of the startup-packet
flow.
Symmetric shutdown
Section titled “Symmetric shutdown”Every engine ships a shutdown that mirrors the boot order in
reverse: stop accepting new clients, stop background workers, flush
dirty pages, write a shutdown checkpoint, set the
“clean-shutdown” flag in the header, finalize lower-level
subsystems. The contract: the next restart that finds the header
flag set may skip recovery. CUBRID’s xboot_shutdown_server
implements this; the matching client-side
boot_shutdown_client walks the analogous teardown.
Theory ↔ CUBRID mapping
Section titled “Theory ↔ CUBRID mapping”| Theoretical concept | CUBRID name |
|---|---|
| Process entry point | main() in src/executables/server.c (cub_server) |
| First-time create entry | xboot_initialize_server (boot_sr.c) |
| Restart entry | boot_restart_server (boot_sr.c) |
| Restart dispatcher | net_server_start (network_sr.c) — calls boot_restart_server then css_init |
| Recovery hand-off | log_initialize (log_manager.c) → log_recovery (log_recovery.c) |
| First-volume formatter | boot_create_all_volumes (boot_sr.c) → disk_format_first_volume |
| Database parameter persistence | BOOT_DB_PARM struct + boot_Db_parm global (boot_sr.c) |
| Boot status flag | BOOT_SERVER_STATUS { UP, DOWN, MAINTENANCE } + boot_Server_status (boot_sr.h) |
| Server credential | BOOT_SERVER_CREDENTIAL struct (boot.h) |
| Client credential | BOOT_CLIENT_CREDENTIAL extends clientids (client_credentials.hpp) |
| Server register-client | xboot_register_client (boot_sr.c) |
| Client register-client RPC | boot_register_client (network_interface_cl.c) |
| Client side restart | boot_restart_client (boot_cl.c) |
| Client side first-time create | boot_initialize_client (boot_cl.c) |
| Client connect to host | boot_client_initialize_css (boot_cl.c) |
| Catalog bootstrap (table creation) | catcls_init + catcls_install (schema_system_catalog_install.cpp) |
| Catalog rehydration on restart | catcls_compile_catalog_classes + catcls_find_and_set_cached_class_oid (catalog_class.c) |
| HA state at boot | css_change_ha_server_state (server_support.c) called near end of boot_restart_server |
| Master process | cub_master — main() in src/executables/master.c |
| Master heartbeat init | hb_master_init called from master’s main() |
| Server shutdown | xboot_shutdown_server (boot_sr.c) |
| Server finalize | boot_server_all_finalize (boot_sr.c) |
| Client shutdown | boot_shutdown_client (boot_cl.c) |
| Client finalize | boot_client_all_finalize (boot_cl.c) |
CUBRID’s Approach
Section titled “CUBRID’s Approach”The boot subsystem in CUBRID has four moving parts: the executable entry points that translate a process invocation into the right flow, the server-side first-create driver that formats volumes and bootstraps the catalog, the server-side restart driver that mounts existing volumes and dispatches into recovery, and the client-side flow that connects, exchanges credentials, and loads the schema cache. We walk them in that order.
Top-level shape
Section titled “Top-level shape”flowchart TB
subgraph EXES["Executables"]
SRV["cub_server (server.c::main)\n→ net_server_start"]
MST["cub_master (master.c::main)\n→ css_master_init + hb_master_init"]
CLI["client (csql / broker / app)\n→ db_restart → boot_restart_client"]
CDB["createdb utility\n→ boot_initialize_client"]
end
subgraph SR["Server boot (boot_sr.c)"]
XINIT["xboot_initialize_server\n(first-time create)"]
XRST["boot_restart_server\n(normal restart)"]
XSHD["xboot_shutdown_server"]
XREG["xboot_register_client"]
XUNREG["xboot_unregister_client"]
end
subgraph CL["Client boot (boot_cl.c)"]
CINIT["boot_initialize_client\n(first-time)"]
CRST["boot_restart_client\n(normal)"]
CSHD["boot_shutdown_client"]
CCSS["boot_client_initialize_css\n(host connect)"]
end
subgraph LR["Recovery (log_recovery.c)"]
LRC["log_recovery\nanalysis → redo → undo"]
end
subgraph CAT["Catalog bootstrap"]
CIN["catcls_init"]
CIS["catcls_install"]
CCC["catcls_compile_catalog_classes"]
end
SRV --> XRST
CDB --> CINIT --> CCSS
CINIT --> XINIT
XINIT --> CIN --> CIS
CLI --> CRST --> CCSS --> XREG
XRST -->|"via log_initialize"| LRC
XRST --> CCC
XSHD --> XUNREG
The figure encodes three sequencing decisions that recur throughout
this document. Two server entry points, never both: a process
either calls xboot_initialize_server or boot_restart_server,
never both, never either twice. The first-time path bootstraps the
catalog on the server but the catalog content is installed by the
client side (catcls_install runs in boot_initialize_client
after boot_initialize_server returns) — a separation that exists
because the system tables are populated through the regular
client-side schema API. The client-side boot_restart_client
drives xboot_register_client via a network RPC — the
register-client return packs the server credential the client uses
to align page sizes, OIDs, and HA state.
Server-side first-time create
Section titled “Server-side first-time create”The flow runs once per database, when createdb (a client utility)
calls boot_initialize_client which in turn calls
boot_initialize_server over the network. The server-side handler
is xboot_initialize_server (boot_sr.c).
// xboot_initialize_server — src/transaction/boot_sr.c (condensed)intxboot_initialize_server (const BOOT_CLIENT_CREDENTIAL *client_credential, BOOT_DB_PATH_INFO *db_path_info, bool db_overwrite, const char *file_addmore_vols, volatile DKNPAGES db_npages, PGLENGTH db_desired_pagesize, ...){ /* 1. early subsystem init (server-mode only) */ lang_init (); tz_load (); msgcat_init (); sysprm_load_and_init (NULL, NULL, SYSPRM_LOAD_ALL); area_init (); set_area_init (); pr_area_init (); tp_init (); tsc_init ();
/* 2. canonicalise paths */ realpath (db_path_info->db_path, fixed_pathbuf); realpath (db_path_info->log_path, fixed_pathbuf); /* compose boot_Db_full_name = "<db_path>/<db_name>" */
/* 3. shut down any prior server in this process */ if (BO_IS_SERVER_RESTARTED ()) { (void) xboot_shutdown_server (thread_p, ER_ALL_FINAL); }
/* 4. thread manager */ cubthread::initialize (thread_p); cubthread::initialize_thread_entries ();
/* 5. update databases.txt directory file */ cfg_read_directory_ex (...); if (existing && !db_overwrite) goto exit_on_error; if (existing && db_overwrite) boot_remove_all_volumes (...);
/* 6. install SIGINT handler so Ctrl-C aborts the create */ os_set_signal_handler (SIGINT, boot_ctrl_c_in_init_server);
/* 7. THE FORMAT: create volumes, log, root class, catalog */ tran_index = boot_create_all_volumes (thread_p, client_credential, ...);
/* 8. publish DB to databases.txt */ cfg_add_db (&dir, ..., db_path, log_path, lob_path, host); cfg_write_directory_ex (...);
/* 9. fill out the root-class OID/HFID for the caller */ *rootclass_oid = boot_Db_parm->rootclass_oid; boot_find_root_heap (rootclass_hfid);
/* 10. session state, version banner */ session_states_init (thread_p); fprintf (stdout, "<format banner>"); return tran_index;}Step 7 is the heart of the create flow.
boot_create_all_volumes (boot_sr.c) runs in this exact order:
// boot_create_all_volumes — src/transaction/boot_sr.c (condensed)static intboot_create_all_volumes (THREAD_ENTRY *thread_p, const BOOT_CLIENT_CREDENTIAL *client_credential, ...){ spage_boot (thread_p); heap_manager_initialize ();
/* (a) create active log + initialise log/recovery managers */ log_create (thread_p, boot_Db_full_name, log_path, log_prefix, log_npages); log_initialize (thread_p, boot_Db_full_name, log_path, log_prefix, false, NULL);
/* (b) assign a transaction index for the create work */ tran_index = logtb_assign_tran_index (thread_p, NULL_TRANID, TRAN_ACTIVE, client_credential, NULL, client_lock_wait, client_isolation);
/* (c) double-write buffer must exist before first volume format */ dwb_create (thread_p, log_path, log_prefix);
/* (d) format the first data volume */ disk_format_first_volume (thread_p, boot_Db_full_name, db_comments, db_npages); logpb_add_volume (NULL, LOG_DBFIRST_VOLID, boot_Db_full_name, DB_PERMANENT_DATA_PURPOSE);
/* (e) initialise the persistent boot_Db_parm structure */ boot_Db_parm->trk_vfid.volid = LOG_DBFIRST_VOLID; boot_Db_parm->hfid.vfid.volid = LOG_DBFIRST_VOLID; /* ... fields for tracker, root class, classname table, catalog, ... */ strncpy (boot_Db_parm->rootclass_name, ROOTCLASS_NAME, ...); boot_Db_parm->nvols = 1; boot_Db_parm->last_volid = LOG_DBFIRST_VOLID;
/* (f) create the persistent files: file tracker, system heap, * root-class heap, TDE-key heap, classname-hash, catalog file */ file_tracker_create (thread_p, &boot_Db_parm->trk_vfid); xheap_create (thread_p, &boot_Db_parm->hfid, NULL, false); xheap_create (thread_p, &boot_Db_parm->rootclass_hfid, NULL, false); xheap_create (thread_p, &boot_Db_parm->tde_keyinfo_hfid, NULL, false); heap_assign_address (thread_p, &boot_Db_parm->rootclass_hfid, NULL, &boot_Db_parm->rootclass_oid, 0); oid_set_root (&boot_Db_parm->rootclass_oid); heap_cache_class_info (thread_p, &boot_Db_parm->rootclass_oid, &boot_Db_parm->rootclass_hfid, FILE_HEAP, boot_Db_parm->rootclass_name); xehash_create (thread_p, &boot_Db_parm->classname_table, ...); catalog_create (thread_p, &boot_Db_parm->ctid);
/* (g) write boot_Db_parm into the system heap as a real heap record */ recdes.data = (char *) boot_Db_parm; recdes.length = DB_SIZEOF (*boot_Db_parm); heap_create_insert_context (&heapop_context, &boot_Db_parm->hfid, &boot_Db_parm->rootclass_oid, &recdes, NULL); heap_insert_logical (thread_p, &heapop_context, NULL); COPY_OID (boot_Db_parm_oid, &heapop_context.res_oid);
/* (h) vacuum data file, dropped-files vacuum tracker */ vacuum_create_file_for_vacuum_data (thread_p, &boot_Db_parm->vacuum_data_vfid); vacuum_create_file_for_dropped_files (thread_p, &boot_Db_parm->dropped_files_vfid); boot_db_parm_update_heap (thread_p);
/* (i) optional add-more-volumes from a control file */ if (file_addmore_vols != NULL) boot_parse_add_volume_extensions (thread_p, file_addmore_vols);
locator_initialize (thread_p); pgbuf_flush_all (thread_p, NULL_VOLID);
/* (j) catalog and query manager init */ oid_set_root (&boot_Db_parm->rootclass_oid); catalog_initialize (&boot_Db_parm->ctid); qmgr_initialize (thread_p); tf_install_meta_classes (); tde_initialize (thread_p, &boot_Db_parm->tde_keyinfo_hfid);
/* (k) flush + checkpoint so the create is durable */ logpb_force_flush_pages (thread_p); pgbuf_flush_all (thread_p, NULL_VOLID); fileio_synchronize_all (thread_p); logpb_checkpoint (thread_p);
boot_server_status (BOOT_SERVER_UP); return tran_index;}The eleven lettered sub-steps are the contract. (a) is what makes
the database recoverable from the moment of creation — every later
step is logged. (e) builds the single OID that every later restart
will read to find the rest of the database; the OID is stored at
boot_Db_parm_oid (a static OID in boot_sr.c) and is the only
piece of information about the database not findable from inside it.
(j) calls tf_install_meta_classes which writes the schema headers
for system classes — but the content of those classes is filled
in later by the client side (catcls_install); see
Catalog bootstrap split.
Catalog bootstrap split
Section titled “Catalog bootstrap split”The system tables (db_class, db_attribute, db_index, etc.)
are bootstrapped in two halves. The server side creates the heap
files and OIDs; the client side, on the same first-time create
call, populates the rows.
sequenceDiagram participant App as createdb (client) participant CLB as boot_initialize_client participant SRB as xboot_initialize_server participant BCV as boot_create_all_volumes participant CAT as catcls_init / catcls_install App->>CLB: create db with name, paths CLB->>CLB: lang_init, tz_load, msgcat_init, sysprm_load CLB->>CLB: area_init, locator_initialize_areas CLB->>CLB: ws_init? (no — postponed) CLB->>SRB: boot_initialize_server (network RPC for CS_MODE) SRB->>BCV: boot_create_all_volumes BCV->>BCV: log_create, log_initialize BCV->>BCV: disk_format_first_volume BCV->>BCV: xheap_create root class HFID BCV->>BCV: write boot_Db_parm header record BCV->>BCV: catalog_create (file) BCV->>BCV: tf_install_meta_classes BCV->>BCV: logpb_checkpoint BCV-->>SRB: tran_index SRB-->>CLB: tran_index, rootclass_oid, rootclass_hfid CLB->>CLB: ws_init (workspace) CLB->>CLB: sm_create_root (rootclass_oid) CLB->>CAT: install_system_metadata CAT->>CAT: au_init / au_install / au_start CAT->>CAT: tr_init CAT->>CAT: catcls_init (define list of CT_*_NAME tables) CAT->>CAT: catcls_install (build_class for each + build_vclass) CAT-->>CLB: error_code CLB->>CLB: tran_commit CLB->>CLB: sp_builtin_install CLB-->>App: NO_ERROR
install_system_metadata lives in boot_cl.c and is the join
point: it is called from boot_initialize_client after the server
side has formatted volumes and returned with the root-class OID.
The body is short and unambiguous:
// install_system_metadata — src/transaction/boot_cl.cstatic intinstall_system_metadata (void){ int error = NO_ERROR; /* Create system classes such as the root and authorization classes */ au_init (); error = au_install (); if (error != NO_ERROR) return error; error = au_start (); if (error != NO_ERROR) return error;
tr_init (); catcls_init (); error = catcls_install (); if (error != NO_ERROR) return error;
return NO_ERROR;}catcls_init (schema_system_catalog_install.cpp) is a static
table builder that emits one (name, definition) pair per system
class — CT_CLASS_NAME, CT_ATTRIBUTE_NAME, CT_DOMAIN_NAME,
CT_INDEX_NAME, CT_TRIGGER_NAME, CT_PARTITION_NAME,
CT_STORED_PROC_NAME, CT_SERIAL_NAME, CT_HA_APPLY_INFO_NAME,
CT_COLLATION_NAME, CT_CHARSET_NAME, CT_DUAL_NAME,
CT_SYNONYM_NAME, CT_SERVER_NAME — plus one (name, definition)
pair per system view (the CTV_*_NAME family). catcls_install
then loops over the list calling catalog_builder::build_class for
each, then build_vclass for each view. The class definitions
themselves are constructed by
cubschema::system_catalog_initializer::get_* factory functions
which encode column types, primary keys, indexes, and view query
specs in C++.
Why split? Because the catalog tables are real heap-resident objects. Creating them by going through the regular schema-manager write path means they share the same logging, locking, and constraint-enforcement code as user-defined tables — no special- case schema bootstrapper is needed in the server.
Server-side restart
Section titled “Server-side restart”boot_restart_server (boot_sr.c) is the path every normal start
takes. It is called from net_server_start (network_sr.c) which
is itself called from cub_server’s main() (server.c). The body
is the single longest function in the boot subsystem; we annotate
it by phase rather than line.
// boot_restart_server — src/transaction/boot_sr.c (condensed, annotated by phase)intboot_restart_server (THREAD_ENTRY *thread_p, bool print_restart, const char *db_name, bool from_backup, CHECK_ARGS *check_coll_and_timezone, BO_RESTART_ARG *r_args, bool skip_vacuum){ /* === Phase A — early infrastructure === */ lang_init (); tz_load (); msgcat_init (); sysprm_load_and_init (db_name, NULL, SYSPRM_LOAD_ALL); common_ha_mode = HA_GET_MODE (); cfg_read_directory (&dir, false); /* find DB in databases.txt */ db = cfg_find_db_list (dir, db_name); GETHOSTNAME (boot_Host_name, ...); COMPOSE_FULL_NAME (boot_Db_full_name, ..., db->pathname, db_name); boot_make_session_server_key (); /* check ha_mode in cubrid.conf vs database section */ msgcat_final (); msgcat_init (); /* reload to pick up MAX_THREADS */ css_init_conn_list (); perfmon_initialize (MAX_NTRANS); thread_p = thread_get_thread_entry_info (); er_init (...); er_clear (); event_log_init (db_name); trace_log_init (db_name); area_init (); set_area_init (); pr_area_init (); locator_initialize_areas (); tp_init (); tsc_init (); cubthread::initialize_thread_entries ();
/* === Phase B — PL server (must precede log_initialize) === */ pl_server_init (db_name);
/* === Phase C — open log + first volume; read boot_Db_parm === */ log_get_io_page_size (thread_p, boot_Db_full_name, log_path, log_prefix); logtb_define_trantable (thread_p, -1, -1); if (from_backup) logpb_restore (thread_p, ...); spage_boot (thread_p); heap_manager_initialize (); boot_mount (thread_p, LOG_DBFIRST_VOLID, boot_Db_full_name, NULL); disk_get_boot_hfid (thread_p, LOG_DBFIRST_VOLID, &boot_Db_parm->hfid); boot_get_db_parm (thread_p, boot_Db_parm, boot_Db_parm_oid); /* read header */ tde_cipher_initialize (thread_p, ...); heap_cache_class_info (thread_p, &boot_Db_parm->rootclass_oid, &boot_Db_parm->rootclass_hfid, FILE_HEAP, boot_Db_parm->rootclass_name);
/* === Phase D — charset cross-check === */ db_charset_db_header = boot_get_db_charset_from_header (thread_p, log_path, log_prefix); lang_set_charset (db_charset_db_header);
/* === Phase E — mount remaining volumes, init disk manager, vacuum === */ boot_find_rest_volumes (thread_p, ..., LOG_DBFIRST_VOLID, boot_mount, NULL); disk_manager_init (thread_p, true); logtb_initialize_global_unique_stats_table (thread_p); file_tracker_load (thread_p, &boot_Db_parm->trk_vfid); catalog_initialize (&boot_Db_parm->ctid); vacuum_initialize (thread_p, ..., &boot_Db_parm->vacuum_data_vfid, &boot_Db_parm->dropped_files_vfid, ...); oid_set_root (&boot_Db_parm->rootclass_oid); dwb_load_and_recover_pages (thread_p, log_path, log_prefix);
/* === Phase F — page-buffer / DWB / parallel-query daemons === */ pgbuf_daemons_init (); dwb_daemons_init (); parallel_query::worker_manager_global::get_manager ().init ();
/* === Phase G — RECOVERY HAND-OFF === */ log_initialize (thread_p, boot_Db_full_name, log_path, log_prefix, from_backup, r_args); /* log_initialize internally calls log_recovery if the header says so. */
boot_after_copydb (thread_p); /* only does work after copydb */
/* === Phase H — flush daemons + vacuum + CDC === */ BO_ENABLE_FLUSH_DAEMONS (); cdc_daemons_init (); vacuum_boot (thread_p);
/* === Phase I — locator, catalog cache, plan cache === */ locator_initialize (thread_p); catcls_find_and_set_cached_class_oid (thread_p); xcache_initialize (thread_p); qmgr_initialize (thread_p); qfile_initialize_list_cache (thread_p); fpcache_initialize (thread_p);
/* === Phase J — read db_root, validate charset/lang, check timezone === */ catcls_get_server_compat_info (thread_p, &db_charset_db_root, db_lang, ...); /* compare db_charset_db_header against db_charset_db_root */ lang_set_language (db_lang); if (check_coll_and_timezone->check_timezone) check_timezone_compat (tzd->checksum, timezone_checksum, "server", "database");
/* === Phase K — short housekeeping transaction === */ tran_index = logtb_assign_tran_index (thread_p, ...); boot_remove_all_temp_volumes (thread_p, REMOVE_TEMP_VOL_DEFAULT_ACTION); if (boot_Lob_path[0] != '\0') es_init (boot_Lob_path); xtran_server_commit (thread_p, false); logtb_free_tran_index (thread_p, tran_index); logtb_set_to_system_tran_index (thread_p);
/* === Phase L — meta-class compile, catalog compatibility check === */ if (!tf_Metaclass_class.mc_n_variable) tf_compile_meta_classes (); if (!skip_to_check_ct_classes_for_rebuild && catcls_Enable != true) catcls_compile_catalog_classes (thread_p); if (check_coll_and_timezone->check_db_coll) catcls_get_db_collation (thread_p, ...);
/* === Phase M — SA-mode vacuum, read-only mode, serial cache === */#if defined (SA_MODE) if (!skip_vacuum) xvacuum (thread_p);#endif if (prm_get_bool_value (PRM_ID_READ_ONLY_MODE)) logtb_disable_update (NULL); serial_initialize_cache_pool (thread_p); pl_server_wait_for_ready (); session_states_init (thread_p);
/* === Phase N — IP control, HA state, partition cache === */#if defined (SERVER_MODE) if (prm_get_bool_value (PRM_ID_ACCESS_IP_CONTROL) && !from_backup) css_set_accessible_ip_info (); css_set_ha_num_of_hosts (db->num_hosts); css_change_ha_server_state (thread_p, (HA_SERVER_STATE) prm_get_integer_value (PRM_ID_HA_SERVER_STATE), false, HA_CHANGE_MODE_IMMEDIATELY, true);#endif partition_cache_init (thread_p);
/* === Phase O — server is up === */ if (boot_Server_status == BOOT_SERVER_DOWN) boot_server_status (BOOT_SERVER_UP); return NO_ERROR;}The phase boundaries are not in the source as comments — they are imposed here because the function does fifteen distinct things. The labels exist to make the dependency story legible: phase A wakes the error reporter and the parameter loader, phase C reads the database header so subsequent phases know what they are working with, phase F starts background daemons that recovery will need running, phase G is the recovery hand-off, phase I rehydrates the in-memory catalog from the recovered pages, phase N publishes the server’s HA state to the master so clients can be told whether to connect.
The crucial sequencing fact: phase G runs before phase I. Recovery completes first, then the catalog cache is built. The reason is that catalog reads issue heap fetches, and heap fetches need page LSAs to match log LSAs — which is precisely what redo restores. Reading the catalog before redo would see a torn database.
sequenceDiagram
autonumber
participant CS as cub_server::main
participant NS as net_server_start
participant BR as boot_restart_server
participant LM as log_initialize
participant LR as log_recovery
participant CC as catcls_find_and_set_cached_class_oid
participant CSS as css_init
participant CL as Client
CS->>NS: net_server_start (server_name)
NS->>NS: er_init, msgcat_init, tz_load
NS->>NS: sysprm_load_and_init, csect_initialize
NS->>NS: net_server_init, css_initialize_server_interfaces
NS->>BR: boot_restart_server
BR->>BR: phase A — early infra (lang, msgcat, sysprm, area, tp, threads)
BR->>BR: phase B — pl_server_init
BR->>BR: phase C — log_get_io_page_size, boot_mount LOG_DBFIRST_VOLID
BR->>BR: boot_get_db_parm — read header from disk
BR->>BR: phase D — charset cross-check
BR->>BR: phase E — mount rest, disk_manager_init, vacuum_initialize
BR->>BR: phase F — pgbuf/dwb daemons up
BR->>LM: log_initialize (recovery hand-off)
LM->>LR: log_recovery (analysis → redo → undo)
LR-->>LM: rcv_phase = LOG_RESTARTED
LM-->>BR: returns
BR->>BR: phase H — flush/cdc daemons, vacuum_boot
BR->>BR: phase I — locator_initialize
BR->>CC: catcls_find_and_set_cached_class_oid
CC-->>BR: cached
BR->>BR: phase J — db_root validate, lang_set_language
BR->>BR: phase K — temp volumes cleanup, lob es_init
BR->>BR: phase L — tf_compile_meta_classes, catcls_compile_catalog_classes
BR->>BR: phase M — serial cache, session states
BR->>BR: phase N — css_change_ha_server_state, partition_cache_init
BR->>BR: phase O — boot_Server_status = UP
BR-->>NS: NO_ERROR
NS->>CSS: css_init (open port_id, accept connections)
loop forever
CL->>CSS: TCP connect
CSS->>CSS: dispatch to net_server_request
end
First-create vs. restart decision
Section titled “First-create vs. restart decision”Both flows live in the same module; the choice between them is made
by which executable / utility calls in. The decision is not
encoded in boot_sr.c itself — boot_sr.c exposes both entry
points and trusts the caller.
flowchart TB
CALL[Process invocation]
Q1{which executable?}
CDB["createdb client utility"]
CSV["cub_server (server.c)"]
RDB["restoredb client utility"]
CB["client app (db_restart_client)"]
CIN["boot_initialize_client\n(client side)"]
CRT["boot_restart_client\n(client side)"]
XINIT["xboot_initialize_server\n(server side, network or in-process)"]
XRST["boot_restart_server"]
XRSTBK["xboot_restart_from_backup → boot_restart_server with from_backup=true"]
REG["xboot_register_client (per-connection)"]
CALL --> Q1
Q1 -->|createdb| CDB --> CIN --> XINIT
Q1 -->|cub_server| CSV --> XRST
Q1 -->|restoredb| RDB --> XRSTBK
Q1 -->|app connect| CB --> CRT --> REG
The key insight: cub_server always calls boot_restart_server
unconditionally, and boot_restart_server in turn always calls
log_initialize, which internally decides whether to dispatch
into log_recovery. The server has no compile-time knowledge of
whether a recovery is needed — that knowledge lives in the log
header (hdr.is_shutdown).
Client-side connect flow
Section titled “Client-side connect flow”sequenceDiagram
autonumber
participant App as Client app
participant BRC as boot_restart_client
participant CCSS as boot_client_initialize_css
participant Net as net_client_init
participant SR as Server (via xboot_register_client)
participant WS as ws_init / sm_init
participant AU as au_start
participant Cache as boot_client_find_and_cache_class_oids
App->>BRC: boot_restart_client (BOOT_CLIENT_CREDENTIAL)
BRC->>BRC: shutdown_client if previously restarted
BRC->>BRC: lang_init, tz_load, msgcat_init, sysprm_load
BRC->>BRC: er_init, area_init, locator_initialize_areas
BRC->>BRC: perfmon_initialize, cfg_find_db
BRC->>BRC: fill in db_user, login_name, host_name, process_id, ip
BRC->>BRC: dl_initiate_module
loop preferred_hosts then db->hosts (max 2 retries)
BRC->>CCSS: boot_client_initialize_css
CCSS->>Net: net_client_init (db_name, host)
Net->>Net: ping_server_with_handshake (check capabilities)
alt connected
Net-->>CCSS: NO_ERROR
else
Net-->>CCSS: ER_NET_*
end
end
BRC->>BRC: er_set BO_CONNECTED_TO
BRC->>BRC: tp_init, tsc_init
BRC->>WS: ws_init
BRC->>SR: boot_register_client (NET_SERVER_BO_REGISTER_CLIENT RPC)
SR-->>BRC: tran_index, BOOT_SERVER_CREDENTIAL
BRC->>BRC: lang_set_charset (server.charset)
BRC->>BRC: lang_set_language (server.lang)
BRC->>BRC: db_set_page_size (server.page_size)
BRC->>BRC: rel_set_disk_compatible (server.disk_compatibility)
BRC->>BRC: boot_client (cache tran settings)
BRC->>WS: sm_init (root_class_oid, root_class_hfid)
BRC->>AU: au_init, au_start
BRC->>Cache: boot_client_find_and_cache_class_oids
BRC->>BRC: db_find_or_create_session
BRC->>BRC: boot_check_locales, boot_check_timezone_checksum
BRC->>BRC: tr_init (trigger manager)
BRC->>BRC: es_init (lob path from server credential)
BRC->>BRC: tran_commit
BRC->>BRC: tran_reset_isolation, tran_reset_wait_times
BRC->>BRC: showstmt_metadata_init
BRC-->>App: NO_ERROR
The host-selection loop is a two-pass strategy. First pass uses
BOOT_CHECK_HA_DELAY_CAP (the client only accepts hosts whose
replication lag is acceptable); second pass relaxes the cap if no
acceptable host was found. Inside each pass, the order of hosts is
either preferred-then-default or sequential-then-random, depending
on client_credential->connect_order and on whether
preferred_hosts is set.
// boot_register_client — src/communication/network_interface_cl.c (CS_MODE branch)intboot_register_client (BOOT_CLIENT_CREDENTIAL *client_credential, int client_lock_wait, TRAN_ISOLATION client_isolation, TRAN_STATE *tran_state, BOOT_SERVER_CREDENTIAL *server_credential){#if defined(CS_MODE) /* pack credential as packable_object, send NET_SERVER_BO_REGISTER_CLIENT */ packing_packer packer; cubmem::extensible_block ext_blk; packer.set_buffer_and_pack_all (ext_blk, *client_credential, client_lock_wait, (int) client_isolation);
net_client_request2 (NET_SERVER_BO_REGISTER_CLIENT, ext_blk.get_ptr (), packer.get_current_size (), reply, OR_ALIGNED_BUF_SIZE (a_reply), NULL, 0, &area, &area_size);
/* unpack server reply: tran_index, tran_state, db_full_name, host_name, lob_path, process_id, root_class_oid, root_class_hfid, page_size, log_page_size, disk_compatibility, ha_server_state, db_charset, db_lang */ ... return tran_index;#else /* SA_MODE */ enter_server_no_thread_entry (); tran_index = xboot_register_client (NULL, client_credential, client_lock_wait, client_isolation, tran_state, server_credential); exit_server_no_thread_entry (); return tran_index;#endif}The two SA/CS mode branches converge on xboot_register_client —
SA mode calls it directly, CS mode reaches it through the network
dispatcher (NET_SERVER_BO_REGISTER_CLIENT is the server’s
sboot_register_client handler in the network table, which
unpacks the credential and calls xboot_register_client).
Credential & login flow
Section titled “Credential & login flow”The BOOT_CLIENT_CREDENTIAL extends clientids (in
client_credentials.hpp):
// boot_client_credential — src/transaction/client_credentials.hppstruct boot_client_credential : public clientids{ std::string db_name; /* DB_MAX_IDENTIFIER_LENGTH */ std::string db_password; /* DB_MAX_PASSWORD_LENGTH */ char *preferred_hosts; /* LINE_MAX */ int connect_order; /* ... pack / unpack methods ... */};
struct clientids : public cubpacking::packable_object{ db_client_type client_type; std::string client_info; std::string db_user; std::string program_name; std::string login_name; std::string host_name; std::string client_ip_addr; int process_id; /* ... */};The struct is packable_object — i.e., it knows how to serialise
itself for the network. The 13 client types
(DB_CLIENT_TYPE_DEFAULT, DB_CLIENT_TYPE_CSQL,
DB_CLIENT_TYPE_BROKER, DB_CLIENT_TYPE_LOG_APPLIER,
DB_CLIENT_TYPE_ADMIN_UTILITY, etc., enumerated in
db_client_type.hpp) parameterise behaviour all the way down to
HA-state checks. The BOOT_NORMAL_CLIENT_TYPE and friends macros in
boot.h slice the type space:
// BOOT_*_CLIENT_TYPE macros — src/transaction/boot.h#define BOOT_NORMAL_CLIENT_TYPE(t) /* default, csql, broker, loaddb-utility, ... */#define BOOT_READ_ONLY_CLIENT_TYPE(t) /* read-only csql/broker/replica */#define BOOT_ADMIN_CLIENT_TYPE(t) /* admin csql / utility */#define BOOT_LOG_REPLICATOR_TYPE(t) /* log copier / applier */#define BOOT_CSQL_CLIENT_TYPE(t)#define BOOT_BROKER_AND_DEFAULT_CLIENT_TYPE(t)#define BOOT_REPLICA_ONLY_BROKER_CLIENT_TYPE(t)#define BOOT_WRITE_ON_STANDY_CLIENT_TYPE(t)The auth glue is in xboot_register_client:
// xboot_register_client — src/transaction/boot_sr.c (auth + HA snippet)db_user_save = client_credential->get_db_user ();if (!client_credential->db_user.empty ()) { intl_identifier_upper (client_credential->get_db_user (), db_user_upper); client_credential->db_user = db_user_upper;}
tran_index = logtb_assign_tran_index (thread_p, NULL_TRANID, TRAN_ACTIVE, client_credential, tran_state, client_lock_wait, client_isolation);/* ... fill server_credential ... */
if (BOOT_NORMAL_CLIENT_TYPE (client_credential->client_type)) { if (css_check_ha_server_state_for_client (thread_p, 1) != NO_ERROR) { logtb_release_tran_index (thread_p, tran_index); *tran_state = TRAN_UNACTIVE_UNKNOWN; return NULL_TRAN_INDEX; }}if (client_credential->client_type == DB_CLIENT_TYPE_LOG_APPLIER) { css_notify_ha_log_applier_state (thread_p, HA_LOG_APPLIER_STATE_UNREGISTERED);}The actual user authentication — password verification, role
membership, schema visibility — happens later, on the client side,
when boot_restart_client calls au_start with the (already
upper-cased) user name. The server’s role at register time is to
record the credential against the transaction descriptor; auth
enforcement is read-side, in the lock manager (lock acquisition
checks user membership) and in the schema manager (catalog access
checks visibility).
HA state at boot
Section titled “HA state at boot”cub_master and cub_server are separate processes. The master
runs heartbeat (hb_master_init), maintains the multi-host topology,
and is the place the cluster’s authoritative HA state lives. A
cub_server at restart consults the master in two places:
boot_make_session_server_keyin phase A — produces a per-process session key fromtime()plus the host IP, used as the server’s identity to the master.css_change_ha_server_statein phase N — sends the requested HA state (read fromPRM_ID_HA_SERVER_STATEparameter, defaultHA_SERVER_STATE_IDLE) to the master and waits for confirmation. The transition is gated by a state-machine table incss_transit_ha_server_state(server_support.c); requests likeIDLE → ACTIVEsucceed inline,IDLE → STANDBY → TO_BE_STANDBYmay require the master to confirm a peer is up.
Once the server has its HA state, it can answer
css_check_ha_server_state_for_client queries — the call inside
xboot_register_client that may reject a normal client when the
server is in MAINTENANCE or DEAD state.
Shutdown — the symmetric tear-down
Section titled “Shutdown — the symmetric tear-down”// xboot_shutdown_server — src/transaction/boot_sr.c (condensed)boolxboot_shutdown_server (REFPTR (THREAD_ENTRY, thread_p), ER_FINAL_CODE is_er_final){ if (!BO_IS_SERVER_RESTARTED ()) return true;
/* phase 1: abort all live transactions, stop vacuum workers */ logtb_set_to_system_tran_index (thread_p); log_abort_all_active_transaction (thread_p); vacuum_stop_workers (thread_p);
/* phase 2: drain caches before removing temp volumes */ logtb_reflect_global_unique_stats_to_btree (thread_p); qfile_finalize_list_cache (thread_p); xcache_finalize (thread_p); fpcache_finalize (thread_p); session_states_finalize (thread_p);
/* phase 3: remove temp volumes, lob temp dir */ boot_remove_all_temp_volumes (thread_p, REMOVE_TEMP_VOL_DEFAULT_ACTION); fileio_lob_remove_matching_dir (BOOT_LOB_TEMP_DIR_KEYWORD);
/* phase 4: stop HA delay registration, then vacuum master */ log_stop_ha_delay_registration (); vacuum_stop_master (thread_p);
/* phase 5: stop daemons */#if defined(SERVER_MODE) pgbuf_daemons_destroy (); cdc_daemons_destroy (); pl_server_destroy (); parallel_query::worker_manager_global::get_manager ().destroy ();#endif
/* phase 6: log_final writes the shutdown checkpoint, sets is_shutdown=true */ log_final (thread_p); dwb_destroy (thread_p);
/* phase 7: release subsystems via boot_server_all_finalize */ boot_server_all_finalize (thread_p, is_er_final, BOOT_SHUTDOWN_EXCEPT_COMMON_MODULES);
#if defined (SA_MODE) cubthread::finalize (); thread_p = NULL;#endif return true;}Phase 6’s log_final is the load-bearing call: it writes the final
LOG_END_CHKPT record and updates the log header
hdr.is_shutdown = true. The next restart that sees this flag set
in the header may bypass log_recovery entirely.
boot_server_all_finalize releases everything in approximately the
reverse order of the boot:
// boot_server_all_finalize — src/transaction/boot_sr.c (condensed)voidboot_server_all_finalize (THREAD_ENTRY *thread_p, ER_FINAL_CODE is_er_final, BOOT_SERVER_SHUTDOWN_MODE shutdown_common_modules){ logtb_finalize_global_unique_stats_table (thread_p); locator_finalize (thread_p); spage_finalize (thread_p); catalog_finalize (); qmgr_finalize (thread_p); heap_manager_finalize (); fileio_dismount_all (thread_p); disk_manager_final (); boot_server_status (BOOT_SERVER_DOWN);
catcls_finalize_class_oid_to_oid_hash_table (thread_p); serial_finalize_cache_pool (); partition_cache_finalize (thread_p);
thread_return_lock_free_transaction_entries (); lf_destroy_transaction_systems (); perfmon_finalize ();
#if defined(SERVER_MODE) shutdown_common_modules = BOOT_SHUTDOWN_ALL_MODULES;#endif
if (shutdown_common_modules == BOOT_SHUTDOWN_ALL_MODULES) { es_final (); tp_final (); locator_free_areas (); set_final (); sysprm_final (); area_final (); msgcat_final (); if (is_er_final == ER_ALL_FINAL) er_final (ER_ALL_FINAL); lang_final (); tz_unload (); }
#if defined(SERVER_MODE) css_free_accessible_ip_info (); event_log_final (); trace_log_final ();#endif}The mirror property: every *_init / *_initialize /
*_load / area_init call in the boot path has a matching
*_final / *_finalize / *_unload here. The pairing is
maintainability glue — a developer adding a new subsystem must add
both halves or face leaks at shutdown. The BOOT_SHUTDOWN_*
enum gates whether to release the common modules (locale,
sysprm, msgcat, er) or only the server-private ones; this matters
in SA mode where the same process is also a client and may want to
keep the common modules alive across multiple SA-mode connect
cycles.
The client-side mirror lives in boot_shutdown_client →
boot_client_all_finalize:
// boot_client_all_finalize — src/transaction/boot_cl.c (condensed)voidboot_client_all_finalize (int final_level){ /* signal handlers ignored during finalize */ signal (SIGTERM, SIG_IGN); signal (SIGABRT, SIG_IGN); signal (SIGINT, SIG_IGN);
if (BOOT_IS_CLIENT_RESTARTED () || boot_Is_client_all_final == false) { /* free server-credential strings */ db_private_free_and_init (NULL, boot_Server_credential.db_full_name); db_private_free_and_init (NULL, boot_Server_credential.host_name); /* ... */
showstmt_metadata_final (); tran_free_savepoint_list (); sm_flush_static_methods (); set_final (); parser_final ();
if (final_level != OPTIONAL_FINALIZATION) { tr_final (); /* trigger manager */ au_final (); /* authorization */ sm_final (); /* schema manager */ method_callback_final (); ws_final (); /* workspace */ es_final (); /* external storage */ tp_final (); /* type / domain */ }
locator_free_areas (); sysprm_final (); perfmon_finalize (); area_final (); msgcat_final (); if (final_level != EXCEPT_ER_FINALIZATION) er_final (ER_ALL_FINAL); lang_final (); tz_unload ();
/* clear server credential, mark client as inactive */ memset (&boot_Server_credential, 0, sizeof (boot_Server_credential)); memset (boot_Server_credential.server_session_key, 0xFF, SERVER_SESSION_KEY_SIZE); boot_client (NULL_TRAN_INDEX, TRAN_LOCK_INFINITE_WAIT, TRAN_DEFAULT_ISOLATION_LEVEL ()); boot_Is_client_all_final = true; }}The final_level enum (ALL_FINALIZATION,
EXCEPT_ER_FINALIZATION, OPTIONAL_FINALIZATION) controls how
aggressively to tear down. EXCEPT_ER_FINALIZATION keeps the error
manager alive so a subsequent fatal-error path can still report;
OPTIONAL_FINALIZATION skips the schema/auth/trigger
finalisers because they were never set up (used after a failed
boot).
cub_master boot
Section titled “cub_master boot”cub_master is the third executable in the boot story.
It is much shorter than the server boot but it is on the path of
every client connect because the master is the registry that maps
(database_name, host) to live server processes.
// master.c::main — src/executables/master.c (condensed)intmain (int argc, char **argv){ utility_initialize ();#if defined(WINDOWS) css_windows_startup ();#endif master_util_config_startup (argv[1], &port_id); GETHOSTNAME (hostname, ...); er_init (errlog, ER_NEVER_EXIT);
if (__gv_cvar.css_does_master_exist (port_id)) return EXIT_FAILURE; /* duplicate master refused */
msgcat_final (); er_final (ER_ALL_FINAL);#if !defined(WINDOWS) if (envvar_get ("NO_DAEMON") == NULL) css_daemon_start ();#endif utility_initialize (); er_init (errlog, ER_NEVER_EXIT); time (&css_Start_time);
css_master_init (port_id, css_Master_socket_fd); /* signal handlers, sockets */
if (envvar_get ("NO_DAEMON") != NULL) os_set_signal_handler (SIGINT, css_master_cleanup);
#if !defined(WINDOWS) if (!HA_DISABLED ()) hb_master_init (); /* heartbeat */ auto_Restart_server = prm_get_bool_value (PRM_ID_AUTO_RESTART_SERVER); if (auto_Restart_server) master_Server_monitor.reset (new server_monitor ());#endif
/* register the two master sockets with the request queue */ conn = __gv_cvar.css_make_conn (css_Master_socket_fd[0]); css_add_request_to_socket_queue (conn, false, NULL, ...); conn = __gv_cvar.css_make_conn (css_Master_socket_fd[1]); css_add_request_to_socket_queue (conn, false, NULL, ...);
css_master_loop (); /* never returns under normal operation */ css_master_cleanup (SIGINT); ...}The master does not call boot_restart_server. Its job is purely
to be the dispatcher — clients connect to the master, the master
forwards them to a cub_server that is bound to the named database.
Heartbeat runs only when HA is not disabled; hb_master_init
brings up the heartbeat threads that exchange health pings with
peer masters. The cubrid-heartbeat.md doc covers heartbeat in
depth.
cub_server main glue
Section titled “cub_server main glue”Finally, the executable that ties phase A through phase O together:
// server.c::main — src/executables/server.c (condensed)intmain (int argc, char **argv){#if !defined(WINDOWS)#if !defined (NDEBUG) register_abort_signal_handler (SIGABRT);#else register_fatal_signal_handler (SIGABRT);#endif register_fatal_signal_handler (SIGILL); register_fatal_signal_handler (SIGFPE); register_fatal_signal_handler (SIGBUS); register_fatal_signal_handler (SIGSEGV); register_fatal_signal_handler (SIGSYS);#endif
if (argc < 2) { PRINT_AND_LOG_ERR_MSG ("Usage: server databasename\n"); return 1; }
fprintf (stdout, "\nThis may take a long time depending on the amount " "of recovery works to do.\n"); binary_name = basename (argv[0]); envvar_bindir_file (executable_path, PATH_MAX, binary_name); database_name = argv[1];
#if !defined(WINDOWS) hb_set_exec_path (executable_path); hb_set_argv (argv); css_set_exec_path (executable_path); css_set_argv (argv); setsid (); /* new process group, decoupled from terminal */#endif
return net_server_start (database_name);}main is forty effective lines. Everything difficult is in
net_server_start → boot_restart_server → log_initialize →
log_recovery → back through phases H..O. The signal handlers
installed at the top capture fatal signals and print a crash
callstack via er_print_crash_callstack; the handlers are
installed before any subsystem so that even a phase A failure
produces a useful crash log.
Source Walkthrough
Section titled “Source Walkthrough”Anchor on symbol names, not line numbers. Line numbers go in the position-hint table at the bottom.
Server boot — entry/restart/shutdown
Section titled “Server boot — entry/restart/shutdown”xboot_initialize_server(boot_sr.c) — first-create driver.boot_restart_server(boot_sr.c) — restart driver, the long one.xboot_restart_from_backup(boot_sr.c) — wrapsboot_restart_serverwithfrom_backup=true.boot_create_all_volumes(boot_sr.c) — formats first volume, initialises log, creates root-class heap, writesboot_Db_parm, emits initial checkpoint.boot_after_copydb(boot_sr.c) — reseed adjustments after a copydb (clears applylogdb LSAs).boot_remove_all_volumes(boot_sr.c) — destructive volume cleanup used on overwrite-on-create.xboot_shutdown_server(boot_sr.c) — shutdown driver.boot_server_all_finalize(boot_sr.c) — release every server module.boot_donot_shutdown_server_at_exit(boot_sr.c) — disables the atexit hook for fatal-failure paths.boot_shutdown_server_at_exit(boot_sr.c) — atexit hook registered byboot_server_status (BOOT_SERVER_UP).boot_get_db_parm/boot_db_parm_update_heap(boot_sr.c) — read/write the persistent header record.boot_make_session_server_key(boot_sr.c) — synthesises the per-process server identity fromtime()+ IP.boot_mount(boot_sr.c) —fileio_mountwrapper used as the callback intoboot_find_rest_volumes.boot_find_rest_permanent_volumes(boot_sr.c) — walks the on-disk volume list to mount everything past the first volume.boot_remove_all_temp_volumes(boot_sr.c) — phase K cleanup.boot_get_db_charset_from_header(boot_sr.c) — reads charset from the log header, used in phase D.boot_ctrl_c_in_init_server(boot_sr.c) — SIGINT handler that longjmps to the create’s cancel path.boot_dbparm_save_volume/boot_get_new_volume_name_and_id(boot_sr.c) — used by add-volume operations.
Server boot — register/unregister
Section titled “Server boot — register/unregister”xboot_register_client(boot_sr.c) — assigns a tran-index, fills the server credential, runs HA-state check.xboot_unregister_client(boot_sr.c) — releases tran-index and conn-entry on disconnect.xboot_notify_unregister_client(boot_sr.c) — server-mode signal that the client conn is closing; flips conn state toCONN_CLOSING.xboot_get_server_session_key(boot_sr.c).
Server boot — globals
Section titled “Server boot — globals”boot_Server_status(boot_sr.c) —BOOT_SERVER_UP / DOWN / MAINTENANCE.boot_Db_parm/boot_Struct_db_parm/boot_Db_parm_oid(boot_sr.c) — persistent header.boot_Db_full_name(boot_sr.c) —<db_path>/<db_name>.boot_Lob_path(boot_sr.c) — external-storage prefix.boot_Server_session_key(boot_sr.c) — 8-byte per-process key.boot_Host_name(boot_sr.c) — populated byGETHOSTNAME.boot_Init_server_jmpbuf/boot_Init_server_is_canceled(boot_sr.c) — cancel-on-Ctrl-C scaffolding.boot_Enabled_flush_daemons(boot_sr.c) — gating flag forBO_ENABLE_FLUSH_DAEMONS/BO_DISABLE_FLUSH_DAEMONS.
Client boot — entry/restart/shutdown
Section titled “Client boot — entry/restart/shutdown”boot_initialize_client(boot_cl.c) — first-time create driver; callsboot_initialize_server(network or in-process) and theninstall_system_metadata.boot_restart_client(boot_cl.c) — normal client restart; drives the host-selection loop andboot_register_client.boot_shutdown_client(boot_cl.c) — graceful shutdown.boot_shutdown_client_at_exit(boot_cl.c) — atexit hook registered byboot_client.boot_donot_shutdown_client_at_exit(boot_cl.c) — fatal-failure override.boot_server_die_or_changed(boot_cl.c) — called when the server connection drops mid-session.boot_client_all_finalize(boot_cl.c) — release every client module.boot_client(boot_cl.c) — caches tran settings into the transaction module; registers the atexit hook.boot_client_initialize_css(boot_cl.c) — host-selection +net_client_init+ handshake.install_system_metadata(boot_cl.c) — callsau_install,tr_init,catcls_init,catcls_install.boot_client_find_and_cache_class_oids(boot_cl.c) — populates the workspace cache from the now-loaded catalog.boot_check_locales/boot_check_timezone_checksum(boot_cl.c).boot_build_catalog_classes/boot_destroy_catalog_classes(boot_cl.c) — SA-mode catalog rebuild path used by thesynccolldb/ migration utilities.
Client boot — globals
Section titled “Client boot — globals”boot_Server_credential(boot_cl.c) — populated from the server’s reply toNET_SERVER_BO_REGISTER_CLIENT.boot_Host_connected/boot_Host_name/boot_Ip_address(boot_cl.c).boot_User_volid/boot_Volume_label(boot_cl.c).boot_Is_client_all_final/boot_Set_client_at_exit/boot_Process_id(boot_cl.c).
Network glue
Section titled “Network glue”boot_register_client(network_interface_cl.c) — CS-mode packs the credential, sendsNET_SERVER_BO_REGISTER_CLIENT, unpacks the server credential reply. SA-mode callsxboot_register_clientdirectly.boot_unregister_client/boot_initialize_server(network wrappers) — same pattern asboot_register_client.net_server_start(network_sr.c) — owns the boot order on the server side: er_init, msgcat_init, sysprm_load_and_init, sync_initialize_sync_stats, csect_initialize_static_critical_sections, net_server_init, css_initialize_server_interfaces,boot_restart_server,css_init.css_init(server_support.c) — opens the listening port, enters the server request loop. Returns only on shutdown.css_initialize_server_interfaces(server_support.c) — setscss_Server_request_handler = net_server_request.css_change_ha_server_state/css_transit_ha_server_state(server_support.c) — the boot’s last call into HA.
Catalog bootstrap (catcls)
Section titled “Catalog bootstrap (catcls)”catcls_init(schema_system_catalog_install.cpp) — populates the staticclistandvclistof system class/view definitions.catcls_install(schema_system_catalog_install.cpp) — for each entry inclist, callscatalog_builder::create_and_mark_system_classandcatalog_builder::build_class; same for vclist viabuild_vclass. Runs withAU_DISABLEand asAu_dba_user.catcls_compile_catalog_classes(catalog_class.c) — restart- time rehydration of the cached catalog metadata; runs in phase L of restart.catcls_find_and_set_cached_class_oid(catalog_class.c) — caches the OIDs of the system classes used by collation lookup (phase I).catcls_get_server_compat_info(catalog_class.c) — reads charset, language and timezone checksum fromdb_root(phase J).catcls_initialize_class_oid_to_oid_hash_table/catcls_finalize_class_oid_to_oid_hash_table(catalog_class.c).catcls_Enable(catalog_class.c) — global gate,falseuntil a successfulcatcls_compile_catalog_classeshas run.
Executables
Section titled “Executables”maininsrc/executables/server.c—cub_serverentry; signal handlers, thennet_server_start (argv[1]).maininsrc/executables/master.c—cub_masterentry; utility init, master socket pair,css_master_init,hb_master_init,css_master_loop.
Position hints as of 2026-04-30
Section titled “Position hints as of 2026-04-30”| Symbol | File | Line |
|---|---|---|
xboot_initialize_server | src/transaction/boot_sr.c | 1385 |
boot_restart_server | src/transaction/boot_sr.c | 1969 |
xboot_restart_from_backup | src/transaction/boot_sr.c | 2808 |
xboot_shutdown_server | src/transaction/boot_sr.c | 3044 |
xboot_get_server_session_key | src/transaction/boot_sr.c | 3121 |
xboot_register_client | src/transaction/boot_sr.c | 3149 |
xboot_unregister_client | src/transaction/boot_sr.c | 3313 |
xboot_notify_unregister_client | src/transaction/boot_sr.c | 3413 |
boot_server_all_finalize | src/transaction/boot_sr.c | 3841 |
boot_create_all_volumes | src/transaction/boot_sr.c | 4859 |
boot_remove_all_volumes | src/transaction/boot_sr.c | 5161 |
boot_get_db_charset_from_header | src/transaction/boot_sr.c | 5882 |
boot_client_type_to_string | src/transaction/boot_sr.c | 5914 |
boot_get_new_volume_name_and_id | src/transaction/boot_sr.c | 5969 |
boot_dbparm_save_volume | src/transaction/boot_sr.c | 6043 |
boot_get_db_parm | src/transaction/boot_sr.c | 295 |
boot_find_root_heap | src/transaction/boot_sr.c | 325 |
boot_reset_db_parm | src/transaction/boot_sr.c | 447 |
boot_db_name | src/transaction/boot_sr.c | 459 |
boot_db_full_name | src/transaction/boot_sr.c | 470 |
boot_get_lob_path | src/transaction/boot_sr.c | 479 |
boot_check_permanent_volumes | src/transaction/boot_sr.c | 1193 |
boot_mount | src/transaction/boot_sr.c | 1264 |
boot_ctrl_c_in_init_server | src/transaction/boot_sr.c | 1313 |
boot_make_session_server_key | src/transaction/boot_sr.c | 1931 |
boot_server_status | src/transaction/boot_sr.c | 227 |
boot_shutdown_server_at_exit | src/transaction/boot_sr.c | 255 |
boot_donot_shutdown_server_at_exit | src/transaction/boot_sr.c | 275 |
BOOT_DB_PARM struct | src/transaction/boot_sr.c | 117 |
BOOT_SERVER_STATUS enum | src/transaction/boot_sr.h | 67 |
BOOT_SERVER_SHUTDOWN_MODE enum | src/transaction/boot_sr.h | 72 |
BO_RESTART_ARG struct | src/transaction/boot_sr.h | 112 |
BOOT_DB_PATH_INFO struct | src/transaction/boot.h | 117 |
BOOT_SERVER_CREDENTIAL struct | src/transaction/boot.h | 152 |
HA_SERVER_STATE enum | src/transaction/boot.h | 131 |
BOOT_NORMAL_CLIENT_TYPE macro | src/transaction/boot.h | 34 |
boot_initialize_client | src/transaction/boot_cl.c | 275 |
boot_restart_client | src/transaction/boot_cl.c | 690 |
boot_shutdown_client | src/transaction/boot_cl.c | 1352 |
boot_shutdown_client_at_exit | src/transaction/boot_cl.c | 1413 |
boot_server_die_or_changed | src/transaction/boot_cl.c | 1458 |
boot_client_all_finalize | src/transaction/boot_cl.c | 1493 |
boot_client_initialize_css | src/transaction/boot_cl.c | 1590 |
install_system_metadata | src/transaction/boot_cl.c | 204 |
boot_client | src/transaction/boot_cl.c | 187 |
boot_check_locales | src/transaction/boot_cl.c | 2047 |
boot_check_timezone_checksum | src/transaction/boot_cl.c | 2115 |
boot_client_find_and_cache_class_oids | src/transaction/boot_cl.c | 2147 |
boot_build_catalog_classes | src/transaction/boot_cl.c | 1769 |
boot_destroy_catalog_classes | src/transaction/boot_cl.c | 1835 |
boot_get_host_connected | src/transaction/boot_cl.c | 1971 |
boot_get_lob_path (cl) | src/transaction/boot_cl.c | 1988 |
boot_get_host_name | src/transaction/boot_cl.c | 2006 |
boot_get_ip | src/transaction/boot_cl.c | 2021 |
boot_Server_credential | src/transaction/boot_cl.c | 123 |
boot_register_client (network wrapper) | src/communication/network_interface_cl.c | 3957 |
boot_unregister_client (network wrapper) | src/communication/network_interface_cl.c | 4037 |
net_server_start | src/communication/network_sr.c | 1058 |
net_server_init | src/communication/network_sr.c | 75 |
css_init | src/connection/server_support.c | 554 |
css_initialize_server_interfaces | src/connection/server_support.c | 516 |
css_change_ha_server_state | src/connection/server_support.c | (HA section) |
css_transit_ha_server_state | src/connection/server_support.c | 1643 |
clientids struct | src/transaction/client_credentials.hpp | 38 |
boot_client_credential struct | src/transaction/client_credentials.hpp | 85 |
clientids::set_ids | src/transaction/client_credentials.cpp | 90 |
clientids::set_system_internal | src/transaction/client_credentials.cpp | 165 |
boot_client_credential::pack | src/transaction/client_credentials.cpp | 263 |
catcls_init | src/object/schema_system_catalog_install.cpp | 257 |
catcls_install | src/object/schema_system_catalog_install.cpp | 315 |
catcls_compile_catalog_classes | src/storage/catalog_class.c | 4737 |
catcls_get_server_compat_info | src/storage/catalog_class.c | 4865 |
catcls_find_and_set_cached_class_oid | src/storage/catalog_class.c | 5728 |
catcls_initialize_class_oid_to_oid_hash_table | src/storage/catalog_class.c | 260 |
catcls_finalize_class_oid_to_oid_hash_table | src/storage/catalog_class.c | 286 |
catcls_Enable | src/storage/catalog_class.c | 108 |
main (cub_server) | src/executables/server.c | 276 |
main (cub_master) | src/executables/master.c | 1207 |
css_master_init | src/executables/master.c | 259 |
css_master_loop | src/executables/master.c | 1159 |
Cross-check Notes
Section titled “Cross-check Notes”This section cross-checks claims in this doc against the recovery manager and heartbeat docs already in the knowledge base.
Recovery hand-off matches cubrid-recovery-manager.md. That doc
states “log_recovery … is called from log_initialize in
log_manager.c if the active log header indicates the database
was not cleanly shut down (is_shutdown == false)”. The boot
side calls log_initialize exactly once, in phase G of
boot_restart_server, between the page-buffer / DWB daemon init
(phase F) and the catalog cache rehydration (phase I). This
ordering is what makes the textbook three-pass safe — the buffer
pool is alive so redo can fix-and-apply pages, but the catalog
cache is empty so there is no in-memory state to invalidate
mid-recovery. The crucial invariant: log_recovery runs before
catcls_find_and_set_cached_class_oid and
catcls_compile_catalog_classes — verified in the source
walkthrough above. The recovery doc’s three-pass timeline diagram
is consistent with the boot’s phase G being a single opaque
“recover everything” step.
The five recovery phases. cubrid-recovery-manager.md lists
LOG_RECVPHASE { LOG_RESTARTED, ANALYSIS, REDO, UNDO, FINISH_2PC } as the recovery-phase enum. Boot does not write any
of these directly — they are written by log_recovery itself.
However, boot reads log_Gl.rcv_phase indirectly via the
BO_IS_SERVER_RESTARTED macro (which keys off boot_Server_status,
not rcv_phase); the two are independent. The boot side considers
the server “up” at the moment boot_server_status (BOOT_SERVER_UP)
is called in phase O (or in boot_create_all_volumes step k for
first-create), which is strictly later than the moment
log_Gl.rcv_phase = LOG_RESTARTED is set inside log_recovery.
So BO_IS_SERVER_RESTARTED returning true implies
rcv_phase == LOG_RESTARTED but not the reverse.
HA state transitions and cubrid-heartbeat.md.
cubrid-heartbeat.md documents the heartbeat threads in
master_heartbeat.c. Boot interacts with heartbeat in two places:
the master executable’s hb_master_init call at master start-up,
and the server’s css_change_ha_server_state call in phase N of
restart. The server’s transition table
(css_transit_ha_server_state) is where the boot/heartbeat
handshake is enforced. A server that requests IDLE → ACTIVE
without coordination with the master may be rejected if the master
already promoted a peer; this is the only place the boot path can
fail after recovery succeeded.
xboot_register_client and the lock manager. The
cubrid-lock-manager.md doc describes lock-resource lookup keyed
by OID. Boot’s role is to assign the transaction descriptor
(logtb_assign_tran_index) which is the credential-bearing struct
locks reference for ownership. The credential-upper-casing in
xboot_register_client (running intl_identifier_upper on
db_user) means lock-owner equality compares uppercase identifiers
— a subtle invariant: the lock manager would think two transactions
have different users if one capitalised differently from the
other. The uppercase-at-register protocol prevents this.
boot_Db_parm interaction with cubrid-disk-manager.md.
The disk manager doc covers volume formatting (disk_format_*,
disk_get_boot_hfid). Boot owns the content of the system header
(BOOT_DB_PARM) but the disk manager owns the format of the
first page of the first volume. The disk_set_boot_hfid /
disk_get_boot_hfid pair is the cross-module boundary: boot writes
the HFID once (step e of boot_create_all_volumes), restart reads
it once (phase C of boot_restart_server).
Open Questions
Section titled “Open Questions”-
Recovery parallelism configuration.
log_recovery_redo_parallel.{cpp,hpp}is invoked transparently fromlog_initialize→log_recovery→log_recovery_redo. Boot does not set up any worker pool size for it. Investigation: readlog_recovery_redo_parallel.cpp’s constructor; checksystem_parameter.{c,h}for aPRM_ID_LOG_RECOVERY_*parameter. Cross-cuts withcubrid-recovery-manager.mdopen question #1. -
Which HA states block restart entirely vs. allow degraded operation? The
css_transit_ha_server_statetable inserver_support.cadmits or rejects requests based on the current state and HA policy. Fromboot_restart_serverphase N alone we cannot tell whetherHA_SERVER_STATE_TO_BE_STANDBYis a pause point that waits for the master, or a transient state that proceeds toSTANDBYbefore phase O. Investigation: trace state transitions in the heartbeat code; cross-reference withcubrid-heartbeat.md. -
Behaviour of
BO_IS_SERVER_RESTARTED()during recovery.boot_server_status (BOOT_SERVER_UP)is set in phase O — after recovery has completed. Subsystems consulted during redo (the buffer manager, the lock manager) therefore seeBO_IS_SERVER_RESTARTED == falsewhile redo is replaying page changes. This is fine for redo (which uses page LSAs, not the boot status flag), but it means a defensive checkif (BO_IS_SERVER_RESTARTED ()) ...in any subsystem is a reliable way to skip recovery-time logic. Open: are there subsystems that should be active during recovery but are gated onBO_IS_SERVER_RESTARTEDand silently skipped? -
The PL server init early in restart. Phase B calls
pl_server_init (db_name)before the log is even open (phase C). The comment in the source justifies this with afork()interaction: large memory allocations duringlog_initializemay causefork()to fail. The PL server is forked early so it has a small parent. Open: does this matter for SA-mode (where there is no fork), and is the early init safe with respect to recovery — i.e., can the PL server miss a database state change because it was initialised before redo? -
Catalog-class compatibility check.
catcls_compile_catalog_classesruns in phase L and is protected by theskip_to_check_ct_classes_for_rebuildflag, set byboot_set_skip_check_ct_classes. The flag is true only during a catalog rebuild (used bysynccolldband migration utilities). What is the recovery semantics if the catalog is legitimately corrupt at restart and this check fails? Does it produce a usable error or a fatal exit? -
Master / server connection failure window. Between
cub_server’s phase N (where it sendscss_change_ha_server_stateto the master) andcss_init(where it opens the listening socket), there is a brief window where the master knows the server’s HA state but clients cannot yet connect. What does the master report to a client that arrives during this window? -
Symmetric finalisation under partial failure. The error-recovery branches inside
boot_restart_servercallboot_server_all_finalize (..., BOOT_SHUTDOWN_EXCEPT_COMMON_MODULES). In SERVER_MODE, the macro at the top ofboot_server_all_finalizeoverrides this toBOOT_SHUTDOWN_ALL_MODULES. What is the consequence of the forced override on a partial-failure restart in SERVER_MODE — specifically, does it tear down msgcat/sysprm and prevent any subsequent retry?
Sources
Section titled “Sources”CUBRID source (/data/hgryoo/references/cubrid/)
Section titled “CUBRID source (/data/hgryoo/references/cubrid/)”src/transaction/boot_sr.{c,h}— server boot.src/transaction/boot_cl.{c,h}— client boot.src/transaction/boot.h— shared types (BOOT_DB_PATH_INFO,BOOT_SERVER_CREDENTIAL,HA_SERVER_STATE, client-type macros).src/transaction/client_credentials.{cpp,hpp}— credential packable.src/transaction/log_recovery.c— the recovery driver boot hands off to.src/storage/catalog_class.c—catcls_compile_catalog_classes,catcls_get_server_compat_info,catcls_find_and_set_cached_class_oid,catcls_initialize_class_oid_to_oid_hash_table.src/object/schema_system_catalog_install.cpp—catcls_init,catcls_install(the table-by-table catalog builder).src/communication/network_sr.c—net_server_start, the thin wrapper aroundboot_restart_server.src/communication/network_interface_cl.c—boot_register_client(CS-mode RPC wrapper).src/connection/server_support.c—css_init,css_change_ha_server_state,css_transit_ha_server_state.src/executables/server.c—cub_servermain.src/executables/master.c—cub_mastermain.
Sibling docs in this knowledge base
Section titled “Sibling docs in this knowledge base”knowledge/code-analysis/cubrid/cubrid-recovery-manager.md— three-pass restart, called fromlog_initializein phase G of this doc.knowledge/code-analysis/cubrid/cubrid-heartbeat.md— heartbeat threads insidecub_master; HA state transitions phase N consults.knowledge/code-analysis/cubrid/cubrid-page-buffer-manager.md— the buffer pool whose daemons are started in phase F.knowledge/code-analysis/cubrid/cubrid-log-manager.md— the WAL whoselog_initializeboot calls.knowledge/code-analysis/cubrid/cubrid-catalog-manager.md— the catalog whose tables are bootstrapped bycatcls_installand loaded bycatcls_compile_catalog_classes.knowledge/code-analysis/cubrid/cubrid-disk-manager.md— the disk manager whosedisk_format_first_volumeanddisk_get_boot_hfidboot calls.knowledge/code-analysis/cubrid/cubrid-authentication.md— the auth subsystem whoseau_install/au_startare called byinstall_system_metadata.knowledge/code-analysis/cubrid/cubrid-transaction.md— the TDES whoselogtb_assign_tran_indexis called fromxboot_register_client.knowledge/code-analysis/cubrid/cubrid-vacuum.md— the vacuum subsystem whosevacuum_initializeis in phase E andvacuum_bootin phase H.knowledge/code-analysis/cubrid/cubrid-ha-replication.md— the HA replication path that hooks into thecss_change_ha_server_statecall in phase N.
Textbook chapters (under knowledge/research/dbms-general/)
Section titled “Textbook chapters (under knowledge/research/dbms-general/)”- Database Internals (Petrov), Ch. 5 §“Recovery Algorithm” — the ARIES restart whose dispatch boot phase G performs.
- Bovet & Cesati, Understanding the Linux Kernel, ch. on the boot process — the OS-side analogue of the dependency graph in the Theoretical Background section.