GetDutchie / brick

An intuitive way to work with persistent data in Dart
https://getdutchie.github.io/brick/#/
371 stars 31 forks source link

When remote update local does not receive update #480

Closed richard457 closed 1 day ago

richard457 commented 4 days ago

Also when there is change from remote it does not update local

 final repository = brick.Repository();
    final query = brick.Query(where: [
      brick.Where('businessId').isExactly(businessId),
      brick.Where('paymentCompletedByUser').isExactly(true),

    ]);
    final result = await repository.get<models.Plan>(
        query: query, policy: OfflineFirstGetPolicy.awaitRemote);

cc @tshedor

tshedor commented 4 days ago

@richard457 I don't entirely understand your question, but I'm going to assume it's "When my Supabase table has an INSERT, why doesn't my app sync with the new row?" Check out the Realtime section of the docs; you'll need to use subscribeToRealtime instead of get

richard457 commented 3 days ago

so this means get will always get local change if we have not called MyRepository().subscribeToRealtime(); ? my assumption was that if I call get on app start and pass in awaitRemote it will fetch the row from remote and update the local copy.

tshedor commented 3 days ago

I'm sorry, I don't understand your question. Could you please rephrase it in simple, step-by-step expectations. The same way you would as if you're giving navigation directions.

The get request does retrieve from remote. It does not update automatically. If it is not retrieving from remote, make sure that your local debugger is throwing on all Uncaught exceptions. I'm very confident that this works in Brick, and it has extensive unit test coverage. The error is most likely in discrepancies between your remote and local model definition.

richard457 commented 2 days ago

Here's my question, explained in as much detail as possible:

  1. Understanding OfflineFirstGetPolicy.awaitRemote:

    • From my interpretation of the comment, OfflineFirstGetPolicy.awaitRemote:
      Ensures results must be updated from the remote provider(s) before returning if the app is online.
      An empty array will be returned if the app is offline.

      This suggests that when the app is online, it will check the remote provider for any updates before returning results. If no updates are available, it will fall back on the local data.

  2. Expected Behavior Example:

    • Let's say on the remote server I have {"id": 1, "money": 30}, while locally I have {"id": 1, "money": 29}. If I use OfflineFirstGetPolicy.awaitRemote while online, the result I expect is {"id": 1, "money": 30}—essentially the latest data from the remote source.
  3. Limitations of Using subscribeToRealtime:

    • Although I understand that subscribeToRealtime could be used to ensure I always receive real-time updates, I prefer not to use streams. Using multiple subscribeToRealtime calls can lead to increased costs on Supabase, which I would like to avoid.
tshedor commented 2 days ago

Thank you for the additional detail. The expected behavior you've described is accurate. What is the actual behavior?

richard457 commented 2 days ago

The actual behavior is that I always get a local copy and when the respective row on supabase changes it does not reflect. to the local copy.

tshedor commented 2 days ago

@richard457 I've verified that the actual behavior is working as expected. I changed a text column's value in Supabase and when I re-ran the get with awaitRemote, the new value was immediately available. The get request does not have a query.

Some troubleshooting steps:

richard457 commented 2 days ago

I have tried

Future<PaymentPlan?> getPaymentPlan({required int businessId}) async {
    try {
      final repository = brick.Repository();
      // repository.reset();
      final query = brick.Query(where: [
        brick.Where('businessId').isExactly(businessId),
        brick.Where('paymentCompletedByUser').isExactly(true),
      ]);
      final result = await repository.get<models.Plan>(
          query: query, policy: OfflineFirstGetPolicy.awaitRemote);
      return result
          .map((e) => PaymentPlan(
                businessId: e.businessId,
                selectedPlan: e.selectedPlan,
                additionalDevices: e.additionalDevices,
                isYearlyPlan: e.isYearlyPlan,
                totalPrice: e.totalPrice?.toDouble(),
                createdAt: e.createdAt,
                paymentCompletedByUser: e.paymentCompletedByUser,
                payStackCustomerId: e.payStackCustomerId,
                rule: e.rule,
                paymentMethod: e.paymentMethod,
              ))
          .firstOrNull;
    } catch (e) {
      talker.error(e);
      rethrow;
    }
  }

but there is no error reported

  1. Internet was available, e.g I can call HTTP API
  2. I deleted cache and called Repository.reset() as well
  3. I have verified that I was editing the right table
  4. Also confirmed by calling upsert which updated the table on supabase from local
richard457 commented 2 days ago
image
richard457 commented 2 days ago
image
richard457 commented 2 days ago

Looking at the query beig send is

Query ({"action":0,"providerArgs":{},"where":[{"subclass":"Where","evaluatedField":"businessId","compare":0,"required":true,"value":1},{"subclass":"Where","evaluatedField":"paymentCompletedByUser","compare":0,"required":true,"value":true}]})

A question arise I can see it is using e.g paymentCompletedByUser instead of payment_completed_by_user is this normal? because the column at supabse is payment_completed_by_user not paymentCompletedByUser etc..

richard457 commented 2 days ago

Also to test I create new sqlite db to see if I will receive remote data in

 _singleton = Repository._(
      supabaseProvider: provider,
      sqliteProvider: SqliteProvider(
        'db_v10.sqlite',
        databaseFactory: databaseFactory,
        modelDictionary: sqliteModelDictionary,
      ),
      migrations: migrations,
      offlineRequestQueue: queue,
      memoryCacheProvider: MemoryCacheProvider(),
    );

and calling

 await repository.upsert(models.Plan(
      id: businessId,
      businessId: businessId,
      selectedPlan: selectedPlan,
      additionalDevices: additionalDevices,
      isYearlyPlan: isYearlyPlan,
      totalPrice: totalPrice.toInt(),
      createdAt: DateTime.now(),
      payStackCustomerId: payStackUserId,
      paymentMethod: paymentMethod,
      paymentCompletedByUser: false,
    ))

will upsert the remote supabase with update but calling .get after upsert does not return same document, also I realize upsert does create another row in local sqlite even when it is same ID given probably because it has brick_id in it but I think upsert should not create new row in local db

tshedor commented 2 days ago

I have tried

This code is very useful, thank you for sharing it and for formatting it with dart. A few things stand out:

A question arise I can see it is using e.g paymentCompletedByUser instead of payment_completed_by_user is this normal? because the column at supabse is payment_completed_by_user not paymentCompletedByUser etc..

This is fine because Brick does the conversion. It tracks the names of Supabase columns and SQLite columns in a map keyed by the Dart field name

richard457 commented 1 day ago

I was able to get the code to work I can summarize the problem like this:

  1. I started the model and iterated over several versions as it was the first time experimenting with brick
  2. I ended up with different migration files
  3. for some reason the query did not work after several iterations e.g adding @Sqlite(unique: true) @Supabase(unique: true) final int? id; Then I deleted all generated migration files they were about 8 files then I did run runner again, after delete the query worked as I expected.
hortigado commented 1 day ago

Hello, although deleting migration files usually solves problems, you should be careful when the app is already released to the public because you cannot delete a migration that a user has already performed because it will be relaunched and generate errors that can only be solved by deleting the app cache.

tshedor commented 1 day ago

Glad to hear the problem is resolved.

And thanks @hortigado , you continue to provide useful and accurate information on all the issues you comment on. It's much appreciated.