-
Notifications
You must be signed in to change notification settings - Fork 4
feat: expose variation ID, SDK status, and harden runtime safety #14
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
WuJiayi0307
wants to merge
6
commits into
featbit:master
Choose a base branch
from
WuJiayi0307:feat/openfeature-provider
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
b10fc9b
feat: expose variation id in evaluation details
WuJiayi0307 e09df9d
feat: integrate OpenFeature server provider
WuJiayi0307 06d29d1
fix: address OpenFeature provider review feedback
WuJiayi0307 824aee8
refactor: move OpenFeature provider to dedicated repository
WuJiayi0307 32e0f9c
fix: preserve update status notification order
WuJiayi0307 bf4c842
fix: harden Python SDK runtime and add reliability review
WuJiayi0307 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,4 @@ | ||
| include requirements.txt | ||
| include README.md | ||
| include dev-requirements.txt | ||
| include release/package.json | ||
| include release/package.json |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,108 @@ | ||
| # Python Server SDK reliability review | ||
|
|
||
| This review covers the production risks requested for the Python Server SDK: | ||
| unit behavior, WebSocket synchronization, event processing, evaluation, | ||
| logging/error isolation, thread safety, shutdown, memory retention, redundant | ||
| code, and live FeatBit service interoperability. | ||
|
|
||
| ## Review result | ||
|
|
||
| The review found and fixed the following concrete issues: | ||
|
|
||
| - `RepeatableTask` stored an `Event` in `Thread._stop`, which breaks Python's | ||
| own `Thread.join()` cleanup path. It now uses a separate stop event and joins | ||
| deterministically. | ||
| - WebSocket reconnect backoff used an uninterruptible sleep. Client shutdown | ||
| now interrupts the wait, closes the socket defensively, stops the ping task, | ||
| and joins the streaming thread. | ||
| - The notice broadcaster mutated its listener registry concurrently. Listener | ||
| operations now use a lock and dispatch from an immutable snapshot; a queue | ||
| sentinel makes shutdown immediate and repeatable. | ||
| - The event processor did not retain or join its dispatcher. It now owns the | ||
| dispatcher lifecycle, joins the periodic flush task, and always completes a | ||
| synchronous message even if message handling fails. | ||
| - `FBClient.stop()` could expose an extension exception and skip all later | ||
| cleanup. Shutdown is now idempotent, producer-first, and isolates each | ||
| component failure. | ||
| - Custom event processor errors could escape from `identify`, `track_*`, or | ||
| `flush`. Event delivery is now best-effort and cannot fail the application | ||
| request. | ||
| - Invalid offline bootstrap JSON and unsupported runtime fallback objects could | ||
| raise from evaluation-related calls. They now return `False` or the supplied | ||
| fallback, with a diagnostic log message. | ||
| - `Config` and the HTTP helper used mutable default objects. Each client now | ||
| gets independent HTTP/WebSocket options and headers. | ||
| - A duplicate `FBUser.from_dict()` call in `track_metric` was removed. | ||
|
|
||
| Constructor/configuration validation still raises for invalid required | ||
| configuration. This is intentional fail-fast behavior before an SDK client is | ||
| usable; runtime evaluation, event, status, and shutdown paths are isolated. | ||
|
|
||
| ## Automated evidence | ||
|
|
||
| Run from the repository root: | ||
|
|
||
| ```shell | ||
| python -m pytest | ||
| python -m flake8 . | ||
| python scripts/resource_audit.py --evaluations 80000 --workers 8 | ||
| python -m build | ||
| ``` | ||
|
|
||
| Result on Python 3.11.8 (2026-07-29): | ||
|
|
||
| - 66 unit/integration tests passed. | ||
| - Flake8 passed. | ||
| - 80,000 evaluations across 8 workers completed with 0 concurrent errors. | ||
| - Throughput was 11,425 evaluations/second on the final audit run. | ||
| - No FeatBit SDK worker threads remained after shutdown. | ||
| - 24,389 traced bytes remained after garbage collection, below the 2 MiB | ||
| regression threshold. | ||
| - A clean wheel built from the source distribution contained 27 files, no | ||
| `tests/` package, and no stale `featbit_openfeature/` package. | ||
|
|
||
| The tests specifically cover concurrent evaluation, status-listener ordering, | ||
| listener registration/removal under contention, event-processor failure | ||
| isolation, repeated client lifecycle, periodic task joining, and interrupting a | ||
| blocked WebSocket lifecycle. | ||
|
|
||
| ## Live FeatBit service verification | ||
|
|
||
| Use a real environment Server Key without storing it in the repository: | ||
|
|
||
| ```shell | ||
| export FEATBIT_ENV_SECRET='<server-key>' | ||
| export FEATBIT_FLAG_KEY='python-app-release' | ||
| python scripts/live_integration_check.py | ||
| ``` | ||
|
|
||
| Optional variables are `FEATBIT_STREAMING_URL`, `FEATBIT_EVENT_URL`, and | ||
| `FEATBIT_START_WAIT_SECONDS`. The script verifies: | ||
|
|
||
| 1. authenticated WebSocket connection and initial data synchronization; | ||
| 2. SDK status reaches `OK`; | ||
| 3. remote flag evaluation returns value, reason, and variation ID; | ||
| 4. feature-flag, identify, and custom metric events enter the delivery path; | ||
| 5. explicit flush and final synchronous drain complete; | ||
| 6. status changes to `OFF` and no SDK threads remain after close. | ||
|
|
||
| The secret is read only from the process environment and is never printed or | ||
| written to disk. | ||
|
|
||
| A recorded FeatBit Cloud run on 2026-07-24 reached the WebSocket connected | ||
| state, processed the initial data-sync payload, evaluated the remote | ||
| `python-app-release` flag, exercised the gray-release application under | ||
| concurrent HTTP traffic, and later demonstrated automatic reconnection after a | ||
| remote-host disconnect. The checked-in script turns that one-off validation | ||
| into a repeatable, secret-safe release check. | ||
|
|
||
| ## Remaining operational considerations | ||
|
|
||
| - The SDK is designed as a long-lived singleton, not one client per request. | ||
| - Event delivery is intentionally best-effort so analytics failure cannot | ||
| affect application availability. | ||
| - A custom `DataStorage`, `EventProcessor`, or `UpdateProcessor` remains | ||
| responsible for its own internal correctness; the client isolates its | ||
| runtime and shutdown exceptions. | ||
| - Memory thresholds are regression guards, not universal capacity limits; | ||
| production sizing should use the application's real flag and segment data. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Make the live-check criterion enforce the documented OFF transition.
The documented script records
state_after_closebut does not assert that it equalsStateType.OFF; it only checks pre-close readiness and leaked threads. Add that assertion toscripts/live_integration_check.py, or remove the OFF-state claim from this checklist.🤖 Prompt for AI Agents