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

Subscriptions, State machine for ReconcileAndLocalSaveOperation #149

Merged
merged 19 commits into from
Nov 26, 2019

Conversation

palpatim
Copy link
Member

@palpatim palpatim commented Nov 23, 2019

Notes V2

  • Removed handling of incoming mutations; we'll get those from our subscriptions. Derp.
  • PR feedback

Notes V1

  • Added a ReconcileAndLocalSaveOperation that will handle any incoming cloud sync events, whether from mutations or subscriptions.
  • Refactored OutgoingMutationQueue to be based on Operation/OperationQueue rather than Combine streams. Logic is much simpler now.
  • Added new StateMachine class that we can promote to an AWSPluginsCore support type if it looks useful in other contexts
  • Added a trivial DefaultLogger support protocol to provide a default log property.
  • Updated handling of optional fields during Model updates
  • Hardcoded a _deleted conflict resolution field alongside the previously-existing _version field. We'll remove those once @drochetti finishes conflict resolution

Note to reviewers

Many of the integration tests added in this PR are broken due to errors in connections handling. Once codegen/CLI changes are pushed, and #164 is merged, I'll dive deeper and get those fixed.

@drochetti Could you please pay special attention to the changes I made to model & schema, especially the optional field handling in
AmplifyPlugins/DataStore/AWSDataStoreCategoryPlugin/Storage/SQLite/Model+SQLite.swift sqlValues(for fields:)? Unit tests are passing, but I haven't added additional tests to cover this case and I want to get another set of eyes on this to validate my thinking.

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

@palpatim palpatim requested a review from wooj2 November 23, 2019 01:57
- Remove AWSDataStoreCategoryPlugin references that got reinstated after merge
- Initial subscriptions are now performed at startup
- Mutation queue is now an OperationQueue instead of a Combine stream
- Add 'unknown' case to DataStoreError
- Fix SQLite tests and disable init logging for SQLiteStorageEngineAdapter
* master:
  API Predicate to GraphQL filter variables (#150)
  model associations: refactor + missing use cases (#153)
  Basic unit tests for comprehend and translate (#160)
  CoreML logic to Detect faces (#155)
  API Category - Updates to APIError and GraphQLResponse (#133)
  Initial commit for unit test for translate service behavior (#158)
  bug fix change (#152)
* master:
  fix API model association (#163)
@palpatim palpatim requested a review from drochetti November 26, 2019 16:06
@palpatim palpatim changed the title WIP: Subscriptions, State machine for ReconcileAndLocalSaveOperation Subscriptions, State machine for ReconcileAndLocalSaveOperation Nov 26, 2019
Copy link
Member Author

@palpatim palpatim left a comment

Choose a reason for hiding this comment

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

Self review to set file comparison checkpoint

@palpatim
Copy link
Member Author

Note to reviewers: I will move DefaultLogger to Amplify core in a subsequent PR.

@palpatim palpatim requested a review from lawmicha November 26, 2019 20:51
Comment on lines 64 to 68
if models.count > 1 {
completion(.failure(.nonUniqueResult(model: modelType.modelName, count: count)))
} else {
completion(.success(models.first))
}
Copy link
Contributor

Choose a reason for hiding this comment

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

seems like we should reverse this in the event that models is empty.

if models.count == 1 {
completion(.success(...
} else {
...
}

Copy link
Member Author

Choose a reason for hiding this comment

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

Upon reflection I think I'll change it to a guard to really capture the intent that this is an error case.

Copy link
Member Author

Choose a reason for hiding this comment

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

Upon even more reflection, I now remember why I did it this way: a 0-count model array is also valid!

Copy link
Member Author

Choose a reason for hiding this comment

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

switch to the rescue:

switch models.count {
case 0, 1:
    completion(.success(models.first))
default:
    completion(.failure(.nonUniqueResult(model: modelType.modelName, count: models.count)))
}

/// mutated data from the service.
static let subscriptionReceived = "DataStore.subscriptionReceived"
/// Dispatched when DataStore receives a sync response from the cloud via the API category. This event does not
/// define the source of the event--it could be in response to either a subscription or a locally-sourced mutation.
Copy link
Contributor

Choose a reason for hiding this comment

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

Currently this we seem to only be using the variable in the subscription flow (ReconcileAndLocalSaveOperation.swift) -- maybe it would be better to have two separate variables -- one so that customers can listen to subscription events, and one for when location mutations are made.

Copy link
Member Author

Choose a reason for hiding this comment

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

I thought about that, but the comment explains why I don't think it's a meaningful distinction: any incoming event could potentially overwrite the data in the local store, regardless of source. What would the customer use case be for ignoring one type over the other, given that either could result in a change?

Amplify/Categories/DataStore/Model/Model+Syncable.swift Outdated Show resolved Hide resolved
//
// SPDX-License-Identifier: Apache-2.0
//

Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm, we have
OutgoingMutationQueue.swift
and
SyncMutationToCloudOperation.swift

Maybe it would be clearer to rename these to:
OutgoingMutationOperationQueue.swift
and
OutgoingMutationOperation.swift

Copy link
Member Author

Choose a reason for hiding this comment

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

Hmm, the "outgoing mutation" includes saving the local mutation event as well as creating & enqueing the cloud sync operations. The SyncMutationToCloudOperation only does that one thing.

Part of the problem is that the OutgoingMutationQueue is doing too much, at too many different levels:

  • loading initial mutations from disk (infrastructure @ global scope)
  • Creating a sync operation from a loaded mutation event (infrastructure at local scope)
  • accepting incoming mutation submissions, creating a sync operation from it (transactional at system scope)

The SyncMutationToCloudOperation on the other hand is a little bit more targeted:

  • send to cloud (
  • validate response
  • notify

Rather than just rename the operation, I'd rather refactor the queue to do less itself & delegate more, and enforce a better separation of levels of responsibility. Thoughts?

Copy link
Contributor

@wooj2 wooj2 left a comment

Choose a reason for hiding this comment

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

My comments are minor.. I'm largely good with broad framework for both outgoing/ingoing mutations as well as subscriptions. We can iterate from here! thanks!!

@palpatim
Copy link
Member Author

Addressed PR feedback (less a few open questions for further discussion). Merging now to serve as a base for upcoming changes--notably #161 to stop integration tests from crashing :(

@palpatim palpatim merged commit 0d5b48f into master Nov 26, 2019
@palpatim palpatim deleted the palpatim/api-sync branch November 27, 2019 03:03
waleedbutt pushed a commit to hassan31/amplify-swift that referenced this pull request Sep 18, 2024
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