-
Notifications
You must be signed in to change notification settings - Fork 128
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
RUM-3462 feat: Introduce DataStore
#1730
RUM-3462 feat: Introduce DataStore
#1730
Conversation
a0fefeb
to
b7948b2
Compare
Datadog ReportBranch report: ✅ 0 Failed, 2903 Passed, 0 Skipped, 12m 29.43s Wall Time 🔻 Code Coverage Decreases vs Default Branch (8)
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wow! Really nice piece 💪
Great tests, nice docs. I like how it's exposed to features 🎖️
One thing I would consider around naming is dropping Core
to avoid having CoreData
. It might confuse some developers.
} | ||
} | ||
|
||
extension CoreDataStore: Flushable { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice!
b7948b2
to
dffd7c5
Compare
/// Retrieves the data value associated with the result, if it matches the expected version. | ||
/// | ||
/// - Parameter expectedVersion: The version expected for the retrieved data. | ||
/// - Returns: The data value if the version matches the expected version; otherwise, nil. | ||
public func data(expectedVersion: DataStoreKeyVersion = dataStoreDefaultKeyVersion) -> Data? { | ||
guard case .value(let data, let storedVersion) = self, storedVersion == expectedVersion else { | ||
return nil | ||
} | ||
return data | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alternatively we can change the semantic to make it always return Data
(if found) with performing an optional check on expectedVersion
(if provided) as in following code. I have no strong opinion.
public func data(expectedVersion: DataStoreKeyVersion? = nil) -> Data? {
guard case .value(let data, let storedVersion) = self else {
return nil
}
if let expectedVersion == expectedVersion, storedVersion != expectedVersion {
return nil
}
return data
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not sure if version expectation should be built in.
I would assume that the consumer of the api will handle different model version migration
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The version
is now made a pure convenience. It is fully hidden in the basic usage:
store.setValue(data, forKey: "key")
store.value(forKey: "key") { result in
_ = result.data()
}
If only versioning problem occurs, we can leverage it:
store.setValue(data_in_new_version, forKey: "key", version: 2)
store.value(forKey: "key") { result in
_ = result.data(expectedVersion: 2)
}
I think this is the best balance between having this support and keeping a clean API. Requiring the caller to handle versioning will cause unnecessary repetitive code infection across different places.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Requiring the caller to handle versioning will cause unnecessary repetitive code infection across different places.
Also, if version
is not encoded for every data upfront, we won't be able to ever support migration from "no version" to "version: 2". The decoding failure will become ambiguous and (if not filtered somehow) will flood telemetry with false-positives.
|
||
/// Retrieve data store. | ||
/// - Returns: Data store configured for storing data for this feature. | ||
func dataStore() -> DataStore |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The presence of DataStore
in FeatureScope
interface limits its usage to only DatadogRemoteFeatures
(the ones that can write and read batch files). In consequence, it won't be available in following features:
CrashReportingFeature
NetworkInstrumentationFeature
BacktraceReportingFeature
While last two are rather fine, there might be use cases for DataStore
in Crash Reporting. I don't foresee any at this point, hence I don't consider it blocking.
Even if DataStore
is required for CrashReportingFeature
at some point, I rather thing we should work on the FeatureScope
interface rather than re-design the DataStore
management. I'm open for discussion 🙂.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I rather thing we should work on the FeatureScope interface rather than re-design the DataStore management.
Totally 👍
/suggestion can we use a var
instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks great, well done!!
I have left minor suggestions but it's good to go 👍
|
||
/// Retrieve data store. | ||
/// - Returns: Data store configured for storing data for this feature. | ||
func dataStore() -> DataStore |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I rather thing we should work on the FeatureScope interface rather than re-design the DataStore management.
Totally 👍
/suggestion can we use a var
instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice! Thanks for taking the extra effort and improving the API 💪
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice!
What and why?
📦 This PR introduces a
DataStore
interface designed to store key-value pairs for a given feature. The primary goal is to provide a mechanism for persisting data between app launches.For the full list of use cases and requirements considered in this implementation, see RFC - Local Datastore (internal).
How?
New APIs
DataStore
interface:DataStoreKeyVersion
(anUInt16
): Values are persisted in files and SDK upgrades may occur between app launches. To ensure proper data reading, the caller can specify the version of expected data on read. This way it can handle the situation when data was written in old format, so it must skip decoding it in the new format.The
DataStore
is specific for a feature. It can be obtained throughFeatureScope
interface:This comes in-line with Event Write Context API that already exists on the
FeatureScope
:TLV format
Data is encoded in TLV format, leveraging prior work on batch files and recent refinements in #1725.
Review checklist
Custom CI job configuration (optional)
tools/