feat: Store users by package for targeting#81
Closed
ohalushchak-exadel wants to merge 5 commits intomainfrom
Closed
feat: Store users by package for targeting#81ohalushchak-exadel wants to merge 5 commits intomainfrom
ohalushchak-exadel wants to merge 5 commits intomainfrom
Conversation
Adds DeleteUserProfile and DeleteUserProfiles to Engine as counterparts to the existing setters, enabling callers to remove user segment profiles from the store. To support this, Del and MDel are added to the Store interface with corresponding implementations in MockStore and valkeystore.Store. The Valkey/Redis implementation uses a single DEL call for both the single and batch cases, since the command natively accepts multiple keys.
## Summary
Replaces the `user → segments` audience model with a `hash(packageID) → {hash(userToken): intent}` HSET model, and optimises identity evaluation to a fixed number of Valkey round-trips regardless of package count.
### Privacy: package-scoped audience keys
Previously, audience membership was stored per-user (`user:profile:<hash>` → JSON blob of segments). Any caller who knew a user token hash could read that user's full segment membership across all sellers.
Audience data is now stored per-package: `audience:<hash(packageID)>` → HSET where each field is `hash(userToken)` and each value is the intent score. A caller can only derive the key if they know the package ID, so sellers are isolated from each other's audience data. User tokens are still hashed before use.
### Write API
`PackageIdentityConfig.TargetSegments []string` is replaced by `Audience bool`.
New Engine write methods (all hash-based, all tokens hashed internally):
| Method | Description |
|---|---|
| `SetPackageUser(ctx, packageID, userToken, intent)` | Single user → single package |
| `AddPackageUsers(ctx, packageID, users map[string]float64)` | Batch users → single package |
| `RemovePackageUsers(ctx, packageID, userTokens []string)` | Remove specific users from a package |
| `DeletePackageUsers(ctx, packageID)` | Delete an entire package audience |
| `MSetPackageUsers(ctx, packages map[string]map[string]float64)` | Batch users → multiple packages |
| `MDeletePackageUsers(ctx, packageIDs []string)` | Delete multiple package audiences (single `DEL` round-trip) |
### Read path: N HMGET → 1 pipelined round-trip
`EvaluateIdentityResolved` previously issued one `HMGET` per audience-gated package. The new `Store.HMGetBatch(keys, fields)` method collects all audience keys before the evaluation loop and fetches them in a single pipelined call. In Valkey this is one TCP round-trip regardless of how many packages are audience-gated.
### Bug fix
`MockStore.Del` and `MDel` previously only cleared `m.strings`. They now clear all data structures (`strings`, `sets`, `zsets`, `hsets`, `expiry`), so `DeletePackageUsers` and `MDeletePackageUsers` work correctly in tests.
subtle-supernova
approved these changes
Apr 23, 2026
Contributor
|
from a security perspective packages are on the line protocol back and forth from the agent to the ad server at least in some form, so I'm not sure this is more secure. I could ping the server with package IDs that I have seen and get the same information out, right? |
Contributor
|
this also loses the ability to add a value to a segment, which is useful for minimizing the number of categorical segments - think age:12 vs age1, age2, age3, ... |
Contributor
|
also doesn't this have to then be seller_url+package_id |
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Summary
Replaces the
user → segmentsaudience model with ahash(packageID) → {hash(userToken): intent}HSET model, and optimises identity evaluation to a fixed number of Valkey round-trips regardless of package count.Privacy: package-scoped audience keys
Previously, audience membership was stored per-user (
user:profile:<hash>→ JSON blob of segments). Any caller who knew a user token hash could read that user's full segment membership across all sellers.Audience data is now stored per-package:
audience:<hash(packageID)>→ HSET where each field ishash(userToken)and each value is the intent score. A caller can only derive the key if they know the package ID, so sellers are isolated from each other's audience data. User tokens are still hashed before use.Write API
PackageIdentityConfig.TargetSegments []stringis replaced byAudience bool.New Engine write methods (all hash-based, all tokens hashed internally):
SetPackageUser(ctx, packageID, userToken, intent)AddPackageUsers(ctx, packageID, users map[string]float64)RemovePackageUsers(ctx, packageID, userTokens []string)DeletePackageUsers(ctx, packageID)MSetPackageUsers(ctx, packages map[string]map[string]float64)MDeletePackageUsers(ctx, packageIDs []string)DELround-trip)Read path: N HMGET → 1 pipelined round-trip
EvaluateIdentityResolvedpreviously issued oneHMGETper audience-gated package. The newStore.HMGetBatch(keys, fields)method collects all audience keys before the evaluation loop and fetches them in a single pipelined call. In Valkey this is one TCP round-trip regardless of how many packages are audience-gated.Bug fix
MockStore.DelandMDelpreviously only clearedm.strings. They now clear all data structures (strings,sets,zsets,hsets,expiry), soDeletePackageUsersandMDeletePackageUserswork correctly in tests.