EG-4662 (12): Share _InstanceRegistry using a singleton - #423
Conversation
InstanceRegistry using a singleton
0e1732e to
4d52383
Compare
5aff4c5 to
7624b9d
Compare
a7a3450 to
fa31283
Compare
fa31283 to
05010c3
Compare
5b09f3b to
aad3ead
Compare
aad3ead to
a3ac936
Compare
a3ac936 to
afb7f7b
Compare
afb7f7b to
bb76cf0
Compare
bb76cf0 to
d40073d
Compare
1317629 to
51cc048
Compare
b86cb9e to
779e820
Compare
779e820 to
063acbf
Compare
InstanceRegistry using a singleton_InstanceRegistry using a singleton
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Resolve the critical async registration and positional API compatibility issues before approval.
Get a fresh assessment by requesting another Copilot review.
Review effort: Lite
Findings: 2
Open (4)
What changed in this PR
Moves duplicate-instance tracking into a shared singleton registry for synchronous and asynchronous clients.
Changes:
- Replaces
InstanceCounterwith_InstanceRegistry. - Adds async-client registration and duplicate-instance modes.
- Updates tests and changelog documentation.
| File | Summary |
|---|---|
UnleashClient/utils.py |
Removes the old instance counter. |
UnleashClient/clients/unleash_client.py |
Uses the shared registry for synchronous clients. |
UnleashClient/clients/async_unleash_client.py |
Adds async registration; critical issues remain around unfinished lifecycle methods and positional API compatibility (1 and 2 votes). |
UnleashClient/_instance_registry.py |
Implements the singleton registry; entries are never removed after client destruction (moderate, 2 votes). |
tests/unit_tests/test_instance_registry.py |
Tests registry behavior and singleton identity. |
tests/unit_tests/clients/test_async_unleash_client.py |
Tests async duplicate-instance behavior. |
CHANGELOG.md |
Documents the changes; the async behavior description is inaccurate (nit, 2 votes). |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| _get_instance().register( | ||
| identifier=self._config.instance_identifier, mode=multiple_instance_mode | ||
| ) |
There was a problem hiding this comment.
Valid point, registration should happen at initialization.
| self._closed = threading.Event() | ||
|
|
||
| self._do_instance_check(multiple_instance_mode) | ||
| _get_instance().register( |
There was a problem hiding this comment.
| _get_instance().register( | |
| INSTANCES.register( |
There was a problem hiding this comment.
The idea is that using _get_instance() left the door open to lazy initialization, but I did not implement it. 🤔 I think I prefer to leave the function in place for the moment. Sounds right?
| * (Minor): The Sphinx documentation site is no longer built or published. It was served from a GitHub Pages custom domain that stopped resolving, so nothing published there had been reachable for some time. Reference documentation for this SDK is at https://docs.getunleash.io/reference/sdks/python, and `README.md` covers installation, usage, configuration options, custom strategies, custom caches, event callbacks and the WSGI and Celery notes. Contributor setup and the release checklist are in `DEVELOPMENT.md`. The `Documentation` URL in the package metadata now points at the docs site rather than a page that returned 404. | ||
| * (Minor): `AsyncUnleashClient` ignores `custom_options`. The constructor still accepts it and it still reaches the shared configuration, but the async request path never passes it to the HTTP library, so the async client's surface does not depend on aiohttp's own keyword arguments. `UnleashClient` is unaffected and keeps passing `custom_options` to `requests`. | ||
| * (Minor): The in-progress asynchronous client now builds an `AsyncMetricsReporter` over its `AsyncTransport`, and exposes `impact_metrics` like the synchronous client does. Nothing starts it yet — `initialize_client()` still raises `NotImplementedError` — so no metrics are sent, and nothing changes for code using `UnleashClient`. The request body, the impact-metrics collection and the restore-after-a-failed-send are shared with the synchronous reporter; only the request itself and the recurring schedule are separate. | ||
| * (Minor): Duplicate-instance detection now happens through one internal `_InstanceRegistry`, in the private `UnleashClient._instance_registry` module, instead of an `InstanceCounter` and a private method on the client. The identifier a client is keyed by, the message, the exception raised under `InstanceAllowType.BLOCK`, the error logged under `InstanceAllowType.WARN` and the counting are exactly what they were, and a client rejected under `BLOCK` is still not counted. `INSTANCES` keeps its name, both its import paths and all of its methods, and remains the supported way to reach the registry. The module is not part of the public API and may change or disappear without notice. `multiple_instance_mode` is unchanged. `UnleashClient.utils.InstanceCounter` is gone, so this affects code importing that class directly. Nothing changes for code using `UnleashClient`. |
There was a problem hiding this comment.
should we be referencing internal modules in the changelog? 🤔
There was a problem hiding this comment.
Uhmm right now the change log is generated, but it's not final. I am using it so we can later decide what goes into the final changelog + the docs.
But I'm open to better ways to do this 👍
Co-authored-by: Alexandra <szanto.szandra@gmail.com>
for more information, see https://pre-commit.ci
…-registry' into eg-4662-12-instance-registry



Part of what the SDK does is registering how many copies of a client you've got running. There's different modes in which this can run:
The changes in this PR move the logic of registering an SDK as "in use" to a new
InstanceRegistryclass. SDKs will use it as a singleton so that there is only one count of connected SDKs per runtime, regardless of what combination of SDKs the user decides to use.NOTE: The original client did not de-register clients, which is something that the Copilot review has brought up. I'd leave it as a possible improvement after an initial review because I don't see it as a big issue. But I'm open to feedback on that as well.