Add timestamps to cache entries and server events (#19976)

This commit is contained in:
Mads Rasmussen
2025-08-22 13:49:46 +02:00
committed by GitHub
parent 7f2d515763
commit fee6722ecc
8 changed files with 43 additions and 21 deletions

View File

@@ -9,7 +9,7 @@ import {
} from '@umbraco-cms/backoffice/external/backend-api';
import {
UmbManagementApiDetailDataRequestManager,
UmbManagementApiInflightRequestCache,
UmbManagementApiInFlightRequestCache,
} from '@umbraco-cms/backoffice/management-api';
export class UmbManagementApiDataTypeDetailDataRequestManager extends UmbManagementApiDetailDataRequestManager<
@@ -17,7 +17,7 @@ export class UmbManagementApiDataTypeDetailDataRequestManager extends UmbManagem
UpdateDataTypeRequestModel,
CreateDataTypeRequestModel
> {
static #inflightRequestCache = new UmbManagementApiInflightRequestCache<DataTypeResponseModel>();
static #inflightRequestCache = new UmbManagementApiInFlightRequestCache<DataTypeResponseModel>();
constructor(host: UmbControllerHost) {
super(host, {

View File

@@ -9,7 +9,7 @@ import {
} from '@umbraco-cms/backoffice/external/backend-api';
import {
UmbManagementApiDetailDataRequestManager,
UmbManagementApiInflightRequestCache,
UmbManagementApiInFlightRequestCache,
} from '@umbraco-cms/backoffice/management-api';
export class UmbManagementApiDocumentTypeDetailDataRequestManager extends UmbManagementApiDetailDataRequestManager<
@@ -17,7 +17,7 @@ export class UmbManagementApiDocumentTypeDetailDataRequestManager extends UmbMan
UpdateDocumentTypeRequestModel,
CreateDocumentTypeRequestModel
> {
static #inflightRequestCache = new UmbManagementApiInflightRequestCache<DocumentTypeResponseModel>();
static #inflightRequestCache = new UmbManagementApiInFlightRequestCache<DocumentTypeResponseModel>();
constructor(host: UmbControllerHost) {
super(host, {

View File

@@ -1,7 +1,8 @@
// Keep internal
interface UmbCacheEntryModel<DetailDataModelType> {
interface UmbDetailCacheEntryModel<DetailDataModelType> {
id: string;
data: DetailDataModelType;
timestamp: string;
}
/**
@@ -10,7 +11,7 @@ interface UmbCacheEntryModel<DetailDataModelType> {
* @template DetailDataModelType
*/
export class UmbManagementApiDetailDataCache<DetailDataModelType> {
#entries: Map<string, UmbCacheEntryModel<DetailDataModelType>> = new Map();
#entries: Map<string, UmbDetailCacheEntryModel<DetailDataModelType>> = new Map();
/**
* Checks if an entry exists in the cache
@@ -29,9 +30,10 @@ export class UmbManagementApiDetailDataCache<DetailDataModelType> {
* @memberof UmbManagementApiDetailDataCache
*/
set(id: string, data: DetailDataModelType): void {
const cacheEntry: UmbCacheEntryModel<DetailDataModelType> = {
const cacheEntry: UmbDetailCacheEntryModel<DetailDataModelType> = {
id: id,
data,
timestamp: new Date().toISOString(),
};
this.#entries.set(id, cacheEntry);

View File

@@ -1,5 +1,5 @@
import { UMB_MANAGEMENT_API_SERVER_EVENT_CONTEXT } from '../server-event/constants.js';
import type { UmbManagementApiInflightRequestCache } from '../inflight-request/cache.js';
import type { UmbManagementApiInFlightRequestCache } from '../inflight-request/cache.js';
import type { UmbManagementApiDetailDataCache } from './cache.js';
import {
tryExecute,
@@ -21,7 +21,7 @@ export interface UmbManagementApiDetailDataRequestManagerArgs<
update: (id: string, data: UpdateRequestModelType) => Promise<UmbApiResponse<{ data: unknown }>>;
delete: (id: string) => Promise<UmbApiResponse<{ data: unknown }>>;
dataCache: UmbManagementApiDetailDataCache<DetailResponseModelType>;
inflightRequestCache: UmbManagementApiInflightRequestCache<DetailResponseModelType>;
inflightRequestCache: UmbManagementApiInFlightRequestCache<DetailResponseModelType>;
}
export class UmbManagementApiDetailDataRequestManager<
@@ -30,7 +30,7 @@ export class UmbManagementApiDetailDataRequestManager<
UpdateRequestModelType,
> extends UmbControllerBase {
#dataCache: UmbManagementApiDetailDataCache<DetailResponseModelType>;
#inflightRequestCache: UmbManagementApiInflightRequestCache<DetailResponseModelType>;
#inflightRequestCache: UmbManagementApiInFlightRequestCache<DetailResponseModelType>;
#create;
#read;
@@ -86,11 +86,13 @@ export class UmbManagementApiDetailDataRequestManager<
const hasInflightRequest = this.#inflightRequestCache.has(inflightCacheKey);
const request = hasInflightRequest
? this.#inflightRequestCache.get(inflightCacheKey)
? this.#inflightRequestCache.get(inflightCacheKey)?.requestPromise
: tryExecute(this, this.#read(id));
if (!request) {
throw new Error(`Failed to create or retrieve 'read' request for ID: ${id} (cache key: ${inflightCacheKey}). Aborting read.`);
throw new Error(
`Failed to create or retrieve 'read' request for ID: ${id} (cache key: ${inflightCacheKey}). Aborting read.`,
);
}
this.#inflightRequestCache.set(inflightCacheKey, request);

View File

@@ -1,5 +1,11 @@
import type { UmbApiResponse } from '@umbraco-cms/backoffice/resources';
interface UmbInFlightRequestCacheEntryModel<ResponseModelType> {
key: string;
requestPromise: Promise<RequestResolvedType<ResponseModelType>>;
timestamp: string;
}
// Keep internal
type RequestResolvedType<ResponseModelType> = UmbApiResponse<{ data?: ResponseModelType }>;
@@ -8,8 +14,8 @@ type RequestResolvedType<ResponseModelType> = UmbApiResponse<{ data?: ResponseMo
* @class UmbManagementApiInflightRequestCache
* @template ResponseModelType
*/
export class UmbManagementApiInflightRequestCache<ResponseModelType> {
#entries = new Map<string, Promise<RequestResolvedType<ResponseModelType>>>();
export class UmbManagementApiInFlightRequestCache<ResponseModelType> {
#entries = new Map<string, UmbInFlightRequestCacheEntryModel<ResponseModelType>>();
/**
* Checks if an entry exists in the cache
@@ -28,7 +34,11 @@ export class UmbManagementApiInflightRequestCache<ResponseModelType> {
* @memberof UmbManagementApiInflightRequestCache
*/
set(key: string, promise: Promise<RequestResolvedType<ResponseModelType>>): void {
this.#entries.set(key, promise);
this.#entries.set(key, {
key,
requestPromise: promise,
timestamp: new Date().toISOString(),
});
}
/**
@@ -37,7 +47,7 @@ export class UmbManagementApiInflightRequestCache<ResponseModelType> {
* @returns {Promise<RequestResolvedType<ResponseModelType>> | undefined} - The cached promise or undefined if not found
* @memberof UmbManagementApiInflightRequestCache
*/
get(key: string): Promise<RequestResolvedType<ResponseModelType>> | undefined {
get(key: string): UmbInFlightRequestCacheEntryModel<ResponseModelType> | undefined {
return this.#entries.get(key);
}

View File

@@ -1,7 +1,8 @@
// Keep internal
interface UmbCacheEntryModel<ItemDataModelType> {
interface UmbItemCacheEntryModel<ItemDataModelType> {
id: string;
data: ItemDataModelType;
timestamp: string;
}
/**
@@ -10,7 +11,7 @@ interface UmbCacheEntryModel<ItemDataModelType> {
* @template ItemDataModelType
*/
export class UmbManagementApiItemDataCache<ItemDataModelType> {
#entries: Map<string, UmbCacheEntryModel<ItemDataModelType>> = new Map();
#entries: Map<string, UmbItemCacheEntryModel<ItemDataModelType>> = new Map();
/**
* Checks if an entry exists in the cache
@@ -29,9 +30,10 @@ export class UmbManagementApiItemDataCache<ItemDataModelType> {
* @memberof UmbManagementApiItemDataCache
*/
set(id: string, data: ItemDataModelType): void {
const cacheEntry: UmbCacheEntryModel<ItemDataModelType> = {
const cacheEntry: UmbItemCacheEntryModel<ItemDataModelType> = {
id: id,
data,
timestamp: new Date().toISOString(),
};
this.#entries.set(id, cacheEntry);

View File

@@ -94,8 +94,13 @@ export class UmbManagementApiServerEventContext extends UmbContextBase {
})
.build();
this.#connection.on('notify', (payload: UmbManagementApiServerEventModel) => {
this.#events.next(payload);
this.#connection.on('notify', (payload) => {
const event: UmbManagementApiServerEventModel = {
...payload,
clientTimestamp: new Date().toISOString(),
};
this.#events.next(event);
});
this.#connection

View File

@@ -2,4 +2,5 @@ export interface UmbManagementApiServerEventModel {
eventSource: string;
eventType: string;
key: string;
clientTimestamp: string;
}