fix: remove recommended app caching in favour of stronger caching at app fetch layer (#2748)

This commit is contained in:
Daniel Salazar
2026-03-29 13:07:00 -07:00
committed by GitHub
parent 8c47f12aa5
commit 107521e45a
5 changed files with 8 additions and 73 deletions
+1 -1
View File
@@ -118,6 +118,6 @@ export const setRedisCacheValue = async (
if ( ttlSeconds ) {
await redisClient.set(key, value, 'EX', ttlSeconds);
} else {
await redisClient.set(key, value);
await redisClient.set(key, value, 'EX', 60 * 10); // default to 10 min if no ttl provided
}
};
+2 -2
View File
@@ -364,7 +364,7 @@ export async function get_app (options) {
const cacheApp = async (app) => {
if ( ! app ) return;
AppRedisCacheSpace.setCachedApp(app, {
ttlSeconds: 30,
ttlSeconds: 300,
});
};
const isDecoratedAppCacheEntry = (app) => (
@@ -550,7 +550,7 @@ export const get_apps = spanify('get_apps', async (specifiers, options = {}) =>
const cacheApp = async (app) => {
if ( ! app ) return;
AppRedisCacheSpace.setCachedApp(app, {
ttlSeconds: 60,
ttlSeconds: 300,
});
};
@@ -81,7 +81,7 @@ export const AppRedisCacheSpace = {
if ( ! app ) return;
const serialized = JSON.stringify(app);
const writes = AppRedisCacheSpace.keysForApp(app)
.map(key => setKey(key, serialized, { ttlSeconds }));
.map(key => setKey(key, serialized, { ttlSeconds: ttlSeconds || 60 }));
if ( writes.length ) {
await Promise.all(writes);
}
@@ -1,21 +0,0 @@
/*
* Copyright (C) 2024-present Puter Technologies Inc.
*
* This file is part of Puter.
*
* Puter is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
export const RecommendedAppsRedisCacheSpace = {
key: ({ iconSize } = {}) => `global:recommended-apps${iconSize ? `:icon-size:${iconSize}` : ''}`,
};
@@ -17,15 +17,11 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { redisClient } from '../../clients/redis/redisSingleton.js';
import { deleteRedisKeys } from '../../clients/redis/deleteRedisKeys.js';
import { setRedisCacheValue } from '../../clients/redis/cacheUpdate.js';
import { get_apps } from '../../helpers.js';
import BaseService from '../../services/BaseService.js';
import { RecommendedAppsRedisCacheSpace } from './RecommendedAppsRedisCacheSpace.js';
import { BaseService } from '../../services/BaseService.js';
export default class RecommendedAppsService extends BaseService {
static APP_NAMES = [
appNames = new Set([
'app-center',
'dev-center',
'editor',
@@ -44,49 +40,13 @@ export default class RecommendedAppsService extends BaseService {
'galaxy-troops',
'blend-fruits',
'traffic-tap-puzzle',
];
_construct () {
this.app_names = new Set(RecommendedAppsService.APP_NAMES);
}
'__on_boot.consolidation' () {
const svc_appIcon = this.services.get('app-icon');
const svc_event = this.services.get('event');
svc_event.on('apps.invalidate', async (_, { app }) => {
const sizes = svc_appIcon.getSizes();
// If it's a single-app invalidation, only invalidate if the
// app is in the list of recommended apps
if ( app ) {
const name = app.name;
if ( ! this.app_names.has(name) ) return;
}
const keys = [RecommendedAppsRedisCacheSpace.key()];
for ( const size of sizes ) {
const key = RecommendedAppsRedisCacheSpace.key({ iconSize: size });
keys.push(key);
}
await deleteRedisKeys(keys);
});
}
]);
async get_recommended_apps ({ icon_size: iconSize }) {
const recommendedCacheKey = RecommendedAppsRedisCacheSpace.key({ iconSize });
const cachedRecommended = await redisClient.get(recommendedCacheKey);
if ( cachedRecommended ) {
try {
return JSON.parse(cachedRecommended);
} catch (e) {
// no op cache is in an invalid state
}
}
// Prepare each app for returning to user by only returning the necessary fields
// and adding them to the retobj array
let recommended = (await get_apps(Array.from(this.app_names).map(name => ({ name })))).filter(app => !!app).map(app => {
let recommended = (await get_apps(Array.from(this.appNames).map(name => ({ name })))).filter(app => !!app).map(app => {
return {
uuid: app.uid,
name: app.name,
@@ -108,10 +68,6 @@ export default class RecommendedAppsService extends BaseService {
});
}
await setRedisCacheValue(recommendedCacheKey, JSON.stringify(recommended), {
eventData: recommended,
});
return recommended;
}
}