aws-amplify / amplify-js

A declarative JavaScript library for application development using cloud services.
https://docs.amplify.aws/lib/q/platform/js
Apache License 2.0
9.42k stars 2.12k forks source link

[DataStore] Performance decrease v4 > v5 #10766

Open chrisbonifacio opened 1 year ago

chrisbonifacio commented 1 year ago

Before opening, please confirm:

JavaScript Framework

Not applicable

Amplify APIs

DataStore

Amplify Categories

Not applicable

Environment information

``` # Put output below this line ```

Describe the bug

Recently I upgraded our app to aws-amplify version 5.0.5 from 4.3.43 And we noticed quite slower queries for larger data sets.

version 4.3.43 image

version 5.0.5 image

Expected behavior

Similar performance between major versions v4 and v5.

Reproduction steps

Compare performance differences between DataStore.query and DataStore.observeQuery in v4 and v5.

Code Snippet

image

Log output

``` // Put your logs below this line ```

aws-exports.js

No response

Manual configuration

No response

Additional configuration

No response

Mobile Device

No response

Mobile Operating System

No response

Mobile Browser

No response

Mobile Browser Version

No response

Additional information and screenshots

No response

schutzelaars commented 1 year ago

Some more screenshots of logs incoming. The source code is the same, except for the new datSstore v5 syntax. All the queries are DataStore.observeQuery with filtering. The timing includes the wait time for "isSynced" to be true (I made sure the indexedDb was synced before running this test).

V5: image

V4: image

chrisbonifacio commented 1 year ago

Hi @schutzelaars 👋

Thank you for sharing more benchmarks and details with us. We are currently working on optimization and will post updates here.

If you'd like to follow the PR as well: https://github.com/aws-amplify/amplify-js/pull/10734

david-mcafee commented 1 year ago

The PR for this has been merged @schutzelaars, @chrisbonifacio

schutzelaars commented 1 year ago

I ran my test environment again with the latest aws-amplify version, see code snippet above. It seems the update did not improve the query speed. @chrisbonifacio

version 4.3.43 image

version 5.0.14 image

schutzelaars commented 1 year ago

More screenshots of logs incoming. The source code is the same, except for the new dataStore v5 syntax. All the queries are DataStore.observeQuery with filtering. The timing includes the wait time for "isSynced" to be true (I made sure the indexedDb was synced before running this test).

version 4.3.43 image

version 5.0.14 image

Dennis-Dekker commented 1 year ago

+1, also having decreased performance with the latest versions

svidgen commented 1 year ago

@schutzelaars @Dennis-Dekker I'll be spending some time on this. Do you have full schemas you can share that demonstrate the degradation? Is there a scale at which things become noticeably slower in V5?

svidgen commented 1 year ago

@schutzelaars I've been digging into this for almost a full day, running benchmarks and profiling between V4 and V5, looking for differences, measuring some of the specific, new pieces of logic, and I haven't been able to come up with anything that reproduces or explains the fairly massive difference you're seeing.

On 5k records, I'm seeing latencies in the range of ~50 to ~75ms on my heavy tests in a "warm" DataStore. (And around 150ms +/- 1ms on a cold query(Predicate.ALL) in both V4 and V5.) I thought I had a few datapoints to suggest about a 15% increase in some queries, but the runtimes are still so small the profiler isn't showing me anything helpful. And, the difference vanishes for me when I run the samples concurrently, suggesting the difference I saw a red herring.

The results you're seeing, on the other hand, are enormous -- orders of magnitude different. If I could replicate that consistently myself, my profiling efforts might be more fruitful. Can you share your schema and some test data that you know will trigger the issue?

schutzelaars commented 1 year ago

@svidgen


type Location @model @auth(rules: [{ allow: private, provider: iam }, { allow: private }]) {
  id: ID
  organisationId: String! @primaryKey(sortKeyFields: ["locationId", "X", "Y", "Z"])
  locationId: String!
  X: Int!
  Y: Int!
  Z: Int!
  zMax: Int
  type: String!
  name: String!
  userId: String!
  parentLocationIdRef: String! @index(name: "ByParentIdRef", queryField: "LocationByParentIdRef")
  parents: [Location!] @hasMany(fields: ["parentLocationIdRef"])
  childIdRef: String @index(name: "ByChildIdRef", queryField: "LocationByChildIdRef")
  children: [Location!] @hasMany(fields: ["childIdRef"])
  mapTop: String
  mapLeft: String
  mapWidth: String
  mapHeight: String
  itemIdRefs: [String]
  amount: Int
  capacity: Int
  split: Boolean
  splitType: String
  splitNumber: Int
  hasCustomSpots: Boolean
  rowNamingScheme: String
  code: String
}
svidgen commented 1 year ago

Thanks! I'll take a look at what I can do with this slice of your schema.

But, if you have the associated models you can share, that might also help. Also, if you have a test-data generation script you're using, that would also help.

svidgen commented 1 year ago

Whoops! Disregard the request for associated models. My eyeballs didn't catch the fact that they're all just Location. 😅

svidgen commented 1 year ago

My benchmarks with this model didn't yield anything more helpful for me. I'm still seeing only differences on timings much much smaller than you're seeing, @schutzelaars.

@chrisbonifacio I'll touch base with you 1:1 to see what else we can do here.

chrisbonifacio commented 1 year ago

Hey @schutzelaars, following up on @svidgen 's comment. Would you be willing to jump on a call with us to profile your app?

If so, please let me know what email would be okay to reach you at to schedule the call.

Or, if you haven't already joined our community Discord server, please consider reaching out to me (@chris#5450) through there instead: https://discord.gg/amplify

schutzelaars commented 1 year ago
Shema.js

   "Location": {
            "name": "Location",
            "fields": {
                "id": {
                    "name": "id",
                    "isArray": false,
                    "type": "ID",
                    "isRequired": true,
                    "attributes": []
                },
                "organisationId": {
                    "name": "organisationId",
                    "isArray": false,
                    "type": "String",
                    "isRequired": true,
                    "attributes": []
                },
                "locationId": {
                    "name": "locationId",
                    "isArray": false,
                    "type": "String",
                    "isRequired": true,
                    "attributes": []
                },
                "X": {
                    "name": "X",
                    "isArray": false,
                    "type": "Int",
                    "isRequired": true,
                    "attributes": []
                },
                "Y": {
                    "name": "Y",
                    "isArray": false,
                    "type": "Int",
                    "isRequired": true,
                    "attributes": []
                },
                "Z": {
                    "name": "Z",
                    "isArray": false,
                    "type": "Int",
                    "isRequired": true,
                    "attributes": []
                },
                "zMax": {
                    "name": "zMax",
                    "isArray": false,
                    "type": "Int",
                    "isRequired": false,
                    "attributes": []
                },
                "type": {
                    "name": "type",
                    "isArray": false,
                    "type": "String",
                    "isRequired": true,
                    "attributes": []
                },
                "name": {
                    "name": "name",
                    "isArray": false,
                    "type": "String",
                    "isRequired": true,
                    "attributes": []
                },
                "userId": {
                    "name": "userId",
                    "isArray": false,
                    "type": "String",
                    "isRequired": true,
                    "attributes": []
                },
                "parentLocationIdRef": {
                    "name": "parentLocationIdRef",
                    "isArray": false,
                    "type": "String",
                    "isRequired": true,
                    "attributes": []
                },
                "parents": {
                    "name": "parents",
                    "isArray": true,
                    "type": {
                        "model": "Location"
                    },
                    "isRequired": true,
                    "attributes": [],
                    "isArrayNullable": true,
                    "association": {
                        "connectionType": "HAS_MANY",
                        "associatedWith": "organisationId"
                    }
                },
                "childIdRef": {
                    "name": "childIdRef",
                    "isArray": false,
                    "type": "String",
                    "isRequired": false,
                    "attributes": []
                },
                "children": {
                    "name": "children",
                    "isArray": true,
                    "type": {
                        "model": "Location"
                    },
                    "isRequired": true,
                    "attributes": [],
                    "isArrayNullable": true,
                    "association": {
                        "connectionType": "HAS_MANY",
                        "associatedWith": "organisationId"
                    }
                },
                "mapTop": {
                    "name": "mapTop",
                    "isArray": false,
                    "type": "String",
                    "isRequired": false,
                    "attributes": []
                },
                "mapLeft": {
                    "name": "mapLeft",
                    "isArray": false,
                    "type": "String",
                    "isRequired": false,
                    "attributes": []
                },
                "mapWidth": {
                    "name": "mapWidth",
                    "isArray": false,
                    "type": "String",
                    "isRequired": false,
                    "attributes": []
                },
                "mapHeight": {
                    "name": "mapHeight",
                    "isArray": false,
                    "type": "String",
                    "isRequired": false,
                    "attributes": []
                },
                "itemIdRefs": {
                    "name": "itemIdRefs",
                    "isArray": true,
                    "type": "String",
                    "isRequired": false,
                    "attributes": [],
                    "isArrayNullable": true
                },
                "amount": {
                    "name": "amount",
                    "isArray": false,
                    "type": "Int",
                    "isRequired": false,
                    "attributes": []
                },
                "capacity": {
                    "name": "capacity",
                    "isArray": false,
                    "type": "Int",
                    "isRequired": false,
                    "attributes": []
                },
                "split": {
                    "name": "split",
                    "isArray": false,
                    "type": "Boolean",
                    "isRequired": false,
                    "attributes": []
                },
                "splitType": {
                    "name": "splitType",
                    "isArray": false,
                    "type": "String",
                    "isRequired": false,
                    "attributes": []
                },
                "splitNumber": {
                    "name": "splitNumber",
                    "isArray": false,
                    "type": "Int",
                    "isRequired": false,
                    "attributes": []
                },
                "hasCustomSpots": {
                    "name": "hasCustomSpots",
                    "isArray": false,
                    "type": "Boolean",
                    "isRequired": false,
                    "attributes": []
                },
                "rowNamingScheme": {
                    "name": "rowNamingScheme",
                    "isArray": false,
                    "type": "String",
                    "isRequired": false,
                    "attributes": []
                },
                "code": {
                    "name": "code",
                    "isArray": false,
                    "type": "String",
                    "isRequired": false,
                    "attributes": []
                },
                "createdAt": {
                    "name": "createdAt",
                    "isArray": false,
                    "type": "AWSDateTime",
                    "isRequired": false,
                    "attributes": [],
                    "isReadOnly": true
                },
                "updatedAt": {
                    "name": "updatedAt",
                    "isArray": false,
                    "type": "AWSDateTime",
                    "isRequired": false,
                    "attributes": [],
                    "isReadOnly": true
                }
            },
            "syncable": true,
            "pluralName": "Locations",
            "attributes": [
                {
                    "type": "model",
                    "properties": {}
                },
                {
                    "type": "key",
                    "properties": {
                        "fields": [
                            "organisationId",
                            "locationId",
                            "X",
                            "Y",
                            "Z"
                        ]
                    }
                },
                {
                    "type": "key",
                    "properties": {
                        "name": "ByParentIdRef",
                        "queryField": "LocationByParentIdRef",
                        "fields": [
                            "parentLocationIdRef"
                        ]
                    }
                },
                {
                    "type": "key",
                    "properties": {
                        "name": "ByChildIdRef",
                        "queryField": "LocationByChildIdRef",
                        "fields": [
                            "childIdRef"
                        ]
                    }
                },
                {
                    "type": "key",
                    "properties": {
                        "name": "gsi-Location.parents",
                        "fields": [
                            "organisationId"
                        ]
                    }
                },
                {
                    "type": "key",
                    "properties": {
                        "name": "gsi-Location.children",
                        "fields": [
                            "organisationId"
                        ]
                    }
                },
                {
                    "type": "auth",
                    "properties": {
                        "rules": [
                            {
                                "allow": "private",
                                "provider": "iam",
                                "operations": [
                                    "create",
                                    "update",
                                    "delete",
                                    "read"
                                ]
                            },
                            {
                                "allow": "private",
                                "operations": [
                                    "create",
                                    "update",
                                    "delete",
                                    "read"
                                ]
                            }
                        ]
                    }
                }
            ]
        },
schutzelaars commented 1 year ago

Any developments concerning this issue? @svidgen @chrisbonifacio