openvibers/openvibe.host Commits
Host: a data switch stops a socket-activated service's socket too; ov_live gets its own PgBouncer pool (#36)
Changes
@@@ -44,6 +44,11 @@ function switchEnv(svc) {const units = [...svc.units, ...svc.workerUnits].map(unitPattern);// Deploys restart every unit matching unitsMatch (release-ops), so the switch stops them too.if (svc.unitsMatch && !units.includes(svc.unitsMatch)) units.push(svc.unitsMatch);++ // A socket-activated service comes straight back on the next request while its socket listens: the old++ // release would write the database during the import (2026-10-08, Live: analytics rows moved under the verify).++ // The socket is stopped after the service (the first unit stays the one whose environment runs the import); the++ // deploy's restart starts it again through the service's Requires=.++ if (svc.socketUnit && !units.includes(svc.socketUnit)) units.push(svc.socketUnit);if (units.length) env.SWITCH_UNITS = units.join(' ');env.SWITCH_DIR = svc.repo;return env;@@@ -1,6 +1,8 @@; OpenVibe data role (ADR-035): PgBouncer in transaction-pooling mode (ADR-007 2026-09-24 rule 1).; Applications use DATABASE_URL (port 6432); migrations use DATABASE_DIRECT_URL (port 5432).[databases]++; Live is the busiest service (chat, viewers, analytics): its own pool, above the default 5 (2026-10-08, plan T4).++ov_live = host=127.0.0.1 port=5432 pool_size=10 max_db_connections=20* = host=127.0.0.1 port=5432[pgbouncer]@@@ -101,7 +101,9 @@ runTests([assert.strictEqual(r.code, 0, r.out);assert.deepStrictEqual(runs[0].args, ['live']);assert.strictEqual(runs[0].env.SWITCH_DIR, '/opt/openvibe.live');-- assert.strictEqual(runs[0].env.SWITCH_UNITS, 'openvibe-live.service');++ // Live is socket-activated: its socket is stopped too (after the service), or the next request starts the old++ // release again while the import runs.++ assert.strictEqual(runs[0].env.SWITCH_UNITS, 'openvibe-live.service openvibe-live.socket');}),test('switch also stops the units matching unitsMatch', async () => {@@@ -112,7 +114,7 @@ runTests([const runs = installScripts(host);const r = await cli(host, 'data', 'switch', 'live');assert.strictEqual(r.code, 0, r.out);-- assert.strictEqual(runs[0].env.SWITCH_UNITS, 'openvibe-live.service openvibe-live*.service');++ assert.strictEqual(runs[0].env.SWITCH_UNITS, 'openvibe-live.service openvibe-live*.service openvibe-live.socket');}),test('without root both are refused and nothing runs (sudo would drop SWITCH_UNITS/SWITCH_DIR)', async () => {@@@ -139,7 +141,7 @@ runTests([const s = await cli(host, 'data', 'switch', 'live', '--sqlite', '/var/lib/openvibe-live/live.db', '--dry-run');assert.strictEqual(s.code, 0, s.out);-- assert.match(s.out, /would run: SWITCH_UNITS="openvibe-live\.service" SWITCH_DIR="\/opt\/openvibe\.live" \/opt\/ovhost-install\/roles\/data\/switch-service\.sh "live" "\/var\/lib\/openvibe-live\/live\.db"/);++ assert.match(s.out, /would run: SWITCH_UNITS="openvibe-live\.service openvibe-live\.socket" SWITCH_DIR="\/opt\/openvibe\.live" \/opt\/ovhost-install\/roles\/data\/switch-service\.sh "live" "\/var\/lib\/openvibe-live\/live\.db"/);assert.deepStrictEqual(runs, []);}),
@@ -44,6 +44,11 @@ function switchEnv(svc) {
@@ -44,6 +44,11 @@ function switchEnv(svc) {
const units = [...svc.units, ...svc.workerUnits].map(unitPattern);
const units = [...svc.units, ...svc.workerUnits].map(unitPattern);
// Deploys restart every unit matching unitsMatch (release-ops), so the switch stops them too.
// Deploys restart every unit matching unitsMatch (release-ops), so the switch stops them too.
if (svc.unitsMatch && !units.includes(svc.unitsMatch)) units.push(svc.unitsMatch);
if (svc.unitsMatch && !units.includes(svc.unitsMatch)) units.push(svc.unitsMatch);
+ // A socket-activated service comes straight back on the next request while its socket listens: the old
+ // release would write the database during the import (2026-10-08, Live: analytics rows moved under the verify).
+ // The socket is stopped after the service (the first unit stays the one whose environment runs the import); the
+ // deploy's restart starts it again through the service's Requires=.
+ if (svc.socketUnit && !units.includes(svc.socketUnit)) units.push(svc.socketUnit);
if (units.length) env.SWITCH_UNITS = units.join(' ');
if (units.length) env.SWITCH_UNITS = units.join(' ');
env.SWITCH_DIR = svc.repo;
env.SWITCH_DIR = svc.repo;
return env;
return env;
@@ -1,6 +1,8 @@
@@ -1,6 +1,8 @@
; OpenVibe data role (ADR-035): PgBouncer in transaction-pooling mode (ADR-007 2026-09-24 rule 1).
; OpenVibe data role (ADR-035): PgBouncer in transaction-pooling mode (ADR-007 2026-09-24 rule 1).
; Applications use DATABASE_URL (port 6432); migrations use DATABASE_DIRECT_URL (port 5432).
; Applications use DATABASE_URL (port 6432); migrations use DATABASE_DIRECT_URL (port 5432).
[databases]
[databases]
+; Live is the busiest service (chat, viewers, analytics): its own pool, above the default 5 (2026-10-08, plan T4).
+ov_live = host=127.0.0.1 port=5432 pool_size=10 max_db_connections=20
* = host=127.0.0.1 port=5432
* = host=127.0.0.1 port=5432
[pgbouncer]
[pgbouncer]
@@ -101,7 +101,9 @@ runTests([
@@ -101,7 +101,9 @@ runTests([
assert.strictEqual(r.code, 0, r.out);
assert.strictEqual(r.code, 0, r.out);
assert.deepStrictEqual(runs[0].args, ['live']);
assert.deepStrictEqual(runs[0].args, ['live']);
assert.strictEqual(runs[0].env.SWITCH_DIR, '/opt/openvibe.live');
assert.strictEqual(runs[0].env.SWITCH_DIR, '/opt/openvibe.live');
- assert.strictEqual(runs[0].env.SWITCH_UNITS, 'openvibe-live.service');
+ // Live is socket-activated: its socket is stopped too (after the service), or the next request starts the old
+ // release again while the import runs.
+ assert.strictEqual(runs[0].env.SWITCH_UNITS, 'openvibe-live.service openvibe-live.socket');
}),
}),
test('switch also stops the units matching unitsMatch', async () => {
test('switch also stops the units matching unitsMatch', async () => {
@@ -112,7 +114,7 @@ runTests([
@@ -112,7 +114,7 @@ runTests([
const runs = installScripts(host);
const runs = installScripts(host);
const r = await cli(host, 'data', 'switch', 'live');
const r = await cli(host, 'data', 'switch', 'live');
assert.strictEqual(r.code, 0, r.out);
assert.strictEqual(r.code, 0, r.out);
- assert.strictEqual(runs[0].env.SWITCH_UNITS, 'openvibe-live.service openvibe-live*.service');
+ assert.strictEqual(runs[0].env.SWITCH_UNITS, 'openvibe-live.service openvibe-live*.service openvibe-live.socket');
}),
}),
test('without root both are refused and nothing runs (sudo would drop SWITCH_UNITS/SWITCH_DIR)', async () => {
test('without root both are refused and nothing runs (sudo would drop SWITCH_UNITS/SWITCH_DIR)', async () => {
@@ -139,7 +141,7 @@ runTests([
@@ -139,7 +141,7 @@ runTests([
const s = await cli(host, 'data', 'switch', 'live', '--sqlite', '/var/lib/openvibe-live/live.db', '--dry-run');
const s = await cli(host, 'data', 'switch', 'live', '--sqlite', '/var/lib/openvibe-live/live.db', '--dry-run');
assert.strictEqual(s.code, 0, s.out);
assert.strictEqual(s.code, 0, s.out);
- assert.match(s.out, /would run: SWITCH_UNITS="openvibe-live\.service" SWITCH_DIR="\/opt\/openvibe\.live" \/opt\/ovhost-install\/roles\/data\/switch-service\.sh "live" "\/var\/lib\/openvibe-live\/live\.db"/);
+ assert.match(s.out, /would run: SWITCH_UNITS="openvibe-live\.service openvibe-live\.socket" SWITCH_DIR="\/opt\/openvibe\.live" \/opt\/ovhost-install\/roles\/data\/switch-service\.sh "live" "\/var\/lib\/openvibe-live\/live\.db"/);
assert.deepStrictEqual(runs, []);
assert.deepStrictEqual(runs, []);
}),
}),