Skip to content

Commit e5a2234

Browse files
committed
Re-architecture initial run
1 parent d909c9d commit e5a2234

File tree

2 files changed

+102
-34
lines changed

2 files changed

+102
-34
lines changed

database/init/00-replica-set.js

Lines changed: 88 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,93 @@
1-
// Initialize the replica set on first startup.
2-
// See: https://www.mongodb.com/docs/manual/tutorial/convert-standalone-to-replica-set/
1+
// Ensure a single-node replica set is initialized and advertising the
2+
// expected host for this environment.
33
//
44
// MONGO_INITDB_REPLICA_HOST can be set to the resolvable hostname:port
5-
// used to advertise this member (e.g. the Kubernetes Service name "database:27017").
6-
// It defaults to "localhost:27017" for local development.
7-
try {
8-
rs.status();
9-
} catch (e) {
10-
print(`Replica set not yet initialized (${e}), initializing now...`);
11-
const host = process.env.MONGO_INITDB_REPLICA_HOST || "localhost:27017";
12-
rs.initiate({ _id: "rs0", members: [{ _id: 0, host: host }] });
13-
// Wait for replica set to reach PRIMARY state before other init scripts run.
14-
const maxWaitMs = 30000;
15-
const intervalMs = 500;
16-
let waited = 0;
17-
let isPrimary = false;
18-
while (!isPrimary && waited < maxWaitMs) {
19-
sleep(intervalMs);
20-
waited += intervalMs;
21-
const status = rs.status();
22-
isPrimary =
23-
status.members !== undefined &&
24-
status.members.some((m) => m.stateStr === "PRIMARY");
5+
// used to advertise this member (e.g. "database:27017" in Kubernetes).
6+
const host = process.env.MONGO_INITDB_REPLICA_HOST || "localhost:27017";
7+
const maxWaitMs = 60000;
8+
const intervalMs = 1000;
9+
const start = Date.now();
10+
11+
function ensurePrimaryHost(forceReconfig) {
12+
let conf;
13+
try {
14+
conf = rs.conf();
15+
} catch (error) {
16+
if (!forceReconfig) {
17+
throw error;
18+
}
19+
20+
conf = db.getSiblingDB("local").system.replset.findOne();
21+
if (!conf) {
22+
throw error;
23+
}
2524
}
26-
if (!isPrimary) {
27-
throw new Error(
28-
`Replica set did not reach PRIMARY state after ${maxWaitMs}ms`
29-
);
25+
26+
if (!conf.members || conf.members.length === 0) {
27+
throw new Error("Replica set config has no members");
3028
}
29+
30+
if (conf.members[0].host === host) {
31+
return true;
32+
}
33+
34+
if (conf.members[0].host !== host) {
35+
print(`Updating replica set member host to ${host}`);
36+
conf.members[0].host = host;
37+
conf.version = (conf.version || 1) + 1;
38+
if (forceReconfig) {
39+
rs.reconfig(conf, { force: true });
40+
} else {
41+
rs.reconfig(conf);
42+
}
43+
44+
return false;
45+
}
46+
47+
return true;
48+
}
49+
50+
while (Date.now() - start < maxWaitMs) {
51+
try {
52+
const hello = db.hello();
53+
if (hello.isWritablePrimary) {
54+
try {
55+
const hostIsAligned = ensurePrimaryHost(false);
56+
if (hostIsAligned) {
57+
print("Replica set is PRIMARY with correct host");
58+
quit(0);
59+
}
60+
61+
print("Replica set host updated, waiting for PRIMARY");
62+
} catch (error) {
63+
print(`Host alignment deferred (${error})`);
64+
}
65+
}
66+
} catch (error) {
67+
print(`Replica set not writable yet (${error})`);
68+
}
69+
70+
try {
71+
const hostIsAligned = ensurePrimaryHost(true);
72+
if (!hostIsAligned) {
73+
print("Forced host alignment requested, waiting for PRIMARY");
74+
}
75+
} catch (error) {
76+
print(`Forced host alignment deferred (${error})`);
77+
}
78+
79+
try {
80+
rs.initiate({ _id: "rs0", members: [{ _id: 0, host: host }] });
81+
print(`Initialized replica set with host ${host}`);
82+
} catch (error) {
83+
if (!String(error).includes("already initialized")) {
84+
print(`Replica set init deferred (${error})`);
85+
}
86+
}
87+
88+
sleep(intervalMs);
3189
}
90+
91+
throw new Error(
92+
`Replica set did not reach PRIMARY state with host ${host} after ${maxWaitMs}ms`
93+
);

deploy/helm/thecombine/charts/database/templates/database.yaml

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -57,17 +57,23 @@ spec:
5757
- -c
5858
- |
5959
set -e
60-
max_attempts=60
61-
attempt=1
62-
while [ "$attempt" -le "$max_attempts" ]; do
63-
if mongosh --quiet --host 127.0.0.1 --eval "const host = '${MONGO_INITDB_REPLICA_HOST}'; try { rs.status(); const conf = rs.conf(); if (conf.members[0].host !== host) { print('Updating replica set member host to ' + host); conf.members[0].host = host; conf.version++; rs.reconfig(conf); } else { print('Replica set already initialized with correct host'); } } catch (e) { print('Initializing replica set (' + e + ')'); rs.initiate({_id: 'rs0', members: [{_id: 0, host: host}]}); }" ; then
64-
exit 0
60+
echo "[postStart] Waiting for mongod to accept connections"
61+
attempts=0
62+
until mongosh --quiet --host 127.0.0.1 --eval "db.adminCommand({ ping: 1 }).ok" >/dev/null 2>&1; do
63+
attempts=$((attempts + 1))
64+
if [ "${attempts}" -ge 120 ]; then
65+
echo "[postStart] Timed out waiting for mongod" >&2
66+
exit 1
6567
fi
6668
sleep 1
67-
attempt=$((attempt + 1))
6869
done
69-
echo "Failed to ensure replica set initialization after ${max_attempts} attempts"
70-
exit 1
70+
echo "[postStart] Ensuring replica set host: ${MONGO_INITDB_REPLICA_HOST}"
71+
if ! mongosh --quiet --host 127.0.0.1 /docker-entrypoint-initdb.d/00-replica-set.js; then
72+
rc=$?
73+
echo "[postStart] 00-replica-set.js failed with exit code ${rc}" >&2
74+
exit ${rc}
75+
fi
76+
echo "[postStart] Replica set script completed successfully"
7177
env:
7278
- name: POD_IP
7379
valueFrom:

0 commit comments

Comments
 (0)