Mirajv1.0
EN

15. Server Administration

This chapter is intended for administrators who operate miraj-server, the Miraj network server. It covers startup and shutdown, configuration (command line and XML file), the layout of the data folder, durability, operational security, monitoring, backup, and performance tuning.

15.1 Starting and stopping the server#

15.1.1 Basic command#

miraj-server.exe --root D:\data --lang fr --log

--root designates the server's data folder (one folder per database, plus a miraj subfolder for accounts and internal logs). If it is omitted, the server uses a data folder relative to the current directory.

For troubleshooting and account maintenance (see 15.5.3), the server has a dedicated mode:

miraj-server.exe --root D:\data --reset-accounts [--key-dir <folder>]

15.1.2 Stopping the server#

The only orderly shutdown of miraj-server is the SQL statement:

SHUTDOWN;

It requires the SHUTDOWN privilege. The server writes all modified tables, answers the client, writes one last time whatever arrived in the meantime, then the process exits (SHUTDOWN: server stopped at a client's request. in the console or the log). A stop through this statement therefore leaves nothing to replay at restart.

The server does not handle any termination signal, however: whether the stop comes from a Ctrl-C, from the process being closed (service stop, kill), or from a machine power loss, no final checkpoint is written before exit. Every stop that does not go through SHUTDOWN must therefore be treated, from the administrator's point of view, as an abrupt stop: it is the write-ahead log (WAL) and its replay at restart that guarantee data integrity (see 15.4). This differs from embedded use of Miraj (miraj.dll library or native Rust API): there, closing the instance (Miraj::drop) writes everything before returning control.

A data folder is opened by only one process at a time: the miraj.lck file in the root is locked as long as the server runs. miraj-cli, miraj-backup --root or miraj-dump --root launched on this folder in the meantime fail with "Data folder … already opened by another Miraj process"; go through the network instead.

15.1.3 Command-line options#

All options are optional; the default values are those applied when neither the miraj_config.xml file nor the command line changes them (see 15.2 for the order of precedence).

OptionDefault valueDescription
--root <folder>dataServer data folder.
--config <file><root>\miraj_config.xmlExplicit path of the XML configuration file (see 15.2).
--port <port>7007TCP listening port.
--bind <adresse>127.0.0.1Listening address. A warning is issued if a non-local address is chosen (see 15.5.4).
--lang <code>enLanguage of session error messages: en, fr, zh, hi, es, ar, pt, ru, de, ja.
--logdisabledMaintains <root>\miraj\server.log (connections, SQL errors, internal failures; see 15.6).
--result-buffer <Mo>64Memory kept per connection for a client that is slow to read its result.
--write-timeout <secondes>60Maximum wait for a client that no longer reads its result before disconnection.
--connect-timeout <secondes>10Maximum wait for a client's handshake.
--idle-timeout <secondes>28800 (8 h)Maximum wait for a command from an already connected client.
--key-dir <folder>profile of the account that launches the serverFolder of the account vault key, outside the data folder (see 15.5.1).
--lock-wait-timeout <secondes>50Maximum wait for an explicit table lock (LOCK TABLES) or for a row held by SELECT … FOR UPDATE before error 1205 (initial value of lock_wait_timeout and innodb_lock_wait_timeout).
--sql-mode <modes>STRICT_TRANS_TABLESInitial value of the sessions' sql_mode variable (@@GLOBAL.sql_mode, modifiable by SET GLOBAL for subsequent sessions). Strict mode by default: a NULL written to a NOT NULL column, or an invalid value, returns an error (1048, 1366…). --sql-mode "" (or a list without STRICT_TRANS_TABLES, STRICT_ALL_TABLES or TRADITIONAL): non-strict mode, the value becomes the column's implicit value (0, empty string…) with a warning; this is the setting expected by applications written for a server configured in non-strict mode.
--deferred-update ON|OFFONDeferred update: an eligible UPDATE of a row identified by its key does not take it and is re-evaluated at COMMIT, so that two transactions that modify the same row both commit (see 13.2). Initial value of the session variable deferred_update; OFF: the UPDATE takes its row immediately.
--concurrency mvocc|pessimisticmvoccConcurrency model (see 13.2). mvocc: optimistic multiversion control, with no row locks and no waiting. pessimistic restores the former row-lock model (bounded wait, errors 1205 / 1213); temporary option, for the time needed to compare the two models.
--lazy-databases ON|OFFONLazy loading: the tables of a database are only read on its first use (USE, a table named, SHOW TABLES FROM…), which speeds up startup of a folder with many databases. The system database and databases that carry scheduled events are opened at startup; a cluster node opens all of its own. OFF reads all databases at startup.
--lob-threshold <octets>8192Size from which a BLOB value leaves the table and moves to the .bmrj store.
--lob-cache <Mo>128Size of the read cache for offloaded BLOBs.
--event-scheduler ON|OFF|DISABLEDOFFState of the event scheduler at startup; DISABLED forbids SET GLOBAL event_scheduler = ON afterwards.
--parallel-threads <n>one thread per coreThreads that all of the server's queries may occupy in total (Enterprise edition; no effect in Express edition, with a warning).
--save-policy relaxed|statement|periodicrelaxedDurability policy (see 15.4.1).
--save-interval <millisecondes>5000Interval of the periodic background flush (relaxed and periodic policies).
--slow-query-logdisabledEnables the slow query log.
--slow-query-log-file <file><root>\miraj\slow.logSlow query log file; specifying it also enables the log.
--long-query-time <secondes>10Duration beyond which a statement is considered a slow query.
--max-statement-time <secondes>0 (no limit)Duration beyond which a statement is aborted (initial value of the session variable max_statement_time).
--secure-file-priv <folder>empty (LOAD_FILE disabled)Only folder readable by LOAD_FILE, for accounts with the FILE privilege. Must neither contain nor be located inside the data folder or the keys folder.
--backup-dir <folder>empty (BACKUP / RESTORE refused)Only folder where BACKUP DATABASE writes and from which RESTORE DATABASE reads; created if it does not exist. Must neither contain nor be located inside the data folder or the keys folder (see 15.7 and chapter 18).
--tls-cert <file.pem> / --tls-key <file.pem>absentTLS certificate and private key offered during the client handshake.
--require-tlsdisabledRefuses clients that do not connect over TLS (error 3159).
--cluster-config <cluster.toml>cluster.toml next to the executableCluster edition: node configuration in a multi-node replication (see 15.1.4).
--proxy-protocol-from <adresse,…>noneIP addresses of miraj-proxy, comma-separated. A connection coming from one of them must start with the PROXY v2 header, which gives the real client's address: it is what selects the account (user@host), is subject to blocking after failures, and is shown in SHOW PROCESSLIST. A client announced this way is never localhost, even on the loopback: @localhost accounts (and those of hosts 127.0.0.1, ::1) are closed to it. Without a header within the connection delay, the connection is closed. See chapter 22, section 22.5.
--journal-gap-replaydisabledForced recovery, for one startup: in a database whose log is damaged in the middle, the intact records that follow the lost zone are also replayed (see 15.4.3). To be used only as a last resort.
--reset-accounts—Recreates the account vault with only the local root (see 15.5.3). Can be used with --root and --key-dir.
--help / -h / /?—Displays usage and exits.

The MCP endpoint and the REST endpoint each have their own options (--mcp-*, --rest-*); the detailed tables are in chapters 19 and 23. The archive journal options (continuous incremental backup) are set through miraj_config.xml and SET GLOBAL, not on the command line (see 18.1.5).

Note on terminology: command-line option names use hyphens (--long-query-time), while XML keys and session variables use underscores (long_query_time); this chapter follows this convention throughout.

15.1.4 Replication (Cluster edition)#

The Cluster edition adds multi-node replication: a primary node writes, secondary nodes receive its log continuously and replay it read-only. Clients connect to a secondary on its usual client port (7007 by default); any write attempt there fails with error

  1. Nodes communicate with each other on a separate port, over mutual TLS (certificate signed by the cluster authority). Configuration is done through --cluster-config:
[node]
id = "n1"                    # stable node identifier
listen = "0.0.0.0:7107"      # inter-node port (not the clients' port)
advertise = "10.0.0.1:7107"  # address advertised to other nodes

[cluster]
name = "gestium-prod"
seeds = ["10.0.0.1:7107", "10.0.0.2:7107"]
tls_cert = "node.pem"        # paths relative to the file
tls_key = "node-key.pem"
tls_ca = "cluster-ca.pem"
ack = "written"              # or "durable": the secondary flushes its
                              # log to disk before acknowledging
journal_retention_mb = 1024  # beyond this, a secondary that was absent is
                              # re-seeded upon its return

At first startup, all nodes are secondaries. The primary role is designated once with:

SET GLOBAL cluster_role = 'primary';

the other nodes then join the primary (copy of the databases, then log stream). Promoting a secondary uses the same command; a former primary restarted after a failover is set aside (@@cluster_role = 'FENCED', read-only) until it is made to rejoin the new primary:

SET GLOBAL cluster_role = 'secondary';

its unreplicated writes are then set aside in <root>\miraj\quarantaine. Replication monitoring:

SHOW CLUSTER STATUS;
SELECT * FROM information_schema.MIRAJ_NODES;
SELECT * FROM information_schema.MIRAJ_REPLICATION;
SELECT @@read_only, @@cluster_role, @@cluster_epoch, @@cluster_primary;

15.2 The miraj_config.xml configuration file#

Miraj accepts configuration through an XML file, modeled on a classic .cnf/.ini file. Three levels combine, in this order of increasing priority:

  1. internal default value;
  2. value from the miraj_config.xml file;
  3. command-line argument (highest priority).

The file read is <root>\miraj_config.xml by default, or the path given by --config. A variable that is absent or commented out keeps its default value. Excerpt:

<?xml version="1.0" encoding="UTF-8"?>
<miraj>
    <!-- Language of session error messages (en, fr, zh, hi, es, ar, pt, ru, de, ja) -->
    <!-- <language>en</language> -->
    <!-- Save policy (relaxed, statement, periodic) -->
    <!-- <save_policy>relaxed</save_policy> -->
    <!-- Server listening address -->
    <!-- <bind>127.0.0.1</bind> -->
    <!-- Server TCP listening port -->
    <!-- <port>7007</port> -->
    <!-- Maintain the <folder>/miraj/server.log file -->
    <!-- <log>false</log> -->
</miraj>

To apply a value, uncomment the line and change the value; the rest of the file (the other commented-out variables) does not need to be modified.

A second file, miraj_default.xml, is regenerated at every startup of the server: it lists all recognized variables with their current default value. It is a reference file, never read back by the server — do not write your own settings in it, they would be erased at the next startup. Use it to retrieve, version after version, the complete list of available keys and their default value.

The keys of the XML file cover the same settings as the command-line options: language, save_policy, save_interval, checkpoint_interval, lock_wait_timeout, sql_mode, deferred_update, concurrency, key_dir, lob_threshold, lob_cache, event_scheduler, parallel_threads, slow_query_log, slow_query_log_file, long_query_time, max_statement_time, secure_file_priv, bind, port, log, result_buffer, write_timeout, connect_timeout, idle_timeout, tls_cert, tls_key, require_tls, proxy_protocol_from, journal_archive, journal_archive_dir, journal_archive_max_lag (see 18.1.5), change_events_buffer_events, change_events_buffer_size, change_events_bulk_rows, change_events_max_payload, change_events_max_listeners, change_events_wait_timeout, change_events_retention, mcp, mcp_port, mcp_bind, mcp_idle_timeout, mcp_statement_timeout, mcp_max_rows, rest, rest_port, rest_bind, rest_idle_timeout, rest_statement_timeout, rest_max_rows, rest_cors_origins, rest_endpoints, rest_tables, rest_sql, rest_basic, rest_basic_access. Only --lazy-databases, --journal-gap-replay, --reset-accounts and --config have no XML key (journal_archive* have no command-line option). The exact list, with the default value of each, is that of miraj_default.xml.

checkpoint_interval (60000 ms, i.e. 60 s, by default) has no equivalent on the command line: it is the interval between two checkpoints (see 15.4.2), adjustable only through this file.

Recommended usage: the XML file is suited to a permanent setting, specific to an instance (one data folder), that you do not want to repeat at each launch in a script or a Windows service; the command line remains useful for a one-off setting (diagnostics, test script) that must take precedence over the file.

15.3 Layout of the data folder#

<root folder>/
  miraj_config.xml       configuration (if present)
  miraj_default.xml      reference of default values, regenerated at startup
  miraj.lck              folder lock: one process at a time
  <database>/
    <table>.mrj          one table = one file (MIRA v2 format)
    <table>.bmrj         store of the table's long BLOBs (created on first deposit)
    <table>.dmrj         a disk table (ENGINE = Aria / MyISAM / DISK): rows in pages
    <table>.dbmrj        large values of a disk table (created on first deposit)
    <table>.dmrj.ckpt    checkpoint in progress for a disk table (temporary)
    doublewrite.mrw      double write of disk-table pages (emptied after use)
    journal.mrl          write-ahead log (WAL) of the database
    sequences.mrq        sequences of the database (definition and state), if it has any
    views.mrv, routines.mrp, triggers.mrt, events.mre
                         views, procedures and functions, triggers, events
    replica.mrs          replication state of the node (Cluster edition)
  miraj/
    accounts.mra         encrypted account vault
    server.log           server log, if --log is active
    slow.log             slow query log, if enabled
    quarantaine/          unreplicated writes of a FENCED node (Cluster edition)

Practical points for administration:

  • Each database is a subfolder of the root folder; each table is a .mrj file in that subfolder. Creating or dropping a database amounts to creating or deleting that subfolder (done by the server itself through CREATE DATABASE / DROP DATABASE, never by hand while the server is running).
  • A BLOB that exceeds --lob-threshold (8192 bytes by default) is deposited in the .bmrj file of its table rather than in the .mrj; this file only appears after the first deposit of a BLOB of that size. Both files must be copied together: a .mrj without its associated .bmrj loses its offloaded BLOB values. After lowering --lob-threshold, values already stored that reach the new threshold move to the .bmrj when the database is opened (the table is rewritten at the next checkpoint). On the primary of a cluster, this move goes through the log, within a cap of 256 MiB per table (see 16.7): beyond that, the values remain in the table, readable, and a AVERTISSEMENT line is written to the server log.
  • A disk table is a .dmrj file instead of the .mrj (see Storage engines): its rows stay there, only its indexes are loaded into memory, with a 256 MB page cache (not configurable for now). Its values larger than a quarter of a page go to <table>.dbmrj; as with the .bmrj, both files go together. At checkpoint, modified pages are first written to <table>.dmrj.ckpt, then in place: a complete .ckpt found at opening is replayed, an incomplete .ckpt is ignored. Do not delete this file by hand. A page written outside a checkpoint first goes through doublewrite.mrw, one file per database: at opening, any page torn by a power loss is restored from it, before the tables are read (finding noted in the recovery notes). A damaged page with no copy is reported by CHECK TABLE and repaired by REPAIR TABLE (see chapter 6, table maintenance).
  • journal.mrl is the database's write-ahead log: it makes it possible to replay the writes made after each table's last checkpoint in the event of an abrupt stop (see 15.4). Never delete or modify it by hand.
  • The account vault miraj/accounts.mra is specific to the data folder; its decryption key is stored elsewhere (see 15.5.1). Copying a data folder without its key makes the vault unreadable at restart.
  • Backup: with the server stopped (see 15.1.2 and 15.7), a plain copy of the root folder (databases, miraj/, configuration files) is enough to obtain a consistent image of it, provided that you also copy the account vault key if it is needed for the restoration (see 15.5.1 and 11.7).

15.4 Durability and crash recovery#

15.4.1 Save policies (save_policy)#

Miraj writes each modification statement to the log (WAL) of the database concerned before considering the statement complete; how this log reaches the disk depends on the chosen policy:

PolicyBehaviorMaximum loss tolerated on an abrupt process stopMaximum loss tolerated on a machine power loss
relaxed (default)The log is written to its file before the statement returns, without waiting for the disk; a background thread flushes it to disk every --save-interval ms (5000 by default).None (the file has already received the write from the operating system).The writes of the last --save-interval interval.
statementLike relaxed, additionally waiting for the flush to disk at the end of each write statement.None.None.
periodicNothing is written before the background thread runs (every --save-interval ms).The writes of the last --save-interval ms.The writes of the last --save-interval ms.

These three policies can also be chosen per session, without changing the server setting:

SET SESSION save_policy = 'statement';

(a fourth value, manual, exists internally but is refused as a server policy: it would never write anything unless the session orders it, which makes no sense as a default policy).

How to choose:

  • statement maximizes safety (no possible loss, including during a power outage) at the cost of a per-statement write latency tied to the disk — to be chosen for data that no loss, however small, can be allowed to affect.
  • relaxed (the default setting) offers a good compromise: no loss in the event of a stop of the server process alone (the most frequent case in practice), and a loss window bounded by --save-interval only in case of a power loss of the machine itself.
  • periodic further reduces write latency in exchange for an identical loss window in both cases (process or machine) — to be reserved for workloads where write throughput takes precedence over strict durability guarantees.

In all cases, the log itself remains consistent: what may be lost are the very last writes not yet flushed, never the integrity of the file.

15.4.2 Checkpoints#

A checkpoint consolidates the current state of the tables and makes it possible to truncate the write-ahead log accordingly. It is triggered automatically:

  • every checkpoint_interval milliseconds (60,000 ms, i.e. 60 s, by default — adjustable only via miraj_config.xml, see 15.2);
  • when the log exceeds 64 MB;
  • on the clean closing of an embedded instance (Miraj::drop).

A log flush or a checkpoint that fails in the background (full disk, file held by an antivirus, privileges removed) is retried at every cycle and recorded in the server log, once per database and per cause, without repetition at each cycle: SAUVEGARDE: checkpoint of database … impossible: …, then SAUVEGARDE: … restored when it succeeds again. As long as the failure lasts, modifications remain protected by the log, which grows.

15.4.3 Recovery after an abrupt stop#

At startup, the server opens the table files of each database and then replays the log records that follow the LSN (log sequence number) already present in each .mrj file. File writes are atomic (temporary file, flush, then replacement — on Windows through MoveFileExW with MOVEFILE_WRITE_THROUGH), and the BLOB store (.bmrj) is always flushed to disk before the log and the table file that reference it: an abrupt stop can at worst leave orphaned BLOB bytes in the .bmrj, never a reference without content. According to the project's technical file, this mechanism was validated by 120 random abrupt kills of the process during writes, with no inconsistency observed.

Damaged files found at opening: before any repair, the files concerned are copied to <root>\miraj\quarantaine\<timestamp>\<database>\ with a rapport.txt, and each finding is recorded at startup (RÉCUPÉRATION: … lines of the server log).

  • End of the log: an interrupted write (incomplete last record, run of zeros) is removed silently; this is the normal effect of an abrupt stop. An unreadable end longer than one record, with nothing valid after it (media wear rather than an interrupted write), is copied to quarantine then removed, with a finding.
  • Damaged zone in the middle of the log: only the records that precede it are replayed; those that follow it stay in the quarantined copy. --journal-gap-replay replays them too, for one startup: they may depend on the lost modifications, and a table for which a record does not apply keeps what was applied before it (finding forced replay: …). Tables completed this way are written immediately.
  • .mrj and .dmrj files of the same table (interrupted engine conversion): the most recent is kept, a .mrj with invalid content loses; the other is copied to quarantine before being removed. If one of the two cannot be read (file held by an antivirus), nothing is deleted and the table stays unreadable until the next startup.
  • Unreadable miraj system database (log held by an antivirus, disk in error): its opening is retried for a few seconds, then the server starts without it. It is marked unavailable (directory intact), the other databases are served, and only local connections are accepted: a remote host receives error 9048, on the main port as well as on the REST and MCP endpoints. The server log announces it (BASE SYSTÈME INDISPONIBLE (…)); fix the cause then restart the server.

As noted in 11.1.2, outside of SHUTDOWN, miraj-server has no clean shutdown: it is precisely this recovery mechanism, and not a shutdown sequence, that guarantees data integrity at each restart.

15.5 Operational security#

15.5.1 Account vault and key management#

Accounts (users, passwords, privileges, roles) are kept in an encrypted vault authenticated with AES-256-GCM, <root>\miraj\accounts.mra — never logged, written atomically, protected against the restoration of an old copy by an increasing generation number.

The 32-byte key that protects this vault is stored outside the data folder:

  • on Windows, it is encrypted with DPAPI for the account that launches the server, in %LOCALAPPDATA%\MIRAJ\keys;
  • on other systems, in a file with 0600 permissions;
  • --key-dir <folder> (or the XML key key_dir) lets you choose another location, for example to share the key between several services or to store it on a medium separate from the data folder.

Consequence for backup and deployment: copying only the data folder is not enough to be able to reopen the accounts on another machine or under another Windows account; the key (or its --key-dir folder) must also be transferred, protecting it at least as well as an administrator password.

15.5.2 Accounts without a password and network listening#

A local root account, without a password, exists as soon as the data folder is created; it is the only account that manages the other accounts. By default the server listens on the loopback interface (127.0.0.1): this setting implicitly protects an account that still has no password against remote access. Choosing a non-local listening address (--bind) triggers a warning, and the server flags the accounts without a password that would then become reachable remotely. In practice, before opening listening beyond the loopback interface, give root a password:

ALTER USER 'root'@'localhost' IDENTIFIED BY 'un mot de passe robuste';

15.5.3 --reset-accounts: recovery procedure#

An account vault that is altered, deleted, or whose key is missing/unreadable prevents the server from starting. The local recovery procedure is:

miraj-server.exe --root D:\data --reset-accounts [--key-dir <folder>]

This command recreates the vault with the local root as its only account, without a password, then exits (it does not start the network server). It must be run locally, with access to the data folder and, implicitly, access equivalent to that of the key. After a --reset-accounts:

  • all previously defined accounts and privileges are lost (only the local root remains); recreate the application accounts and grant back the necessary privileges;
  • give root a password again without delay before restarting the server with non-local listening (see the ALTER USER command above).

Use this procedure only as a last resort (corrupted vault, lost key, administrator account locked with no other access): it restores nothing, it starts over from a blank account state.

15.5.4 Recommendations for a production deployment#

  • Keep listening on the loopback interface (127.0.0.1, the default value) as long as a application firewall does not protect wider access; choose a non-local address only after having secured all accounts with passwords.
  • Enable TLS (--tls-cert / --tls-key) and consider --require-tls as soon as clients connect across a network that is not physically isolated.
  • Keep the account vault key (--key-dir) separate from the backup medium of the data folder, or clearly document that it must be copied separately during a restoration.
  • Leave secure_file_priv empty (default value, LOAD_FILE disabled) unless explicitly needed; if you enable it, point it to a dedicated folder, distinct from the data folder and the keys folder.
  • Systematically start with --log (see 15.6) to keep a trace of SQL errors and internal incidents.
  • Keep in mind the absence of a clean shutdown (15.1.2): plan the save policy (15.4.1) according to the data loss you can tolerate in the event of a power loss, rather than counting on a careful shutdown of the service.

15.6 Monitoring#

15.6.1 SHOW STATUS and SHOW VARIABLES#

SHOW VARIABLES;              -- current session/server settings
SHOW VARIABLES LIKE 'save_%';
SHOW STATUS;                 -- activity counters
SHOW STATUS LIKE 'Miraj_%';

SHOW VARIABLES returns the same values as SELECT @@name for each variable; this is what common client tools (graphical consoles, connectors) read at connection time.

SHOW STATUS exposes, as of now, in particular:

VariableMeaning
Miraj_parallel_queriesNumber of queries that used parallel execution (Enterprise edition).
Miraj_parallel_queries_serializedQueries that fell back to serial execution for lack of available threads.
Miraj_parallel_workersThreads in the configured parallelism budget.
Miraj_parallel_workers_busyThreads currently busy.
Miraj_journal_archive, Miraj_journal_archive_databases, _bases, _bases_pending, _broken, _bytes, _lag_bytes, _last_timeContinuous log archiving (18.1.5): state, tracked databases, full backups taken by the server or pending, databases whose lineage is broken, bytes archived, lag behind the log, time of the last segment.
Miraj_cluster_fragment_*, Miraj_cluster_sync_*, Miraj_cluster_unlogged_rebootstrapsCluster edition: reads and writes of partitions held by another node, waits for the acknowledgment of synchronous secondaries, re-seeds (chapter 16).
Slow_queriesNumber of queries recorded in the slow query log.
Max_statement_time_exceededNumber of statements aborted for exceeding max_statement_time.

This list will grow over the course of versions; consult SHOW STATUS without a filter for the complete list of the running instance.

15.6.2 information_schema#

information_schema (read-only) provides a structured view useful for administration, in particular: SCHEMATA, TABLES, COLUMNS, STATISTICS, VIEWS, TABLE_CONSTRAINTS, KEY_COLUMN_USAGE, REFERENTIAL_CONSTRAINTS, as well as the privilege tables USER_PRIVILEGES, SCHEMA_PRIVILEGES, TABLE_PRIVILEGES, APPLICABLE_ROLES, ENABLED_ROLES. An account sees, in SHOW DATABASES, SHOW TABLES and information_schema, only what it has the privilege to access.

In the Cluster edition, information_schema.MIRAJ_NODES and information_schema.MIRAJ_REPLICATION complement SHOW CLUSTER STATUS for replication monitoring (see 15.1.4).

Other monitoring views: PROCESSLIST (like SHOW PROCESSLIST), INNODB_TRX (open transactions), PARTITIONS, ENDPOINTS and ENDPOINT_PRIVILEGES (REST API, chapter 23), ENGINES, COLLATIONS. MIRAJ_PARALLELISM gives one row: edition, budget threads and busy threads, chunk size (MORSEL_ROWS), queries run in parallel, serially or reduced, and fair share, the same figures as the Miraj_parallel_* counters.

Tables specific to Miraj for concurrency: MIRAJ_CONCURRENCY (model in use, active transactions, purge watermark, oldest transaction and deferred update counters), MIRAJ_VERSIONS (pending versions, per table) and MIRAJ_DEFERRED_UPDATE (deferred update, one row of counters accumulated since startup):

SELECT INTENTS_RECORDED, ROWS_APPLIED, COMMIT_FAILURES, FAILED_READ_VALIDATION,
       NORMAL_KEY, NORMAL_TRIGGER
FROM information_schema.MIRAJ_DEFERRED_UPDATE;

INTENTS_RECORDED counts deferred UPDATEs, ROWS_APPLIED the rows written at COMMIT, IMAGES_MATERIALIZED the deferred UPDATEs caught up by an ordinary write of the same transaction; COMMIT_FAILURES the COMMITs refused for a transaction that had deferred UPDATEs, broken down by cause (FAILED_WITNESS: row changed so that the WHERE or a column read by an AFTER UPDATE trigger no longer holds; FAILED_ROW_GONE, FAILED_CONCURRENT_WRITER, FAILED_WARNING, FAILED_TABLE_REDEFINED, FAILED_ERROR, FAILED_READ_VALIDATION: the transaction had read a row modified since); ENGINE_RETRIES the autocommit statements replayed by the engine after a conflict; the NORMAL_* columns the UPDATEs of a session set to ON that could not be deferred, by reason (SESSION, FORM, TABLE, TRIGGER, KEY, ROW_COUNT, ROW, CALCULATION). A high FAILED_READ_VALIDATION indicates transactions that read a row before updating it (see 13.2).

15.6.3 Logging#

  • --log (or log in the XML file) maintains <root>\miraj\server.log: this file receives, timestamped, only the SQL errors returned to clients (with the statement at fault), internal failures (captured panics, never passed to the caller) and server operating problems. Successful queries do not appear there; connections and errors are however also displayed on standard output.
  • --slow-query-log (or slow_query_log / slow_query_log_file) records in <root>\miraj\slow.log (or the chosen file) each statement longer than --long-query-time seconds, with the volume sent to the client (Bytes_sent). SET GLOBAL log_output = 'TABLE' switches this log to the miraj.slow_log table; FLUSH SLOW LOGS reopens the file after an external rotation.

15.7 Backup and restore#

The server backs up a database hot, without stopping it. Start it with a backup folder (--backup-dir, see 15.1.3), then:

BACKUP DATABASE gestion TO 'gestion-2026-09-24';
RESTORE DATABASE gestion_copie FROM 'gestion-2026-09-24';

The copy is consistent, with all transactions committed up to one instant and none beyond. Writes are suspended for only a few milliseconds. The miraj-backup tool drives these statements from a scheduled task, and miraj-dump exports a database as a portable SQL script, accounts included. Everything is detailed in chapter 18.

These backups do not contain the account vault. Export the accounts with miraj-dump --users, or cold-copy the miraj\ subfolder and the vault key (see below).

Cold copy of the complete folder, for a change of machine with the accounts, for example:

  1. Stop the server. As noted in 11.1.2, miraj-server offers no clean shutdown sequence: treat every stop as an abrupt stop. It is log recovery (15.4.3) that guarantees a consistent folder at the next startup.
  2. Copy the data folder (--root) in its entirety: databases (.mrj, .bmrj, .dmrj and .dbmrj of disk tables, journal.mrl), miraj\ subfolder (accounts, internal logs) and any XML configuration files.
  3. Copy the account vault key separately if it is needed for the restoration on another machine or under another Windows account (see 15.5.1): it is not in the data folder.
  4. To restore, put this folder (and the key, if needed) back at the expected location, then restart the server normally.

For an embedded database (use through miraj.dll or the Rust API, outside the network server), the normal closing of the instance (Miraj::drop) writes everything before returning control: a cold backup after this closing does not have to worry about an abrupt stop.

15.8 Performance: settings to know about#

This chapter remains administration-oriented; for the technical details of parallel execution and its limits, see roadmap.md and docs/LIMITES.md.

Parallel execution (Enterprise edition only — the Express edition, compiled without the Cargo feature parallel, runs each query on the single thread of its session) spreads large reads across several threads: filters, projections, aggregation, sorting, DISTINCT, INNER/LEFT joins, and the underlying read of UPDATE/DELETE/INSERT ... SELECT — with, in all cases, the same results and the same order as in serial execution.

Two settings control this parallelism:

  • --parallel-threads <n> (server): sets the total number of threads that all the server's queries may occupy together. By default, one thread per core of the machine. Reduce it if the server shares the machine with other services, or adjust it if measurements show contention between concurrent queries.
  • max_parallel_degree (session variable): bounds the degree of parallelism of a given session, within the limit of the server budget above. Useful for reserving most of the threads for interactive processing while letting a background job (import, bulk recalculation) run at a lower degree, or the reverse.

EXPLAIN displays the degree of parallelism chosen for a given query, which makes it possible to check that a setting has the expected effect before generalizing it. EXPLAIN ANALYZE (or ANALYZE, see 8.21) executes the query and additionally returns the actual rows and timings of each operator: the way to spot the costly step of a slow query recorded in the slow query log. Without sending the rows to the client, it really executes the query, and ANALYZE UPDATE|DELETE really modifies the rows: run it with the same care as an ordinary statement.

For BLOB volumes, --lob-threshold and --lob-cache (see 15.1.3 and 15.3) also influence performance: a lower threshold offloads more values out of the table files (useful if rows must remain compact in memory), a larger cache reduces repeated reads of large BLOBs at the cost of the memory it occupies.