Refactor the computation of experiment config.#6709
Refactor the computation of experiment config.#6709lannka merged 10 commits intoampproject:masterfrom
Conversation
src/experiments.js
Outdated
| let enabled = true; | ||
| if (experimentId[0] == '-') { | ||
| // It is a disabling flag. | ||
| experimentId = experimentId.substr(1); |
There was a problem hiding this comment.
Disable flag here takes precedence over enabled. This doesn't seem to be currently the case.
There was a problem hiding this comment.
In fact, currently there is a bug that can be reproduced by:
AMP.toggleExperiment('-some-experiment', true);
AMP.toggleExperiment('some-experiment', true);
AMP.toggleExperiment('some-experiment', true);
This will give you duplicated entries in the cookie
That's to say, we should probably not have both "some-experiment" and "-some-experiment" in the cookie.
There was a problem hiding this comment.
Possible. But the goal is very straightforward and I believe needed. We need to be able to explicitly disable an experiment that has already been launched. Does this allow us to do that?
There was a problem hiding this comment.
Yes, we still have this feature. You can still disable an experiment by
AMP.toggleExperiment('-some-experiment', true);
My logic will allow you to re-enable it by
AMP.toggleExperiment('some-experiment', true);
The bug I was talking about is a separate issue we should fix.
There was a problem hiding this comment.
After merging with the other refactoring, this is obsolete now. PTAL
test/functional/test-experiments.js
Outdated
| it('should return "off" when disabling value is in the list', () => { | ||
| expectExperiment('AMP_EXP=-e1,e1', 'e1').to.be.false; | ||
| it('should return "off" when disabling value is after enabling', () => { | ||
| expectExperiment('AMP_EXP=-e1,e1', 'e1').to.be.true; |
There was a problem hiding this comment.
Hence here it should be false. Why are you switching it to true?
|
Does |
|
nope. we can hide it. actually, shall we just remove this cache? it's only used by toggleExperiment() function, so i guess there should not be a performance concern? |
|
Let's remove |
|
Removed. PTAL |
dvoytenko
left a comment
There was a problem hiding this comment.
Couple requests, but otherwise LGTM
src/experiments.js
Outdated
| if (win.AMP_CONFIG) { | ||
| for (const experimentId in win.AMP_CONFIG) { | ||
| if (win.AMP_CONFIG.hasOwnProperty(experimentId)) { | ||
| const frequency = win.AMP_CONFIG[experimentId]; |
There was a problem hiding this comment.
We probably should use Object.prototype.hasOwnProperty.call, otherwise I don't think we can guarantee that it has prototype.
test/functional/test-experiments.js
Outdated
|
|
||
| afterEach(() => { | ||
| resetExperimentToggles_(); | ||
| resetExperimentTogglesForTesting(); |
There was a problem hiding this comment.
I'd kind of prefer we call this method in the _init_tests.js's afterEach. Could you please at least try? It might cause too many errors, but I think it should be ok.
|
@tdrl hi Terran, this PR made changes to traffic-experiments.js. would you take a look see if it makes sense to you? |
tdrl
left a comment
There was a problem hiding this comment.
Only substantial issue is that it looks like you've changed the semantics of a number of tests. Other than that, seems like a good cleanup to me.
| // Read the default config of this build. | ||
| if (win.AMP_CONFIG) { | ||
| for (const experimentId in win.AMP_CONFIG) { | ||
| const frequency = win.AMP_CONFIG[experimentId]; |
There was a problem hiding this comment.
Seems not, I added a new test case to make sure.
| if (getMode(win).localDev) { | ||
| // In local dev mode, it can be difficult to configure AMP_CONFIG | ||
| // externally. Default it here if necessary. | ||
| win.AMP_CONFIG = win.AMP_CONFIG || {}; |
There was a problem hiding this comment.
I assume this "AMP_CONFIG in local dev mode" initialization thing is now handled centrally by your refactor? I can't immediately find it.
There was a problem hiding this comment.
As explained below, we let experiments.js to handle all the experiment status read write. No one should access AMP_CONFIG directly. How we store the status in AMP_CONFIG is really an implementation detail, and subject to change.
There was a problem hiding this comment.
All the experiment flags are calculated once, no matter AMP_CONFIG exists or not.
https://github.com/ampproject/amphtml/blob/master/src/experiments.js#L134
| it('handles empty experiments list', () => { | ||
| // Opt out of experiment. | ||
| sandbox.win.AMP_CONFIG['testExperimentId'] = 0.0; | ||
| toggleExperiment(sandbox.win, 'testExperimentId', false, true); |
There was a problem hiding this comment.
This changes the semantics of the test, doesn't it? The previous semantic was, "When AMP_CONFIG says that this experiment has 0 probability, then randomly selection should not turn it on." Here, you're forcing it off via the toggle and then testing to see that it's still off.
There was a problem hiding this comment.
Ditto for the other places where you replace the AMP_CONFIG with a toggle.
There was a problem hiding this comment.
That's exactly the point of this refactoring. We should NOT directly access AMP_CONFIG to read/write experiment status. We should always to it via the methods exposed by experiments.js.
In your unit test here, you don't really care how the experiment is set. You only care if the experiment is on.
There was a problem hiding this comment.
No, I agree that you shouldn't have to touch AMP_CONFIG. But this experiment does do more than just care whether the experiment is on. randomlySelectUnsetPageExperiments does what its name says: it rolls the dice for each requested experiment. The test is verifying that it sets the experiment value as expected given that the experiment probability is a certain value. In this change, you're setting the experiment value before calling randomlySelectUnsetPageExperiments. That method will look and say, "Oh, that experiment already has a value. I won't touch it." So you're no longer testing its behavior, which is really the point of this test.
There was a problem hiding this comment.
If I read the code correctly, the randomness of randomlySelectUnsetPageExperiments is about "branches", which we still have tests covering that. On the other side, AMP_CONFIG['testExperimentId'] = 0.0 is just turning off the experiment, which should have been done via toggleExperiment. Otherwise, it's just testing the logic inside toggleExperiment method.
I could have mis-read the logic here, and that's why to have you to help :-).
| config['fooExpt'] = 0.0; | ||
| toggleExperiment(sandbox.win, 'fooExpt', false, true); | ||
| randomlySelectUnsetPageExperiments(sandbox.win, exptAInfo); | ||
| config['fooExpt'] = 1.0; |
There was a problem hiding this comment.
I will remove this test case if no objection. Since the goal of this PR is to guarantee an immutable AMP_CONFIG (no one should change it), there's no point to have such a test.
@tdrl
|
@tdrl and I didn't reach a consensus on my changes in |
|
LGTM. |
* Refactor the computation of experiment config. * Removed cache for cookie toggles since it's only retrieved once. * Fix type checks * Fix tests * Remove unnecessary change of AMP_CONFIG for local testing. * Call resetExperimentToggleForTesting in _init_tests.js * Remove directly access to AMP_CONFIG for toggling experiments. * Add one more test case to make sure we have the right experiment status map. * Revert changes in test-traffic-experiments.js
* Refactor the computation of experiment config. * Removed cache for cookie toggles since it's only retrieved once. * Fix type checks * Fix tests * Remove unnecessary change of AMP_CONFIG for local testing. * Call resetExperimentToggleForTesting in _init_tests.js * Remove directly access to AMP_CONFIG for toggling experiments. * Add one more test case to make sure we have the right experiment status map. * Revert changes in test-traffic-experiments.js
This is a preparation work for #6684