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
Feature Single-id to handle jobs that should only run once #82
Conversation
…es to keep persisted jobs
JobManager checks if there is already a non-running Job in the proper queue and returns the id of that Job if found. onAdded is still called so that data can be saved in local database.
…r to avoid repeated jobs
Thanks a lot for the PR. I have not yet carefully reviewed it yet but there is an unfortunate problem :/. This will simplify the flow control within the JobQueue. Current architecture makes it really hard to add new features while avoiding race conditions. If we merge this now, I won't be able to rebase 2.0 because it touches many files. Thanks a lot and sorry for this inconvenient issue. |
Yes, please review the current implementation and let me know what you think. Any ETA on the 2.0 (at least to the point where I can re-implement this on it) or any part of it where you would use some help? |
Thanks for understanding, I'll take a look at this. I'll ping this thread once it is ready. Btw, about this implementation, have you tried implementing it on top of tags/groups instead of adding a new field? I've not thought about this thoroughly so maybe not feasible but I think it should be. |
It should be feasible with the tags. I was thinking of adding a tag with a prefix like "job-queue-single-id:userSelectedId". Maybe leaving the singleId field in Params and adding the tag in the Job constructor. Both ways seem OK to me. Another thing I was considering was adding a new onAdded method and deprecating the original (like shouldReRunOnThrowable). This method would receive some JobInfo as parameter and return a RunConstraint. Based on the JobInfo, the client could decide if they want onRun to be called or if they want a previous job not to be run. That would require more discussion on what the JobInfo should include. Maybe the id, tags, and delayUntilNs for all Jobs in the same group? Do you think it's worth it to explore that route. |
I think it is fine to keep a field in the Params object but I was thinking that we can use the already existing JobQueue#tag API to store the information internally.
I think user should also clarify the behavior when there is a job running vs waiting + do we cancel the previous job or the new one. If the behavior is to cancel the existing job, then JobManager has to wait until that running job resolves to something before adding the new one otherwise we may add the new one and right after if the app crashes, both jobs will stay in the database (if they are persistent). Maybe, for simplicity, we can start with saying that the new job will not be added if there is an existing one. Also, is there a particular reason why you are still calling onAdded on the new job even if there is an existing one? I don't have strong opinion on it but I wanted to know if there is a use case (like dispatching a loading event) |
@@ -4,7 +4,7 @@ buildscript { | |||
} | |||
|
|||
dependencies { | |||
classpath 'com.android.tools.build:gradle:1.3.0' | |||
classpath 'com.android.tools.build:gradle:1.5.0' |
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.
already done in the message-queue branch
Yes, that's what I was trying to say with keeping the field in the Params. The use case that I was thinking of when I decided to always call onAdded was that you might be editing some resource while offline and you only need the last change to be sent. For example, let's say that you are editing the profile name with a Job like this:
As you can see, this would make sure that many changes can be stored locally, and once there's internet connection, the latest change will go to the server. I'm thinking now that maybe it would make sense to add an
I was thinking that if the previous job is already running, it is safer to err on the side of adding it anyways. Consider the example above of PostNameJob. If we run the job twice, we waste some bandwidth, but it's not such a big deal, whereas not making the request is a big problem. It would be better to wait until the job resolves to something, as you said, but my "simplicity" approach was to add it anyways. I assume that the jobs where you would use the single instance are the kind of jobs where it's not a big deal if it runs twice (like PostNameJob or like FetchTweetsJob), but maybe you have a different use case in mind. |
OK for all your line notes |
Hmm I agree on the use case, we should definitely support it. A case like this will require waiting for the running job to finish or cancel though, otherwise, it may create unexpected inconsistencies because Actually, the right way to implement that job would be reading the parameters from the Job not database. Maybe we should consider a special job to handle these cases that takes care of such synchronization issues. Think about it (i'll do so as well). If we can provide a solid way for these, I think it is worth having a separate API. Btw, I implemented a first pass on JobQueue constraint change. It removed some optimizations but can be re-added. I'm hoping that queues can implement caching using its uniqueId although in current form it includes the time which will surely invalidate any cache. I'll take a look at making it cache-able later. Here is a generic implementation for constraints handling in sqlite job queue: Line 288 in dfc3b3f
And here is an in memory implementation: Lmk what you think. |
Ok, here is a proposal (very similar to your suggestion). In terms of behavior:
I think this is the most complete cycle from the Job's perspective. It runs like a regular job, still receives all callbacks (e.g. In terms of internal implementation, I think we should keep it as a parameter in the Btw, I've just added query caching to sqlite queue as well so I think it is at a stage where you can add this feature. |
Looks good to me. At first I didn't like the idea of calling I'll do this during the week. Are the tests also good now? |
yea, i was not very comfortable w/ that either but we have to call onAdded (in case it is doing database changes) so to complete the lifecycle we need the Btw, we should start sending cancel reason to the Tests should be fine, only the RetryTest is a bit flaky, i didn't have time to look at it yet. Thanks. |
I'm more or less done, just missing the "cancel reason", but I won't finish today. Should I update the PR (will include all your message-queue commits) or make a new one based on message-queue? |
It would be better to have it based on message-queue branch (it is like 2.0 branch now). |
Closing this since it's been rebased on new branch. |
This resolves path#51 and addresses part of what was requested in #36 .
When building a job, you can pass a singleId Param, so that if another Job with the same singleId is already queued, they will only run once. This avoids having to manually use static atomic variables or other kinds of locks. It also improves from what was suggested in path#51 in that onAdded() will always be called, so you can do database operations in the JobManager's Executor (if using addJobInBackground). The feature covers the KEEP_OTHER scenario.
Some details: