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

When object returned by 'use' is mutated before, 'try' returns. Hence comparison is sabotaged. #10

Open
soswow opened this issue Jun 1, 2016 · 4 comments

Comments

@soswow
Copy link

soswow commented Jun 1, 2016

If subj is not enough:
we have 'use' and 'try' functions returning promises (so, async = true). I'll call these funcs as main and secondary.
There is Function consumer that uses main. I am introducing parallel call, so now consumer uses result of science() call.
Consumer mutates object A after promise from main is resolved. But secondary hasn't been resolved yet.
Secondary is resolved with object B and scientist grabs it to compare with A (which at this point has been mutated already). Result is - false positive mismatch.

We currently solve it by cloning deep object A on the way from science().

return science(name, function (experiment) {
// ... 
}).then(function(result) {
    return _.cloneDeep(result);
});

I thought you might be interested in making some general solution since our scenario seems quite real.

@dpatti
Copy link
Contributor

dpatti commented Jun 2, 2016

The general solution I can think of is to have the science() call return a promise that resolves with the control result only after the experiment has been completely finished. My first feeling is to put this behind a secondary option, such as experiment.async(true, { blockOnResult: true }) for a few reasons.

  1. There are potential performance-related issues delaying the control result, but I think this is a weak argument since those already exist for synchronous experiments.

  2. We might only be able to return our internal (bluebird) promises. I haven't tried it, but it's possible we could do something like this:

    return control.evaluation().then((result) => eventToPromise(@, 'result').return(result));

    to force the result to pause but still be returned using the author's promise library of choice.

  3. We would need to dictate that all behaviors (or at least the control) in async experiments return a promise, which we don't do now. You could call your experiment "async" and still return 1, which would get returned synchronously. It feels tricky to just wrap that in a promise without the author's knowledge, since that could definitely break the rule of "science() always returns what the control returns"

As I wrote these points out, it feels like this is a more consistent async-mode behavior, but definitely not possible to bake in until a major version bump. I'm thinking we can go with a flag for now and roll it in if it still feels right.

@DavidTPate
Copy link
Contributor

Would this be something where Object.assign() would be beneficial?

Prior to invocation the incoming arguments could be "cloned" to address this. This would mean a performance delay there, but I'm not sure the impact.

@dpatti
Copy link
Contributor

dpatti commented Jun 6, 2016

I don't think so. The library has no way to see incoming values – only the result values produced by the behaviors. We still can't naively clone those because, well, cloning is hard in JavaScript. The result might be (or contain) an array, a date, or an object with a non-trivial prototype chain. I think the best solution is to just make guarantees for the user instead of allowing them to introduce a race condition.

@Jascha-Sundaresan
Copy link

Is anybody still maintaining this project?

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

No branches or pull requests

4 participants