mirror of
https://github.com/outline/outline.git
synced 2026-06-13 11:25:03 +03:00
281b778b2d
* fix: Suspended users should not be included in cached member count for groups * fix: Defer CounterCache hook registration until model is initialized The previous test-only no-op hid a timing bug where setImmediate could fire before the Sequelize instance had registered the related model, causing "Model not initialized" failures. Poll until the model is ready, and unref the pending immediate so it does not keep the event loop alive in environments where the database is never initialized. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * perf: Reduce overhead of group member count invalidation Select only the groupId column with raw queries and de-duplicate before issuing Redis deletes, avoiding loading full GroupUser rows into memory when a user belongs to many groups. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * chore: unref Redis healthcheck interval Don't keep the Node event loop alive solely for the periodic ping; the event loop should drain on its own when the application is shutting down or a Jest worker is finishing. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * refactor: Centralize counter cache key in RedisPrefixHelper Avoid duplicating the "count:<Model>:<relation>:<id>" string between the CounterCache decorator and the User suspension hook by routing both through a single getCounterCacheKey helper. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix: Walk to parent transaction when scheduling cache invalidation Nested savepoints commit independently of their outer transaction, so afterCommit callbacks attached to the inner transaction may run after the outer rolls back, or never run at all. Match the pattern used in Collection, Event, and base/Model and walk to the parent transaction so the cache invalidation fires after the real outer commit. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
63 lines
1.7 KiB
TypeScript
63 lines
1.7 KiB
TypeScript
/**
|
|
* Helper class for Redis cache key generation.
|
|
*/
|
|
export class RedisPrefixHelper {
|
|
/**
|
|
* Gets key against which unfurl response for the given url is stored.
|
|
*
|
|
* @param teamId The team ID to generate a key for.
|
|
* @param url The url to generate a key for.
|
|
*/
|
|
public static getUnfurlKey(teamId: string, url = "") {
|
|
return `unfurl:${teamId}:${url}`;
|
|
}
|
|
|
|
/**
|
|
* Gets key for caching collection documents structure.
|
|
*
|
|
* @param collectionId The collection ID to generate a key for.
|
|
* @returns the cache key string.
|
|
*/
|
|
public static getCollectionDocumentsKey(collectionId: string) {
|
|
return `cd:${collectionId}`;
|
|
}
|
|
|
|
/**
|
|
* Gets key for caching embed check results. This is a global cache key
|
|
* (not team-specific) since embed headers are the same for all users.
|
|
*
|
|
* @param url The URL to generate a cache key for.
|
|
* @returns the cache key string.
|
|
*/
|
|
public static getEmbedCheckKey(url: string) {
|
|
return `embed:${url}`;
|
|
}
|
|
|
|
/**
|
|
* Gets key for caching a user's accessible collection IDs.
|
|
*
|
|
* @param userId The user ID to generate a key for.
|
|
* @returns the cache key string.
|
|
*/
|
|
public static getUserCollectionIdsKey(userId: string) {
|
|
return `uc:${userId}`;
|
|
}
|
|
|
|
/**
|
|
* Gets key for caching the count of a relationship managed by the
|
|
* `CounterCache` decorator.
|
|
*
|
|
* @param modelName The owning model name (e.g. "Group").
|
|
* @param relationName The relationship reference name (e.g. "members").
|
|
* @param id The owning record id.
|
|
* @returns the cache key string.
|
|
*/
|
|
public static getCounterCacheKey(
|
|
modelName: string,
|
|
relationName: string,
|
|
id: string
|
|
) {
|
|
return `count:${modelName}:${relationName}:${id}`;
|
|
}
|
|
}
|