From 4435e2829da76a1e3acb09278105879db0d8ee9b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Julian=20B=C3=A4ume?= <julian.baeume@open-xchange.com>
Date: Wed, 17 Aug 2022 11:37:00 +0200
Subject: [PATCH] add first dedicated metrics to analyse cache behaviour

this should help to get insights about how the caches are filled locally
---
 src/files.js     |  9 +++++++++
 src/manifests.js |  2 +-
 src/version.js   | 27 +++++++++++++++++++++++++++
 3 files changed, 37 insertions(+), 1 deletion(-)

diff --git a/src/files.js b/src/files.js
index 1836884..cc9703c 100644
--- a/src/files.js
+++ b/src/files.js
@@ -7,12 +7,19 @@ import { NotFoundError } from './errors.js'
 import * as redis from './redis.js'
 import zlib from 'node:zlib'
 import { promisify } from 'node:util'
+import { Gauge } from 'prom-client'
+
 const gzip = promisify(zlib.gzip)
 
 const compressFileSize = Number(process.env.COMPRESS_FILE_SIZE)
 const compressionMimeTypes = (process.env.COMPRESS_FILE_TYPES || '').replace(/([.+*?^$()[\]{}|])/g, '\\$1').split(' ')
 const compressionWhitelistRegex = new RegExp(`^(${compressionMimeTypes.join('|')})($|;)`, 'i')
 
+export const fileCacheSizeGauge = new Gauge({
+  name: 'file_cache_size',
+  help: 'Number of entries in file cache'
+})
+
 export function createWritable (body) {
   if (typeof body !== 'string' && !(body instanceof Buffer)) return JSON.stringify(body)
   return body
@@ -96,6 +103,7 @@ export function getFile ({ version, path }) {
 
       if (body) {
         logger.debug(`[Files] Resolve from redis: ${key}`)
+        fileCacheSizeGauge.inc()
         return (cache.getCache()[key] = { body, ...JSON.parse(meta) })
       }
     }
@@ -113,6 +121,7 @@ export function getFile ({ version, path }) {
 
     // overwrite cache with synchronous data
     logger.debug(`[Files] Store in memory: ${key}`)
+    fileCacheSizeGauge.inc()
     return (cache.getCache()[key] = dataFromServer)
   })()
 
diff --git a/src/manifests.js b/src/manifests.js
index 937b0bf..bf014cc 100644
--- a/src/manifests.js
+++ b/src/manifests.js
@@ -27,7 +27,7 @@ export async function fetchViteManifests () {
   return viteManifests.reduce((memo, manifest) => Object.assign(memo, manifest), {})
 }
 
-export function getViteManifests ({ version }) {
+export async function getViteManifests ({ version }) {
   return cache.get(getRedisKey({ version, name: 'viteManifests' }), () => fetchViteManifests())
 }
 
diff --git a/src/version.js b/src/version.js
index 387f259..990dfcb 100644
--- a/src/version.js
+++ b/src/version.js
@@ -3,9 +3,27 @@ import { getRedisKey, hash } from './util.js'
 import { logger } from './logger.js'
 import * as cache from './cache.js'
 import * as redis from './redis.js'
+import { Gauge } from 'prom-client'
+import { getViteManifests } from './manifests.js'
 
 let latestVersion
 
+const manifestFileEntriesGauge = new Gauge({
+  name: 'manifest_file_entries',
+  help: 'Number of entries in merged vite manifest (number of all known files)',
+  async collect () {
+    const version = latestVersion
+    this.set({ version }, Object.keys(await getViteManifests({ version })).length)
+  },
+  labelNames: ['version']
+})
+
+const versionUpdateGauge = new Gauge({
+  name: 'version_update_event',
+  help: 'Timestamp of a version update event',
+  labelNames: ['version']
+})
+
 export const fetchLatestVersion = async () => {
   const infos = await Promise.all(configMap.urls.map(async baseUrl => {
     try {
@@ -36,6 +54,7 @@ export async function getLatestVersion () {
     const version = await redis.client.get(getRedisKey({ name: 'latestVersion' }))
     if (version) {
       logger.info(`[Version] Got initial version from redis: '${version}'`)
+      versionUpdateGauge.setToCurrentTime({ version })
       return (latestVersion = version)
     }
   }
@@ -61,6 +80,7 @@ export function registerLatestVersionListener (client) {
     if (channel !== key) return
     logger.info(`[Version] Received 'updateLatestVersion' event. Clearing cache. New version: '${version}'`)
     await configMap.load()
+    versionUpdateGauge.setToCurrentTime({ version })
     cache.clear()
     latestVersion = version
   })
@@ -73,6 +93,12 @@ export async function updateVersionProcessor () {
     getLatestVersion(),
     fetchLatestVersion()
   ])
+
+  // don't wait for the data, can be done in background
+  getViteManifests({ fetchedVersion }).then(manifests => {
+    manifestFileEntriesGauge.set({ version: fetchedVersion }, Object.keys(manifests).length)
+  })
+
   if (storedVersion === fetchedVersion) {
     logger.info(`[Version] No new version has been found. No update needed. Current version: ${storedVersion}`)
     return fetchedVersion
@@ -91,6 +117,7 @@ export async function updateVersionProcessor () {
       await redis.client.set(getRedisKey({ name: 'prevProcessedVersion' }), fetchedVersion)
     }
   } else {
+    versionUpdateGauge.setToCurrentTime({ version: latestVersion })
     // if redis is disabled, this will only be trigger by a setInterval and not from a redis event
     logger.info('[Version] Clear local cache due to version update.')
     cache.clear()
-- 
GitLab