refactor(worker): add configurable NATS JetStream replica count
Introduce COMPUTE_NATS_REPLICAS environment variable to control the number of replicas for JetStream streams and key-value buckets. Only values 1, 3, or 5 are accepted, defaulting to 1 for other inputs. Update worker server logic and deployment documentation to describe and support this configuration.
This commit is contained in:
parent
64a68b1011
commit
653dbedaee
3 changed files with 16 additions and 1 deletions
|
|
@ -40,6 +40,7 @@ S3_FORCE_PATH_STYLE=true
|
||||||
# COMPUTE_PDF_JOB_ATTEMPTS=1
|
# COMPUTE_PDF_JOB_ATTEMPTS=1
|
||||||
# COMPUTE_JOBS_STREAM_MAX_BYTES=268435456
|
# COMPUTE_JOBS_STREAM_MAX_BYTES=268435456
|
||||||
# COMPUTE_JOB_STATES_MAX_BYTES=67108864
|
# COMPUTE_JOB_STATES_MAX_BYTES=67108864
|
||||||
|
# COMPUTE_NATS_REPLICAS=1
|
||||||
# Optional stale window for reusing in-flight opKey entries before forcing a new attempt
|
# Optional stale window for reusing in-flight opKey entries before forcing a new attempt
|
||||||
# Default is max(30m, 4x max compute timeout); running jobs also refresh heartbeat every 5s
|
# Default is max(30m, 4x max compute timeout); running jobs also refresh heartbeat every 5s
|
||||||
# COMPUTE_OP_STALE_MS=1800000
|
# COMPUTE_OP_STALE_MS=1800000
|
||||||
|
|
|
||||||
|
|
@ -128,6 +128,11 @@ function readIntEnv(name: string, fallback: number): number {
|
||||||
return Math.floor(parsed);
|
return Math.floor(parsed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function normalizeNatsReplicas(value: number): number {
|
||||||
|
if (value === 3 || value === 5) return value;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
function parseBoolEnv(name: string, fallback: boolean): boolean {
|
function parseBoolEnv(name: string, fallback: boolean): boolean {
|
||||||
const raw = process.env[name]?.trim();
|
const raw = process.env[name]?.trim();
|
||||||
if (!raw) return fallback;
|
if (!raw) return fallback;
|
||||||
|
|
@ -383,6 +388,7 @@ async function ensureJetStreamResources(
|
||||||
pdfTimeoutMs: number,
|
pdfTimeoutMs: number,
|
||||||
pdfAttempts: number,
|
pdfAttempts: number,
|
||||||
maxBytes: number,
|
maxBytes: number,
|
||||||
|
natsReplicas: number,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const streamConfig = {
|
const streamConfig = {
|
||||||
name: JOBS_STREAM_NAME,
|
name: JOBS_STREAM_NAME,
|
||||||
|
|
@ -390,6 +396,7 @@ async function ensureJetStreamResources(
|
||||||
retention: RetentionPolicy.Workqueue,
|
retention: RetentionPolicy.Workqueue,
|
||||||
storage: StorageType.File,
|
storage: StorageType.File,
|
||||||
max_bytes: maxBytes,
|
max_bytes: maxBytes,
|
||||||
|
num_replicas: natsReplicas,
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
@ -399,6 +406,7 @@ async function ensureJetStreamResources(
|
||||||
await jsm.streams.update(JOBS_STREAM_NAME, {
|
await jsm.streams.update(JOBS_STREAM_NAME, {
|
||||||
subjects: [WHISPER_JOBS_SUBJECT, LAYOUT_JOBS_SUBJECT],
|
subjects: [WHISPER_JOBS_SUBJECT, LAYOUT_JOBS_SUBJECT],
|
||||||
max_bytes: maxBytes,
|
max_bytes: maxBytes,
|
||||||
|
num_replicas: natsReplicas,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -451,6 +459,7 @@ async function main(): Promise<void> {
|
||||||
const prewarmModels = parseBoolEnv('COMPUTE_PREWARM_MODELS', true);
|
const prewarmModels = parseBoolEnv('COMPUTE_PREWARM_MODELS', true);
|
||||||
const jobsStreamMaxBytes = readIntEnv('COMPUTE_JOBS_STREAM_MAX_BYTES', 256 * 1024 * 1024);
|
const jobsStreamMaxBytes = readIntEnv('COMPUTE_JOBS_STREAM_MAX_BYTES', 256 * 1024 * 1024);
|
||||||
const jobStatesMaxBytes = readIntEnv('COMPUTE_JOB_STATES_MAX_BYTES', 64 * 1024 * 1024);
|
const jobStatesMaxBytes = readIntEnv('COMPUTE_JOB_STATES_MAX_BYTES', 64 * 1024 * 1024);
|
||||||
|
const natsReplicas = normalizeNatsReplicas(readIntEnv('COMPUTE_NATS_REPLICAS', 1));
|
||||||
const opStaleMs = getComputeOpStaleMs();
|
const opStaleMs = getComputeOpStaleMs();
|
||||||
|
|
||||||
const connectOpts: Parameters<typeof connect>[0] = { servers: natsUrl };
|
const connectOpts: Parameters<typeof connect>[0] = { servers: natsUrl };
|
||||||
|
|
@ -533,8 +542,9 @@ async function main(): Promise<void> {
|
||||||
const nc: NatsConnection = await connect(connectOpts);
|
const nc: NatsConnection = await connect(connectOpts);
|
||||||
const js: JetStreamClient = jetstream(nc, { timeout: NATS_API_TIMEOUT_MS });
|
const js: JetStreamClient = jetstream(nc, { timeout: NATS_API_TIMEOUT_MS });
|
||||||
const jsm: JetStreamManager = await jetstreamManager(nc, { timeout: NATS_API_TIMEOUT_MS });
|
const jsm: JetStreamManager = await jetstreamManager(nc, { timeout: NATS_API_TIMEOUT_MS });
|
||||||
await ensureJetStreamResources(jsm, whisperTimeoutMs, pdfTimeoutMs, pdfAttempts, jobsStreamMaxBytes);
|
await ensureJetStreamResources(jsm, whisperTimeoutMs, pdfTimeoutMs, pdfAttempts, jobsStreamMaxBytes, natsReplicas);
|
||||||
const kv = await new Kvm(js).create(COMPUTE_STATE_BUCKET, {
|
const kv = await new Kvm(js).create(COMPUTE_STATE_BUCKET, {
|
||||||
|
replicas: natsReplicas,
|
||||||
history: 1,
|
history: 1,
|
||||||
ttl: COMPUTE_STATE_TTL_MS,
|
ttl: COMPUTE_STATE_TTL_MS,
|
||||||
max_bytes: jobStatesMaxBytes,
|
max_bytes: jobStatesMaxBytes,
|
||||||
|
|
@ -614,6 +624,7 @@ async function main(): Promise<void> {
|
||||||
availableCpuCores: getAvailableCpuCores(),
|
availableCpuCores: getAvailableCpuCores(),
|
||||||
onnxThreadsPerJob: getOnnxThreadsPerJob(),
|
onnxThreadsPerJob: getOnnxThreadsPerJob(),
|
||||||
natsApiTimeoutMs: NATS_API_TIMEOUT_MS,
|
natsApiTimeoutMs: NATS_API_TIMEOUT_MS,
|
||||||
|
natsReplicas,
|
||||||
pdfLayoutHardCapMs: pdfHardCapMs,
|
pdfLayoutHardCapMs: pdfHardCapMs,
|
||||||
}, 'compute runtime config');
|
}, 'compute runtime config');
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,7 @@ Advanced tuning (usually leave unset unless you need overrides):
|
||||||
- `COMPUTE_PDF_JOB_ATTEMPTS=1` (PDF layout retry attempts)
|
- `COMPUTE_PDF_JOB_ATTEMPTS=1` (PDF layout retry attempts)
|
||||||
- `COMPUTE_JOBS_STREAM_MAX_BYTES=268435456` (256MB JetStream jobs stream cap)
|
- `COMPUTE_JOBS_STREAM_MAX_BYTES=268435456` (256MB JetStream jobs stream cap)
|
||||||
- `COMPUTE_JOB_STATES_MAX_BYTES=67108864` (64MB JetStream KV bucket cap)
|
- `COMPUTE_JOB_STATES_MAX_BYTES=67108864` (64MB JetStream KV bucket cap)
|
||||||
|
- `COMPUTE_NATS_REPLICAS=1` (JetStream stream + KV replicas; valid: `1`, `3`, `5`)
|
||||||
- `COMPUTE_OP_STALE_MS=1800000` (stale op replacement window)
|
- `COMPUTE_OP_STALE_MS=1800000` (stale op replacement window)
|
||||||
|
|
||||||
## App server environment variables (worker mode)
|
## App server environment variables (worker mode)
|
||||||
|
|
@ -158,6 +159,7 @@ COMPUTE_WORKER_TOKEN=<long-random-shared-token>
|
||||||
# COMPUTE_PDF_JOB_ATTEMPTS=1
|
# COMPUTE_PDF_JOB_ATTEMPTS=1
|
||||||
# COMPUTE_JOBS_STREAM_MAX_BYTES=268435456
|
# COMPUTE_JOBS_STREAM_MAX_BYTES=268435456
|
||||||
# COMPUTE_JOB_STATES_MAX_BYTES=67108864
|
# COMPUTE_JOB_STATES_MAX_BYTES=67108864
|
||||||
|
# COMPUTE_NATS_REPLICAS=1
|
||||||
|
|
||||||
NATS_URL=tls://connect.ngs.global:4222
|
NATS_URL=tls://connect.ngs.global:4222
|
||||||
NATS_CREDS="-----BEGIN NATS USER JWT-----
|
NATS_CREDS="-----BEGIN NATS USER JWT-----
|
||||||
|
|
@ -180,6 +182,7 @@ Notes:
|
||||||
- On Railway, leave `PORT` managed by the platform.
|
- On Railway, leave `PORT` managed by the platform.
|
||||||
- If your platform supports mounted files, you can use `NATS_CREDS_FILE` instead of `NATS_CREDS`.
|
- If your platform supports mounted files, you can use `NATS_CREDS_FILE` instead of `NATS_CREDS`.
|
||||||
- `COMPUTE_JOBS_STREAM_MAX_BYTES` and `COMPUTE_JOB_STATES_MAX_BYTES` are optional; defaults are `268435456` (256MiB) and `67108864` (64MiB).
|
- `COMPUTE_JOBS_STREAM_MAX_BYTES` and `COMPUTE_JOB_STATES_MAX_BYTES` are optional; defaults are `268435456` (256MiB) and `67108864` (64MiB).
|
||||||
|
- `COMPUTE_NATS_REPLICAS` is optional; default is `1`. Valid values are `1`, `3`, `5`.
|
||||||
|
|
||||||
### 4. Configure the OpenReader app server (worker mode)
|
### 4. Configure the OpenReader app server (worker mode)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue