Skip to content

Refactor the computation of experiment config.#6709

Merged
lannka merged 10 commits intoampproject:masterfrom
lannka:experiment-toggle
Dec 22, 2016
Merged

Refactor the computation of experiment config.#6709
lannka merged 10 commits intoampproject:masterfrom
lannka:experiment-toggle

Conversation

@lannka
Copy link
Copy Markdown
Contributor

@lannka lannka commented Dec 16, 2016

This is a preparation work for #6684

let enabled = true;
if (experimentId[0] == '-') {
// It is a disabling flag.
experimentId = experimentId.substr(1);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Disable flag here takes precedence over enabled. This doesn't seem to be currently the case.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

After merging with the other refactoring, this is obsolete now. PTAL

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;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hence here it should be false. Why are you switching it to true?

@jridgewell
Copy link
Copy Markdown
Contributor

Does win._experimentCookie need to be exposed?

@lannka
Copy link
Copy Markdown
Contributor Author

lannka commented Dec 20, 2016

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?

@dvoytenko
Copy link
Copy Markdown
Contributor

Let's remove

@lannka
Copy link
Copy Markdown
Contributor Author

lannka commented Dec 21, 2016

Removed. PTAL

Copy link
Copy Markdown
Contributor

@dvoytenko dvoytenko left a comment

Choose a reason for hiding this comment

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

Couple requests, but otherwise LGTM

if (win.AMP_CONFIG) {
for (const experimentId in win.AMP_CONFIG) {
if (win.AMP_CONFIG.hasOwnProperty(experimentId)) {
const frequency = win.AMP_CONFIG[experimentId];
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We probably should use Object.prototype.hasOwnProperty.call, otherwise I don't think we can guarantee that it has prototype.


afterEach(() => {
resetExperimentToggles_();
resetExperimentTogglesForTesting();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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.

@lannka
Copy link
Copy Markdown
Contributor Author

lannka commented Dec 21, 2016

@tdrl hi Terran, this PR made changes to traffic-experiments.js. would you take a look see if it makes sense to you?
The idea is we should not access AMP_CONFIG directly to toggle an experiment. Instead, we should always do it via toggleExperiment() function.

Copy link
Copy Markdown

@tdrl tdrl left a comment

Choose a reason for hiding this comment

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

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];
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Do you need to check hasOwnProperty?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Seems not, I added a new test case to make sure.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

👍

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 || {};
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I assume this "AMP_CONFIG in local dev mode" initialization thing is now handled centrally by your refactor? I can't immediately find it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Ditto for the other places where you replace the AMP_CONFIG with a toggle.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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;
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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

@lannka
Copy link
Copy Markdown
Contributor Author

lannka commented Dec 22, 2016

@tdrl and I didn't reach a consensus on my changes in test-traffic-experiments.js, hence I reverted the changes and left a TODO for removing the direct access to AMP_CONFIG.

@tdrl
Copy link
Copy Markdown

tdrl commented Dec 22, 2016

LGTM.

@lannka lannka merged commit 78bdf43 into ampproject:master Dec 22, 2016
@lannka lannka deleted the experiment-toggle branch December 22, 2016 23:23
torch2424 pushed a commit to torch2424/amphtml that referenced this pull request Jan 3, 2017
* 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
jridgewell pushed a commit to jridgewell/amphtml that referenced this pull request Jan 31, 2017
* 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
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

Successfully merging this pull request may close these issues.

5 participants