Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
7c92921
Initial Stub Classes
aashishpatil-g Aug 19, 2025
f56a8cc
Wire up Initialization of CacheProvider
aashishpatil-g Aug 20, 2025
f655a15
query level caching
aashishpatil-g Aug 22, 2025
90c7ae1
Cache Normalization Implementation
aashishpatil-g Aug 28, 2025
5174e03
Persistent Provider
aashishpatil-g Aug 29, 2025
ad71e3c
Add last_accessed field
aashishpatil-g Sep 4, 2025
c0a024b
Uber Cache object, support Auth uid scope and Refactor classes
aashishpatil-g Sep 24, 2025
0bcdf09
Implement operationId on QueryRef
aashishpatil-g Sep 25, 2025
ad749e7
Fix sha256 formatter.
aashishpatil-g Sep 25, 2025
35dc280
Accumulate Impacted Refs and Reload Local
aashishpatil-g Oct 1, 2025
6cd60ef
Update construction of cacheIdentifier to include connector and locat…
aashishpatil-g Oct 1, 2025
824a41e
Use inmemory SQLite for Ephemeral cache provider
aashishpatil-g Oct 2, 2025
f503f5e
Minor updates to API
aashishpatil-g Oct 7, 2025
e57bbb4
Refactor BackingDataObject, STubDataObject names to EntityDataObject …
aashishpatil-g Oct 8, 2025
353caf5
DispatchQueue for EntityDataObject access
aashishpatil-g Oct 9, 2025
78599a3
Externalize table and column name strings into constants
aashishpatil-g Oct 10, 2025
250e905
API Review feedback
aashishpatil-g Oct 10, 2025
f1aa717
Code formatting updates
aashishpatil-g Oct 11, 2025
d9b4807
Refactor name of OperationResultSource
aashishpatil-g Oct 13, 2025
1c666ec
API feedback - rename maxSize => maxSizeBytes
aashishpatil-g Oct 13, 2025
8ea6762
API Council Review Feedback
aashishpatil-g Oct 15, 2025
a807d9f
Move ServerResponse
aashishpatil-g Oct 24, 2025
336e41b
Update cache type to match API review feedback
aashishpatil-g Nov 5, 2025
37493c4
Cleanup and minor fixes to docs
aashishpatil-g Nov 11, 2025
dd8569c
Update globalID value
aashishpatil-g Nov 11, 2025
fad7e2f
Fix format
aashishpatil-g Nov 12, 2025
a62c16b
Fix copyright notices
aashishpatil-g Nov 12, 2025
30940a6
Fix @available
aashishpatil-g Nov 12, 2025
5482987
fix integration tests
aashishpatil-g Nov 12, 2025
1a9aeb9
Gemini Review part 1
aashishpatil-g Nov 13, 2025
12da757
formatting fixes
aashishpatil-g Nov 13, 2025
334e710
Convert sync queue calls to actor
aashishpatil-g Nov 13, 2025
b1569e8
comments
aashishpatil-g Nov 13, 2025
517fdcd
Nick feedback
aashishpatil-g Nov 18, 2025
b9265dc
Remove unnecessary custom codable in EDO
aashishpatil-g Nov 19, 2025
6689c9c
Convert data connect to a weak reference in cache
aashishpatil-g Nov 19, 2025
2b90b52
formatting fixes
aashishpatil-g Nov 19, 2025
edfcd83
Doc comments for Settings
aashishpatil-g Nov 19, 2025
2572377
Incorporate maxAge
aashishpatil-g Nov 19, 2025
ba3b894
Nick feedback. Update doc comments on CacheSettings.maxAge
aashishpatil-g Dec 3, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions Sources/Cache/Cache.swift
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,6 @@ actor Cache {

let hydratedTree = ResultTree(
data: hydratedResults,
ttl: dehydratedTree.ttl,
cachedAt: dehydratedTree.cachedAt,
lastAccessed: dehydratedTree.lastAccessed,
rootObject: rootObj
Expand Down Expand Up @@ -147,7 +146,6 @@ actor Cache {
queryId: queryId,
tree: .init(
data: dehydratedResults,
ttl: response.ttl,
cachedAt: Date(),
lastAccessed: Date(),
rootObject: rootObj
Expand Down
13 changes: 12 additions & 1 deletion Sources/Cache/CacheSettings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.

import Foundation

/// Specifies the cache configuration for a `DataConnect` instance.
///
/// You can configure the cache's storage policy and its maximum size.
Expand All @@ -33,13 +35,22 @@ public struct CacheSettings: Sendable {
/// to trigger cleanup procedures. The default is 100MB (100,000,000 bytes).
public let maxSizeBytes: UInt64

/// Max time interval before a queries cache is considered stale and refreshed from the server
/// This interval does not imply that cached data is evicted and it can still be accessed using
/// the `cacheOnly` fetch policy
public let maxAge: TimeInterval

/// Creates a new cache settings configuration.
///
/// - Parameters:
/// - storage: The storage mechanism to use. Defaults to `.persistent`.
/// - maxSize: The maximum desired size of the cache in bytes. Defaults to 100MB.
public init(storage: Storage = .persistent, maxSize: UInt64 = 100_000_000) {
/// - maxAge: The max time interval before a queries cache is considered stale and refreshed
/// from the server
public init(storage: Storage = .persistent, maxSize: UInt64 = 100_000_000,
maxAge: TimeInterval = 0) {
self.storage = storage
maxSizeBytes = maxSize
self.maxAge = maxAge
}
}
4 changes: 0 additions & 4 deletions Sources/Cache/ResultTree.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,6 @@ struct ResultTree: Codable {
// tree data - could be hydrated or dehydrated.
let data: String

// interval during which query results are considered fresh
let ttl: TimeInterval

// Local time when the entry was cached / updated
let cachedAt: Date

Expand All @@ -38,7 +35,6 @@ struct ResultTree: Codable {
enum CodingKeys: String, CodingKey {
case cachedAt = "ca" // cached at
case lastAccessed = "la" // last accessed
case ttl = "ri" // revalidation interval
case data = "d" // data cached
}
}
4 changes: 2 additions & 2 deletions Sources/Internal/GrpcClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,8 @@ actor GrpcClient: CustomStringConvertible {
If we have Partial Errors, we follow the partial error route below.
*/
guard !errorInfoList.isEmpty else {
// TODO: Extract ttl, server timestamp
return ServerResponse(jsonResults: resultsString, ttl: 10.0)
// TODO: Extract ttl, server timestamp when available
return ServerResponse(jsonResults: resultsString, maxAge: nil)
}

// We have partial errors returned
Expand Down
2 changes: 1 addition & 1 deletion Sources/Internal/ServerResponse.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@ import Foundation

struct ServerResponse {
let jsonResults: String
let ttl: TimeInterval
let maxAge: TimeInterval?
}
29 changes: 23 additions & 6 deletions Sources/Queries/GenericQueryRef.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,24 @@ actor GenericQueryRef<ResultData: Decodable & Sendable, Variable: OperationVaria

private let cache: Cache?

private var ttl: TimeInterval? = 10.0 //
// maxAge received from server in query response
private var serverMaxAge: TimeInterval? = nil

// maxAge computed based on server or cache settings
// server is given priority over cache settings
private var maxAge: TimeInterval {
if let serverMaxAge {
DataConnectLogger.debug("Using maxAge specified from server \(serverMaxAge)")
return serverMaxAge
}

if let ma = cache?.config.maxAge {
DataConnectLogger.debug("Using maxAge specified from cache settings \(ma)")
return ma
}

return 0
}

// Ideally we would like this to be part of the QueryRef protocol
// Not adding for now since the protocol is Public
Expand Down Expand Up @@ -94,6 +111,7 @@ actor GenericQueryRef<ResultData: Decodable & Sendable, Variable: OperationVaria

do {
if let cache {
serverMaxAge = response.maxAge
await cache.update(queryId: operationId, response: response, requestor: self)
}
}
Expand All @@ -112,15 +130,14 @@ actor GenericQueryRef<ResultData: Decodable & Sendable, Variable: OperationVaria
}

private func fetchCachedResults(allowStale: Bool) async throws -> OperationResult<ResultData> {
guard let cache,
let ttl,
ttl > 0 else {
DataConnectLogger.info("No cache provider configured or ttl is not set \(ttl)")
guard let cache
else {
DataConnectLogger.info("No cache provider configured")
return OperationResult(data: nil, source: .cache)
}

if let cacheEntry = await cache.resultTree(queryId: request.requestId),
(cacheEntry.isStale(ttl) && allowStale) || !cacheEntry.isStale(ttl) {
(cacheEntry.isStale(maxAge) && allowStale) || !cacheEntry.isStale(maxAge) {
let decoder = JSONDecoder()
let decodedData = try decoder.decode(
ResultData.self,
Expand Down
Loading