fix(github): Fix update detection for GraphQL incremental cache (#24503)

This commit is contained in:
Sergei Zharinov 2023-09-19 07:40:05 +03:00 committed by GitHub
parent d036c044c9
commit 8a25d33478
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 40 additions and 44 deletions

View file

@ -38,10 +38,9 @@ export abstract class AbstractGithubGraphqlCacheStrategy<
protected createdAt = this.now; protected createdAt = this.now;
/** /**
* This flag helps to indicate whether the cache record * This flag indicates whether there is any new or updated items
* should be persisted after the current cache access cycle.
*/ */
protected hasUpdatedItems = false; protected hasNovelty = false;
/** /**
* Loading and persisting data is delegated to the concrete strategy. * Loading and persisting data is delegated to the concrete strategy.
@ -107,18 +106,18 @@ export abstract class AbstractGithubGraphqlCacheStrategy<
let isPaginationDone = false; let isPaginationDone = false;
for (const item of items) { for (const item of items) {
const { version } = item; const { version } = item;
const oldItem = cachedItems[version];
// If we reached previously stored item that is stabilized, // If we reached previously stored item that is stabilized,
// we assume the further pagination will not yield any new items. // we assume the further pagination will not yield any new items.
const oldItem = cachedItems[version]; if (oldItem && this.isStabilized(oldItem)) {
if (oldItem) {
if (this.isStabilized(oldItem)) {
isPaginationDone = true; isPaginationDone = true;
break;
} }
if (!dequal(oldItem, item)) { // Check if item is new or updated
this.hasUpdatedItems = true; if (!oldItem || !dequal(oldItem, item)) {
} this.hasNovelty = true;
} }
cachedItems[version] = item; cachedItems[version] = item;
@ -137,13 +136,19 @@ export abstract class AbstractGithubGraphqlCacheStrategy<
const cachedItems = await this.getItems(); const cachedItems = await this.getItems();
const resultItems: Record<string, GithubItem> = {}; const resultItems: Record<string, GithubItem> = {};
let hasDeletedItems = false;
for (const [version, item] of Object.entries(cachedItems)) { for (const [version, item] of Object.entries(cachedItems)) {
if (this.isStabilized(item) || this.reconciledVersions.has(version)) { if (this.isStabilized(item) || this.reconciledVersions.has(version)) {
resultItems[version] = item; resultItems[version] = item;
} else {
hasDeletedItems = true;
} }
} }
if (this.hasNovelty || hasDeletedItems) {
await this.store(resultItems); await this.store(resultItems);
}
return Object.values(resultItems); return Object.values(resultItems);
} }

View file

@ -68,10 +68,7 @@ describe('util/github/graphql/cache-strategies/memory-cache-strategy', () => {
expect(res).toEqual([]); expect(res).toEqual([]);
expect(isPaginationDone).toBe(false); expect(isPaginationDone).toBe(false);
expect(memCache.get('github-graphql-cache:foo:bar')).toEqual({ expect(memCache.get('github-graphql-cache:foo:bar')).toEqual(cacheRecord);
items: {},
createdAt: isoTs(now),
});
}); });
it('reconciles old cache record with new items', async () => { it('reconciles old cache record with new items', async () => {
@ -136,7 +133,7 @@ describe('util/github/graphql/cache-strategies/memory-cache-strategy', () => {
expect(isPaginationDone).toBe(true); expect(isPaginationDone).toBe(true);
}); });
it('reconciles entire page', async () => { it('reconciles only not stabilized items in page', async () => {
const oldItems = { const oldItems = {
'1': { releaseTimestamp: isoTs('2020-01-01 00:00'), version: '1' }, '1': { releaseTimestamp: isoTs('2020-01-01 00:00'), version: '1' },
'2': { releaseTimestamp: isoTs('2020-01-01 01:00'), version: '2' }, '2': { releaseTimestamp: isoTs('2020-01-01 01:00'), version: '2' },
@ -164,9 +161,9 @@ describe('util/github/graphql/cache-strategies/memory-cache-strategy', () => {
expect(isPaginationDone).toBe(true); expect(isPaginationDone).toBe(true);
expect(memCache.get('github-graphql-cache:foo:bar')).toMatchObject({ expect(memCache.get('github-graphql-cache:foo:bar')).toMatchObject({
items: { items: {
'1': { releaseTimestamp: isoTs('2022-12-31 10:00') }, '1': { releaseTimestamp: isoTs('2020-01-01 00:00') },
'2': { releaseTimestamp: isoTs('2022-12-31 11:00') }, '2': { releaseTimestamp: isoTs('2020-01-01 01:00') },
'3': { releaseTimestamp: isoTs('2022-12-31 12:00') }, '3': { releaseTimestamp: isoTs('2020-01-01 02:00') },
'4': { releaseTimestamp: isoTs('2022-12-31 13:00') }, '4': { releaseTimestamp: isoTs('2022-12-31 13:00') },
}, },
}); });

View file

@ -36,21 +36,17 @@ describe('util/github/graphql/cache-strategies/package-cache-strategy', () => {
const now = '2022-10-30 12:00'; const now = '2022-10-30 12:00';
mockTime(now); mockTime(now);
const updatedItem = {
...item3,
releaseTimestamp: isoTs('2020-01-01 12:30'),
};
const newItem = { const newItem = {
version: '4', version: '4',
releaseTimestamp: isoTs('2022-10-15 18:00'), releaseTimestamp: isoTs('2022-10-15 18:00'),
}; };
const page = [newItem, updatedItem]; const page = [newItem, item3, item2, item1];
const strategy = new GithubGraphqlPackageCacheStrategy('foo', 'bar'); const strategy = new GithubGraphqlPackageCacheStrategy('foo', 'bar');
const isPaginationDone = await strategy.reconcile(page); const isPaginationDone = await strategy.reconcile(page);
const res = await strategy.finalize(); const res = await strategy.finalize();
expect(res).toEqual([item1, item2, updatedItem, newItem]); expect(res).toEqual([item1, item2, item3, newItem]);
expect(isPaginationDone).toBe(true); expect(isPaginationDone).toBe(true);
expect(cacheSet.mock.calls).toEqual([ expect(cacheSet.mock.calls).toEqual([
[ [
@ -60,7 +56,7 @@ describe('util/github/graphql/cache-strategies/package-cache-strategy', () => {
items: { items: {
'1': item1, '1': item1,
'2': item2, '2': item2,
'3': updatedItem, '3': item3,
'4': newItem, '4': newItem,
}, },
createdAt: isoTs('2022-10-15 12:00'), createdAt: isoTs('2022-10-15 12:00'),

View file

@ -15,7 +15,6 @@ export class GithubGraphqlPackageCacheStrategy<
async persist( async persist(
cacheRecord: GithubGraphqlCacheRecord<GithubItem> cacheRecord: GithubGraphqlCacheRecord<GithubItem>
): Promise<void> { ): Promise<void> {
if (this.hasUpdatedItems) {
const expiry = this.createdAt const expiry = this.createdAt
.plus({ .plus({
// Not using 'days' as it does not handle adjustments for Daylight Saving time. // Not using 'days' as it does not handle adjustments for Daylight Saving time.
@ -33,5 +32,4 @@ export class GithubGraphqlPackageCacheStrategy<
); );
} }
} }
}
} }