Status Update
Comments
ra...@google.com <ra...@google.com> #2
I am able to also reproduce issue #1 now; I've committed a change to the sample project that sets enableMultiInstanceInvalidation()
on the DB and adjusts the timing a bit more. After running it for 15 minutes or so I am seeing multiple skipped updates.
My production app uses enableMultiInstanceInvalidation()
as well, so this may be a significant factor.
ap...@google.com <ap...@google.com> #3
Any thoughts on this one? I mean, it makes JournalMode.TRUNCATE largely useless, unless one has a penchant for random app misbehaviors. I'd like to keep using TRUNCATE, as it avoids certain headaches from WRITE_AHEAD_LOGGING.
ra...@google.com <ra...@google.com> #4
To be clear, "batching" does not take care of the missed invalidation notifications. Updates for a table may be spaced hours apart, and in my production app about 5% of notifications are lost, meaning that the user does not see the update until the next one comes in, which may be hours later. It's a chatroom feature, where reliable, timely updates are important.
Also, not setting enableMultiInstanceInvalidation() does not fix the issue, it only changes the timing; notifications go missing regardless of whether I include one process or multiple.
ap...@google.com <ap...@google.com> #5
Hi - Sorry, we haven't had the chance to investigate this. I know this might be a lot to ask and thanks for giving us a sample app, but have you try adding a transaction in the invalidation tracker? You can check out Room's source code here:
Also what headaches are you trying to avoid from WAL mode?
ro...@gmail.com <ro...@gmail.com> #6
Thanks for the pointer. I've built Room from source now as you suggested and do see that the problem goes away once I use the transactionality block in InvalidationTracker.mRefreshRunnable for all JournalModes. Don't know why that wasn't done in the first place, it certainly is a misconception to assume that TRUNCATE does not need it.
Regarding headaches from WAL mode:
- Android's SqliteDatabase runs TRUNCATE transactions effectively serializable due to grabbing exclusive locks right at the start of a transaction, so I can worry less about transactions failing, at the cost of reduced concurrency. My app isn't that heavy on DB ops, so favoring reliability over concurrency makes lots of sense. I'd actually assume that this is true for most Android apps, and that TRUNCATE would make a better default, not fancy WAL.
- WAL has certain other disadvantages, as pointed out here:
https://sqlite.org/wal.html In particular the risk of extended failure from SQLITE_BUSY is high for my app as it has DB connections from two processes (https://sqlite.org/wal.html#busy ); of particular concern is the recovery case after a crash, where one process may hold an exclusive lock for an extended period of time at startup, essentially causing the other process to error out without much recourse.
Hope this can be fixed soon in an official build; seems a trivial change.
an...@google.com <an...@google.com> #7
ro...@gmail.com <ro...@gmail.com> #8
Yes please! It would be ideal if you can write a test for this though, in the test app specifically:
Similar to your sample app, open a DB in TRUNCATE mode, have a thread that inserts a lot and another one reading notifications and at the end make sure the amount of notifications received match the amount of items inserted.
su...@google.com <su...@google.com> #9
ra...@google.com <ra...@google.com> #10
just to clarify on #8, we cannot count the number of invalidation events as database might combine them. Instead, we need to make sure latest value is always eventually dispatched.
Description
Version used:2.1.0
Devices/Android versions reproduced on:MINIX/Android 5.1.1
If this is a bug in the library, we would appreciate if you could attach:
07-23 15:02:22.508 1042-1127/E/WM-SystemAlarmDispatche: Unexpected error in onHandleIntent
java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask@2713e108 rejected from java.util.concurrent.ScheduledThreadPoolExecutor@63770a1[Terminated, pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 0]
at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2011)
at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:793)
at java.util.concurrent.ScheduledThreadPoolExecutor.delayedExecute(ScheduledThreadPoolExecutor.java:298)
at java.util.concurrent.ScheduledThreadPoolExecutor.schedule(ScheduledThreadPoolExecutor.java:503)
at java.util.concurrent.Executors$DelegatedScheduledExecutorService.schedule(Executors.java:644)
at androidx.work.impl.background.systemalarm.WorkTimer.startTimer(WorkTimer.java:82)
at androidx.work.impl.background.systemalarm.DelayMetCommandHandler.onAllConstraintsMet(DelayMetCommandHandler.java:135)
at androidx.work.impl.background.systemalarm.DelayMetCommandHandler.handleProcessWork(DelayMetCommandHandler.java:211)
at androidx.work.impl.background.systemalarm.CommandHandler.handleDelayMet(CommandHandler.java:272)
at androidx.work.impl.background.systemalarm.CommandHandler.onHandleIntent(CommandHandler.java:171)
at androidx.work.impl.background.systemalarm.SystemAlarmDispatcher$1.run(SystemAlarmDispatcher.java:272)
at androidx.work.impl.utils.SerialExecutor$Task.run(SerialExecutor.java:75)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
at java.lang.Thread.run(Thread.java:818)
07-23 15:02:22.963 1042-1055/com.yantranet.signware.agent.staging A/PowerManager: WakeLock finalized while still held: WorkManager: 72a8dc67-9b97-430d-9fc9-876f7e7c836c (2)
I need to run work request on-demand for a list of workers. The on-demand request can come in multiple times. So I basically loop the requests and create OneTimeWorkRequest for each Worker. I end up with the Exectuor RejectionException as above.