Skip to content

CUBRID Boot — Server Startup, First-Time Creation, Restart-Recovery Dispatch, and Client Connect

Contents:

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:

  1. Error reporting and message catalogue. Nothing else can report a failure intelligibly until these are alive. They depend on nothing.
  2. System parameter loader. Reads the configuration file. The error reporter must be alive first, otherwise parse failures are silent.
  3. Memory and area allocators. Bound to the parameter loader because pool sizes are configurable.
  4. Locale, timezone, charset. Mostly inert; need the message catalogue to report failures.
  5. Thread / worker-pool manager. Everything below this point may need a THREAD_ENTRY for diagnostics or for system transactions.
  6. File I/O layer. Mounts volumes by descriptor. Depends only on the OS layer plus error reporting.
  7. Page buffer pool. Caches disk pages, depends on file I/O and on parameters (cache sizes, replacement policy).
  8. Log manager. Reads and writes the WAL. Needs the page buffer for the data side and direct file I/O for the log itself.
  9. Recovery manager. Walks the log to bring the database to crash-consistent state. Needs the log manager, the page buffer, and the disk manager.
  10. Catalog (system tables). Depends on heap files, B+Trees, file tracker — none of which can answer queries until recovery has restored their pages.
  11. Locking and MVCC. Lock manager needs catalog OIDs to identify resources; MVCC needs the active transaction table the recovery manager rebuilt.
  12. Query optimizer / plan cache. Depends on the catalog.
  13. 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.

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.

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.

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.

Theoretical conceptCUBRID name
Process entry pointmain() in src/executables/server.c (cub_server)
First-time create entryxboot_initialize_server (boot_sr.c)
Restart entryboot_restart_server (boot_sr.c)
Restart dispatchernet_server_start (network_sr.c) — calls boot_restart_server then css_init
Recovery hand-offlog_initialize (log_manager.c) → log_recovery (log_recovery.c)
First-volume formatterboot_create_all_volumes (boot_sr.c) → disk_format_first_volume
Database parameter persistenceBOOT_DB_PARM struct + boot_Db_parm global (boot_sr.c)
Boot status flagBOOT_SERVER_STATUS { UP, DOWN, MAINTENANCE } + boot_Server_status (boot_sr.h)
Server credentialBOOT_SERVER_CREDENTIAL struct (boot.h)
Client credentialBOOT_CLIENT_CREDENTIAL extends clientids (client_credentials.hpp)
Server register-clientxboot_register_client (boot_sr.c)
Client register-client RPCboot_register_client (network_interface_cl.c)
Client side restartboot_restart_client (boot_cl.c)
Client side first-time createboot_initialize_client (boot_cl.c)
Client connect to hostboot_client_initialize_css (boot_cl.c)
Catalog bootstrap (table creation)catcls_init + catcls_install (schema_system_catalog_install.cpp)
Catalog rehydration on restartcatcls_compile_catalog_classes + catcls_find_and_set_cached_class_oid (catalog_class.c)
HA state at bootcss_change_ha_server_state (server_support.c) called near end of boot_restart_server
Master processcub_mastermain() in src/executables/master.c
Master heartbeat inithb_master_init called from master’s main()
Server shutdownxboot_shutdown_server (boot_sr.c)
Server finalizeboot_server_all_finalize (boot_sr.c)
Client shutdownboot_shutdown_client (boot_cl.c)
Client finalizeboot_client_all_finalize (boot_cl.c)

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.

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.

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)
int
xboot_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 int
boot_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.

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.c
static int
install_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.

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)
int
boot_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

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).

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)
int
boot_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).

The BOOT_CLIENT_CREDENTIAL extends clientids (in client_credentials.hpp):

// boot_client_credential — src/transaction/client_credentials.hpp
struct 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).

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:

  1. boot_make_session_server_key in phase A — produces a per-process session key from time() plus the host IP, used as the server’s identity to the master.
  2. css_change_ha_server_state in phase N — sends the requested HA state (read from PRM_ID_HA_SERVER_STATE parameter, default HA_SERVER_STATE_IDLE) to the master and waits for confirmation. The transition is gated by a state-machine table in css_transit_ha_server_state (server_support.c); requests like IDLE → ACTIVE succeed inline, IDLE → STANDBY → TO_BE_STANDBY may 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.

// xboot_shutdown_server — src/transaction/boot_sr.c (condensed)
bool
xboot_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)
void
boot_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_clientboot_client_all_finalize:

// boot_client_all_finalize — src/transaction/boot_cl.c (condensed)
void
boot_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 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)
int
main (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.

Finally, the executable that ties phase A through phase O together:

// server.c::main — src/executables/server.c (condensed)
int
main (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_startboot_restart_serverlog_initializelog_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.

Anchor on symbol names, not line numbers. Line numbers go in the position-hint table at the bottom.

  • 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) — wraps boot_restart_server with from_backup=true.
  • boot_create_all_volumes (boot_sr.c) — formats first volume, initialises log, creates root-class heap, writes boot_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 by boot_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 from time() + IP.
  • boot_mount (boot_sr.c) — fileio_mount wrapper used as the callback into boot_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.
  • 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 to CONN_CLOSING.
  • xboot_get_server_session_key (boot_sr.c).
  • 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 by GETHOSTNAME.
  • 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 for BO_ENABLE_FLUSH_DAEMONS / BO_DISABLE_FLUSH_DAEMONS.
  • boot_initialize_client (boot_cl.c) — first-time create driver; calls boot_initialize_server (network or in-process) and then install_system_metadata.
  • boot_restart_client (boot_cl.c) — normal client restart; drives the host-selection loop and boot_register_client.
  • boot_shutdown_client (boot_cl.c) — graceful shutdown.
  • boot_shutdown_client_at_exit (boot_cl.c) — atexit hook registered by boot_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) — calls au_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 the synccolldb / migration utilities.
  • boot_Server_credential (boot_cl.c) — populated from the server’s reply to NET_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).
  • boot_register_client (network_interface_cl.c) — CS-mode packs the credential, sends NET_SERVER_BO_REGISTER_CLIENT, unpacks the server credential reply. SA-mode calls xboot_register_client directly.
  • boot_unregister_client / boot_initialize_server (network wrappers) — same pattern as boot_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) — sets css_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.
  • catcls_init (schema_system_catalog_install.cpp) — populates the static clist and vclist of system class/view definitions.
  • catcls_install (schema_system_catalog_install.cpp) — for each entry in clist, calls catalog_builder::create_and_mark_system_class and catalog_builder::build_class; same for vclist via build_vclass. Runs with AU_DISABLE and as Au_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 from db_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, false until a successful catcls_compile_catalog_classes has run.
  • main in src/executables/server.ccub_server entry; signal handlers, then net_server_start (argv[1]).
  • main in src/executables/master.ccub_master entry; utility init, master socket pair, css_master_init, hb_master_init, css_master_loop.
SymbolFileLine
xboot_initialize_serversrc/transaction/boot_sr.c1385
boot_restart_serversrc/transaction/boot_sr.c1969
xboot_restart_from_backupsrc/transaction/boot_sr.c2808
xboot_shutdown_serversrc/transaction/boot_sr.c3044
xboot_get_server_session_keysrc/transaction/boot_sr.c3121
xboot_register_clientsrc/transaction/boot_sr.c3149
xboot_unregister_clientsrc/transaction/boot_sr.c3313
xboot_notify_unregister_clientsrc/transaction/boot_sr.c3413
boot_server_all_finalizesrc/transaction/boot_sr.c3841
boot_create_all_volumessrc/transaction/boot_sr.c4859
boot_remove_all_volumessrc/transaction/boot_sr.c5161
boot_get_db_charset_from_headersrc/transaction/boot_sr.c5882
boot_client_type_to_stringsrc/transaction/boot_sr.c5914
boot_get_new_volume_name_and_idsrc/transaction/boot_sr.c5969
boot_dbparm_save_volumesrc/transaction/boot_sr.c6043
boot_get_db_parmsrc/transaction/boot_sr.c295
boot_find_root_heapsrc/transaction/boot_sr.c325
boot_reset_db_parmsrc/transaction/boot_sr.c447
boot_db_namesrc/transaction/boot_sr.c459
boot_db_full_namesrc/transaction/boot_sr.c470
boot_get_lob_pathsrc/transaction/boot_sr.c479
boot_check_permanent_volumessrc/transaction/boot_sr.c1193
boot_mountsrc/transaction/boot_sr.c1264
boot_ctrl_c_in_init_serversrc/transaction/boot_sr.c1313
boot_make_session_server_keysrc/transaction/boot_sr.c1931
boot_server_statussrc/transaction/boot_sr.c227
boot_shutdown_server_at_exitsrc/transaction/boot_sr.c255
boot_donot_shutdown_server_at_exitsrc/transaction/boot_sr.c275
BOOT_DB_PARM structsrc/transaction/boot_sr.c117
BOOT_SERVER_STATUS enumsrc/transaction/boot_sr.h67
BOOT_SERVER_SHUTDOWN_MODE enumsrc/transaction/boot_sr.h72
BO_RESTART_ARG structsrc/transaction/boot_sr.h112
BOOT_DB_PATH_INFO structsrc/transaction/boot.h117
BOOT_SERVER_CREDENTIAL structsrc/transaction/boot.h152
HA_SERVER_STATE enumsrc/transaction/boot.h131
BOOT_NORMAL_CLIENT_TYPE macrosrc/transaction/boot.h34
boot_initialize_clientsrc/transaction/boot_cl.c275
boot_restart_clientsrc/transaction/boot_cl.c690
boot_shutdown_clientsrc/transaction/boot_cl.c1352
boot_shutdown_client_at_exitsrc/transaction/boot_cl.c1413
boot_server_die_or_changedsrc/transaction/boot_cl.c1458
boot_client_all_finalizesrc/transaction/boot_cl.c1493
boot_client_initialize_csssrc/transaction/boot_cl.c1590
install_system_metadatasrc/transaction/boot_cl.c204
boot_clientsrc/transaction/boot_cl.c187
boot_check_localessrc/transaction/boot_cl.c2047
boot_check_timezone_checksumsrc/transaction/boot_cl.c2115
boot_client_find_and_cache_class_oidssrc/transaction/boot_cl.c2147
boot_build_catalog_classessrc/transaction/boot_cl.c1769
boot_destroy_catalog_classessrc/transaction/boot_cl.c1835
boot_get_host_connectedsrc/transaction/boot_cl.c1971
boot_get_lob_path (cl)src/transaction/boot_cl.c1988
boot_get_host_namesrc/transaction/boot_cl.c2006
boot_get_ipsrc/transaction/boot_cl.c2021
boot_Server_credentialsrc/transaction/boot_cl.c123
boot_register_client (network wrapper)src/communication/network_interface_cl.c3957
boot_unregister_client (network wrapper)src/communication/network_interface_cl.c4037
net_server_startsrc/communication/network_sr.c1058
net_server_initsrc/communication/network_sr.c75
css_initsrc/connection/server_support.c554
css_initialize_server_interfacessrc/connection/server_support.c516
css_change_ha_server_statesrc/connection/server_support.c(HA section)
css_transit_ha_server_statesrc/connection/server_support.c1643
clientids structsrc/transaction/client_credentials.hpp38
boot_client_credential structsrc/transaction/client_credentials.hpp85
clientids::set_idssrc/transaction/client_credentials.cpp90
clientids::set_system_internalsrc/transaction/client_credentials.cpp165
boot_client_credential::packsrc/transaction/client_credentials.cpp263
catcls_initsrc/object/schema_system_catalog_install.cpp257
catcls_installsrc/object/schema_system_catalog_install.cpp315
catcls_compile_catalog_classessrc/storage/catalog_class.c4737
catcls_get_server_compat_infosrc/storage/catalog_class.c4865
catcls_find_and_set_cached_class_oidsrc/storage/catalog_class.c5728
catcls_initialize_class_oid_to_oid_hash_tablesrc/storage/catalog_class.c260
catcls_finalize_class_oid_to_oid_hash_tablesrc/storage/catalog_class.c286
catcls_Enablesrc/storage/catalog_class.c108
main (cub_server)src/executables/server.c276
main (cub_master)src/executables/master.c1207
css_master_initsrc/executables/master.c259
css_master_loopsrc/executables/master.c1159

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).

  1. Recovery parallelism configuration. log_recovery_redo_parallel.{cpp,hpp} is invoked transparently from log_initializelog_recoverylog_recovery_redo. Boot does not set up any worker pool size for it. Investigation: read log_recovery_redo_parallel.cpp’s constructor; check system_parameter.{c,h} for a PRM_ID_LOG_RECOVERY_* parameter. Cross-cuts with cubrid-recovery-manager.md open question #1.

  2. Which HA states block restart entirely vs. allow degraded operation? The css_transit_ha_server_state table in server_support.c admits or rejects requests based on the current state and HA policy. From boot_restart_server phase N alone we cannot tell whether HA_SERVER_STATE_TO_BE_STANDBY is a pause point that waits for the master, or a transient state that proceeds to STANDBY before phase O. Investigation: trace state transitions in the heartbeat code; cross-reference with cubrid-heartbeat.md.

  3. 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 see BO_IS_SERVER_RESTARTED == false while redo is replaying page changes. This is fine for redo (which uses page LSAs, not the boot status flag), but it means a defensive check if (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 on BO_IS_SERVER_RESTARTED and silently skipped?

  4. 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 a fork() interaction: large memory allocations during log_initialize may cause fork() 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?

  5. Catalog-class compatibility check. catcls_compile_catalog_classes runs in phase L and is protected by the skip_to_check_ct_classes_for_rebuild flag, set by boot_set_skip_check_ct_classes. The flag is true only during a catalog rebuild (used by synccolldb and 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?

  6. Master / server connection failure window. Between cub_server’s phase N (where it sends css_change_ha_server_state to the master) and css_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?

  7. Symmetric finalisation under partial failure. The error-recovery branches inside boot_restart_server call boot_server_all_finalize (..., BOOT_SHUTDOWN_EXCEPT_COMMON_MODULES). In SERVER_MODE, the macro at the top of boot_server_all_finalize overrides this to BOOT_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?

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.ccatcls_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.cppcatcls_init, catcls_install (the table-by-table catalog builder).
  • src/communication/network_sr.cnet_server_start, the thin wrapper around boot_restart_server.
  • src/communication/network_interface_cl.cboot_register_client (CS-mode RPC wrapper).
  • src/connection/server_support.ccss_init, css_change_ha_server_state, css_transit_ha_server_state.
  • src/executables/server.ccub_server main.
  • src/executables/master.ccub_master main.
  • knowledge/code-analysis/cubrid/cubrid-recovery-manager.md — three-pass restart, called from log_initialize in phase G of this doc.
  • knowledge/code-analysis/cubrid/cubrid-heartbeat.md — heartbeat threads inside cub_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 whose log_initialize boot calls.
  • knowledge/code-analysis/cubrid/cubrid-catalog-manager.md — the catalog whose tables are bootstrapped by catcls_install and loaded by catcls_compile_catalog_classes.
  • knowledge/code-analysis/cubrid/cubrid-disk-manager.md — the disk manager whose disk_format_first_volume and disk_get_boot_hfid boot calls.
  • knowledge/code-analysis/cubrid/cubrid-authentication.md — the auth subsystem whose au_install / au_start are called by install_system_metadata.
  • knowledge/code-analysis/cubrid/cubrid-transaction.md — the TDES whose logtb_assign_tran_index is called from xboot_register_client.
  • knowledge/code-analysis/cubrid/cubrid-vacuum.md — the vacuum subsystem whose vacuum_initialize is in phase E and vacuum_boot in phase H.
  • knowledge/code-analysis/cubrid/cubrid-ha-replication.md — the HA replication path that hooks into the css_change_ha_server_state call 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.