Repository navigation
Segment membership inspection index core #7406
Copy link
Copy link
Closed as not planned
Labels
apiIssue related to the REST APIIssue related to the REST API
Description
Scope
Core data model
segment_membershipDjango app around theAtomBitmapmodel.- PK-as-ordinal:
Identity.idis the bitmap ord. - One blob per atom (no sharding).
- Atom canonical key:
(environment_id, kind, property, operator, operand_canonical, segment_key).segment_keyis populated only for atoms whose operator is% Split(the engine salts that operator by segment id).
Maintenance dispatch (self-hosted core envs)
All listed signals are gated, in order, by:
settings.SEGMENT_MEMBERSHIP_ENABLED(Django global).segment_membership_indexFlagsmith on Flagsmith flag (per-organisation; defaults true in self-hosted).
If either gate is off, the signal handler returns early without enqueuing a task.
| Signal source | Task enqueued | Task body |
|---|---|---|
Identity.post_save(created=True) |
process_identity_created(identity_id, environment_id) |
Set bits at identity.id in every "identifier" / "identity_key" atom in the env. One-shot per identity (those properties are immutable). |
Trait.post_save (single-row write) |
process_traits_changed(identity_id, environment_id, changed_keys=[trait_key]) |
For each "trait" atom whose property is in changed_keys, re-evaluate against the identity's current trait state and flip the bit at identity.id. |
Trait.post_delete (single-row delete) |
process_traits_changed(identity_id, environment_id, changed_keys=[trait_key]) |
Same as above; absence of the trait flips operators like is_set and equals. |
Custom traits_changed signal emitted by Identity.update_traits (the bulk path) |
process_traits_changed(identity_id, environment_id, changed_keys=[…]) |
Same as above with every key touched by the bulk write. |
Identity.post_delete |
process_identity_deleted(identity_id, environment_id) |
Clear the bit at identity.id from every atom in the env. |
Segment.post_save, gated on version_of_id == id (canonical rows only — non-canonical revision snapshots created by the segment serialiser are skipped) |
process_segment_canonical_changed(segment_id) |
ensure_atoms for the segment, then backfill any new atoms across every env in the project. |
Tasks are idempotent: each reads current state and recomputes the affected bits. Out-of-order delivery and at-least-once retries converge.
Other
- Async maintenance through
flagsmith-task-processor. - Management command
backfill_segment_membershipfor ops use (full-env or single-segment rebuilds). - Tests covering the operator vocabulary with a differential check against
get_evaluation_result.
Acceptance criteria
Gating
- With
SEGMENT_MEMBERSHIP_ENABLED=False, no signal handler enqueues a task. - With
segment_membership_index=Falsefor an organisation, no signal handler enqueues a task for projects in that organisation. - With both gates on, signals enqueue tasks per the dispatch table.
Per-signal behaviour (asserted via integration tests against a fresh env with one or more atoms backfilled)
- Creating an
Identityenqueuesprocess_identity_created. After drain, the identity's PK is set in every"identifier"and"identity_key"atom for the env. - Creating a
Traitenqueuesprocess_traits_changedwithchanged_keys=[trait_key]. After drain, every"trait"atom whose property equalstrait_keyagrees withget_evaluation_resultat that identity's PK. - Deleting a
Traitenqueuesprocess_traits_changed. After drain, the bit reflects the trait's absence (e.g.is_set=falseflips true,equalsflips false). Identity.update_traitswith N changed keys enqueues exactly oneprocess_traits_changedtask whosechanged_keysis the union of those keys. After drain, every affected atom is correct.- Deleting an
Identityenqueuesprocess_identity_deleted. After drain, no bit remains set at that PK in any atom for the env. - Saving live
Segmentversion enqueuesprocess_segment_canonical_changed. Saving a non-live version does not. - After
process_segment_canonical_changeddrains, every atom newly required by the segment has a bitmap that matchesget_evaluation_resultfor every identity in every env in the project.
Idempotency
- Re-running any task with the same arguments leaves the bitmap unchanged (no double-flips, no resurrection of cleared bits).
- Tasks enqueued out of order (e.g. a delete observed before its create due to retry) converge to the same bitmap as in-order delivery once both have drained.
Differential correctness
backfill_segment_membership --environment <id> --segment <id|all>produces a bitmap that matchesget_evaluation_resultfor every identity over the operator vocabulary.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
apiIssue related to the REST APIIssue related to the REST API
Activity
khvn26 commentedon May 9, 2026
Closing as not planned. Replaced by #7469 — V1 (Beta) epic for the columnar design. The bitmap design this issue was scoped against was retired at the 5/5/2026 RFC review in favour of the columnar approach now shipping in Alpha. See Segment Membership Inspection (columnar) (private to Flagsmith team) for the new RFC.