Uploaded image for project: 'Nuxeo Platform'
  1. Nuxeo Platform
  2. NXP-23374

Allow to prevent a job from being scheduled several times

    XMLWordPrintable

    Details

      Description

      In some cases, we want to not schedule a work if it is already scheduled or running.

      Typically when asking for the same lazy rendition computation twice in a row on an unchanged document, the first call should schedule a rendition work, the second one shouldn't.
      The current code in AbstractLazyCachableRenditionProvider tries to avoid this by doing:

      String workId = work.getId();
      WorkManager wm = Framework.getService(WorkManager.class);
      if (wm.find(workId, null) == null) {
          wm.schedule(work, Scheduling.IF_NOT_SCHEDULED);
      }
      

      See https://github.com/nuxeo/nuxeo/commit/e67cf15d06257a90df1bd78aaaa2ed5761cae9db and NXP-20716 for details.

      Yet, there are at least 2 issues:

      1/ We cannot rely on the work state

      Introspect works to deduce a state or a result isn't good:

      • There can be some race conditions.
      • It's not a work's function to keep a state or a result.
      • This is not supported on Stream WorkManager

      So we shouldn't be using WorkManager#find(String workId, State state)
      that relies on WorkQueuing#find(String workId, State state), thus on:

      • Work#getWorkInstanceState() in memory
      • RedisWorkQueuing#isWorkInState(String workId, State state) with Redis

      2/ The current WorkManager API bypasses the scheduling parameter

      Indeed, in WorkManagerImpl#schedule(Work work, Scheduling scheduling, boolean afterCommit), the work is scheduled in any case when using IF_NOT_SCHEDULED or IF_NOT_RUNNING_OR_SCHEDULED:

      case IF_NOT_SCHEDULED:
      case IF_NOT_RUNNING_OR_SCHEDULED:
          // TODO disabled for now because hasWorkInState uses isScheduled
          // which is buggy
          boolean disabled = Boolean.TRUE.booleanValue();
          if (!disabled && hasWorkInState(workId, scheduling.state)) {
              if (log.isDebugEnabled()) {
                  log.debug("Canceling schedule because found: " + scheduling);
              }
              return;
      
          }
          break;
      
      }
      queuing.workSchedule(queueId, work);
      

      Possible solution

      Use the Key/Value store to save the scheduled and running jobs with a temporary value, "processing" for instance, and let the job remove the key when it's completed.
      We should use a short TTL.

      Let's try to write a standard pattern for the lazy renditions so that it serves as a global example.

        Attachments

          Issue Links

            Activity

              People

              • Votes:
                0 Vote for this issue
                Watchers:
                5 Start watching this issue

                Dates

                • Created:
                  Updated: