-
Notifications
You must be signed in to change notification settings - Fork 22
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
Access kolibri's job storage database to reconcile tasks #179
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is all making sense to me so far, even if the precise details escape or confuse me on occasion!
* @param stateRef The job status in the Kolibri database for which to find jobs | ||
* @return A future that will complete when all jobs have been checked, with a list of jobs | ||
*/ | ||
public CompletableFuture<Bundle[]> check(StateMap stateRef) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I definitely find reading overloaded implementation definitions a bit confusing - but also get why it's useful, I am just unused to it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I definitely went a little overboard with the method overloading, but it is one of my favorite features. I like it because you can break down larger functions into smaller bits and tie them all together by overloading the name. It's also helpful for making optional parameters of course
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I understand what is happening, and it makes sense to me. The sequential processing does seem like a sure fire way of stopping weird concurrency issues.
CompletableFuture<List<Result>> chain = CompletableFuture.completedFuture(allResults); | ||
|
||
for (Bundle job : jobs) { | ||
chain = chain.thenComposeAsync((results) -> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, am I reading it correctly that now each job is being processed sequentially here, whereas previously there could be parallel processing of individual jobs?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep! The thenComposeAsync
accepts a callback that itself returns another future, sort of like promise chaining, making everything process sequentially. This particular change seemed to provide the most improvement, and it makes the logs easier to read too.
My only worry was how we managed the DB connection without a try-with resources statement, but rather rely on the future completing in order to close the connection. CoPilot indicated there could be issues mixing futures and the try-with. I was also happy that CoPilot had nothing to say other than 'no issues could be detected and the code is well structured [....]'. With the addition of some timeouts, it should also prevent any stalling should it re-occur.
future.addListener(() -> { | ||
if (future.isCancelled()) { | ||
Log.i(TAG, "Interrupting python thread"); | ||
threadFuture.cancel(true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just some minor simplification here as I learned more about how these futures and runnables work.
980074d
to
ed1d5e5
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Squinting, I think I get the high level of the diff here. I am going to check it out locally, to read through the code to make sure I have a good picture in my head.
My hope is that some of this code can get trimmed somewhat once we move to chaquopy, so this diff is a bit of a bump that won't be completely permanent.
…ointer when called from a Python context.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are a couple of issues from manual QA to follow up on here - but it's not clear where the issues lie just yet, and this PR is big enough and robust enough for us to merge this and iterate further, here, and in Kolibri.
Summary
job_storage.jobs.worker_*
Task.java
into a shared utilityjob_storage.jobs
tableSentinel
class which can cross reference jobs in Kolibri'sjob_storage
database with Work Manager's tasksReconciler
class which can process results fromSentinel
and re-queue tasks for any issues observedTask.reconcile
static method that usesSentinel
andReconciler
to reconcile tasksReconcileWorker
which processes task reconciliationWIP / TBD
Reconciler
implements exclusive locking, as I intended for reconciliation to be able to run both in a task and at startup of an app process, but currently it's only running in the task, pending results of QAReconciler
implements a database transaction, but that is currently commented out, as it encountered a 'database is locked' error too many times