Skip to content

TranslatorSv2::start() panics on second call due to process-wide OnceLock statics (breaks sequential integration tests) #430

@gimballock

Description

@gimballock

Problem

TPROXY_MODE and VARDIFF_ENABLED are declared as process-wide static OnceLock<_> in miner-apps/translator/src/lib/mod.rs:

static TPROXY_MODE: OnceLock<TproxyMode> = OnceLock::new();
static VARDIFF_ENABLED: OnceLock<bool> = OnceLock::new();

TranslatorSv2::start() initializes them unconditionally on every call:

TPROXY_MODE
    .set(self.config.aggregate_channels.into())
    .expect("TPROXY_MODE initialized more than once");
VARDIFF_ENABLED
    .set(self.config.downstream_difficulty_config.enable_vardiff)
    .expect("VARDIFF_ENABLED initialized more than once");

nextest.toml sets test-threads = 1, so all integration tests run sequentially in the same process. The first test that calls start() on a TranslatorSv2 sets the OnceLocks successfully. Every subsequent test that creates a new TranslatorSv2 and calls start() causes OnceLock::set() to return Err — the .expect() panics. Since start() is invoked inside tokio::spawn, the panic is silently swallowed, the translator exits immediately, and the test framework only sees a downstream symptom (e.g. "Connection refused" on the monitoring or SV1 port).

Observed symptom

When running the monitoring integration tests with --test-threads=1, the first tProxy test passes and the remaining 9 fail with "Connection refused" on the monitoring endpoint. There are 10 tProxy tests total, producing a consistent 10/9 pass/fail split. Confirmed reproducible on 3773dd5 (upstream main before PR #373).

Fix

TPROXY_MODE and VARDIFF_ENABLED should not be process-wide singletons. Options:

  1. Make them instance fields on TranslatorSv2 (preferred) — remove the statics, store the values in the struct, and pass them through to callers.
  2. Use get_or_init everywhere instead of set + expect — this silently ignores the second initialization, which is safe if all instances are configured identically, but masks misconfiguration.

Option 1 is the correct fix as it allows multiple TranslatorSv2 instances with different configs to coexist in the same process (required for integration tests and any future multi-instance deployment).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status

    Todo 📝

    Status

    Todo 📝

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions