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

View File

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

View File

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

View File

@@ -1,5 +1,5 @@
import { UMB_MANAGEMENT_API_SERVER_EVENT_CONTEXT } from '../server-event/constants.js'; 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 type { UmbManagementApiDetailDataCache } from './cache.js';
import { import {
tryExecute, tryExecute,
@@ -21,7 +21,7 @@ export interface UmbManagementApiDetailDataRequestManagerArgs<
update: (id: string, data: UpdateRequestModelType) => Promise<UmbApiResponse<{ data: unknown }>>; update: (id: string, data: UpdateRequestModelType) => Promise<UmbApiResponse<{ data: unknown }>>;
delete: (id: string) => Promise<UmbApiResponse<{ data: unknown }>>; delete: (id: string) => Promise<UmbApiResponse<{ data: unknown }>>;
dataCache: UmbManagementApiDetailDataCache<DetailResponseModelType>; dataCache: UmbManagementApiDetailDataCache<DetailResponseModelType>;
inflightRequestCache: UmbManagementApiInflightRequestCache<DetailResponseModelType>; inflightRequestCache: UmbManagementApiInFlightRequestCache<DetailResponseModelType>;
} }
export class UmbManagementApiDetailDataRequestManager< export class UmbManagementApiDetailDataRequestManager<
@@ -30,7 +30,7 @@ export class UmbManagementApiDetailDataRequestManager<
UpdateRequestModelType, UpdateRequestModelType,
> extends UmbControllerBase { > extends UmbControllerBase {
#dataCache: UmbManagementApiDetailDataCache<DetailResponseModelType>; #dataCache: UmbManagementApiDetailDataCache<DetailResponseModelType>;
#inflightRequestCache: UmbManagementApiInflightRequestCache<DetailResponseModelType>; #inflightRequestCache: UmbManagementApiInFlightRequestCache<DetailResponseModelType>;
#create; #create;
#read; #read;
@@ -86,11 +86,13 @@ export class UmbManagementApiDetailDataRequestManager<
const hasInflightRequest = this.#inflightRequestCache.has(inflightCacheKey); const hasInflightRequest = this.#inflightRequestCache.has(inflightCacheKey);
const request = hasInflightRequest const request = hasInflightRequest
? this.#inflightRequestCache.get(inflightCacheKey) ? this.#inflightRequestCache.get(inflightCacheKey)?.requestPromise
: tryExecute(this, this.#read(id)); : tryExecute(this, this.#read(id));
if (!request) { 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); this.#inflightRequestCache.set(inflightCacheKey, request);

View File

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

View File

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

View File

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

View File

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