Moor: watch and watchSingle documentation

Created on 24 Nov 2020  路  2Comments  路  Source: simolus3/moor

I've been scouring the documentation and source code to try establish the inner workings of watch() and watchSingle(), without any success.

Is there a chance of providing some documentation on this on https://moor.simonbinder.eu/docs/ ?

Specifically, I'd like to know what SQL statements are being used (I'm guessing triggers?), what the considerations are for re-use/disposal, and if there is advice on best practice in using these (such as efficiency and if we need to consider regular gets in some instances). I would imagine that using a watch on a large group of tables combined could be relatively expensive/dangerous.

question

All 2 comments

Most moor internals aren't explained on the website because users rarely need to know them. I'd say the source code is fairly well commented though, so if you have issues with that I'm glad to improve readability. The core of stream queries is implemented here. I'm happy to answer your questions though:

'd like to know what SQL statements are being used (I'm guessing triggers?)

We don't use any special SQL features to implement stream queries, that feature is implemented in Dart. Basically,

  • we know the set of tables a query reads from: The high-level query-builder automatically reports this and we have static analysis for SQL queries defined in moor files. For entirely custom queries, you'd have to manually provide that set.
  • we know which tables an update writes to, with the same approach that we use for reads
  • finally, we know how triggers or foreign key constraints might propagate writes that we don't see. For instance, if a table you define in a moor file has a FOREIGN KEY ... ON DELETE CASCADE, we know that a delete on some table can cause a delete on other tables too.

With that information, it's relatively straightforward to implement stream queries. A .watch() basically listens on a set of tables. When a write on any of these tables happens, we run the query again.

what the considerations are for re-use/disposal

If you .watch() the same SQL query (with the same variables) more than once, that has virtually no additional cost. We cache queries and their results, so we won't run the query more often in that case. We dispose a query when there are no remaining listens (a tiny bit later actually, since in Flutter its common for say a StreamBuilder to disconnect and then immediately listen again when setState() is called, we wouldn't want to clear the cache in that case).

such as efficiency and if we need to consider regular gets in some instances

What you should now is that stream updates aren't _exact_, they update more often than they need to. So if you had SELECT * FROM users WHERE id = 3 for instance, the query would invalidate whenever anything is written to the users table. However, I'm not aware of any performance issues caused by stream queries even in large apps with lots of concurrently active streams. Most select statements are really cheap.
If you do have very complex select statements, it can make sense to not .watch() them or implement your own invalidation logic to avoid unnecessary updates. But again, I've never heard of a use-case where that would be necessary.

Thank you for the explanation, that is helpful! I'll add to this issue if I think of anything else in the near future.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

felixjunghans picture felixjunghans  路  4Comments

apoleo88 picture apoleo88  路  3Comments

kira1752 picture kira1752  路  3Comments

simolus3 picture simolus3  路  4Comments

tony123S picture tony123S  路  4Comments