Auto-chunking conveniences for batch operations (#307)#389
Conversation
Apply the page-primitive + auto-paginating-extension pattern (from queryRecords/queryAllRecords) to the non-deprecated 200-item-capped batch operations: - lookupAllRecords(recordNames:desiredKeys:database:batchSize:) over the records/lookup primitive - discoverAllUserIdentities(lookupInfos:batchSize:) over the users/discover (POST) primitive Both delegate to a shared chunkedBatches engine that splits input into <=batchSize batches (clamped to 1...maxRecordsPerRequest), calls the primitive per batch, and concatenates results in input order. Chunk count is deterministic, so there is no maxPages-style throwing ceiling; batchSize is the only knob. maxRecordsPerRequest is now public. The users/lookup/email and users/lookup/id primitives are deprecated by Apple in favor of users/discover (verified against Apple's archived CloudKit Web Services reference), so they intentionally get no chunking convenience. listZones (zones/list) has no continuation marker and is not a pagination candidate. fetchAllRecordChanges / fetchAllZoneChanges already implement the pattern. Tests: ResponseProvider now records request count/bodies so tests can assert exact batch boundaries, order, accumulation, batchSize clamping, and empty input. MistDemo: adds `lookup-all` and `discover-all` subcommands with a `--batch-size` flag for exercising chunking end-to-end. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Code Review — PR #389: Auto-chunking conveniences for batch operationsOverviewSolid addition that follows the established IssuesBug: Division-by-zero in command batch-count previewBoth // LookupAllRecordsCommand.swift:219
let batches = (config.recordNames.count + config.batchSize - 1) / config.batchSizeIf a caller passes Hardcoded
|
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## v1.0.0-beta.2 #389 +/- ##
================================================
Coverage ? 70.06%
================================================
Files ? 165
Lines ? 3758
Branches ? 0
================================================
Hits ? 2633
Misses ? 1125
Partials ? 0
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Code Review — Auto-chunking conveniences for batch operations (#307)OverviewClean, well-scoped PR. The Bugs / Correctness1. Division-by-zero crash when Both command implementations compute the expected batch count for the progress log before passing // LookupAllRecordsCommand.swift:219, DiscoverAllUserIdentitiesCommand.swift:123
let batches = (config.recordNames.count + config.batchSize - 1) / config.batchSizeIf a user passes let effectiveBatchSize = max(1, min(config.batchSize, CloudKitService.maxRecordsPerRequest))
let batches = (config.recordNames.count + effectiveBatchSize - 1) / effectiveBatchSizeNote also that because clamping happens only inside Design / Style2.
internal static func makeLookupRecordsService(provider: ResponseProvider) throws -> CloudKitService {
try makeUserService(provider: provider)
}This wrapping function adds no behaviour and will confuse the next reader who wonders whether the two differ. Tests in 3.
batchSize: Int = 200
configuration.int(forKey: MistDemoConstants.ConfigKeys.batchSize, default: 200) ?? 200
4. Inconsistent error handling between the two new demo commands
Test Coverage Gaps5. No test for error propagation from a mid-run failing batch The chunking loop: results.append(contentsOf: try await perBatch(Array(items[index..<end])))...should short-circuit on the first failure and not call subsequent batches. This is the correct behaviour, but there is no test asserting it. A test that queues a success response followed by an error response and verifies (a) the call throws the error and (b) only the first batch was issued would close this gap. 6. No test for the The cancellation check is the only non-trivial control-flow in Minor / Performance7. var results: [Output] = []For large inputs the array will reallocate multiple times. Since the total item count is known upfront from What's Good
🤖 Generated with Claude Code |
Code Review: Auto-chunking conveniences for batch operations (#307)OverviewThis PR adds auto-chunking convenience wrappers for two CloudKit batch endpoints capped at 200 items per request: IssuesBug: Division by zero in MistDemo batch-count display Both new commands compute and print the expected request count before the library can clamp // LookupAllRecordsCommand.swift:219 / DiscoverAllUserIdentitiesCommand.swift:123
let batches = (config.recordNames.count + config.batchSize - 1) / config.batchSizeIf Minor: Inconsistent error handling between the two commands
Design ObservationsSequential batch execution
Test helper indirection
Positives
|
- Clamp the stderr batch-count preview in lookup-all/discover-all to 1...maxRecordsPerRequest, fixing a division-by-zero when --batch-size 0 - Use CloudKitService.maxRecordsPerRequest instead of the literal 200 for the batchSize defaults in LookupConfig/DiscoverConfig - Stop wrapping CloudKitError in LookupError in LookupAllRecordsCommand so it propagates structured errors like DiscoverAllUserIdentitiesCommand - Remove the transparent makeLookupRecordsService alias; call makeUserService directly and add a test asserting a failing batch throws and stops the loop - Clarify the singleBatch comment and the CLAUDE.md no-arg discover annotation https://claude.ai/code/session_01CiP2JokL5xB62ctP1iFasJ
The batchSize default arguments on the public LookupConfig/DiscoverConfig initializers reference CloudKitService.maxRecordsPerRequest, which requires the MistKit import to be public rather than internal. https://claude.ai/code/session_01CiP2JokL5xB62ctP1iFasJ
Code Review: Auto-chunking conveniences for batch operations (#307)OverviewThis PR adds a Code QualityStrengths:
IssuesMinor: stderr note shows raw In both let effectiveBatchSize = min(max(config.batchSize, 1), CloudKitService.maxRecordsPerRequest)
let batches = (config.emails.count + effectiveBatchSize - 1) / effectiveBatchSize
let note = "discover-all: \(config.emails.count) lookup(s), batchSize \(config.batchSize) → \(batches) request(s)"
// ^^^^^^^^^^^^ should be effectiveBatchSizeIf the caller passes Minor:
Test CoverageThe test suite is comprehensive:
Gap: no error-propagation test for
PerformanceNo concerns. Sequential batching is correct here. The SecurityNo concerns. SummaryThis is a well-scoped, well-tested PR. The two minor issues (misleading stderr label and config struct leakage) are cosmetic rather than correctness problems. The missing error-propagation test for 🤖 Generated with Claude Code |
Closes #307 (partial — see scope note).
Summary
Applies the page-primitive + auto-paginating-extension pattern (established by
queryRecords/queryAllRecords) to the remaining non-deprecated, 200-item-capped batch operations. Most of #307 was already done on this milestone (fetchAllRecordChanges,fetchAllZoneChanges); this PR closes the gap for the batch-chunking candidates.New public API
lookupRecords(recordNames:desiredKeys:database:)lookupAllRecords(recordNames:desiredKeys:database:batchSize:)discoverUserIdentities(lookupInfos:)discoverAllUserIdentities(lookupInfos:batchSize:)Both delegate to a shared internal
chunkedBatchesengine: splits the input into≤batchSizebatches (clamped to1...maxRecordsPerRequest), calls the primitive per batch, concatenates results in input order. Chunk count isceil(n/batchSize)— deterministic and finite — so there is nomaxPages-style throwing ceiling;batchSize(defaultmaxRecordsPerRequest) is the only knob.CloudKitService.maxRecordsPerRequestis nowpublic.Scope decisions
users/lookup/emailandusers/lookup/idget no chunking convenience. Both are deprecated by Apple in favor of POSTusers/discover(verified against Apple's archived CloudKit Web Services reference). Adding new public API on deprecated endpoints would be wrong; callers needing >200 should usediscoverAllUserIdentities(lookupInfos:). The existing primitives are left as-is (no deprecation annotations) pending public feedback.listZonesis not a pagination candidate —zones/list(GET) returns every zone in one response, no continuation marker.modifyRecords/sync<T>already chunk by 200;fetchAllRecordChanges/fetchAllZoneChangesalready implement the pattern.Tests
ResponseProvidernow records request count + bodies, so tests assert exact batch boundaries, input order, accumulation,batchSizeclamping (low/high), and empty input. 9 new chunking tests; full suite (529) passes../Scripts/lint.shclean (swiftlint, headers, periphery).MistDemo
Adds
lookup-allanddiscover-allsubcommands with a--batch-sizeflag. Verified end-to-end against live CloudKit:--batch-size 1issues N separate requests and the results are correctly accumulated in order (identical to a single--batch-size 200request).🤖 Generated with Claude Code