mirror of
https://github.com/HeyPuter/puter.git
synced 2026-09-24 14:16:06 +00:00
Use ClickHouse for app open stats (#2662)
When a global ClickHouse client is available, run two parallel ClickHouse queries (JSONEachRow) to fetch per-app open_count and distinct user_count, parse results into Maps (parsing counts as integers). If ClickHouse is not present, fall back to the original MySQL aggregate queries. Apps list still comes from MySQL as before. This enables using ClickHouse for analytics performance while preserving the existing behavior as a fallback.
This commit is contained in:
@@ -552,25 +552,50 @@ class AppInformationService extends BaseService {
|
||||
|
||||
const db = this.services.get('database').get(DB_READ, 'apps');
|
||||
|
||||
// Fetch all stats in two aggregate queries instead of per-app queries
|
||||
const [openCounts, userCounts] = await Promise.all([
|
||||
db.read(`
|
||||
SELECT app_uid, COUNT(_id) AS open_count
|
||||
FROM app_opens
|
||||
GROUP BY app_uid
|
||||
`),
|
||||
db.read(`
|
||||
SELECT app_uid, COUNT(DISTINCT user_id) AS user_count
|
||||
FROM app_opens
|
||||
GROUP BY app_uid
|
||||
`),
|
||||
]);
|
||||
let openCountMap;
|
||||
let userCountMap;
|
||||
|
||||
// Build maps for quick lookup
|
||||
const openCountMap = new Map(openCounts.map(row => [row.app_uid, row.open_count]));
|
||||
const userCountMap = new Map(userCounts.map(row => [row.app_uid, row.user_count]));
|
||||
if ( global.clickhouseClient ) {
|
||||
const [openResult, userResult] = await Promise.all([
|
||||
global.clickhouseClient.query({
|
||||
query: `
|
||||
SELECT app_uid, COUNT(_id) AS open_count
|
||||
FROM app_opens
|
||||
GROUP BY app_uid
|
||||
`,
|
||||
format: 'JSONEachRow',
|
||||
}),
|
||||
global.clickhouseClient.query({
|
||||
query: `
|
||||
SELECT app_uid, COUNT(DISTINCT user_id) AS user_count
|
||||
FROM app_opens
|
||||
GROUP BY app_uid
|
||||
`,
|
||||
format: 'JSONEachRow',
|
||||
}),
|
||||
]);
|
||||
const openRows = await openResult.json();
|
||||
const userRows = await userResult.json();
|
||||
openCountMap = new Map(openRows.map(row => [row.app_uid, parseInt(row.open_count, 10)]));
|
||||
userCountMap = new Map(userRows.map(row => [row.app_uid, parseInt(row.user_count, 10)]));
|
||||
} else {
|
||||
const [openCounts, userCounts] = await Promise.all([
|
||||
db.read(`
|
||||
SELECT app_uid, COUNT(_id) AS open_count
|
||||
FROM app_opens
|
||||
GROUP BY app_uid
|
||||
`),
|
||||
db.read(`
|
||||
SELECT app_uid, COUNT(DISTINCT user_id) AS user_count
|
||||
FROM app_opens
|
||||
GROUP BY app_uid
|
||||
`),
|
||||
]);
|
||||
openCountMap = new Map(openCounts.map(row => [row.app_uid, row.open_count]));
|
||||
userCountMap = new Map(userCounts.map(row => [row.app_uid, row.user_count]));
|
||||
}
|
||||
|
||||
// Get all app UIDs and update the cache
|
||||
// Get all app UIDs and update the cache (apps list lives in MySQL)
|
||||
const apps = await db.read('SELECT uid FROM apps');
|
||||
|
||||
for ( const app of apps ) {
|
||||
|
||||
Reference in New Issue
Block a user