5. The concept dictionary
The eregister_concepts_release_v1 repo ships a mysqldump of the OpenMRS
concept dictionary — omrs_concept_dictionary_<timestamp>.sql. It is loaded
into the openmrs database inside the openmrsdb service.
Info
Nothing imports this during the upgrade, and catch-up.sh only ever reports
on it. The import drops and recreates whole tables, which is a big enough
operation to belong to a job of its own, or to a deliberate manual run.
5.1 What the import actually does
The dump is a plain mysqldump with FOREIGN_KEY_CHECKS=0, DROP TABLE IF
EXISTS + CREATE TABLE + INSERTs. Importing it therefore replaces those
tables wholesale rather than merging into them.
The table list is not hard-coded. It is read straight out of the .sql file
being imported:
sed -n 's/^DROP TABLE IF EXISTS `\([A-Za-z0-9_]*\)`.*/\1/p' <dump> | sort -u
so the pre-import backup always matches the file about to be imported, even when a new release adds a table.
Danger
drug_order is among those tables. Any drug orders the site currently
holds are replaced. This is the single most important thing to understand
about this import.
A pre-import dump of exactly those tables is written to
<base>/v1/bahmni-backup/concepts-preimport-<stamp>.sql before anything is
replaced, so the import can be undone by feeding that file back in.
5.2 Who imports it, and when
| Path | When |
|---|---|
| Delayed first run | ~3 hours after install.sh finishes, once, via a transient systemd timer (eregister-concept-import-first.timer) or an at job |
| The daily job | 04:30 every night — but only when the dump's content differs from the one already loaded |
./import-concepts.sh |
Whenever you want, by hand |
catch-up.sh |
Never. It reports only |
If neither systemd nor at is available, the daily job simply takes the first
import.
Why the delay
When install.sh finishes, the stack has only just been started. openmrsdb
answers early, but the instance behind it needs 30+ minutes — often hours on site
hardware — before importing a dictionary is worth doing.
Doing it inline therefore either blocked the run or skipped for nothing, and
either way it asked the operator to authorise replacing the concept_*/drug*
tables at the least informative possible moment.
Change detection
The daily job records the sha256 of the dump it imported in
<base>/v1/.eregister_concept_import_state. A night where the clone has not
moved does nothing at all.
5.3 What catch-up.sh reports
✔ OK concepts dump omrs_concept_dictionary_20260804.sql (newest of 3)
✔ OK concepts imported sha256 matches the dump on disk
✔ OK concepts database openmrs.concept holds 41,208 rows
The severity of the imported row depends on whether the daily job is actually
scheduled:
- a newer dump with the job in place →
OK. It is simply pending tonight. - the same dump with no job scheduled →
GAP. Nothing will ever load it.
--no-concepts silences these rows and leaves the daily job alone — reporting
a gap that tells you to run the job you just disabled would be noise, and it
would fail the exit code for a state you chose.
5.4 The OCL concept-name fix
Run once, after the v1 instance has fully started and finished its OCL import (~30+ minutes after the stack starts).
During that import OCL voids the local concept names (reason "Removed from OCL")
and inserts its own replacements. ocl-fix.sh:
- Unvoids the original names and voids OCL's replacements, in the
openmrsdatabase of theopenmrsdbservice. - Renames
CIEL_*.zip→.DONEin the OCL directory of theopenmrsservice, so the import does not run again on the next startup.
Naturally idempotent: a second run finds nothing to unvoid and no zip to move.
./ocl-fix.sh [--yes] [--install-dir DIR] [--no-color] [--help]
curl -fsSL --retry 8 --retry-max-time 180 \
https://raw.githubusercontent.com/Lesotho-eRegister-v1/upgrade-to-v1/refs/heads/main/ocl-fix.sh | bash
| Variable | Default |
|---|---|
EREGISTER_INSTALL_BASE |
/var/lib |
EREGISTER_DB_SERVICE |
openmrsdb |
EREGISTER_EMR_SERVICE |
openmrs |
EREGISTER_OCL_DIR |
/openmrs/data/configuration/ocl |
EREGISTER_DB_PASS |
(the container's own MYSQL_ROOT_PASSWORD) |
5.5 import-concepts.sh
./import-concepts.sh [--yes] [--install-dir DIR] [--no-color] [--help]
./import-concepts.sh --schedule # install the DAILY job instead of importing now
Import now:
sudo ./import-concepts.sh
Install (or re-install) the daily job without importing:
sudo ./import-concepts.sh --schedule
From the network:
curl -fsSL --retry 8 --retry-max-time 180 \
https://raw.githubusercontent.com/Lesotho-eRegister-v1/upgrade-to-v1/refs/heads/main/import-concepts.sh | bash
Before it writes, it warns
[⚠] This REPLACES the concept dictionary in the 'openmrs' database with
[⚠] the dump in /var/lib/v1/eregister_concepts_release_v1/omrs_concept_dictionary_20260804.sql.
[⚠] The tables it drops and recreates include drug_order — so any rows those
[⚠] tables currently hold are replaced.
[ℹ] Backing up the tables about to be replaced -> /var/lib/v1/bahmni-backup/concepts-preimport-20260921_141203.sql
Undoing an import
cd /var/lib/v1/bahmni-docker-ls/bahmni-standard
sudo cat /var/lib/v1/bahmni-backup/concepts-preimport-<stamp>.sql \
| sudo docker compose exec -T openmrsdb mysql -uroot -p openmrs
5.6 Choosing which dump to import
The repo's own tooling names each release with a timestamp
(run_concept_dump.sh writes omrs_concept_dictionary_$(date +%Y%m%d_%H%M%S).sql),
so there is no fixed filename to pin.
- Leave
EREGISTER_CONCEPTS_SQL_NAMEempty (the normal case) to pick the newest file matchingEREGISTER_CONCEPTS_SQL_PATTERN(defaultomrs_concept_dictionary*.sql). - Set it to pin one exact filename.
5.7 Database connectivity
Every step that talks to the database asks once and does not poll:
[⚠] openmrsdb:openmrs is not accepting connections right now.
[⚠] A freshly started v1 stack needs 30+ minutes before its database answers,
[⚠] so this is expected straight after an upgrade — it is not an error.
[⚠] Skipping the concept dictionary; nothing else in this run depends on it.
[⚠] Load it once the stack is up with: ./import-concepts.sh
This used to poll for up to five minutes, on the theory that the import runs
while the stack is still booting. In practice that turned a skippable step into a
five-minute hang whenever openmrsdb was not coming up at all — and the import
is not on the critical path. So: ask once, and if the answer is no, say why and
move on.
The password comes from EREGISTER_DB_PASS when set, else the container's own
MYSQL_ROOT_PASSWORD, so it never lands on a host process list.
5.8 Configuration summary
| Variable | Default | Meaning |
|---|---|---|
EREGISTER_IMPORT_CONCEPTS |
1 |
0 (or --no-concepts) disables the import |
EREGISTER_CONCEPTS_SQL_NAME |
(empty) | Pin one exact dump filename |
EREGISTER_CONCEPTS_SQL_PATTERN |
omrs_concept_dictionary*.sql |
Which files to consider |
EREGISTER_CONCEPT_IMPORT |
1 |
0 does not install the daily job |
EREGISTER_CONCEPT_IMPORT_CRON |
30 4 * * * |
cron schedule |
EREGISTER_CONCEPT_IMPORT_ONCALENDAR |
*-*-* 04:30:00 |
systemd schedule |
EREGISTER_CONCEPT_IMPORT_FIRST_RUN |
1 |
0 = no delayed first run |
EREGISTER_CONCEPT_IMPORT_FIRST_DELAY_SEC |
10800 (3h) |
How long to wait for it |
EREGISTER_CONCEPT_IMPORT_RESTART_EMR |
0 |
Restart the EMR after an import (30+ min downtime) |
Note
An imported dictionary is only visible after the EMR restarts.