Specify local version when fetching storage manifest

This commit is contained in:
Nora Trapp 2020-02-24 17:30:36 -08:00 committed by Michael Kirk
parent 53ca4ca6d3
commit 63bedcb33b
2 changed files with 70 additions and 39 deletions

View File

@ -547,20 +547,18 @@ class StorageServiceOperation: OWSOperation {
manifestVersion = StorageServiceOperation.manifestVersion(transaction: transaction)
}
StorageService.fetchManifest().done(on: .global()) { manifest in
guard let manifest = manifest else {
// There is no existing manifest, lets create one with all our contacts.
StorageService.fetchLatestManifest(greaterThanVersion: manifestVersion).done(on: .global()) { response in
switch response {
case .noExistingManifest:
// There is no existing manifest, lets create one.
return self.createNewManifest(version: 1)
}
guard manifest.version != manifestVersion else {
case .noNewerManifest:
// Our manifest version matches the server version, nothing to do here.
return self.reportSuccess()
case .latestManifest(let manifest):
// Our manifest is not the latest, merge in the latest copy.
self.mergeLocalManifest(withRemoteManifest: manifest, backupAfterSuccess: false)
}
// Our manifest is not the latest, merge in the latest copy.
self.mergeLocalManifest(withRemoteManifest: manifest, backupAfterSuccess: false)
}.catch { error in
if let storageError = error as? StorageService.StorageError {

View File

@ -119,13 +119,27 @@ public struct StorageService {
}
}
/// Fetch the latest manifest from the storage service
public enum FetchLatestManifestResponse {
case latestManifest(StorageServiceProtoManifestRecord)
case noNewerManifest
case noExistingManifest
}
/// Fetch the latest manifest from the storage service.
/// If the greater than version is provided, only returns a manifest
/// if a newer one exists on the service, otherwise indicates
/// that there is no new content.
///
/// Returns nil if a manifest has never been stored.
public static func fetchManifest() -> Promise<StorageServiceProtoManifestRecord?> {
public static func fetchLatestManifest(greaterThanVersion: UInt64? = nil) -> Promise<FetchLatestManifestResponse> {
Logger.info("")
return storageRequest(withMethod: "GET", endpoint: "v1/storage/manifest").map(on: .global()) { response in
var endpoint = "v1/storage/manifest"
if let greaterThanVersion = greaterThanVersion {
endpoint += "/version/\(greaterThanVersion)"
}
return storageRequest(withMethod: "GET", endpoint: endpoint).map(on: .global()) { response in
switch response.status {
case .success:
let encryptedManifestContainer = try StorageServiceProtoStorageManifest.parseData(response.data)
@ -138,9 +152,11 @@ public struct StorageService {
} catch {
throw StorageError.manifestDecryptionFailed(version: encryptedManifestContainer.version)
}
return try StorageServiceProtoManifestRecord.parseData(manifestData)
return .latestManifest(try StorageServiceProtoManifestRecord.parseData(manifestData))
case .notFound:
return nil
return .noExistingManifest
case .noContent:
return .noNewerManifest
default:
owsFailDebug("unexpected response \(response.status)")
throw StorageError.retryableAssertion
@ -285,6 +301,7 @@ public struct StorageService {
case success
case conflict
case notFound
case noContent
}
let status: Status
let data: Data
@ -362,6 +379,8 @@ public struct StorageService {
switch response.statusCode {
case 200:
status = .success
case 204:
status = .noContent
case 409:
status = .conflict
case 404:
@ -414,42 +433,56 @@ public extension StorageService {
var ourManifestVersion: UInt64 = 0
// Fetch Existing
fetchManifest().map { manifest in
let previousVersion = manifest?.version ?? ourManifestVersion
ourManifestVersion = previousVersion + 1
fetchLatestManifest().map { response in
let previousVersion: UInt64
var existingKeys: [Data]?
switch response {
case .latestManifest(let latestManifest):
previousVersion = latestManifest.version
existingKeys = latestManifest.keys
case .noNewerManifest, .noExistingManifest:
previousVersion = ourManifestVersion
}
// set keys
let newManifestBuilder = StorageServiceProtoManifestRecord.builder(version: ourManifestVersion)
newManifestBuilder.setKeys(recordsInManifest.map { $0.identifier.data })
return (try! newManifestBuilder.build(), manifest?.keys.map { StorageIdentifier(data: $0) } ?? [])
return (try! newManifestBuilder.build(), existingKeys?.map { StorageIdentifier(data: $0) } ?? [])
// Update or create initial manifest with test data
}.then { manifest, deletedKeys in
updateManifest(manifest, newItems: recordsInManifest, deletedIdentifiers: deletedKeys)
}.map { manifest in
guard manifest == nil else {
}.then { latestManifest, deletedKeys in
updateManifest(latestManifest, newItems: recordsInManifest, deletedIdentifiers: deletedKeys)
}.map { latestManifest in
guard latestManifest == nil else {
owsFailDebug("Manifest conflicted unexpectedly, should be nil")
throw StorageError.assertion
}
// Fetch the manifest we just created
}.then { fetchManifest() }.map { manifest in
guard let manifest = manifest else {
}.then { fetchLatestManifest() }.map { response in
guard case .latestManifest(let latestManifest) = response else {
owsFailDebug("manifest should exist, we just created it")
throw StorageError.assertion
}
guard Set(manifest.keys) == Set(identifiersInManfest.map { $0.data }) else {
guard Set(latestManifest.keys) == Set(identifiersInManfest.map { $0.data }) else {
owsFailDebug("manifest should only contain our test keys")
throw StorageError.assertion
}
guard manifest.version == ourManifestVersion else {
guard latestManifest.version == ourManifestVersion else {
owsFailDebug("manifest version should be the version we set")
throw StorageError.assertion
}
// Fetch the manifest we just created specifying the local version
}.then { fetchLatestManifest(greaterThanVersion: ourManifestVersion) }.map { response in
guard case .noNewerManifest = response else {
owsFailDebug("no new manifest should exist, we just created it")
throw StorageError.assertion
}
// Fetch the first contact we just stored
}.then {
fetchItem(for: identifiersInManfest.first!)
@ -500,27 +533,27 @@ public extension StorageService {
ourManifestVersion += 1
let newManifestBuilder = StorageServiceProtoManifestRecord.builder(version: ourManifestVersion)
return try! newManifestBuilder.build()
}.then { manifest in
updateManifest(manifest, newItems: [], deletedIdentifiers: identifiersInManfest)
}.map { manifest in
guard manifest == nil else {
}.then { latestManifest in
updateManifest(latestManifest, newItems: [], deletedIdentifiers: identifiersInManfest)
}.map { latestManifest in
guard latestManifest == nil else {
owsFailDebug("Manifest conflicted unexpectedly, should be nil")
throw StorageError.assertion
}
// Fetch the manifest we just stored
}.then { fetchManifest() }.map { manifest in
guard let manifest = manifest else {
}.then { fetchLatestManifest() }.map { latestManifest in
guard case .latestManifest(let latestManifest) = latestManifest else {
owsFailDebug("manifest should exist, we just created it")
throw StorageError.assertion
}
guard manifest.keys.isEmpty else {
guard latestManifest.keys.isEmpty else {
owsFailDebug("manifest should have no keys")
throw StorageError.assertion
}
guard manifest.version == ourManifestVersion else {
guard latestManifest.version == ourManifestVersion else {
owsFailDebug("manifest version should be the version we set")
throw StorageError.assertion
}
@ -539,18 +572,18 @@ public extension StorageService {
return (try! oldManifestBuilder.build(), try! StorageItem(identifier: identifier, contact: try! recordBuilder.build()))
}.then { oldManifest, item in
updateManifest(oldManifest, newItems: [item], deletedIdentifiers: [])
}.done { manifest in
guard let manifest = manifest else {
}.done { latestManifest in
guard let latestManifest = latestManifest else {
owsFailDebug("manifest should exist, because there was a conflict")
throw StorageError.assertion
}
guard manifest.keys.isEmpty else {
guard latestManifest.keys.isEmpty else {
owsFailDebug("manifest should still have no keys")
throw StorageError.assertion
}
guard manifest.version == ourManifestVersion else {
guard latestManifest.version == ourManifestVersion else {
owsFailDebug("manifest version should be the version we set")
throw StorageError.assertion
}