Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
47 changes: 29 additions & 18 deletions web/src/lib/managers/AssetCacheManager.svelte.ts
Original file line number Diff line number Diff line change
@@ -1,56 +1,67 @@
import { authManager } from '$lib/managers/auth-manager.svelte';
import { eventManager } from '$lib/managers/event-manager.svelte';
import { getAssetInfo, getAssetOcr, type AssetOcrResponseDto, type AssetResponseDto } from '@immich/sdk';
import { getAssetInfo, getAssetOcr } from '@immich/sdk';

const defaultSerializer = <K>(params: K) => JSON.stringify(params);

class AsyncCache<V> {
class AsyncCache<K, V> {
#cache = new Map<string, V>();

async getOrFetch<K>(
params: K,
fetcher: (params: K) => Promise<V>,
keySerializer: (params: K) => string = defaultSerializer,
updateCache: boolean,
): Promise<V> {
const cacheKey = keySerializer(params);
constructor(private fetcher: (params: K) => Promise<V>) {}

async getOrFetch(params: K, updateCache: boolean): Promise<V> {
const cacheKey = defaultSerializer(params);

const cached = this.#cache.get(cacheKey);
if (cached) {
return cached;
}

const value = await fetcher(params);
const value = await this.fetcher(params);
if (value && updateCache) {
this.#cache.set(cacheKey, value);
}

return value;
}

clearKey(params: K) {
const cacheKey = defaultSerializer(params);
this.#cache.delete(cacheKey);
}

clear() {
this.#cache.clear();
}
}

class AssetCacheManager {
#assetCache = new AsyncCache<AssetResponseDto>();
#ocrCache = new AsyncCache<AssetOcrResponseDto[]>();
#assetCache = new AsyncCache(getAssetInfo);
#ocrCache = new AsyncCache(getAssetOcr);

constructor() {
eventManager.on({
AssetEditsApplied: () => {
this.#assetCache.clear();
this.#ocrCache.clear();
AssetEditsApplied: (assetId) => {
this.invalidateAsset(assetId);
},
AssetUpdate: (asset) => {
this.invalidateAsset(asset.id);
},
});
}

async getAsset(assetIdentifier: { key?: string; slug?: string; id: string }, updateCache = true) {
return this.#assetCache.getOrFetch(assetIdentifier, getAssetInfo, defaultSerializer, updateCache);
async getAsset({ id, key, slug }: { id: string; key?: string; slug?: string }, updateCache = true) {
return this.#assetCache.getOrFetch({ id, key, slug }, updateCache);
Comment on lines +53 to +54
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
async getAsset({ id, key, slug }: { id: string; key?: string; slug?: string }, updateCache = true) {
return this.#assetCache.getOrFetch({ id, key, slug }, updateCache);
async getAsset(asset: { id: string; key?: string; slug?: string }, updateCache = true) {
return this.#assetCache.getOrFetch(asset, updateCache);

Why destructure it just to pass it in again?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

To make sure the order stays the same because JSON.stringify({ a, b }) !== JSON.stringify({ b, a })

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Oooh right that makes sense

}

async getAssetOcr(id: string) {
return this.#ocrCache.getOrFetch({ id }, getAssetOcr, (params) => params.id, true);
return this.#ocrCache.getOrFetch({ id }, true);
}

invalidateAsset(id: string) {
const { key, slug } = authManager.params;
this.#assetCache.clearKey({ id, key, slug });
this.#ocrCache.clearKey({ id });
}

clearAssetCache() {
Expand Down
1 change: 1 addition & 0 deletions web/src/lib/stores/ocr.svelte.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { beforeEach, describe, expect, it, vi } from 'vitest';

// Mock the SDK
vi.mock('@immich/sdk', () => ({
getAssetInfo: vi.fn(),
getAssetOcr: vi.fn(),
}));

Expand Down
1 change: 1 addition & 0 deletions web/src/lib/stores/websocket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ websocket
.on('on_new_release', (event) => eventManager.emit('ReleaseEvent', event))
.on('on_session_delete', () => authManager.logout())
.on('on_user_delete', (id) => eventManager.emit('UserAdminDeleted', { id }))
.on('on_asset_update', (asset) => eventManager.emit('AssetUpdate', asset))
.on('on_person_thumbnail', (id) => eventManager.emit('PersonThumbnailReady', { id }))
.on('on_notification', () => notificationManager.refresh())
.on('connect_error', (e) => console.log('Websocket Connect Error', e));
Expand Down
Loading