mirror of
https://github.com/expo/entity.git
synced 2026-11-15 03:48:08 -10:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b61341057b | ||
|
|
bc378b6c79 |
No files matched your search
@@ -1,4 +1,4 @@
|
|||||||
import { CacheLoadResult, CacheStatus } from '@expo/entity';
|
import { CacheLoadResult, CacheStatus, IEntityGenericCacher } from '@expo/entity';
|
||||||
import LRUCache from 'lru-cache';
|
import LRUCache from 'lru-cache';
|
||||||
|
|
||||||
// Sentinel value we store in local memory to negatively cache a database miss.
|
// Sentinel value we store in local memory to negatively cache a database miss.
|
||||||
@@ -77,8 +77,8 @@ type LRUCacheOptionsV7<K, V> = {
|
|||||||
updateRecencyOnHas?: boolean;
|
updateRecencyOnHas?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default class GenericLocalMemoryCacher<TFields> {
|
export default class GenericLocalMemoryCacher<TFields> implements IEntityGenericCacher<TFields> {
|
||||||
constructor(private readonly lruCache: LocalMemoryCache<TFields>) {}
|
constructor(private readonly localMemoryCache: LocalMemoryCache<TFields>) {}
|
||||||
|
|
||||||
static createLRUCache<TFields>(
|
static createLRUCache<TFields>(
|
||||||
options: { maxSize?: number; ttlSeconds?: number } = {}
|
options: { maxSize?: number; ttlSeconds?: number } = {}
|
||||||
@@ -95,7 +95,7 @@ export default class GenericLocalMemoryCacher<TFields> {
|
|||||||
return new LRUCache<string, LocalMemoryCacheValue<TFields>>(lruCacheOptions as any);
|
return new LRUCache<string, LocalMemoryCacheValue<TFields>>(lruCacheOptions as any);
|
||||||
}
|
}
|
||||||
|
|
||||||
static createNoOpLRUCache<TFields>(): LocalMemoryCache<TFields> {
|
static createNoOpCache<TFields>(): LocalMemoryCache<TFields> {
|
||||||
return new LRUCache<string, LocalMemoryCacheValue<TFields>>({
|
return new LRUCache<string, LocalMemoryCacheValue<TFields>>({
|
||||||
max: 0,
|
max: 0,
|
||||||
maxAge: -1,
|
maxAge: -1,
|
||||||
@@ -107,7 +107,7 @@ export default class GenericLocalMemoryCacher<TFields> {
|
|||||||
): Promise<ReadonlyMap<string, CacheLoadResult<TFields>>> {
|
): Promise<ReadonlyMap<string, CacheLoadResult<TFields>>> {
|
||||||
const cacheResults = new Map<string, CacheLoadResult<TFields>>();
|
const cacheResults = new Map<string, CacheLoadResult<TFields>>();
|
||||||
for (const key of keys) {
|
for (const key of keys) {
|
||||||
const cacheResult = this.lruCache.get(key);
|
const cacheResult = this.localMemoryCache.get(key);
|
||||||
if (cacheResult === DOES_NOT_EXIST_LOCAL_MEMORY_CACHE) {
|
if (cacheResult === DOES_NOT_EXIST_LOCAL_MEMORY_CACHE) {
|
||||||
cacheResults.set(key, {
|
cacheResults.set(key, {
|
||||||
status: CacheStatus.NEGATIVE,
|
status: CacheStatus.NEGATIVE,
|
||||||
@@ -128,19 +128,19 @@ export default class GenericLocalMemoryCacher<TFields> {
|
|||||||
|
|
||||||
public async cacheManyAsync(objectMap: ReadonlyMap<string, Readonly<TFields>>): Promise<void> {
|
public async cacheManyAsync(objectMap: ReadonlyMap<string, Readonly<TFields>>): Promise<void> {
|
||||||
for (const [key, item] of objectMap) {
|
for (const [key, item] of objectMap) {
|
||||||
this.lruCache.set(key, item);
|
this.localMemoryCache.set(key, item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async cacheDBMissesAsync(keys: string[]): Promise<void> {
|
public async cacheDBMissesAsync(keys: readonly string[]): Promise<void> {
|
||||||
for (const key of keys) {
|
for (const key of keys) {
|
||||||
this.lruCache.set(key, DOES_NOT_EXIST_LOCAL_MEMORY_CACHE);
|
this.localMemoryCache.set(key, DOES_NOT_EXIST_LOCAL_MEMORY_CACHE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async invalidateManyAsync(keys: string[]): Promise<void> {
|
public async invalidateManyAsync(keys: readonly string[]): Promise<void> {
|
||||||
for (const key of keys) {
|
for (const key of keys) {
|
||||||
this.lruCache.del(key);
|
this.localMemoryCache.del(key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,10 +8,10 @@ export default class LocalMemoryCacheAdapter<TFields> extends EntityCacheAdapter
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
entityConfiguration: EntityConfiguration<TFields>,
|
entityConfiguration: EntityConfiguration<TFields>,
|
||||||
lruCache: LocalMemoryCache<TFields>
|
localMemoryCache: LocalMemoryCache<TFields>
|
||||||
) {
|
) {
|
||||||
super(entityConfiguration);
|
super(entityConfiguration);
|
||||||
this.genericLocalMemoryCacher = new GenericLocalMemoryCacher(lruCache);
|
this.genericLocalMemoryCacher = new GenericLocalMemoryCacher(localMemoryCache);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async loadManyAsync<N extends keyof TFields>(
|
public async loadManyAsync<N extends keyof TFields>(
|
||||||
@@ -29,7 +29,7 @@ export default class LocalMemoryCacheAdapter<TFields> extends EntityCacheAdapter
|
|||||||
const fieldValue = localMemoryCacheKeyToFieldValueMapping.get(cacheKey);
|
const fieldValue = localMemoryCacheKeyToFieldValueMapping.get(cacheKey);
|
||||||
invariant(
|
invariant(
|
||||||
fieldValue !== undefined,
|
fieldValue !== undefined,
|
||||||
'Unspecified cache key %s returned from generic Local Memory cacher',
|
'Unspecified cache key %s returned from generic local memory cacher',
|
||||||
cacheKey
|
cacheKey
|
||||||
);
|
);
|
||||||
return fieldValue;
|
return fieldValue;
|
||||||
|
|||||||
@@ -8,13 +8,13 @@ import {
|
|||||||
import GenericLocalMemoryCacher, { LocalMemoryCache } from './GenericLocalMemoryCacher';
|
import GenericLocalMemoryCacher, { LocalMemoryCache } from './GenericLocalMemoryCacher';
|
||||||
import LocalMemoryCacheAdapter from './LocalMemoryCacheAdapter';
|
import LocalMemoryCacheAdapter from './LocalMemoryCacheAdapter';
|
||||||
|
|
||||||
export class LocalMemoryCacheAdapterProvider implements IEntityCacheAdapterProvider {
|
export default class LocalMemoryCacheAdapterProvider implements IEntityCacheAdapterProvider {
|
||||||
// local memory cache adapters should be shared/reused across requests
|
// local memory cache adapters should be shared/reused across requests
|
||||||
static localMemoryCacheAdapterMap = new Map<string, LocalMemoryCacheAdapter<any>>();
|
static localMemoryCacheAdapterMap = new Map<string, LocalMemoryCacheAdapter<any>>();
|
||||||
|
|
||||||
static getNoOpProvider(): IEntityCacheAdapterProvider {
|
static getNoOpProvider(): IEntityCacheAdapterProvider {
|
||||||
return new LocalMemoryCacheAdapterProvider(<TFields>() =>
|
return new LocalMemoryCacheAdapterProvider(<TFields>() =>
|
||||||
GenericLocalMemoryCacher.createNoOpLRUCache<TFields>()
|
GenericLocalMemoryCacher.createNoOpCache<TFields>()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -26,7 +26,9 @@ export class LocalMemoryCacheAdapterProvider implements IEntityCacheAdapterProvi
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private constructor(private readonly lruCacheCreator: <TFields>() => LocalMemoryCache<TFields>) {}
|
private constructor(
|
||||||
|
private readonly localMemoryCacheCreator: <TFields>() => LocalMemoryCache<TFields>
|
||||||
|
) {}
|
||||||
|
|
||||||
public getCacheAdapter<TFields>(
|
public getCacheAdapter<TFields>(
|
||||||
entityConfiguration: EntityConfiguration<TFields>
|
entityConfiguration: EntityConfiguration<TFields>
|
||||||
@@ -35,8 +37,8 @@ export class LocalMemoryCacheAdapterProvider implements IEntityCacheAdapterProvi
|
|||||||
LocalMemoryCacheAdapterProvider.localMemoryCacheAdapterMap,
|
LocalMemoryCacheAdapterProvider.localMemoryCacheAdapterMap,
|
||||||
entityConfiguration.tableName,
|
entityConfiguration.tableName,
|
||||||
() => {
|
() => {
|
||||||
const lruCache = this.lruCacheCreator<TFields>();
|
const localMemoryCache = this.localMemoryCacheCreator<TFields>();
|
||||||
return new LocalMemoryCacheAdapter(entityConfiguration, lruCache);
|
return new LocalMemoryCacheAdapter(entityConfiguration, localMemoryCache);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-8
@@ -64,16 +64,16 @@ describe(LocalMemoryCacheAdapter, () => {
|
|||||||
|
|
||||||
describe('cacheManyAsync', () => {
|
describe('cacheManyAsync', () => {
|
||||||
it('correctly caches all objects', async () => {
|
it('correctly caches all objects', async () => {
|
||||||
const lruCache = GenericLocalMemoryCacher.createLRUCache<BlahFields>({
|
const localMemoryCache = GenericLocalMemoryCacher.createLRUCache<BlahFields>({
|
||||||
maxSize: Number.MAX_SAFE_INTEGER,
|
maxSize: Number.MAX_SAFE_INTEGER,
|
||||||
ttlSeconds: Number.MAX_SAFE_INTEGER,
|
ttlSeconds: Number.MAX_SAFE_INTEGER,
|
||||||
});
|
});
|
||||||
|
|
||||||
const cacheAdapter = new LocalMemoryCacheAdapter(entityConfiguration, lruCache);
|
const cacheAdapter = new LocalMemoryCacheAdapter(entityConfiguration, localMemoryCache);
|
||||||
await cacheAdapter.cacheManyAsync('id', new Map([['test-id-1', { id: 'test-id-1' }]]));
|
await cacheAdapter.cacheManyAsync('id', new Map([['test-id-1', { id: 'test-id-1' }]]));
|
||||||
|
|
||||||
const cacheKey = cacheAdapter['makeCacheKey']('id', 'test-id-1');
|
const cacheKey = cacheAdapter['makeCacheKey']('id', 'test-id-1');
|
||||||
expect(lruCache.get(cacheKey)).toMatchObject({
|
expect(localMemoryCache.get(cacheKey)).toMatchObject({
|
||||||
id: 'test-id-1',
|
id: 'test-id-1',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -81,27 +81,27 @@ describe(LocalMemoryCacheAdapter, () => {
|
|||||||
|
|
||||||
describe('cacheDBMissesAsync', () => {
|
describe('cacheDBMissesAsync', () => {
|
||||||
it('correctly caches misses', async () => {
|
it('correctly caches misses', async () => {
|
||||||
const lruCache = GenericLocalMemoryCacher.createLRUCache<BlahFields>({
|
const localMemoryCache = GenericLocalMemoryCacher.createLRUCache<BlahFields>({
|
||||||
maxSize: Number.MAX_SAFE_INTEGER,
|
maxSize: Number.MAX_SAFE_INTEGER,
|
||||||
ttlSeconds: Number.MAX_SAFE_INTEGER,
|
ttlSeconds: Number.MAX_SAFE_INTEGER,
|
||||||
});
|
});
|
||||||
|
|
||||||
const cacheAdapter = new LocalMemoryCacheAdapter(entityConfiguration, lruCache);
|
const cacheAdapter = new LocalMemoryCacheAdapter(entityConfiguration, localMemoryCache);
|
||||||
await cacheAdapter.cacheDBMissesAsync('id', ['test-id-1']);
|
await cacheAdapter.cacheDBMissesAsync('id', ['test-id-1']);
|
||||||
|
|
||||||
const cacheKey = cacheAdapter['makeCacheKey']('id', 'test-id-1');
|
const cacheKey = cacheAdapter['makeCacheKey']('id', 'test-id-1');
|
||||||
expect(lruCache.get(cacheKey)).toEqual(DOES_NOT_EXIST_LOCAL_MEMORY_CACHE);
|
expect(localMemoryCache.get(cacheKey)).toEqual(DOES_NOT_EXIST_LOCAL_MEMORY_CACHE);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('invalidateManyAsync', () => {
|
describe('invalidateManyAsync', () => {
|
||||||
it('invalidates correctly', async () => {
|
it('invalidates correctly', async () => {
|
||||||
const lruCache = GenericLocalMemoryCacher.createLRUCache<BlahFields>({
|
const localMemoryCache = GenericLocalMemoryCacher.createLRUCache<BlahFields>({
|
||||||
maxSize: Number.MAX_SAFE_INTEGER,
|
maxSize: Number.MAX_SAFE_INTEGER,
|
||||||
ttlSeconds: Number.MAX_SAFE_INTEGER,
|
ttlSeconds: Number.MAX_SAFE_INTEGER,
|
||||||
});
|
});
|
||||||
|
|
||||||
const cacheAdapter = new LocalMemoryCacheAdapter(entityConfiguration, lruCache);
|
const cacheAdapter = new LocalMemoryCacheAdapter(entityConfiguration, localMemoryCache);
|
||||||
await cacheAdapter.cacheManyAsync('id', new Map([['test-id-1', { id: 'test-id-1' }]]));
|
await cacheAdapter.cacheManyAsync('id', new Map([['test-id-1', { id: 'test-id-1' }]]));
|
||||||
await cacheAdapter.cacheDBMissesAsync('id', ['test-id-2']);
|
await cacheAdapter.cacheDBMissesAsync('id', ['test-id-2']);
|
||||||
await cacheAdapter.invalidateManyAsync('id', ['test-id-1', 'test-id-2']);
|
await cacheAdapter.invalidateManyAsync('id', ['test-id-1', 'test-id-2']);
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
/* eslint-disable tsdoc/syntax */
|
||||||
|
/**
|
||||||
|
* @packageDocumentation
|
||||||
|
* @module @expo/entity-cache-adapter-local-memory
|
||||||
|
*/
|
||||||
|
|
||||||
|
export { default as LocalMemoryCacheAdapter } from './LocalMemoryCacheAdapter';
|
||||||
|
export { default as LocalMemoryCacheAdapterProvider } from './LocalMemoryCacheAdapterProvider';
|
||||||
|
export { default as GenericLocalMemoryCacher } from './GenericLocalMemoryCacher';
|
||||||
|
export * from './GenericLocalMemoryCacher';
|
||||||
@@ -97,7 +97,7 @@ export default class GenericRedisCacher<TFields> implements IEntityGenericCacher
|
|||||||
await wrapNativeRedisCallAsync(() => redisTransaction.exec());
|
await wrapNativeRedisCallAsync(() => redisTransaction.exec());
|
||||||
}
|
}
|
||||||
|
|
||||||
public async cacheDBMissesAsync(keys: string[]): Promise<void> {
|
public async cacheDBMissesAsync(keys: readonly string[]): Promise<void> {
|
||||||
if (keys.length === 0) {
|
if (keys.length === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -114,7 +114,7 @@ export default class GenericRedisCacher<TFields> implements IEntityGenericCacher
|
|||||||
await wrapNativeRedisCallAsync(() => redisTransaction.exec());
|
await wrapNativeRedisCallAsync(() => redisTransaction.exec());
|
||||||
}
|
}
|
||||||
|
|
||||||
public async invalidateManyAsync(keys: string[]): Promise<void> {
|
public async invalidateManyAsync(keys: readonly string[]): Promise<void> {
|
||||||
if (keys.length === 0) {
|
if (keys.length === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ export default interface IEntityGenericCacher<TFields> {
|
|||||||
|
|
||||||
cacheManyAsync(objectMap: ReadonlyMap<string, Readonly<TFields>>): Promise<void>;
|
cacheManyAsync(objectMap: ReadonlyMap<string, Readonly<TFields>>): Promise<void>;
|
||||||
|
|
||||||
cacheDBMissesAsync(keys: string[]): Promise<void>;
|
cacheDBMissesAsync(keys: readonly string[]): Promise<void>;
|
||||||
|
|
||||||
invalidateManyAsync(keys: string[]): Promise<void>;
|
invalidateManyAsync(keys: readonly string[]): Promise<void>;
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user