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

Introduce AMP Live List Cache Busting #25295

Merged
merged 22 commits into from
Nov 4, 2019
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
cfc797f
Move from media 'not-blocking' to 'print'
kristoferbaxter Jun 24, 2019
7c9902e
latest master
kristoferbaxter Jul 16, 2019
e1eb02c
Merge branch 'master' of github.com:ampproject/amphtml
kristoferbaxter Aug 6, 2019
3a5465a
Merge branch 'master' of github.com:ampproject/amphtml
kristoferbaxter Aug 15, 2019
870b22f
Merge branch 'master' of github.com:ampproject/amphtml
kristoferbaxter Aug 16, 2019
e81a2d1
font stylesheet changes
kristoferbaxter Aug 16, 2019
e073300
Merge branch 'master' of github.com:ampproject/amphtml
kristoferbaxter Aug 21, 2019
03935f4
Merge branch 'master' of github.com:ampproject/amphtml
kristoferbaxter Sep 17, 2019
1c78f0d
Merge branch 'master' of github.com:ampproject/amphtml
kristoferbaxter Sep 25, 2019
ce266db
Merge branch 'master' of github.com:ampproject/amphtml
kristoferbaxter Sep 27, 2019
2073a5b
Merge branch 'master' of github.com:ampproject/amphtml
kristoferbaxter Oct 2, 2019
78062ac
Merge branch 'master' of github.com:ampproject/amphtml
kristoferbaxter Oct 16, 2019
d0feed7
Use a random number as a URL parameter for live list refreshes
kristoferbaxter Oct 16, 2019
2372cbf
JsonObject casting required
kristoferbaxter Oct 16, 2019
c237398
Is a dict
kristoferbaxter Oct 16, 2019
5af1ea2
Merge branch 'master' of github.com:ampproject/amphtml into live-list…
kristoferbaxter Oct 28, 2019
99127ec
Add amp-live-list-random origin trial to gate adding a random identif…
kristoferbaxter Oct 28, 2019
3b689d8
Changes based on PR feedback
kristoferbaxter Oct 29, 2019
f948724
Guard origin trial check in tri-state boolean
kristoferbaxter Oct 29, 2019
166e182
Add dep-check allowance
kristoferbaxter Oct 29, 2019
2ac4168
moved dep-check to correct segment
kristoferbaxter Oct 29, 2019
30ef565
Only check for origin trial if the list is enabled
kristoferbaxter Oct 29, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 70 additions & 51 deletions extensions/amp-live-list/0.1/amp-live-list.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ import {AmpEvents} from '../../../src/amp-events';
import {CSS} from '../../../build/amp-live-list-0.1.css';
import {Layout} from '../../../src/layout';
import {childElementByAttr} from '../../../src/dom';
import {
installOriginExperimentsForDoc,
originExperimentsForDoc,
} from '../../../src/service/origin-experiments-impl';
import {user, userAssert} from '../../../src/log';

/**
Expand Down Expand Up @@ -192,70 +196,72 @@ export class AmpLiveList extends AMP.BaseElement {
buildCallback() {
this.viewport_ = this.getViewport();

LiveListManager.forDoc(this.element).then(manager => {
this.manager_ = manager;
this.manager_.register(this.liveListId_, this);
});
return this.isExperimentEnabled_().then(enrolled => {
LiveListManager.forDoc(this.element, enrolled).then(manager => {

Choose a reason for hiding this comment

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

Will enrolled propagate to the LiveListManager constructor?

static forDoc(element) {
return /** @type {!Promise<!LiveListManager>} */ (getServicePromiseForDoc(
element,
SERVICE_ID
));
}

Maybe we should do this check in LiveListManager on the first register() call instead. You can get an element from liveList.element in that function.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Can refactor this way, will be a little bit.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Moved over, mind taking another look?

this.manager_ = manager;
this.manager_.register(this.liveListId_, this);
});

this.customSlotId_ = this.element[AMP_LIVE_LIST_CUSTOM_SLOT_ID];
this.customSlotId_ = this.element[AMP_LIVE_LIST_CUSTOM_SLOT_ID];

this.updateSlot_ = userAssert(
this.getUpdateSlot_(this.element),
'amp-live-list must have an "update" slot.'
);
this.updateSlot_ = userAssert(
this.getUpdateSlot_(this.element),
'amp-live-list must have an "update" slot.'
);

this.itemsSlot_ = userAssert(
this.getItemsSlot_(this.element),
'amp-live-list must have an "items" slot.'
);
this.itemsSlot_ = userAssert(
this.getItemsSlot_(this.element),
'amp-live-list must have an "items" slot.'
);

this.paginationSlot_ = this.getPaginationSlot_(this.element);
this.paginationSlot_ = this.getPaginationSlot_(this.element);

this.liveListId_ = userAssert(
this.element.getAttribute('id'),
'amp-live-list must have an id.'
);
this.liveListId_ = userAssert(
this.element.getAttribute('id'),
'amp-live-list must have an id.'
);

this.pollInterval_ = getNumberMaxOrDefault(
this.element.getAttribute('data-poll-interval'),
LiveListManager.getMinDataPollInterval()
);
this.pollInterval_ = getNumberMaxOrDefault(
this.element.getAttribute('data-poll-interval'),
LiveListManager.getMinDataPollInterval()
);

const maxItems = this.element.getAttribute('data-max-items-per-page');
userAssert(
Number(maxItems) > 0 || this.element.hasAttribute('disable-pagination'),
'amp-live-list # %s must have data-max-items-per-page attribute with' +
' numeric value. Found %s.',
this.liveListId_,
maxItems
);
const maxItems = this.element.getAttribute('data-max-items-per-page');
userAssert(
Number(maxItems) > 0 || this.element.hasAttribute('disable-pagination'),
'amp-live-list # %s must have data-max-items-per-page attribute with' +
' numeric value. Found %s.',
this.liveListId_,
maxItems
);

const actualCount = [].slice
.call(this.itemsSlot_.children)
.filter(child => !child.hasAttribute('data-tombstone')).length;
const actualCount = [].slice
.call(this.itemsSlot_.children)
.filter(child => !child.hasAttribute('data-tombstone')).length;

this.maxItemsPerPage_ = Math.max(
getNumberMaxOrDefault(maxItems, 1),
actualCount
);
this.maxItemsPerPage_ = Math.max(
getNumberMaxOrDefault(maxItems, 1),
actualCount
);

this.isReverseOrder_ = this.element.getAttribute('sort') === 'ascending';
this.isReverseOrder_ = this.element.getAttribute('sort') === 'ascending';

// Make sure we hide the button
this.toggleUpdateButton_(false);
this.eachChildElement_(this.itemsSlot_, item => {
item.classList.add(classes.ITEM);
});
// Make sure we hide the button
this.toggleUpdateButton_(false);
this.eachChildElement_(this.itemsSlot_, item => {
item.classList.add(classes.ITEM);
});

this.curNumOfLiveItems_ = this.countAndCacheValidItems_(
this.itemsSlot_,
true /** opt_cacheIds */
);
this.curNumOfLiveItems_ = this.countAndCacheValidItems_(
this.itemsSlot_,
true /** opt_cacheIds */
);

this.registerDefaultAction(this.updateAction_.bind(this), 'update');
if (!this.element.hasAttribute('aria-live')) {
this.element.setAttribute('aria-live', 'polite');
}
this.registerDefaultAction(this.updateAction_.bind(this), 'update');
if (!this.element.hasAttribute('aria-live')) {
this.element.setAttribute('aria-live', 'polite');
}
});
}

/** @override */
Expand Down Expand Up @@ -494,6 +500,19 @@ export class AmpLiveList extends AMP.BaseElement {
return count;
}

/**
* Check if amp-live-list-random identifier experiment is enabled
* through origin trial.
* @return {!Promise<boolean>}
*/
isExperimentEnabled_() {
// Check if we are enabled by an origin trial
installOriginExperimentsForDoc(this.getAmpDoc());
return originExperimentsForDoc(this.element)
.getExperiments()
.then(trials => trials && trials.includes('amp-live-list-random'));
}

/**
* Does an inline replace of a list item using the element ID.
* Does nothing if item has already been tombstoned or removed from the
Expand Down
31 changes: 24 additions & 7 deletions extensions/amp-live-list/0.1/live-list-manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@

import {Poller} from './poller';
import {Services} from '../../../src/services';
import {addParamToUrl} from '../../../src/url';
import {addParamsToUrl} from '../../../src/url';
import {dict} from '../../../src/utils/object';
import {fetchDocument} from '../../../src/document-fetcher';
import {getMode} from '../../../src/mode';
import {getServicePromiseForDoc} from '../../../src/service';
Expand All @@ -29,6 +30,10 @@ export const SERVICE_ID = 'liveListManager';

const TRANSFORMED_PREFIX = 'google;v=';

// Maxiumum random number used as a query parameter.
// Cannot use Number.MAX_SAFE_INTEGER due to IE Compatibility.
const AMP_LIVE_LIST_MAX_RANDOM_NUMBER = 9007199254740991;

/**
* Property used for storing id of custom slot. This custom slot can be used to
* replace the default "items" and "update" slot.
Expand All @@ -45,8 +50,9 @@ export const AMP_LIVE_LIST_CUSTOM_SLOT_ID = 'AMP_LIVE_LIST_CUSTOM_SLOT_ID';
export class LiveListManager {
/**
* @param {!../../../src/service/ampdoc-impl.AmpDoc} ampdoc
* @param {boolean} enrolledInAppendRandomExperiment
*/
constructor(ampdoc) {
constructor(ampdoc, enrolledInAppendRandomExperiment) {
/** @const */
this.ampdoc = ampdoc;

Expand Down Expand Up @@ -77,6 +83,9 @@ export class LiveListManager {
/** @private @const {boolean} */
this.isTransformed_ = isDocTransformed(ampdoc.getRootNode());

/** @private {boolean} */
this.enrolledInAppendRandomExperiment_ = enrolledInAppendRandomExperiment;

// Only start polling when doc is ready and when the doc is visible.
this.whenDocReady_().then(() => {
// Switch out the poller interval if we can find a lower one and
Expand Down Expand Up @@ -147,11 +156,19 @@ export class LiveListManager {
fetchDocument_() {
let url = this.url_;
if (this.latestUpdateTime_ > 0) {
url = addParamToUrl(
url,
'amp_latest_update_time',
String(this.latestUpdateTime_)
);
const parameters = this.enrolledInAppendRandomExperiment_

Choose a reason for hiding this comment

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

I think there's a race condition here but the poll interval probably masks it. Probably worth testing to see.

? dict({
'amp_latest_update_time': String(this.latestUpdateTime_),
// AMP Caches do not always evict entries from their caches.
// This experiment adds a random identifier to reduce cache hits for enrolled documents.
'amp_random': String(
Math.floor(Math.random() * AMP_LIVE_LIST_MAX_RANDOM_NUMBER)

Choose a reason for hiding this comment

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

Did we decide to not go with the Date header approach?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The Date header approach needs additional testing on older browsers where our polyfill is providing a stubbed Response object.

Given the timing for the fix, and the remedy on the Google AMP Cache – I instead opted for the more narrow fix for enrolled documents.

kristoferbaxter marked this conversation as resolved.
Show resolved Hide resolved
),
})
: dict({
'amp_latest_update_time': String(this.latestUpdateTime_),
});
url = addParamsToUrl(url, parameters);
}

if (this.isTransformed_) {
Expand Down