Repository navigation
fix(executor): semantic_deduplicate via MinHash + LSH (was O(n²), hung run 8afd8d39) - #13
Merged
Merged
Conversation
… deadline-guarded The rule compared every row's MinHash signature with every kept signature in pure Python (O(n^2)). Run 8afd8d39 (56,745 tweets, num_perm 120, threshold 0.9) needed hours, so it sat on Running until the 15-minute Lambda timeout, was retried by SQS and failed. Signatures used built-in hash(), which PYTHONHASHSEED randomizes per process, so results were not reproducible. - Signatures: blake2b token hashes + fixed universal hashes ((a*x+b) mod 2^61-1, coefficients from blake2b), vectorized in numpy (no new dependency); pinned by a golden digest test - LSH banding: r = largest band width whose b = num_perm//r bands make a pair at the threshold a candidate with p >= 0.99 (0.9/120 -> 12x10, 0.8/64 -> 12x5); candidates are verified on the full signature, same rule as before (estimate >= threshold vs an earlier KEPT row, first occurrence kept). Identical output to brute-force pairwise comparison on the same signatures (tested) - Guards: handler records context.get_remaining_time_in_millis(); the rule raises RuleSkipped (frame restored, reason reported) before it would run into the last min(120s, 25%) of the invocation; 500k-row cap (SEMANTIC_DEDUP_MAX_ROWS); 600s budget without a Lambda context; invalid threshold/num_perm are skipped - 50k short texts: ~2.3-5s (was ~33s at 4k rows, est. ~1.5h at 50k)
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This branch was successfully deployed
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.
Root cause
semantic_deduplicate(lambdas/executor/handler.py) compared each row's MinHash signature with every signature kept so far, element by element in pure Python: O(n² · num_perm). Run 8afd8d39 (56,745-row tweet CSV,num_perm120,threshold0.9) would need hours. The executor Lambda is killed at 15 minutes, so the run stayed on Running, timed out, was redelivered by SQS, and finally failed. Nothing in the rule watched the clock.There was a second bug. Signatures used Python's built-in
hash(), whichPYTHONHASHSEEDrandomizes per process, so the same file could dedupe differently on every invocation.Fix
num_permpermutations is a fixed universal hash((a·x + b) mod 2⁶¹−1) mod 2³², witha/bderived from blake2b. All values stay below 2³², soa·x + bis exact in uint64. Per-row minima come fromnp.minimum.reduceat, chunked to about 16 MB of intermediates. There is no new dependency: numpy already comes with the AWSSDKPandas layer.ris the largest band width whoseb = num_perm // rbands make a pair at the threshold a candidate with probability ≥ 99%. Examples: 0.9/120 → 12 bands × 10, 0.8/64 → 12 × 5, 0.8/128 → 21 × 6. Rows are walked in order. For each row, only the earlier kept rows that share a bucket are candidates, and each candidate is verified with the full-signature estimatematches/num_perm ≥ threshold. That is the same decision rule as before, so the first occurrence is still kept. A test checks the output is identical to brute-force pairwise comparison on the same signatures.handler()recordscontext.get_remaining_time_in_millis(). If hashing or comparison would run into the lastmin(EXECUTOR_RESERVE_S=120s, 25%)of the invocation, the rule raisesRuleSkipped. The frame is restored and the reason shows as "Not applied", for examplesemantic_deduplicate skipped: comparing 56,745 rows would exceed the Lambda time limit …. The rest of the run still completes.SEMANTIC_DEDUP_MAX_ROWS(default 500,000).threshold(outside (0, 1]) ornum_perm(outside 1..512) is skipped with a reason.Complexity
Behaviour change
threshold/num_permvalues are now skipped instead of silently keeping or dropping everything.Tests
New file
lambdas/tests/test_semantic_dedup.py(23 tests). There were no tests for this rule before.PYTHONHASHSEED; same rows on repeat.handler()records the deadline.@pytest.mark.perf, 50k short texts with a generous bound of < 60 s (takes 2–5 s).Locally:
pytest lambdas/testsgives 86 passed and 5 skipped (the 5 integration tests need services) under both pandas 2.2.3/numpy 2.1.3 (Python 3.12) and pandas 3.0.6/numpy 2.5.3.Notes
README.md: updated the rule table, the executor env vars and the known limitation.semantic_deduplicate. Other rules and the output write have no deadline check, and there is still no upload size limit.