You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
fix: apply every distinct organization payload in a data-sink batch - #5171
When one data-sink batch carried two different payloads for the same organization, only the first one's data was written. The batch's organization cache was keyed by verified identities only, so later payloads got the cached organization id and their attributes were dropped. In meetings data this happens when attendees of one meeting are linked to two Salesforce accounts with the same website (for example "Croxel Inc." and "Croxel"): one has a logo and a different industry, and only one of them was ever stored.
This PR applies every distinct payload once, and makes the payload from the newest activity set the defaults.
Changes
The batch's organization cache is keyed by identities, find-or-create source and the payload content, with domains normalized the way findOrCreateOrganization normalizes them. Identical payloads still share one call. A payload with different content runs its own find-or-create. The existing identity locks make it wait for the earlier one, and it goes to the organization that one resolved to, without a lookup, and adds what it carries. No duplicate organizations.
Before member processing, the batch records for each organization payload the newest activity time and how many activities carry it (once per activity, even when both the member and the object member carry it). After the member updates, for each organization and source that got more than one distinct payload, all of them are applied again, to the organization they went to, from the lowest ranked to the highest: newest activity first, then the payload more activities carry, then a fixed order. Each attribute's default for that source ends on the highest-ranked payload that has it, wherever that source is the organization's top source for the attribute. Plain organization columns are only filled when empty, as before. A failure in this step is logged per payload and doesn't fail the batch's activities.
addOrgsToSegments sorts the organization and segment ids, so concurrent inserts of the same new rows always use the same order and can't deadlock. Its callers keep their behavior.
Local end-to-end runs against main, on 92.5k real github/git results and 39,659 real meetings activities seeded read-only from prod:
github/git: identical to main in every table, and in Kafka activity messages, search-sync messages and Redis keys.
meetings: identical to main except 2 added non-default attributes for croxel.com (the meetings logo and industry). The two runs of this branch are identical to each other, and there are no duplicate organizations.
Speed: unchanged within run-to-run noise (meetings 0.159–0.207 s per batch against 0.177–0.192 on main, github/git 0.101–0.103 against 0.106). Find-or-create calls are 1.4% more, and the final step takes 7–10 s per run in total.
The batch's organization cache was keyed by identities only, so when two payloads
in one batch described the same organization differently, only the first one's
data was written. Each distinct payload is now applied once. Payloads that share
identities wait for each other and land on the same organization. Once the batch
is done, for each organization and source that got more than one payload, the one
from the newest activity (then the one most activities carry) is applied again so
its values become the defaults of its source. addOrgsToSegments sorts its rows so
concurrent inserts can't deadlock.
Closeslinuxfoundation/lfx-cdp-insights-ops#972
Signed-off-by: Uroš Marolt <uros@marolt.me>
Medium Risk
Changes organization merge, caching, and default-selection logic in the hot data-sink path; incorrect ranking could skew org attributes, though re-apply failures are isolated from activity success.
Overview
Fixes a bug where a data-sink batch could carry multiple different organization payloads for the same company (same verified identities) but only the first payload’s attributes were persisted, because the batch cache was keyed by identities alone.
Batch org deduplication and merging now keys cache entries by verified identities plus member link source plus normalized payload content (orgPayloadKey). Identical payloads still share one findOrCreate; different content gets its own call. Shared-identity locks and a per-batch map of resolved org IDs keep concurrent updates on one organization without duplicate rows. findOrCreateOrganization accepts an optional organizationId so follow-up applies target the org from an earlier payload in the batch (including when a later payload unverified the lookup identity).
Default / winner selection: Before member work, registerOrgPayloadActivities records newest activity time and activity count per payload. After updateMembers, applyNewestOrgPayloads re-applies every distinct payload for each org+source (lowest rank first, highest last) so per-source attribute defaults reflect the payload tied to the newest activity (then count, then stable key). Errors in that step are logged only and do not fail the batch’s activities.
Deadlock guard: addOrgsToSegments sorts segment and organization IDs before insert so concurrent batches inserting the same junction rows use a consistent lock order.
Reviewed by Cursor Bugbot for commit be35606. Bugbot is set up for automated code reviews on this repo. Configure here.
The reason will be displayed to describe this comment to others. Learn more.
🟡 Changes recommended
Organization defaults remain processing-order dependent for omitted attributes, and duplicate payloads within a single activity can inflate counts and select the wrong tie-break winner.
This PR fixes organization updates being dropped when a data-sink batch contains different payloads for the same organization, while preserving deduplication for identical payloads.
Changes:
Keys organization caching by normalized payload content and source.
Tracks activity timestamps and counts, then reapplies the winning payload to set source defaults.
Sorts organization and segment IDs before inserts to prevent conflicting insertion orders.
An activity whose member and object member carry the same organization
payload counted twice. When an organization got several payloads from one
source, only the winner was applied again, so attributes the winner lacks
kept whichever payload landed last. Every distinct payload is now applied
again from the lowest ranked to the winner.
Refs linuxfoundation/lfx-cdp-insights-ops#972
Signed-off-by: Uroš Marolt <uros@marolt.me>
The reason will be displayed to describe this comment to others. Learn more.
🟡 Changes recommended
Replaying organization resolution can create duplicates and send updates to the wrong organization, while malformed organization entries can cause an entire batch to fail.
Organization entries are not validated by prepareMemberData, so an entry such as organizations: [null] throws during this pre-pass, outside the per-member error handlers. This makes the whole batch retry and eventually fail, including valid activities. Catch registration errors per activity and leave malformed organizations to the existing member-level handling.
A later payload of the batch can unverify the identity an earlier one was
found by, so looking it up again could miss and create a second
organization. findOrCreateOrganization takes an optional organization id
that skips the lookup, and the replay passes the recorded one.
Refs linuxfoundation/lfx-cdp-insights-ops#972
Signed-off-by: Uroš Marolt <uros@marolt.me>
The reason will be displayed to describe this comment to others. Learn more.
🔵 Needs a closer look
The initial pass can still create duplicate organizations when a domain is unverified between payload variants, splitting member links in a way that replaying recorded IDs cannot repair.
No open issues 🎉
1 previously missed issue
In code that hasn't changed since last review
Preserve identity resolution across payload variants during initial pass
The initial pass can still create duplicate organizations: A resolves Acme by a verified domain without a display name, B finds Acme by name and unverifies that domain, then C supplies A's domain with different attributes and no display name. C misses the payload-specific cache and both verified-identity lookups, creating a second organization; pinning replay to recorded IDs cannot repair the split member links. Preserve identity-to-organization resolution separately from payload deduplication and reuse the resolved ID for later variants during the initial pass too.
A payload can unverify the domain an earlier payload of the batch was
found by. A later variant with that domain and no display name then missed
every lookup and created a second organization. The org id each identity
key resolved to is now kept per batch, and later variants with the same
identities go to it without a lookup, like the identity-keyed cache did.
Refs linuxfoundation/lfx-cdp-insights-ops#972
Signed-off-by: Uroš Marolt <uros@marolt.me>
Copilot review on 3560fec, previously missed: Preserve identity resolution across payload variants during initial pass (member.service.ts:297). Fixed in c1d99d8. Each batch keeps the organization id that each identity key resolved to. Later payloads with the same verified identities, or the same name when they have none, go to that organization without a lookup, so a payload that unverifies a domain earlier in the batch can't send a later variant to a new organization. Local meetings and github/git runs give the same data as before this change.
The reason will be displayed to describe this comment to others. Learn more.
🔵 Needs a closer look
Human assessment is needed because identity resolution and verification updates can interact to split one organization into duplicates that replay cannot repair.
The cache key lowercased identity values but kept www and other hostname
differences, so www.croxel.com and croxel.com got different keys while the
lookup, the identity locks and the payload key treat them as one. A later
variant could then miss the organization the first one resolved to. The
cache key and the lock keys now share one normalized identity key.
Refs linuxfoundation/lfx-cdp-insights-ops#972
Signed-off-by: Uroš Marolt <uros@marolt.me>
The reason will be displayed to describe this comment to others. Learn more.
🟡 Changes recommended
The platform-independent organization cache key can route email payloads to a GitHub organization sharing the same domain, writing data and affiliations to the wrong organization.
Two organizations can own the same domain on different platforms. The
lookup tries the payload's platform first and only falls back to any
platform, so a platform-free cache key could send a payload to the other
organization. The cache key keeps the platform; the identity locks still
take a primary domain on any platform.
Refs linuxfoundation/lfx-cdp-insights-ops#972
Signed-off-by: Uroš Marolt <uros@marolt.me>
The reason will be displayed to describe this comment to others. Learn more.
🔵 Needs a closer look
Human assessment is needed because interactions between username updates, domain verification changes, and cache-key differences can cause an existing organization to be duplicated.
Copilot review on 2a85ae8, Needs a closer look: duplicates from username updates, domain verification changes and cache-key differences.
Checked against main. This PR adds no new path to a duplicate:
Payloads with the same verified identities (platform, type, normalized value), or the same name when they have none, go to the organization the first one resolved to. Main's identity-keyed cache returned that same result, so this matches main and adds the later payload's data.
Payloads with different identity sets each run the normal lookup, as on main. The identity locks make them wait for each other, so the later one sees what the earlier one wrote.
Replays go to the recorded organization id and never create one.
The duplicate in that summary comes from upsertOrgIdentities overwriting a stored verified flag with an incoming false. That applies to usernames as well as domains: GitHub sends an organization's website and Twitter username unverified. Once the only verified identity is cleared, a later payload with a different identity set misses every lookup and creates a new organization. That has been the behavior since #2465, across batches as well as within one, and is tracked separately in linuxfoundation/lfx-cdp-insights-ops#978.
Organization payloads are registered before the per-member error
handling, so an entry like organizations: [null] threw there and failed
every activity of the batch. Malformed entries and invalid timestamps are
now skipped in that pass and left to the member-level handling.
Refs linuxfoundation/lfx-cdp-insights-ops#972
Signed-off-by: Uroš Marolt <uros@marolt.me>
Copilot review on 85cd9db, previously missed: Malformed organization entry aborts the entire batch (activity.service.ts:1475). Fixed in be35606. The registration pass runs before the per-member error handling, so it now skips a malformed entry (for example organizations: [null], or a non-array organizations) and an invalid timestamp instead of throwing. The member-level handling reports the malformed organization as before, and the other activities of the batch go through. Well-formed payloads take the same path as before.
Copilot review on c1d99d8, Needs a closer look (identity resolution and verification updates splitting one organization): the same answer as #5171 (comment). The PR adds no new duplicate path. The split comes from incoming unverified identities clearing a stored verified flag, which predates this PR and is tracked in linuxfoundation/lfx-cdp-insights-ops#978.
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
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
When one data-sink batch carried two different payloads for the same organization, only the first one's data was written. The batch's organization cache was keyed by verified identities only, so later payloads got the cached organization id and their attributes were dropped. In meetings data this happens when attendees of one meeting are linked to two Salesforce accounts with the same website (for example "Croxel Inc." and "Croxel"): one has a logo and a different industry, and only one of them was ever stored.
This PR applies every distinct payload once, and makes the payload from the newest activity set the defaults.
Changes
findOrCreateOrganizationnormalizes them. Identical payloads still share one call. A payload with different content runs its own find-or-create. The existing identity locks make it wait for the earlier one, and it goes to the organization that one resolved to, without a lookup, and adds what it carries. No duplicate organizations.addOrgsToSegmentssorts the organization and segment ids, so concurrent inserts of the same new rows always use the same order and can't deadlock. Its callers keep their behavior.Local end-to-end runs against main, on 92.5k real github/git results and 39,659 real meetings activities seeded read-only from prod:
croxel.com(the meetings logo and industry). The two runs of this branch are identical to each other, and there are no duplicate organizations.Type of change
Related issue
Closes linuxfoundation/lfx-cdp-insights-ops#972