Skip to content

Thread safety: OmegaConfigLoader #5660

Description

@ankatiyar

Description

FOllow up from #5535
To share pre-loaded configuration for serving use cases, we must ensure that the OmegaConfigLoader can be thread safe.

Context

Every OmegaConfigLoader.__getitem__ call does this first:

def __getitem__(self, key: str):
    self._register_runtime_params_resolver()   # ← called on every access
    ...

def _register_runtime_params_resolver(self) -> None:
    OmegaConf.register_new_resolver(
        "runtime_params",
        self._get_runtime_value,   # ← closure bound to THIS instance's params
        replace=True,              # ← replaces the global resolver process-wide
    )

OmegaConf.register_new_resolver is a process-global operation. replace=True means every __getitem__ call, from any config loader instance, overwrites the single global "runtime_params" resolver. The resolver is then called lazily during YAML merging to resolve ${runtime_params:x} interpolations.

With two concurrent run() calls using different runtime_params:


Thread 1: config_loader_A._register_runtime_params_resolver()
          → global resolver now = config_loader_A._get_runtime_value
          → resolver reads {"learning_rate": 0.01}

Thread 2: config_loader_B._register_runtime_params_resolver()
          → global resolver now = config_loader_B._get_runtime_value
          → resolver reads {"learning_rate": 0.1}

Thread 1: OmegaConf resolves "${runtime_params.learning_rate}" in parameters.yml
          → calls the global resolver, which is now bound to config_loader_B
          → Thread 1's pipeline runs with learning_rate = 0.1  ← wrong

Possible Implementation

Possible Alternatives

Metadata

Metadata

Assignees

No one assigned

    Labels

    Issue: Feature RequestNew feature or improvement to existing feature

    Type

    Fields

    No fields configured for Spike.

    Projects

    Status
    No status

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions