Return false from bulkUpsert when write result is incomplete - #322
Return false from bulkUpsert when write result is incomplete#322Bhuvan506 wants to merge 3 commits into
Conversation
Mongo and Postgres bulkUpsert previously returned true whenever no exception was thrown, ignoring BulkWriteResult / JDBC batch counts. Callers (e.g. attribute-service) then treated partial writes as success. Validate that every requested document is accounted for before reporting success; keep throwing/returning false on hard failures. Co-authored-by: Cursor <cursoragent@cursor.com>
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #322 +/- ##
============================================
- Coverage 81.06% 80.81% -0.25%
- Complexity 1617 1631 +14
============================================
Files 243 243
Lines 7656 7708 +52
Branches 755 763 +8
============================================
+ Hits 6206 6229 +23
- Misses 960 987 +27
- Partials 490 492 +2
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Address review feedback: keep the helper with the Postgres hierarchy (FlatPostgresCollection already extends it) instead of a commons util. Co-authored-by: Cursor <cursoragent@cursor.com>
| class BatchFullySuccessful { | ||
|
|
||
| @Test | ||
| void allPositive_returnsTrue() { |
There was a problem hiding this comment.
Lets stick to camelCase for consistency.
There was a problem hiding this comment.
Removed those nested tests (Postgres success-path batch-length check was dropped per your later comment). New IT method names are camelCase.
| result.wasAcknowledged() ? result.getUpserts().size() : -1, | ||
| result.wasAcknowledged(), | ||
| result); | ||
| throw new IOException("Incomplete bulk upsert."); |
There was a problem hiding this comment.
This might now break existing clients.
There was a problem hiding this comment.
Agreed it tightens the contract: callers that previously treated incomplete Mongo writes as success will now get false / IOException. That is intentional for the gap we care about. Attribute-service is also adding a client-side post-write verify + evidence logs; we can stage rollout if needed.
| result.wasAcknowledged() ? result.getUpserts().size() : -1, | ||
| result.wasAcknowledged(), | ||
| result); | ||
| throw new IOException("Incomplete bulk upsert."); |
There was a problem hiding this comment.
We should close the cursor here or it'll be a resource leak.
There was a problem hiding this comment.
Fixed — close the Mongo cursor before throwing on incomplete upsert (and on other failure paths before the iterator takes ownership).
| "Incomplete bulk upsert for documents. requested={}, updateCounts={}", | ||
| documents.size(), | ||
| Arrays.toString(updateCounts)); | ||
| throw new IOException("Incomplete bulk upsert."); |
There was a problem hiding this comment.
Close the RS here to prevent leak?
There was a problem hiding this comment.
Fixed — close the ResultSet in a finally when we do not hand it to PostgresResultIterator.
| */ | ||
| @VisibleForTesting | ||
| static boolean isBatchFullySuccessful(final int[] updateCounts, final int expectedSize) { | ||
| if (updateCounts == null || updateCounts.length != expectedSize) { |
There was a problem hiding this comment.
This method will always return true due to the way we're building the batch. Because we add 1 doc per batch, updateCounts.length == expectedSize will always hold true. Also, if a single doc/batch failed upsert, it'll throw BatchUpdateException.
Rather, smth like this should help:
} catch (BatchUpdateException e) {
int[] partial = e.getUpdateCounts();
LOGGER.error(
"BatchUpdateException bulk inserting documents. requested={}, updateCounts={}",
documents.size(),
Arrays.toString(partial),
e);
return false; // partial application: some entries succeeded, some EXECUTE_FAILED
}
Can you check this?
There was a problem hiding this comment.
Checked — agree. Dropped the success-path isBatchFullySuccessful check. BatchUpdateException now logs requested + e.getUpdateCounts() and returns false / fails bulkUpsertAndReturnOlderDocuments.
|
Lets add some integration tests here to validate the behaviour consistency b/w Mongo and PG. |
Close Mongo cursors / Postgres ResultSets on bulkUpsertAndReturnOlderDocuments failure paths. For Postgres, rely on BatchUpdateException (log partial updateCounts) instead of a success-path batch length check that always passed. Add Mongo/PG bulkUpsert consistency integration tests. Co-authored-by: Cursor <cursoragent@cursor.com>
|
Added Mongo/PG consistency ITs in |
Summary
bulkUpsert: returnfalsewhen the write is unacknowledged ormatchedCount + upserts.size() != requested(unordered bulk write can acknowledge without accounting for every doc).bulkUpsert: onBatchUpdateException, log partialupdateCountsand returnfalse(success-path batch length checks removed — they always passed when no exception was thrown).bulkUpsertAndReturnOlderDocuments: fail withIOExceptionon incomplete/failed upsert and close the Mongo cursor / PostgresResultSetso resources are not leaked.false/IOExceptionas before.Context
Investigated while looking at attribute-service create returning success with missing
attribute_metadatadocs. Attribute-service is also adding client-side post-write verification for evidence; this PR tightens the shared library contract.Test plan
MongoCollectionTest.BulkUpsertMongoPostgresWriteConsistencyTest.BulkUpsertConsistencyTest(complete batch + older-documents consistency) — needs Docker/CI