mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-05-23 17:19:37 +02:00
[clsi-cache] meter ingress and egress bandwidth (#27143)
* [mics] fix "app" label in clsi-cache metrics in dev-env * [clsi-cache] validate filePath when processing file * [clsi-cache] meter ingress and egress bandwidth Files are downloaded directly from nginx, hence we cannot meter egress in clsi-cache easily. GitOrigin-RevId: 24de8c41728f0e9c984113c1470dec6153e75f20
This commit is contained in:
@@ -145,6 +145,24 @@ class LoggerStream extends Transform {
|
||||
}
|
||||
}
|
||||
|
||||
class MeteredStream extends Transform {
|
||||
#Metrics
|
||||
#metric
|
||||
#labels
|
||||
|
||||
constructor(Metrics, metric, labels) {
|
||||
super()
|
||||
this.#Metrics = Metrics
|
||||
this.#metric = metric
|
||||
this.#labels = labels
|
||||
}
|
||||
|
||||
_transform(chunk, encoding, callback) {
|
||||
this.#Metrics.count(this.#metric, chunk.byteLength, 1, this.#labels)
|
||||
callback(null, chunk)
|
||||
}
|
||||
}
|
||||
|
||||
// Export our classes
|
||||
|
||||
module.exports = {
|
||||
@@ -153,6 +171,7 @@ module.exports = {
|
||||
LoggerStream,
|
||||
LimitedStream,
|
||||
TimeoutStream,
|
||||
MeteredStream,
|
||||
SizeExceededError,
|
||||
AbortError,
|
||||
}
|
||||
|
||||
3
package-lock.json
generated
3
package-lock.json
generated
@@ -42590,6 +42590,7 @@
|
||||
"@overleaf/o-error": "*",
|
||||
"@overleaf/promise-utils": "*",
|
||||
"@overleaf/settings": "*",
|
||||
"@overleaf/stream-utils": "*",
|
||||
"archiver": "5.3.2",
|
||||
"async": "^3.2.5",
|
||||
"body-parser": "^1.20.3",
|
||||
@@ -42625,6 +42626,7 @@
|
||||
"@overleaf/o-error": "*",
|
||||
"@overleaf/promise-utils": "*",
|
||||
"@overleaf/settings": "*",
|
||||
"@overleaf/stream-utils": "*",
|
||||
"body-parser": "^1.20.3",
|
||||
"bunyan": "^1.8.15",
|
||||
"celebrate": "^15.0.3",
|
||||
@@ -44807,6 +44809,7 @@
|
||||
"@overleaf/promise-utils": "*",
|
||||
"@overleaf/redis-wrapper": "*",
|
||||
"@overleaf/settings": "*",
|
||||
"@overleaf/stream-utils": "*",
|
||||
"@phosphor-icons/react": "^2.1.7",
|
||||
"@slack/webhook": "^7.0.2",
|
||||
"@stripe/stripe-js": "^7.3.0",
|
||||
|
||||
@@ -13,6 +13,7 @@ const {
|
||||
const logger = require('@overleaf/logger')
|
||||
const Metrics = require('@overleaf/metrics')
|
||||
const Settings = require('@overleaf/settings')
|
||||
const { MeteredStream } = require('@overleaf/stream-utils')
|
||||
const { CACHE_SUBDIR } = require('./OutputCacheManager')
|
||||
const { isExtraneousFile } = require('./ResourceWriter')
|
||||
|
||||
@@ -204,7 +205,13 @@ async function downloadOutputDotSynctexFromCompileCache(
|
||||
const dst = Path.join(outputDir, 'output.synctex.gz')
|
||||
const tmp = dst + crypto.randomUUID()
|
||||
try {
|
||||
await pipeline(stream, fs.createWriteStream(tmp))
|
||||
await pipeline(
|
||||
stream,
|
||||
new MeteredStream(Metrics, 'clsi_cache_egress', {
|
||||
path: 'output.synctex.gz',
|
||||
}),
|
||||
fs.createWriteStream(tmp)
|
||||
)
|
||||
await fs.promises.rename(tmp, dst)
|
||||
} catch (err) {
|
||||
try {
|
||||
@@ -253,6 +260,7 @@ async function downloadLatestCompileCache(projectId, userId, compileDir) {
|
||||
let abort = false
|
||||
await pipeline(
|
||||
stream,
|
||||
new MeteredStream(Metrics, 'clsi_cache_egress', { path: 'output.tar.gz' }),
|
||||
createGunzip(),
|
||||
tarFs.extract(compileDir, {
|
||||
// use ignore hook for counting entries (files+folders) and validation.
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
"@overleaf/o-error": "*",
|
||||
"@overleaf/promise-utils": "*",
|
||||
"@overleaf/settings": "*",
|
||||
"@overleaf/stream-utils": "*",
|
||||
"archiver": "5.3.2",
|
||||
"async": "^3.2.5",
|
||||
"body-parser": "^1.20.3",
|
||||
|
||||
@@ -11,6 +11,8 @@ const CompileController = require('./CompileController')
|
||||
const { expressify } = require('@overleaf/promise-utils')
|
||||
const ClsiCacheHandler = require('./ClsiCacheHandler')
|
||||
const ProjectGetter = require('../Project/ProjectGetter')
|
||||
const { MeteredStream } = require('@overleaf/stream-utils')
|
||||
const Metrics = require('@overleaf/metrics')
|
||||
|
||||
/**
|
||||
* Download a file from a specific build on the clsi-cache.
|
||||
@@ -64,7 +66,13 @@ async function downloadFromCache(req, res) {
|
||||
)
|
||||
try {
|
||||
res.writeHead(response.status)
|
||||
await pipeline(stream, res)
|
||||
await pipeline(
|
||||
stream,
|
||||
new MeteredStream(Metrics, 'clsi_cache_egress', {
|
||||
path: ClsiCacheHandler.getEgressLabel(filename),
|
||||
}),
|
||||
res
|
||||
)
|
||||
} catch (err) {
|
||||
const reqAborted = Boolean(req.destroyed)
|
||||
const streamingStarted = Boolean(res.headersSent)
|
||||
|
||||
@@ -34,6 +34,21 @@ function validateFilename(filename) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep in sync with getIngressLabel in services/clsi-cache/app/js/utils.js
|
||||
*
|
||||
* @param {string} fsPath
|
||||
* @return {string}
|
||||
*/
|
||||
function getEgressLabel(fsPath) {
|
||||
if (fsPath.endsWith('.blg')) {
|
||||
// .blg files may have custom names and can be in nested folders.
|
||||
return 'output.blg'
|
||||
}
|
||||
// The rest is limited to 5 file names via validateFilename: output.pdf, etc.
|
||||
return fsPath
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the cache on all clsi-cache instances.
|
||||
*
|
||||
@@ -224,6 +239,7 @@ async function prepareCacheSource(
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getEgressLabel,
|
||||
clearCache,
|
||||
getOutputFile,
|
||||
getLatestOutputFile,
|
||||
|
||||
@@ -7,6 +7,7 @@ const SplitTestHandler = require('../SplitTests/SplitTestHandler')
|
||||
const UserGetter = require('../User/UserGetter')
|
||||
const Settings = require('@overleaf/settings')
|
||||
const { fetchJson, RequestFailedError } = require('@overleaf/fetch-utils')
|
||||
const Metrics = require('@overleaf/metrics')
|
||||
|
||||
/**
|
||||
* Get the most recent build and metadata
|
||||
@@ -71,7 +72,13 @@ async function getLatestCompileResult(projectId, userId) {
|
||||
async function tryGetLatestCompileResult(projectId, userId, signal) {
|
||||
const {
|
||||
internal: { location: metaLocation },
|
||||
external: { isUpToDate, allFiles, zone, shard: clsiCacheShard },
|
||||
external: {
|
||||
isUpToDate,
|
||||
allFiles,
|
||||
zone,
|
||||
shard: clsiCacheShard,
|
||||
size: jsonSize,
|
||||
},
|
||||
} = await getLatestBuildFromCache(
|
||||
projectId,
|
||||
userId,
|
||||
@@ -93,6 +100,9 @@ async function tryGetLatestCompileResult(projectId, userId, signal) {
|
||||
}
|
||||
throw err
|
||||
}
|
||||
Metrics.count('clsi_cache_egress', jsonSize, 1, {
|
||||
path: ClsiCacheHandler.getEgressLabel('output.overleaf.json'),
|
||||
})
|
||||
|
||||
const [, editorId, buildId] = metaLocation.match(
|
||||
/\/build\/([a-f0-9-]+?)-([a-f0-9]+-[a-f0-9]+)\//
|
||||
|
||||
@@ -91,6 +91,7 @@
|
||||
"@overleaf/promise-utils": "*",
|
||||
"@overleaf/redis-wrapper": "*",
|
||||
"@overleaf/settings": "*",
|
||||
"@overleaf/stream-utils": "*",
|
||||
"@phosphor-icons/react": "^2.1.7",
|
||||
"@slack/webhook": "^7.0.2",
|
||||
"@stripe/stripe-js": "^7.3.0",
|
||||
|
||||
Reference in New Issue
Block a user