Serialize group updates using OWSOperation

This commit is contained in:
Sasha Weiss 2022-08-17 13:18:31 -07:00 committed by GitHub
parent 259b43cfe1
commit 7146a02821
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 130 additions and 26 deletions

View File

@ -830,16 +830,23 @@ public class GroupsV2Impl: NSObject, GroupsV2Swift, GroupsV2 {
// MARK: - Generic Group Change
public func updateGroupV2(groupModel: TSGroupModelV2,
changesBlock: @escaping (GroupsV2OutgoingChanges) -> Void) -> Promise<TSGroupThread> {
public func updateGroupV2(
groupId: Data,
groupSecretParamsData: Data,
changesBlock: @escaping (GroupsV2OutgoingChanges) -> Void
) -> Promise<TSGroupThread> {
return firstly(on: .global()) { () throws -> GroupsV2OutgoingChanges in
let changes = GroupsV2OutgoingChangesImpl(groupId: groupModel.groupId,
groupSecretParamsData: groupModel.secretParamsData)
let changes = GroupsV2OutgoingChangesImpl(
groupId: groupId,
groupSecretParamsData: groupSecretParamsData
)
changesBlock(changes)
return changes
}.then(on: .global()) { (changes: GroupsV2OutgoingChanges) -> Promise<TSGroupThread> in
return self.updateExistingGroupOnService(changes: changes,
requiredRevision: nil)
return self.updateExistingGroupOnService(
changes: changes,
requiredRevision: nil
)
}
}
@ -1660,7 +1667,10 @@ public class GroupsV2Impl: NSObject, GroupsV2Swift, GroupsV2 {
// We're already an invited member; try to join by accepting the invite.
// That will make us a full member; requesting to join via
// the invite link might make us a requesting member.
return self.updateGroupV2(groupModel: groupModelV2) { groupChangeSet in
return self.updateGroupV2(
groupId: groupModelV2.groupId,
groupSecretParamsData: groupModelV2.secretParamsData
) { groupChangeSet in
groupChangeSet.promoteInvitedMember(localUuid)
}
} else {

View File

@ -1139,21 +1139,6 @@ public class GroupManager: NSObject {
}
}
// MARK: - Generic Group Change
public static func updateGroupV2(groupModel: TSGroupModelV2,
description: String,
changesBlock: @escaping (GroupsV2OutgoingChanges) -> Void) -> Promise<TSGroupThread> {
return firstly {
self.ensureLocalProfileHasCommitmentIfNecessary()
}.then(on: .global()) { () throws -> Promise<TSGroupThread> in
self.groupsV2Swift.updateGroupV2(groupModel: groupModel, changesBlock: changesBlock)
}.timeout(seconds: GroupManager.groupUpdateTimeoutDuration,
description: description) {
GroupsV2Error.timeout
}
}
// MARK: - Removed from Group or Invite Revoked
public static func handleNotInGroup(groupId: Data,
@ -2224,3 +2209,106 @@ extension GroupManager {
}
}
}
// MARK: - Serialized generic group changes
extension GroupManager {
// Serialize group updates by group ID
private static var groupUpdateOperationQueues: [Data: OperationQueue] = [:]
private static func operationQueue(
forUpdatingGroup groupModel: TSGroupModel
) -> OperationQueue {
if let queue = groupUpdateOperationQueues[groupModel.groupId] {
return queue
}
let newQueue = OperationQueue()
newQueue.name = "GroupManager.updateQueueForGroup.\(UUID().uuidString)"
newQueue.maxConcurrentOperationCount = 1
groupUpdateOperationQueues[groupModel.groupId] = newQueue
return newQueue
}
private class GenericGroupUpdateOperation: OWSOperation {
private let groupId: Data
private let groupSecretParamsData: Data
private let updateDescription: String
private let changesBlock: (GroupsV2OutgoingChanges) -> Void
let promise: Promise<TSGroupThread>
private let future: Future<TSGroupThread>
init(
groupId: Data,
groupSecretParamsData: Data,
updateDescription: String,
changesBlock: @escaping (GroupsV2OutgoingChanges) -> Void
) {
self.groupId = groupId
self.groupSecretParamsData = groupSecretParamsData
self.updateDescription = updateDescription
self.changesBlock = changesBlock
let (promise, future) = Promise<TSGroupThread>.pending()
self.promise = promise
self.future = future
super.init()
self.remainingRetries = 1
}
public override func run() {
firstly {
GroupManager.ensureLocalProfileHasCommitmentIfNecessary()
}.then(on: .global()) { () throws -> Promise<TSGroupThread> in
self.groupsV2Swift.updateGroupV2(
groupId: self.groupId,
groupSecretParamsData: self.groupSecretParamsData,
changesBlock: self.changesBlock
)
}.done(on: .global()) { groupThread in
self.reportSuccess()
self.future.resolve(groupThread)
}.timeout(
seconds: GroupManager.groupUpdateTimeoutDuration,
description: description
) {
GroupsV2Error.timeout
}.catch(on: .global()) { error in
switch error {
case GroupsV2Error.redundantChange:
// From an operation perspective, this is a success!
self.reportSuccess()
self.future.reject(error)
default:
owsFailDebug("Group update failed: \(error)")
self.reportError(error)
}
}
}
public override func didFail(error: Error) {
future.reject(error)
}
}
public static func updateGroupV2(
groupModel: TSGroupModelV2,
description: String,
changesBlock: @escaping (GroupsV2OutgoingChanges) -> Void
) -> Promise<TSGroupThread> {
let operation = GenericGroupUpdateOperation(
groupId: groupModel.groupId,
groupSecretParamsData: groupModel.secretParamsData,
updateDescription: description,
changesBlock: changesBlock
)
operationQueue(forUpdatingGroup: groupModel).addOperation(operation)
return operation.promise
}
}

View File

@ -111,8 +111,11 @@ public protocol GroupsV2Swift: GroupsV2 {
func updateExistingGroupOnService(changes: GroupsV2OutgoingChanges,
requiredRevision: UInt32?) -> Promise<TSGroupThread>
func updateGroupV2(groupModel: TSGroupModelV2,
changesBlock: @escaping (GroupsV2OutgoingChanges) -> Void) -> Promise<TSGroupThread>
func updateGroupV2(
groupId: Data,
groupSecretParamsData: Data,
changesBlock: @escaping (GroupsV2OutgoingChanges) -> Void
) -> Promise<TSGroupThread>
func reuploadLocalProfilePromise() -> Promise<Void>
@ -566,8 +569,11 @@ public class MockGroupsV2: NSObject, GroupsV2Swift, GroupsV2 {
owsFail("Not implemented.")
}
public func updateGroupV2(groupModel: TSGroupModelV2,
changesBlock: @escaping (GroupsV2OutgoingChanges) -> Void) -> Promise<TSGroupThread> {
public func updateGroupV2(
groupId: Data,
groupSecretParamsData: Data,
changesBlock: @escaping (GroupsV2OutgoingChanges) -> Void
) -> Promise<TSGroupThread> {
owsFail("Not implemented.")
}