Skip to content

Commit ca15e88

Browse files
authored
feat: Datastore use configurable syncMaxRecords and syncPageSize (#388)
1 parent b02c3b7 commit ca15e88

File tree

2 files changed

+63
-20
lines changed

2 files changed

+63
-20
lines changed

AmplifyPlugins/DataStore/AWSDataStoreCategoryPlugin/Sync/InitialSync/InitialSyncOperation.swift

+27-20
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,14 @@ final class InitialSyncOperation: AsynchronousOperation {
2020
private let modelType: Model.Type
2121
private let completion: AWSInitialSyncOrchestrator.SyncOperationResultHandler
2222

23-
private var lastSyncTime: Int?
23+
private var recordsReceived: UInt
24+
25+
private var syncMaxRecords: UInt {
26+
return dataStoreConfiguration.syncMaxRecords
27+
}
28+
private var syncPageSize: UInt {
29+
return dataStoreConfiguration.syncPageSize
30+
}
2431

2532
init(modelType: Model.Type,
2633
api: APICategoryGraphQLBehavior?,
@@ -34,6 +41,7 @@ final class InitialSyncOperation: AsynchronousOperation {
3441
self.storageAdapter = storageAdapter
3542
self.dataStoreConfiguration = dataStoreConfiguration
3643
self.completion = completion
44+
self.recordsReceived = 0
3745
}
3846

3947
override func main() {
@@ -43,19 +51,19 @@ final class InitialSyncOperation: AsynchronousOperation {
4351
}
4452

4553
log.info("Beginning sync for \(modelType.modelName)")
46-
setUpLastSyncTime()
47-
query()
54+
let lastSyncTime = getLastSyncTime()
55+
query(lastSyncTime: lastSyncTime)
4856
}
4957

50-
private func setUpLastSyncTime() {
58+
private func getLastSyncTime() -> Int? {
5159
guard !isCancelled else {
5260
super.finish()
53-
return
61+
return nil
5462
}
5563

5664
let lastSyncMetadata = getLastSyncMetadata()
5765
guard let lastSync = lastSyncMetadata?.lastSync else {
58-
return
66+
return nil
5967
}
6068

6169
//TODO: Update to use TimeInterval.milliseconds when it is pushed to master branch
@@ -64,14 +72,11 @@ final class InitialSyncOperation: AsynchronousOperation {
6472
let secondsSinceLastSync = (lastSyncDate.timeIntervalSinceNow * -1)
6573
if secondsSinceLastSync < 0 {
6674
log.info("lastSyncTime was in the future, assuming base query")
67-
lastSyncTime = nil
68-
return
75+
return nil
6976
}
7077

7178
let shouldDoDeltaQuery = secondsSinceLastSync < dataStoreConfiguration.syncInterval
72-
if shouldDoDeltaQuery {
73-
lastSyncTime = lastSync
74-
}
79+
return shouldDoDeltaQuery ? lastSync : nil
7580
}
7681

7782
private func getLastSyncMetadata() -> ModelSyncMetadata? {
@@ -95,7 +100,7 @@ final class InitialSyncOperation: AsynchronousOperation {
95100

96101
}
97102

98-
private func query(nextToken: String? = nil) {
103+
private func query(lastSyncTime: Int?, nextToken: String? = nil) {
99104
guard !isCancelled else {
100105
super.finish()
101106
return
@@ -105,8 +110,10 @@ final class InitialSyncOperation: AsynchronousOperation {
105110
finish(result: .failure(DataStoreError.nilAPIHandle()))
106111
return
107112
}
108-
113+
let minSyncPageSize = Int(min(syncMaxRecords - recordsReceived, syncPageSize))
114+
let limit = minSyncPageSize < 0 ? Int(syncPageSize) : minSyncPageSize
109115
let request = GraphQLRequest<SyncQueryResult>.syncQuery(modelType: modelType,
116+
limit: limit,
110117
nextToken: nextToken,
111118
lastSync: lastSyncTime)
112119

@@ -116,7 +123,7 @@ final class InitialSyncOperation: AsynchronousOperation {
116123
// TODO: Retry query on error
117124
self.finish(result: .failure(DataStoreError.api(apiError)))
118125
case .completed(let graphQLResult):
119-
self.handleQueryResults(graphQLResult: graphQLResult)
126+
self.handleQueryResults(lastSyncTime: lastSyncTime, graphQLResult: graphQLResult)
120127
default:
121128
break
122129
}
@@ -125,7 +132,7 @@ final class InitialSyncOperation: AsynchronousOperation {
125132

126133
/// Disposes of the query results: Stops if error, reconciles results if success, and kick off a new query if there
127134
/// is a next token
128-
private func handleQueryResults(graphQLResult: Result<SyncQueryResult, GraphQLResponseError<SyncQueryResult>>) {
135+
private func handleQueryResults(lastSyncTime: Int?, graphQLResult: Result<SyncQueryResult, GraphQLResponseError<SyncQueryResult>>) {
129136
guard !isCancelled else {
130137
super.finish()
131138
return
@@ -146,23 +153,23 @@ final class InitialSyncOperation: AsynchronousOperation {
146153
}
147154

148155
let items = syncQueryResult.items
149-
lastSyncTime = syncQueryResult.startedAt
156+
recordsReceived += UInt(items.count)
150157

151158
for item in items {
152159
reconciliationQueue.offer(item)
153160
}
154161

155-
if let nextToken = syncQueryResult.nextToken {
162+
if let nextToken = syncQueryResult.nextToken, recordsReceived < syncMaxRecords {
156163
DispatchQueue.global().async {
157-
self.query(nextToken: nextToken)
164+
self.query(lastSyncTime: lastSyncTime, nextToken: nextToken)
158165
}
159166
} else {
160-
updateModelSyncMetadata()
167+
updateModelSyncMetadata(lastSyncTime: syncQueryResult.startedAt)
161168
}
162169

163170
}
164171

165-
private func updateModelSyncMetadata() {
172+
private func updateModelSyncMetadata(lastSyncTime: Int?) {
166173
guard !isCancelled else {
167174
super.finish()
168175
return

AmplifyPlugins/DataStore/AWSDataStoreCategoryPluginTests/Sync/InitialSync/InitialSyncOperationTests.swift

+36
Original file line numberDiff line numberDiff line change
@@ -358,4 +358,40 @@ class InitialSyncOperationTests: XCTestCase {
358358

359359
wait(for: [apiWasQueried], timeout: 1.0)
360360
}
361+
362+
func testBaseQueryWithCustomSyncPageSize() throws {
363+
let storageAdapter = try SQLiteStorageEngineAdapter(connection: Connection(.inMemory))
364+
try storageAdapter.setUp(models: StorageEngine.systemModels + [MockSynced.self])
365+
366+
let apiWasQueried = expectation(description: "API was queried for a PaginatedList of AnyModel")
367+
let responder = QueryRequestListenerResponder<PaginatedList<AnyModel>> { request, listener in
368+
let lastSync = request.variables?["lastSync"] as? Int
369+
XCTAssertNil(lastSync)
370+
XCTAssert(request.document.contains("limit: Int"))
371+
let limitValue = request.variables?["limit"] as? Int
372+
XCTAssertEqual(10, limitValue)
373+
374+
let list = PaginatedList<AnyModel>(items: [], nextToken: nil, startedAt: nil)
375+
let event: GraphQLOperation<PaginatedList<AnyModel>>.Event = .completed(.success(list))
376+
listener?(event)
377+
apiWasQueried.fulfill()
378+
return nil
379+
}
380+
381+
let apiPlugin = MockAPICategoryPlugin()
382+
apiPlugin.responders[.queryRequestListener] = responder
383+
384+
let reconciliationQueue = MockReconciliationQueue()
385+
let configuration = DataStoreConfiguration.custom(syncPageSize: 10)
386+
let operation = InitialSyncOperation(
387+
modelType: MockSynced.self,
388+
api: apiPlugin,
389+
reconciliationQueue: reconciliationQueue,
390+
storageAdapter: storageAdapter,
391+
dataStoreConfiguration: configuration) {_ in }
392+
393+
operation.main()
394+
395+
wait(for: [apiWasQueried], timeout: 1.0)
396+
}
361397
}

0 commit comments

Comments
 (0)