Skip to content
Snippets Groups Projects
Unverified Commit 53fa8cc9 authored by Sergei Zharinov's avatar Sergei Zharinov Committed by GitHub
Browse files

refactor(cache): Utils to calculate soft and hard TTL (#33844)

parent 0f068660
No related branches found
No related tags found
No related merge requests found
......@@ -7,7 +7,7 @@ import { logger } from '../../logger';
import { ExternalHostError } from '../../types/errors/external-host-error';
import * as memCache from '../../util/cache/memory';
import * as packageCache from '../../util/cache/package';
import { getTtlOverride } from '../../util/cache/package/decorator';
import { getTtlOverride } from '../../util/cache/package/ttl';
import { clone } from '../../util/clone';
import { regEx } from '../../util/regex';
import * as template from '../../util/template';
......
......@@ -5,6 +5,7 @@ import { logger } from '../../../logger';
import type { Decorator } from '../../decorator';
import { decorate } from '../../decorator';
import { acquireLock } from '../../mutex';
import { resolveTtlValues } from './ttl';
import type { DecoratorCachedRecord, PackageCacheNamespace } from './types';
import * as packageCache from '.';
......@@ -91,17 +92,13 @@ export function cache<T>({
finalKey,
);
const ttlOverride = getTtlOverride(finalNamespace);
const softTtl = ttlOverride ?? ttlMinutes;
const cacheHardTtlMinutes = GlobalConfig.get(
'cacheHardTtlMinutes',
7 * 24 * 60,
);
let hardTtl = softTtl;
if (methodName === 'getReleases' || methodName === 'getDigest') {
hardTtl = Math.max(softTtl, cacheHardTtlMinutes);
}
const ttlValues = resolveTtlValues(finalNamespace, ttlMinutes);
const softTtl = ttlValues.softTtlMinutes;
const hardTtl =
methodName === 'getReleases' || methodName === 'getDigest'
? ttlValues.hardTtlMinutes
: // Skip two-tier TTL for any intermediate data fetching
softTtl;
let oldData: unknown;
if (oldRecord) {
......@@ -148,11 +145,3 @@ export function cache<T>({
}
});
}
export function getTtlOverride(namespace: string): number | undefined {
const ttl: unknown = GlobalConfig.get('cacheTtlOverride', {})[namespace];
if (is.number(ttl)) {
return ttl;
}
return undefined;
}
import is from '@sindresorhus/is';
import { GlobalConfig } from '../../../config/global';
import type { PackageCacheNamespace } from './types';
export function getTtlOverride(
namespace: PackageCacheNamespace,
): number | undefined {
const ttl = GlobalConfig.get('cacheTtlOverride', {})[namespace];
if (is.number(ttl)) {
return ttl;
}
return undefined;
}
export interface TTLValues {
/** TTL for serving cached value without hitting the server */
softTtlMinutes: number;
/** TTL for serving stale cache when upstream responds with errors */
hardTtlMinutes: number;
}
/**
* Apply user-configured overrides and return the final values for soft/hard TTL.
*
* @param namespace Cache namespace
* @param ttlMinutes TTL value configured in Renovate codebase
* @returns
*/
export function resolveTtlValues(
namespace: PackageCacheNamespace,
ttlMinutes: number,
): TTLValues {
const softTtlMinutes = getTtlOverride(namespace) ?? ttlMinutes;
const cacheHardTtlMinutes = GlobalConfig.get(
'cacheHardTtlMinutes',
7 * 24 * 60,
);
const hardTtlMinutes = Math.max(softTtlMinutes, cacheHardTtlMinutes);
return { softTtlMinutes, hardTtlMinutes };
}
......@@ -50,7 +50,7 @@ describe('util/http/cache/package-http-cache-provider', () => {
};
const cacheProvider = new PackageHttpCacheProvider({
namespace: '_test-namespace',
softTtlMinutes: 0,
ttlMinutes: 0,
});
httpMock.scope(url).get('').reply(200, 'new response');
......
import { DateTime } from 'luxon';
import { get, set } from '../../cache/package'; // Import the package cache functions
import { resolveTtlValues } from '../../cache/package/ttl';
import type { PackageCacheNamespace } from '../../cache/package/types';
import { HttpCacheStats } from '../../stats';
import type { HttpResponse } from '../types';
import { AbstractHttpCacheProvider } from './abstract-http-cache-provider';
import type { HttpCache } from './schema';
export interface PackageHttpCacheProviderOptions {
namespace: PackageCacheNamespace;
softTtlMinutes?: number;
hardTtlMinutes?: number;
ttlMinutes?: number;
}
export class PackageHttpCacheProvider extends AbstractHttpCacheProvider {
private namespace: PackageCacheNamespace;
private softTtlMinutes = 15;
private hardTtlMinutes = 24 * 60;
constructor({
namespace,
softTtlMinutes,
hardTtlMinutes,
}: PackageHttpCacheProviderOptions) {
private softTtlMinutes: number;
private hardTtlMinutes: number;
constructor({ namespace, ttlMinutes = 15 }: PackageHttpCacheProviderOptions) {
super();
this.namespace = namespace;
this.softTtlMinutes = softTtlMinutes ?? this.softTtlMinutes;
this.hardTtlMinutes = hardTtlMinutes ?? this.hardTtlMinutes;
const { softTtlMinutes, hardTtlMinutes } = resolveTtlValues(
this.namespace,
ttlMinutes,
);
this.softTtlMinutes = softTtlMinutes;
this.hardTtlMinutes = hardTtlMinutes;
}
async load(url: string): Promise<unknown> {
......@@ -47,9 +49,11 @@ export class PackageHttpCacheProvider extends AbstractHttpCacheProvider {
const deadline = cachedAt.plus({ minutes: this.softTtlMinutes });
const now = DateTime.now();
if (now >= deadline) {
HttpCacheStats.incLocalMisses(url);
return null;
}
HttpCacheStats.incLocalHits(url);
return cached.httpResponse as HttpResponse<T>;
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment