Skip to content
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

[MOB-19274][MOB-15354] Use Disk for Storage Services #966

Merged
merged 27 commits into from
Sep 7, 2023

Conversation

cdhoffmann
Copy link
Contributor

Phase 1 of migration from UserDefaults as our storage mechanism. Next, we will be working on migrating existing data from UserDefaults to this new Disk storage.

@codecov
Copy link

codecov bot commented Aug 31, 2023

Codecov Report

Merging #966 (8ea84f6) into migrateOffUserDefaults (eb4406a) will decrease coverage by 0.21%.
Report is 13 commits behind head on migrateOffUserDefaults.
The diff coverage is 88.89%.

❗ Current head 8ea84f6 differs from pull request most recent head fef2c3a. Consider uploading reports for the commit fef2c3a to get more accurate results

@@                    Coverage Diff                     @@
##           migrateOffUserDefaults     #966      +/-   ##
==========================================================
- Coverage                   90.22%   90.01%   -0.21%     
==========================================================
  Files                         130      131       +1     
  Lines                        8119     8221     +102     
==========================================================
+ Hits                         7325     7400      +75     
- Misses                        794      821      +27     

Copy link
Contributor

@pfransenadb pfransenadb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Getting there -- quite a few questions/comments on FileSystemNamedCollection and NamedCollectionDataStore


func setAppGroup(_ appGroup: String?) {
self.appGroup = appGroup
if let appGroup = appGroup {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this function also handle setting appGroup to nil (and clearing the associated appGroupUrl?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also -- why not use get/set semantics on appGroup variable?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Regardless of how it's done, setting/getting of appGroup needs to be done on queue as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that the get/set semantics would be cleaner, but the get/set function are part of the NamedCollectionProcessing protocol so we would be breaking compatibility by changing it. Or did you mean keeping the setAppGroup and getAppGroup functions and using the computed variable internally to handle the logic?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Didn't consider that there was a pre-existing protocol -- makes sense, leave as is.

guard let fileUrl = fileUrl(for: collectionName) else {
return
}
queue.sync {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be async, this function doesn't need to return anything.

}

func remove(collectionName: String, key: String) {
queue.sync {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can also be async

}

func set(collectionName: String, key: String, value: Any?) {
guard let fileUrl = fileUrl(for: collectionName) else {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fileUrl needs to be called from within queue.sync to maintain ordering.

private var appGroupUrl: URL?
private let fileManager = FileManager.default
private let LOG_TAG = "FileSystemNamedCollection"
var appGroup: String?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be private?

import Foundation

class FileSystemNamedCollection: NamedCollectionProcessing {
private let queue = DispatchQueue(label: "FileSystemNamedCollection.barrierQueue")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: isn't really a barrier, just a FIFO queue.

set(key: key, value: encodedValue)
guard let encodedValue = try? encoder.encode(value) else {return}
let encodedString = String(data: encodedValue, encoding: .utf8)
set(key: key, value: encodedString)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in previous code a failure to encode resulted in a nil value set (removal) -- is the change (failing to set and bailing due to the new guard statement) intentional, or was the previous behavior preferred/depended on?

Copy link
Contributor Author

@cdhoffmann cdhoffmann Sep 6, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a good point. Not too sure if we were depending on this to be nil in failure scenarios. I think it's a matter of preference. I would prefer to simply return, as setting nil on a value when encoding fails feels odd to me. What do you think @praveek? Should we remove the guard and set nil when encoding fails?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I too prefer simply returning instead of setting nil value. However, I would like to maintain the same behavior as before since I'm uncertain whether other extensions depend on the previous behavior and want to keep all the changes for this migration only within Core.

do {
try fileManager.createDirectory(at: adobeBaseUrl, withIntermediateDirectories: true)
} catch {
Log.error(label: adobeDirectory, "Failed to create storage directory with error: \(error)")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you check if directory exists and exit before attempting to create a directory?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apparently Apple doesn't recommend that approach. https://developer.apple.com/documentation/foundation/filemanager/1410277-fileexists

If you see the note towards the bottom which says: "Attempting to predicate behavior based on the current state of the file system or a particular file on the file system is not recommended. Doing so can cause odd behavior or race conditions. It’s far better to attempt an operation (such as loading a file or creating a directory), check for errors, and handle those errors gracefully than it is to try to figure out ahead of time whether the operation will succeed."

I have tested this function, and if the directory exists, it doesn't do anything. So in our case it works just fine.

@cdhoffmann cdhoffmann merged commit 909434e into adobe:migrateOffUserDefaults Sep 7, 2023
1 of 2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants