Skip to content

Commit eaf7d03

Browse files
committed
documentation and logs
1 parent 9610d9f commit eaf7d03

File tree

2 files changed

+81
-16
lines changed

2 files changed

+81
-16
lines changed

api/src/shared/infrastructure/caches/learning-content-cache.js

+11-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
import * as learningContentPubSub from '../caches/learning-content-pubsub.js';
2+
import { child } from '../utils/logger.js';
3+
4+
const logger = child('learningcontent:cache', { event: 'learningcontent' });
25

36
export class LearningContentCache {
47
#map;
@@ -39,8 +42,14 @@ export class LearningContentCache {
3942

4043
async #subscribe() {
4144
for await (const message of this.#pubSub.subscribe(this.#name)) {
42-
if (message.type === 'clear') this.#map.clear();
43-
if (message.type === 'delete') this.#map.delete(message.key);
45+
if (message.type === 'clear') {
46+
logger.debug({ name: this.#name }, 'clearing cache');
47+
this.#map.clear();
48+
}
49+
if (message.type === 'delete') {
50+
logger.debug({ name: this.#name, key: message.key }, 'deleting cache key');
51+
this.#map.delete(message.key);
52+
}
4453
}
4554
}
4655
}

api/src/shared/infrastructure/repositories/learning-content-repository.js

+70-14
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,31 @@ import Dataloader from 'dataloader';
22

33
import { knex } from '../../../../db/knex-database-connection.js';
44
import { LearningContentCache } from '../caches/learning-content-cache.js';
5+
import { child } from '../utils/logger.js';
56

7+
const logger = child('learningcontent:repository', { event: 'learningcontent' });
8+
9+
/**
10+
* @typedef {(knex: import('knex').QueryBuilder) => Promise<string[]|number[]>} QueryBuilderCallback
11+
*/
12+
13+
/**
14+
* Datasource for learning content repositories.
15+
* This datasource uses a {@link Dataloader} to load and cache entities.
16+
*/
617
export class LearningContentRepository {
718
#tableName;
819
#idType;
920
#dataloader;
1021
#findCache;
1122
#findCacheMiss;
1223

24+
/**
25+
* @param {{
26+
* tableName: string
27+
* idType?: 'text' | 'integer'
28+
* }} config
29+
*/
1330
constructor({ tableName, idType = 'text' }) {
1431
this.#tableName = tableName;
1532
this.#idType = idType;
@@ -23,21 +40,15 @@ export class LearningContentRepository {
2340
this.#findCacheMiss = new Map();
2441
}
2542

43+
/**
44+
* Finds several entities using a request and caches results.
45+
* The request is built using a knex query builder given to {@link callback}.
46+
* {@link cacheKey} must vary according to params given to the query builder.
47+
* @param {string} cacheKey
48+
* @param {QueryBuilderCallback} callback
49+
* @returns {Promise<object[]>}
50+
*/
2651
async find(cacheKey, callback) {
27-
return this.#findDtos(callback, cacheKey);
28-
}
29-
30-
async load(id) {
31-
if (!id) return null;
32-
return this.#dataloader.load(id);
33-
}
34-
35-
async loadMany(ids) {
36-
const notNullIds = ids.filter((id) => id);
37-
return this.#dataloader.loadMany(notNullIds);
38-
}
39-
40-
#findDtos(callback, cacheKey) {
4152
let dtos = this.#findCache.get(cacheKey);
4253
if (dtos) return dtos;
4354

@@ -52,14 +63,51 @@ export class LearningContentRepository {
5263
return dtos;
5364
}
5465

66+
/**
67+
* Loads one entity by ID.
68+
* @param {string|number} id
69+
* @returns {Promise<object>}
70+
*/
71+
async load(id) {
72+
if (!id) return null;
73+
return this.#dataloader.load(id);
74+
}
75+
76+
/**
77+
* Loads several entities by ID.
78+
* @param {string[]|number[]} ids
79+
* @returns {Promise<object[]>}
80+
*/
81+
async loadMany(ids) {
82+
const notNullIds = ids.filter((id) => id);
83+
return this.#dataloader.loadMany(notNullIds);
84+
}
85+
86+
/**
87+
* Loads entities from database using a request and writes result to cache.
88+
* @param {string} cacheKey
89+
* @param {QueryBuilderCallback} callback
90+
* @returns {Promise<object[]>}
91+
*/
5592
async #loadDtos(callback, cacheKey) {
5693
const ids = await callback(knex.pluck(`${this.#tableName}.id`).from(this.#tableName));
5794
const dtos = await this.#dataloader.loadMany(ids);
95+
96+
logger.debug({ tableName: this.#tableName, cacheKey }, 'caching find result');
5897
this.#findCache.set(cacheKey, dtos);
98+
5999
return dtos;
60100
}
61101

102+
/**
103+
* Loads a batch of entities from database by ID.
104+
* Entities are returned in the same order as {@link ids}.
105+
* If an ID is not found, it is null in results.
106+
* @param {string[]|number[]} ids
107+
* @returns {Promise<(object|null)[]>}
108+
*/
62109
async #batchLoad(ids) {
110+
logger.debug({ tableName: this.#tableName, count: ids.length }, 'loading from PG');
63111
const dtos = await knex
64112
.select(`${this.#tableName}.*`)
65113
.from(knex.raw(`unnest(?::${this.#idType}[]) with ordinality as ids(id, idx)`, [ids])) // eslint-disable-line knex/avoid-injections
@@ -68,7 +116,15 @@ export class LearningContentRepository {
68116
return dtos.map((dto) => (dto.id ? dto : null));
69117
}
70118

119+
/**
120+
* Clears repository’s cache.
121+
* If {@link id} is undefined, all cache is cleared.
122+
* If {@link id} is given, cache is partially cleared.
123+
* @param {string|number|undefined} id
124+
*/
71125
clearCache(id) {
126+
logger.debug({ tableName: this.#tableName, id }, 'trigerring cache clear');
127+
72128
if (id) {
73129
this.#dataloader.clear(id);
74130
} else {

0 commit comments

Comments
 (0)