Skip to content
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

added method to job to get PromiseValue using handle #14

Merged
merged 11 commits into from
May 12, 2015

Conversation

billy1380
Copy link
Contributor

No description provided.

@@ -385,6 +386,15 @@ private void setCurrentRunGuid(String guid) {
updateSpec.getNonTransactionalGroup().includeSlot(promisedValue.getSlot());
return promisedValue;
}

public <F> PromisedValue<F> promise(String promiseHandle) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

javadoc.
Also, I wonder what is your use case? PromisedValue only expose its handle, so the only value that I can think
of is if you want to "pass around" the Value to deeply nested children and make their run depends on it.
I am pretty sure this is not going to work correctly if you try to use it with different root jobs (so maybe a more elegant form of passing [not by handle] that would only work between parent to child would be preferred).
In any case, I think you should provide tests for it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My use case is described in issue #13 and you are not far off.

I have no intention of using the promised value across root jobs, so I have no problem with adding that as a restriction.

Updates soon...

@billy1380
Copy link
Contributor Author

I will try to submit an update very soon, thanks for the feedback.

* that will be provided asynchronously by some external agent. This can be
* used to share the same value with child {@link Job}s
*
*
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit, one empty line is enough.

@aozarov
Copy link
Contributor

aozarov commented Feb 14, 2015

Looks good. Please add tests and it should be ready for merge.

* @param promiseHandle Key to find the {@link Slot}
* @return A {@link Slot} if the handle can be resolved otherwise <code>null</code>.
*/
public static Slot getPromisedValueSlot(String promiseHandle) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really need this method? Can dealing with the exception be part of Job?
This method would mask InterruptedException.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you are worried about masking the exception I can follow the example of acceptPromisedValue and throw NoSuchObjectException and OrphanedObjectException, but the method itself is required because queryPromisedValueSlot is private and I am not sure I should change it's scope.

Edit (note): if I make it throw the exceptions then I will probably have to throw them in promise() which does not seem to be done by any other job methods.

@billy1380
Copy link
Contributor Author

I have added unit tests and applied all the comments where possible. Please review.


/**
* Invoke this method from within the {@code run} method of a <b>generator
* job</b> in order to get some value that has been previously declared and
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to get a Promised Value that was created by an ancestor job?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is definitely clearer... but... this is the language that the rest of the javadoc uses.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am fine with such inconsistency (though maybe we can phrase it in a slightly more consistent way).
Basically, I think we should make it clear that it should not be used across Jobs.

- updated javadoc language to highlight ansestor constraint for promises
@billy1380
Copy link
Contributor Author

updated @aozarov

futureCall(new FillPromiseJob(), immediate("3"),
immediate(promise.getHandle()));
FutureValue<String> child4 = futureCall(new StringAdderJob(), child1,
child2);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit, I don't think the wrap is necessary here (max width 100)

@aozarov
Copy link
Contributor

aozarov commented May 11, 2015

Thank you. Nice work. few minor comments and should be ready for merge.

billy1380 added 2 commits May 12, 2015 09:35
- added wait setting to subsequent jobs in testFilledPromiseFromHandle
- return boolean from PromiseFromNonExistentHandleParentJob and
asserting true
@billy1380
Copy link
Contributor Author

@aozarov applied comments.

aozarov added a commit that referenced this pull request May 12, 2015
added method to job to get PromiseValue using handle
@aozarov aozarov merged commit 574c75c into GoogleCloudPlatform:master May 12, 2015
@aozarov
Copy link
Contributor

aozarov commented May 12, 2015

Merged. Thank you!

@billy1380
Copy link
Contributor Author

@aozarov
Copy link
Contributor

aozarov commented Jul 26, 2015

Hey @billy1380.
Thank you for your quick response and your willingness to help resolving this issue.

Indeed it feels like testUnfilledPromiseFromHandle is covering the second use-case I described (passing a filled-slot that will never trigger SlotFilled and, possibly, blocked Job[s]) but
the reason this test is passing is because pipeline is, shamefully, not trying to optimize on
task-queue calls. Basically when you pass an immediate as param pipeline will treat it
as a filled slot and register a SlotField task. However when you register a FutureValue
it does not register a SlotField task as it expect that task to be triggered when the
slot get fields.

A SlotField task is the task that checks all the Jobs (run barrier) associated with a Slot
and triggers a RunJob task for every Job that all its other Slots are filled.

If you change testUnfilledPromiseFromHandle and remove the extra immediate arguments
you should be able to reproduce this issue (and the test should never complete).
Here is a modified version that you can try:

  public void testFilledPromiseFromHandle_noArgs() throws Exception {
    PipelineService service = PipelineServiceFactory.newPipelineService();
    String pipelineId = service.startNewPipeline(new FilledPromiseFromHandleParentJobNoArgs());
    String value = waitForJobToComplete(pipelineId);
    assertEquals("13", value);
  }

  @SuppressWarnings("serial")
  private static class FilledPromiseFromHandleParentJobNoArgs extends Job0<String> {

    @Override
    public Value<String> run() throws Exception {
      PromisedValue<String> promise = newPromise();
      WaitForSetting wait = waitFor(futureCall(new FillPromiseJob(), immediate("3"),
          immediate(promise.getHandle())));

      return futureCall(new PromiseDelegateJobNoArgs(), immediate(promise.getHandle()), wait);
    }
  }

  @SuppressWarnings("serial")
  private static class PromiseDelegateJobNoArgs extends Job1<String, String> {
    @Override
    public Value<String> run(String handle) {
      PromisedValue<String> promise = promise(handle);
      return futureCall(new StringAdderJobNoArgs(), promise);
    }
  }

  @SuppressWarnings("serial")
  private static class StringAdderJobNoArgs extends Job1<String, String> {
    @Override
    public Value<String> run(String value) {
      return immediate("1" + value);
    }
  }

We could change PipelineManager to treat PromisedValue similar to immediate if it was field
but then we will face the second issue (that for me is highly reproducible using the w test) where we get a race-condition between reading
a state of a Slot and updating the Slot. This issue could have never happened before
as the PromisedValue Slot binding to a Job was fixed before it could ever be filled.

In my testUnfilledPromiseFromHandle I could clearly see this race conditions (via some printouts)
where StringAdderJob is being registered with a stale copy of the PromisedValue's slot (slot is
marked as yet unfilled) but then before it is saved it gets filled by FillPromiseJob.
BTW, the saving after the JobFilled could 'revert' the filled value (and effect other dependent jobs).
One way to deal with this issue is to treat PromiseValue's slot specially and transactionally verify
it wasn't change before saving it.

@aozarov
Copy link
Contributor

aozarov commented Jul 27, 2015

Hey @billy1380, I plan to revert this change but it should be easy to re-apply it once you have the fix.
Thank you.

@billy1380
Copy link
Contributor Author

Go for it... I have picked up the latest version before the rollback, ran both tests and can recreate the issue quite consistently.

Will keep you posted.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants