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

Error handling Maps as nested Serialized Custom Types in Android #5457

Closed
2 of 14 tasks
kryptx opened this issue Sep 15, 2024 · 6 comments
Closed
2 of 14 tasks

Error handling Maps as nested Serialized Custom Types in Android #5457

kryptx opened this issue Sep 15, 2024 · 6 comments
Labels
datastore Issues related to the DataStore Category to-be-reproduced Issues that have not been reproduced yet, but have reproduction steps provided

Comments

@kryptx
Copy link

kryptx commented Sep 15, 2024

Description

During synchronization on Android, my app produces the following error:

E/amplify:flutter:datastore( 8093): Received an error
E/amplify:flutter:datastore( 8093): DataStoreException{message=Failed to observe items in storage adapter., cause=java.lang.ClassCastException: java.util.Collections$UnmodifiableMap cannot be cast to com.amplifyframework.core.model.SerializedCustomType, recoverySuggestion=Inspect the failure details.}
E/amplify:flutter:datastore( 8093): 	at com.amplifyframework.datastore.storage.sqlite.SQLiteStorageAdapter.lambda$observe$10(SQLiteStorageAdapter.java:762)
E/amplify:flutter:datastore( 8093): 	at com.amplifyframework.datastore.storage.sqlite.SQLiteStorageAdapter$$ExternalSyntheticLambda14.accept(Unknown Source:4)
E/amplify:flutter:datastore( 8093): 	at io.reactivex.rxjava3.internal.observers.LambdaObserver.onError(LambdaObserver.java:77)
E/amplify:flutter:datastore( 8093): 	at io.reactivex.rxjava3.internal.observers.LambdaObserver.onNext(LambdaObserver.java:67)
E/amplify:flutter:datastore( 8093): 	at io.reactivex.rxjava3.subjects.PublishSubject$PublishDisposable.onNext(PublishSubject.java:310)
E/amplify:flutter:datastore( 8093): 	at io.reactivex.rxjava3.subjects.PublishSubject.onNext(PublishSubject.java:226)
E/amplify:flutter:datastore( 8093): 	at io.reactivex.rxjava3.subjects.SerializedSubject.onNext(SerializedSubject.java:104)
E/amplify:flutter:datastore( 8093): 	at com.amplifyframework.datastore.storage.sqlite.SQLiteStorageAdapter.saveInternal(SQLiteStorageAdapter.java:487)
E/amplify:flutter:datastore( 8093): 	at com.amplifyframework.datastore.storage.sqlite.SQLiteStorageAdapter.lambda$batchSyncOperations$3$com-amplifyframework-datastore-storage-sqlite-SQLiteStorageAdapter(SQLiteStorageAdapter.java:339)
E/amplify:flutter:datastore( 8093): 	at com.amplifyframework.datastore.storage.sqlite.SQLiteStorageAdapter$$ExternalSyntheticLambda0.run(Unknown Source:4)
E/amplify:flutter:datastore( 8093): 	at com.amplifyframework.datastore.storage.sqlite.SQLCommandProcessor.lambda$runInTransactionAndSucceedOnDatastoreException$0$com-amplifyframework-datastore-storage-sqlite-SQLCommandProcessor(SQLCommandProcessor.java:145)
E/amplify:flutter:datastore( 8093): 	at com.amplifyframework.datastore.storage.sqlite.SQLCommandProcessor$$ExternalSyntheticLambda0.run(Unknown Source:4)
E/amplify:flutter:datastore( 8093): 	at com.amplifyframework.datastore.storage.sqlite.SQLCommandProcessor.runInTransaction(SQLCommandProcessor.java:163)
E/amplify:flutter:datastore( 8093): 	at com.amplifyframework.datastore.storage.sqlite.SQLCommandProcessor.runInTransactionAndSucceedOnDatastoreException(SQLCommandProcessor.java:143)
E/amplify:flutter:datastore( 8093): 	at com.amplifyframework.datastore.storage.sqlite.SQLiteStorageAdapter.lambda$batchSyncOperations$4$com-amplifyframework-datastore-storage-sqlite-SQLiteStorageAdapter(SQLiteStorageAdapter.java:399)
E/amplify:flutter:datastore( 8093): 	at com.amplifyframework.datastore.storage.sqlite.SQLiteStorageAdapter$$ExternalSyntheticLambda8.run(Unknown Source:8)
E/amplify:flutter:datastore( 8093): 	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:487)
E/amplify:flutter:datastore( 8093): 	at java.util.concurrent.FutureTask.run(FutureTask.java:264)
E/amplify:flutter:datastore( 8093): 	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
E/amplify:flutter:datastore( 8093): 	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:644)
E/amplify:flutter:datastore( 8093): 	at java.lang.Thread.run(Thread.java:1012)
E/amplify:flutter:datastore( 8093): Caused by: java.lang.ClassCastException: java.util.Collections$UnmodifiableMap cannot be cast to com.amplifyframework.core.model.SerializedCustomType
E/amplify:flutter:datastore( 8093): 	at com.amazonaws.amplify.amplify_datastore.types.model.FlutterSerializedModel.parseSerializedDataMap(FlutterSerializedModel.kt:104)
E/amplify:flutter:datastore( 8093): 	at com.amazonaws.amplify.amplify_datastore.types.model.FlutterSerializedModel.<init>(FlutterSerializedModel.kt:15)
E/amplify:flutter:datastore( 8093): 	at com.amazonaws.amplify.amplify_datastore.types.model.FlutterSerializedModel.parseSerializedDataMap(FlutterSerializedModel.kt:60)
E/amplify:flutter:datastore( 8093): 	at com.amazonaws.amplify.amplify_datastore.types.model.FlutterSerializedModel.<init>(FlutterSerializedModel.kt:15)
E/amplify:flutter:datastore( 8093): 	at com.amazonaws.amplify.amplify_datastore.types.model.FlutterSubscriptionEvent.<init>(FlutterSubscriptionEvent.kt:10)
E/amplify:flutter:datastore( 8093): 	at com.amazonaws.amplify.amplify_datastore.AmplifyDataStorePlugin.onSetUpObserve$lambda$26(AmplifyDataStorePlugin.kt:522)
E/amplify:flutter:datastore( 8093): 	at com.amazonaws.amplify.amplify_datastore.AmplifyDataStorePlugin.$r8$lambda$W8b09unHd0AjID_kblCPn89Ob84(Unknown Source:0)
E/amplify:flutter:datastore( 8093): 	at com.amazonaws.amplify.amplify_datastore.AmplifyDataStorePlugin$$ExternalSyntheticLambda2.accept(Unknown Source:4)
E/amplify:flutter:datastore( 8093): 	at com.amplifyframework.datastore.AWSDataStorePlugin.lambda$observe$25(AWSDataStorePlugin.java:549)
E/amplify:flutter:datastore( 8093): 	at com.amplifyframework.datastore.AWSDataStorePlugin$$ExternalSyntheticLambda37.accept(Unknown Source:6)
E/amplify:flutter:datastore( 8093): 	at com.amplifyframework.datastore.storage.sqlite.ObserveQueryExecutor$$ExternalSyntheticLambda1.accept(Unknown Source:4)
E/amplify:flutter:datastore( 8093): 	at io.reactivex.rxjava3.internal.observers.LambdaObserver.onNext(LambdaObserver.java:63)
E/amplify:flutter:datastore( 8093): 	... 17 more

After this, the DataStore sync process must be restarted or records will not be synchronized.

On iOS, this issue does not occur, however I'm not 100% certain that this issue has no impact on iOS. I can confirm only that I'm unable to reproduce the error log output and sync stopping except on Android.

Categories

  • Analytics
  • API (REST)
  • API (GraphQL)
  • Auth
  • Authenticator
  • DataStore
  • Notifications (Push)
  • Storage

Steps to Reproduce

In my app, the error occurs when a FlightSearch is deleted in the app, or when a FlightSearchResult is received from the platform (they are created with IAM, and the app receives them via synchronization).

  1. Create a FlightItineraryOffer containing at least 1 trip leg with at least 1 flight record
  2. Create a FlightSearch owned by the OIDC user
  3. Create a FlightSearchResult associating the two records, owned by the OIDC user
  4. Synchronize the data to the app

I've dug into this stack trace and found that we seem to be receiving a Map when we use a certain data structure in our schema. I believe it is when we define a property as containing an array of objects, and those objects are not themselves "models" (annotated with @model). In the schema, this property is tripLegs.

Additionally, I've created a draft PR in my own space that resolves this issue for me locally. I have little confidence that this is how you would want the problem solved, but hopefully this will make clear what part of the logic is failing for me: https://github.com/kryptx/amplify-flutter/pull/1/files

I'm willing to take this PR further if you confirm you would accept it (once tests are included along with any other contributing guidelines), but my goal with it was just to prove and clearly illustrate the problem I'm having.

Screenshots

No response

Platforms

  • iOS
  • Android
  • Web
  • macOS
  • Windows
  • Linux

Flutter Version

3.22.0

Amplify Flutter Version

3.4.1

Deployment Method

Amplify Gen 2

Schema

enum CabinType {
  MAIN,
  BUSINESS,
  FIRST
}

enum FlexibilityType {
  NONE,
  DATE,
  AIRPORT
}

enum SearchMode {
  ROUND_TRIP,
  ONE_WAY
}

enum SearchStatus {
  QUEUED,
  IN_PROGRESS,
  COMPLETED,
  ERROR
}

type FlightSearch @model @auth(rules: [
  { allow: owner, provider: oidc, identityClaim: "sub" }
  { allow: private, provider: iam, operations: [read, update]}
]){
  id: ID!
  airportSequence: [String!]!
  departureDates: [String!]!
  cabinTypes: [CabinType!]!
  passengerCount: Int!
  flexibilityType: FlexibilityType!
  status: SearchStatus!
  results: [FlightSearchResult] @hasMany(indexName: "bySearch", fields: ["id"])
}

type Flight {
  departureDate: AWSDateTime!
  arrivalDate: AWSDateTime!
  departureAirport: String!
  arrivalAirport: String!
  cabinType: CabinType!
  flightNumber: String!
  carrierCode: String!
  operatorNumber: String!
  operatorCode: String!
  duration: String!
  distanceMiles: Int!
  aircraftType: String!
}

type TripLeg {
  flights: [Flight!]!
}

type FlightPrice {
  points: Int!
  cash: Float!
  currency: String!
}

type FlightItineraryOffer @model @auth(rules: [
  { allow: private, provider: oidc, identityClaim: "sub", operations: [read]},
  { allow: private, provider: iam, operations: [create, read, update, delete]}
]) {
  id: ID!
  searchMode: SearchMode!
  airline: String!
  cost: FlightPrice!
  departureDate: AWSDateTime!
  returnDate: AWSDateTime
  tripLegs: [TripLeg!]!
  duration: String!
  availableSeats: Int!
  searchKey: String! @index(name: "bySearchKey", sortKeyFields: ["offerKey"])
  offerKey: String!
  searchResults: [FlightSearchResult] @hasMany(indexName: "byOffer", fields: ["id"])
}

type FlightSearchResult @model @auth(rules: [
  { allow: owner, provider: oidc, identityClaim: "sub" },
  { allow: private, provider: iam, operations: [create, read, update, delete]}
]) {
  id: ID!
  searchId: ID! @index(name: "bySearch")
  search: FlightSearch! @belongsTo(fields: ["searchId"])
  offerId: ID! @index(name: "byOffer")
  offer: FlightItineraryOffer! @belongsTo(fields: ["offerId"])
  owner: String!
  legIndex: Int
}
@github-actions github-actions bot added pending-triage This issue is in the backlog of issues to triage pending-maintainer-response Pending response from a maintainer of this repository labels Sep 15, 2024
@Equartey
Copy link
Member

Hi @kryptx, thank you for taking the time to open this issue.

We'll need to take some time to reproduce the error to verify if your change is valid.

We will provide updates here when we have them.

@github-actions github-actions bot removed the pending-maintainer-response Pending response from a maintainer of this repository label Sep 16, 2024
@Equartey Equartey added to-be-reproduced Issues that have not been reproduced yet, but have reproduction steps provided datastore Issues related to the DataStore Category labels Sep 16, 2024
@kryptx
Copy link
Author

kryptx commented Sep 20, 2024

To follow up, I made the following changes in my project:

  • removed the many-to-many association table (I'm now only using sync expressions)
  • updated to the latest flutter
  • fixed a bug (I had two separate DataStorePluginInterface instances, and was treating them as one)

Since those changes, I don't have this problem anymore, on version 2.4.1, flutter 3.24.3. I'm comfortable if you want to close this, in the absence of somebody else having the same problem.

@github-actions github-actions bot added the pending-maintainer-response Pending response from a maintainer of this repository label Sep 20, 2024
@khatruong2009
Copy link
Member

@kryptx do you know which of those changes in your project fixed the issue for you or did you make all those changes around the same time and it seemed to fix on its own?

@github-actions github-actions bot removed the pending-maintainer-response Pending response from a maintainer of this repository label Sep 23, 2024
@khatruong2009 khatruong2009 added the pending-community-response Pending response from the issue opener or other community members label Sep 23, 2024
@kryptx
Copy link
Author

kryptx commented Sep 23, 2024

Hi @khatruong2009, thanks for your reply.

Unfortunately I had set this issue aside and proceeded with development using my forked plugin, and also mostly testing on iOS. I have however tested downgrading to flutter 3.22.0 and confirmed that I'm still unable to reproduce the issue. My app does also still have a @belongsTo/ @hasMany association. So it's either specific to the many-to-many association table, or self-inflicted somehow. I'd note though that even if I had a bug, it was still definitely working on iOS.

@github-actions github-actions bot added pending-maintainer-response Pending response from a maintainer of this repository and removed pending-community-response Pending response from the issue opener or other community members labels Sep 23, 2024
@khatruong2009
Copy link
Member

Hi @kryptx, since you're not having the issue anymore, I'll close it for now. If you, or anyone else, has this issue or another one, please feel free to reopen this issue or create a new one, thanks.

@github-actions github-actions bot removed pending-triage This issue is in the backlog of issues to triage pending-maintainer-response Pending response from a maintainer of this repository labels Sep 24, 2024
Copy link

This issue is now closed. Comments on closed issues are hard for our team to see.
If you need more assistance, please open a new issue that references this one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
datastore Issues related to the DataStore Category to-be-reproduced Issues that have not been reproduced yet, but have reproduction steps provided
Projects
None yet
Development

No branches or pull requests

3 participants