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

POC - DBP Processor Refactoring (Abstraction & Testability) #2730

Closed
wants to merge 9 commits into from

Conversation

aataraxiaa
Copy link
Contributor

Task/Issue URL:
Tech Design URL:
CC:

Description:
This PR is a proof-of-concept. It demonstrates how we can make our processor logic more testable. If the proof-of-concept is satisfactory, and we get consensus on the approach, we can use this POC as the basis for a final implementation.

The following changes are included:

  1. Extracts OperationCollection building into a dedicated protocol and concrete type - DataBrokerOperationsCollectionBuilder
  2. Implements DataBrokerProtectionQueueManager, a refactoring of the current DataBrokerProtectionProcessor type, which enables testing of it’s operation queue state
  3. Implements QueueManagerMode, which captures the state of the queue manager
  4. Creates protocol interfaces for dependencies of DataBrokerProtectionQueueManager, which can be mocked
  5. Adds mocks for all dependencies required to test DataBrokerProtectionQueueManager
  6. Adds tests for DataBrokerProtectionQueueManager and QueueManagerMode, and related mocks

Steps to test this PR:
1.

Internal references:

Pull Request Review Checklist
Software Engineering Expectations
Technical Design Template
Pull Request Documentation

@aataraxiaa aataraxiaa requested a review from THISISDINOSAUR May 1, 2024 15:18
@aataraxiaa aataraxiaa force-pushed the pete/poc-dbp-processor-refactoring branch from 47bdf76 to 1b369a5 Compare May 2, 2024 08:54
Copy link
Contributor

github-actions bot commented May 2, 2024

Fails
🚫 Please, don't forget to add a link to the internal task
Warnings
⚠️ PR has more than 500 lines of code changing. Consider splitting into smaller PRs if possible.

Generated by 🚫 dangerJS against 16acb56

Copy link
Contributor

@THISISDINOSAUR THISISDINOSAUR left a comment

Choose a reason for hiding this comment

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

Awesome work! I think this goes a really long way to moving us in the right direction

@@ -0,0 +1,81 @@
//
// DataBrokerOperationsCollectionBuilder.swift
Copy link
Contributor

Choose a reason for hiding this comment

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

I think the introduction of this class is awesome. One minor thing is it really highlights the need to rename DataBrokerOperationsCollection, since this is technically building a DataBrokerOperationsCollectionCollection.

It's my understanding that a DataBrokerOperationsCollection is all of the operations (even operation I think we should rename to avoid confusion with NSOperation) for a single broker. I think using that information if we first come up with a different name for scan/opt out "operations"we should be able to come up with a better name

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Agree on the need for renaming. I thought about this, and came up with different ideas -e.g renaming DataBrokerOperationsCollection to DataBrokerJob, or DataBrokerJobGroup, and other variations. In the end, I decided that as DataBrokerOperationsCollection is in fact an Operation, and it is handled as such by an OperationQueue, it should just be called DataBrokerOperation. However, to implement this I needed to change conflicting type names.

Ultimately, what I propose is in this commit.

Note that we could go further with this - i.e rename other references to operation which should now say task based on these changes. At the moment I think we overuse ‘Operation’. However, want to share this and get input before we make any more related changes.

self.brokerUpdater = brokerUpdater
}

func startManualScans(showWebView: Bool, operationDependencies: OperationDependencies, completion: ((DataBrokerProtectionSchedulerErrorCollection?) -> Void)?) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd maybe change "startManualScans" to something like startManualScansIfPermitted, although they should always be. But since it does check for interruptions I think we should capture that somehow

Copy link
Collaborator

Choose a reason for hiding this comment

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

@THISISDINOSAUR what would be the cases for the manual scan to not start? In other words, how can it not be permitted? Is the depencency other scans running or initial data state, like, not having a profile data

Copy link
Contributor

Choose a reason for hiding this comment

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

I think at present this function should never be called if it can't start, but because it does checks to see if they can and could fail, I think that should be captured in the signature.

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’ve renamed the DataBrokerProtectionQueueManager methods in this commit. Note that I have also renamed the QueueManagerMode cases.

mode = .manual

// New Manual scans ALWAYS interrupt (i.e cancel) ANY current Manual/Scheduled scans
operationQueue.cancelAllOperations()
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd maybe consider making an explicit tear down function, even if it only contains this one line for now, or maybe a single function that handles state transitions

Copy link
Contributor

Choose a reason for hiding this comment

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

Also worth noting this will still have the completion/barrier block bug, where if we interrupt a state, the completion (and anything else in the barrier block) won't be called.


func startManualScans(showWebView: Bool, operationDependencies: OperationDependencies, completion: ((DataBrokerProtectionSchedulerErrorCollection?) -> Void)?) {

guard mode.canInterrupt(forNewMode: .manual) else { return }
Copy link
Contributor

Choose a reason for hiding this comment

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

You mentioned it but recording for prosperity, if we decide not to interrupt, we should call the completion of the function that isn't going to execute (probably with a new error type?)

}
}

func runAllOptOutOperations(showWebView: Bool, operationDependencies: OperationDependencies, completion: ((DataBrokerProtectionSchedulerErrorCollection?) -> Void)?) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we can get rid of some of these, although need to check which ones.
You mentioned it, but recording for prosperity, we should rename them too, since they're not so clear

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’ve removed the unused runAllOperations method - See this commit

} catch {
os_log("DataBrokerProtectionProcessor error: runOperations, error: %{public}@", log: .error, error.localizedDescription)
operationQueue.addBarrierBlock {
completion(DataBrokerProtectionSchedulerErrorCollection(oneTimeError: error))
Copy link
Contributor

Choose a reason for hiding this comment

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

Hold over from before, but it seems weird and wrong that we don't call the completion immediately if there's an error. So if there's already things on the queue unrelated to this set of scans, it won't run for ages.
(with the current design I don't think there's a case where that's ever true, but no reason to encourage it).

Copy link
Collaborator

Choose a reason for hiding this comment

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

You mean here it should call completion directly instead of adding the barrierBlock?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah, or if not we should record that and make it clear it's deliberate

pixelHandler: pixelHandler)

var brokerUpdater: DataBrokerProtectionBrokerUpdater?
if let vault = try? DataBrokerProtectionSecureVaultFactory.makeVault(reporter: nil) {
Copy link
Contributor

Choose a reason for hiding this comment

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

It's tangental and out of scope, but I don't think we should ever be making the vault directly, and should be striving to use a single DB abstraction (Database)


typealias OperationType = DataBrokerOperationsCollection.OperationType

protocol OperationDependencies {
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this is a big improvement, although I personally don't have any great ideas on what we should be doing with our dependancies in DBP more broadly, so if you have any ideas there it would be good.

XCTAssertTrue(result)
}

// TODO: More tests to be added as we define expected interruption behaviorgst
Copy link
Collaborator

Choose a reason for hiding this comment

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

I see the TODO here, just adding the suggestion that the most important thing to add here is the case where it's manual scan interrupting manual scan since it's the "edge case" of all other cases.

Copy link
Contributor Author

@aataraxiaa aataraxiaa May 3, 2024

Choose a reason for hiding this comment

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

Thanks @Bunn, I’ve added a test for this scenario in this commit.

@aataraxiaa
Copy link
Contributor Author

FYI @THISISDINOSAUR and @Bunn - I’m not going to continue any development here. I’ll instead work on new branches to enable opening focused PRs to a feature branch. However, I’ll keep this open for the moment for reference.

@aataraxiaa aataraxiaa closed this May 9, 2024
@aataraxiaa aataraxiaa deleted the pete/poc-dbp-processor-refactoring branch July 17, 2024 14:13
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