mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Merge pull request #20299 from overleaf/jpa-object-persistor-metrics-dep
[object-persistor] depend on @overleaf/metrics directly GitOrigin-RevId: eb0c07af8101d44def14154abb552bc77254e074
This commit is contained in:
parent
3ba3ffe56d
commit
8fa676082e
11 changed files with 50 additions and 58 deletions
|
@ -19,11 +19,10 @@
|
||||||
},
|
},
|
||||||
"author": "Overleaf (https://www.overleaf.com/)",
|
"author": "Overleaf (https://www.overleaf.com/)",
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"peerDependencies": {
|
|
||||||
"@overleaf/logger": "*"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@google-cloud/storage": "^6.10.1",
|
"@google-cloud/storage": "^6.10.1",
|
||||||
|
"@overleaf/logger": "*",
|
||||||
|
"@overleaf/metrics": "*",
|
||||||
"@overleaf/o-error": "*",
|
"@overleaf/o-error": "*",
|
||||||
"aws-sdk": "^2.718.0",
|
"aws-sdk": "^2.718.0",
|
||||||
"fast-crc32c": "overleaf/node-fast-crc32c#aae6b2a4c7a7a159395df9cc6c38dfde702d6f51",
|
"fast-crc32c": "overleaf/node-fast-crc32c#aae6b2a4c7a7a159395df9cc6c38dfde702d6f51",
|
||||||
|
@ -32,7 +31,6 @@
|
||||||
"tiny-async-pool": "^1.1.0"
|
"tiny-async-pool": "^1.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@overleaf/logger": "*",
|
|
||||||
"chai": "^4.3.6",
|
"chai": "^4.3.6",
|
||||||
"chai-as-promised": "^7.1.1",
|
"chai-as-promised": "^7.1.1",
|
||||||
"mocha": "^10.2.0",
|
"mocha": "^10.2.0",
|
||||||
|
|
|
@ -5,6 +5,7 @@ const globCallbacks = require('glob')
|
||||||
const Path = require('path')
|
const Path = require('path')
|
||||||
const { pipeline } = require('stream/promises')
|
const { pipeline } = require('stream/promises')
|
||||||
const { promisify } = require('util')
|
const { promisify } = require('util')
|
||||||
|
const Metrics = require('@overleaf/metrics')
|
||||||
|
|
||||||
const AbstractPersistor = require('./AbstractPersistor')
|
const AbstractPersistor = require('./AbstractPersistor')
|
||||||
const { ReadError, WriteError } = require('./Errors')
|
const { ReadError, WriteError } = require('./Errors')
|
||||||
|
@ -16,7 +17,6 @@ module.exports = class FSPersistor extends AbstractPersistor {
|
||||||
constructor(settings = {}) {
|
constructor(settings = {}) {
|
||||||
super()
|
super()
|
||||||
this.useSubdirectories = Boolean(settings.useSubdirectories)
|
this.useSubdirectories = Boolean(settings.useSubdirectories)
|
||||||
this.metrics = settings.Metrics
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async sendFile(location, target, source) {
|
async sendFile(location, target, source) {
|
||||||
|
@ -231,17 +231,12 @@ module.exports = class FSPersistor extends AbstractPersistor {
|
||||||
transforms.push(md5Observer.transform)
|
transforms.push(md5Observer.transform)
|
||||||
}
|
}
|
||||||
|
|
||||||
let timer
|
const timer = new Metrics.Timer('writingFile')
|
||||||
if (this.metrics) {
|
|
||||||
timer = new this.metrics.Timer('writingFile')
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const writeStream = fs.createWriteStream(tempFilePath)
|
const writeStream = fs.createWriteStream(tempFilePath)
|
||||||
await pipeline(stream, ...transforms, writeStream)
|
await pipeline(stream, ...transforms, writeStream)
|
||||||
if (timer) {
|
timer.done()
|
||||||
timer.done()
|
|
||||||
}
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
await this._cleanupTempFile(tempFilePath)
|
await this._cleanupTempFile(tempFilePath)
|
||||||
throw new WriteError(
|
throw new WriteError(
|
||||||
|
|
|
@ -50,7 +50,6 @@ module.exports = class GcsPersistor extends AbstractPersistor {
|
||||||
// egress from us to gcs
|
// egress from us to gcs
|
||||||
const observeOptions = {
|
const observeOptions = {
|
||||||
metric: 'gcs.egress',
|
metric: 'gcs.egress',
|
||||||
Metrics: this.settings.Metrics,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let sourceMd5 = opts.sourceMd5
|
let sourceMd5 = opts.sourceMd5
|
||||||
|
@ -138,7 +137,6 @@ module.exports = class GcsPersistor extends AbstractPersistor {
|
||||||
// ingress to us from gcs
|
// ingress to us from gcs
|
||||||
const observer = new PersistorHelper.ObserverStream({
|
const observer = new PersistorHelper.ObserverStream({
|
||||||
metric: 'gcs.ingress',
|
metric: 'gcs.ingress',
|
||||||
Metrics: this.settings.Metrics,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const pass = new PassThrough()
|
const pass = new PassThrough()
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
const AbstractPersistor = require('./AbstractPersistor')
|
const AbstractPersistor = require('./AbstractPersistor')
|
||||||
const Logger = require('@overleaf/logger')
|
const Logger = require('@overleaf/logger')
|
||||||
|
const Metrics = require('@overleaf/metrics')
|
||||||
const Stream = require('stream')
|
const Stream = require('stream')
|
||||||
const { pipeline } = require('stream/promises')
|
const { pipeline } = require('stream/promises')
|
||||||
const { NotFoundError, WriteError } = require('./Errors')
|
const { NotFoundError, WriteError } = require('./Errors')
|
||||||
|
@ -194,9 +195,7 @@ module.exports = class MigrationPersistor extends AbstractPersistor {
|
||||||
},
|
},
|
||||||
err
|
err
|
||||||
)
|
)
|
||||||
if (this.settings.Metrics) {
|
Metrics.inc('fallback.copy.failure')
|
||||||
this.settings.Metrics.inc('fallback.copy.failure')
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this.primaryPersistor.deleteObject(destBucket, destKey)
|
await this.primaryPersistor.deleteObject(destBucket, destKey)
|
||||||
|
|
|
@ -9,19 +9,14 @@ function getPersistor(backend, settings) {
|
||||||
switch (backend) {
|
switch (backend) {
|
||||||
case 'aws-sdk':
|
case 'aws-sdk':
|
||||||
case 's3':
|
case 's3':
|
||||||
return new S3Persistor(
|
return new S3Persistor(settings.s3)
|
||||||
Object.assign({}, settings.s3, { Metrics: settings.Metrics })
|
|
||||||
)
|
|
||||||
case 'fs':
|
case 'fs':
|
||||||
return new FSPersistor({
|
return new FSPersistor({
|
||||||
useSubdirectories: settings.useSubdirectories,
|
useSubdirectories: settings.useSubdirectories,
|
||||||
paths: settings.paths,
|
paths: settings.paths,
|
||||||
Metrics: settings.Metrics,
|
|
||||||
})
|
})
|
||||||
case 'gcs':
|
case 'gcs':
|
||||||
return new GcsPersistor(
|
return new GcsPersistor(settings.gcs)
|
||||||
Object.assign({}, settings.gcs, { Metrics: settings.Metrics })
|
|
||||||
)
|
|
||||||
default:
|
default:
|
||||||
throw new SettingsError('unknown backend', { backend })
|
throw new SettingsError('unknown backend', { backend })
|
||||||
}
|
}
|
||||||
|
@ -44,11 +39,7 @@ module.exports = function create(settings) {
|
||||||
if (settings.fallback && settings.fallback.backend) {
|
if (settings.fallback && settings.fallback.backend) {
|
||||||
const primary = persistor
|
const primary = persistor
|
||||||
const fallback = getPersistor(settings.fallback.backend, settings)
|
const fallback = getPersistor(settings.fallback.backend, settings)
|
||||||
persistor = new MigrationPersistor(
|
persistor = new MigrationPersistor(primary, fallback, settings.fallback)
|
||||||
primary,
|
|
||||||
fallback,
|
|
||||||
Object.assign({}, settings.fallback, { Metrics: settings.Metrics })
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return persistor
|
return persistor
|
||||||
|
|
|
@ -2,31 +2,31 @@ const Crypto = require('crypto')
|
||||||
const Stream = require('stream')
|
const Stream = require('stream')
|
||||||
const { pipeline } = require('stream/promises')
|
const { pipeline } = require('stream/promises')
|
||||||
const Logger = require('@overleaf/logger')
|
const Logger = require('@overleaf/logger')
|
||||||
|
const Metrics = require('@overleaf/metrics')
|
||||||
const { WriteError, NotFoundError } = require('./Errors')
|
const { WriteError, NotFoundError } = require('./Errors')
|
||||||
|
|
||||||
// Observes data that passes through and computes some metadata for it
|
/**
|
||||||
// - specifically, it computes the number of bytes transferred, and optionally
|
* Observes data that passes through and optionally computes hash for content.
|
||||||
// computes a cryptographic hash based on the 'hash' option. e.g., pass
|
*/
|
||||||
// { hash: 'md5' } to compute the md5 hash of the stream
|
|
||||||
// - if 'metric' is supplied as an option, this metric will be incremented by
|
|
||||||
// the number of bytes transferred
|
|
||||||
class ObserverStream extends Stream.Transform {
|
class ObserverStream extends Stream.Transform {
|
||||||
constructor(options) {
|
/**
|
||||||
super({ autoDestroy: true, ...options })
|
* @param {string} metric prefix for metrics
|
||||||
|
* @param {string} hash optional hash algorithm, e.g. 'md5'
|
||||||
|
*/
|
||||||
|
constructor({ metric, hash = '' }) {
|
||||||
|
super({ autoDestroy: true })
|
||||||
|
|
||||||
this.bytes = 0
|
this.bytes = 0
|
||||||
|
|
||||||
if (options.hash) {
|
if (hash) {
|
||||||
this.hash = Crypto.createHash(options.hash)
|
this.hash = Crypto.createHash(hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.metric && options.Metrics) {
|
const onEnd = () => {
|
||||||
const onEnd = () => {
|
Metrics.count(metric, this.bytes)
|
||||||
options.Metrics.count(options.metric, this.bytes)
|
|
||||||
}
|
|
||||||
this.once('error', onEnd)
|
|
||||||
this.once('end', onEnd)
|
|
||||||
}
|
}
|
||||||
|
this.once('error', onEnd)
|
||||||
|
this.once('end', onEnd)
|
||||||
}
|
}
|
||||||
|
|
||||||
_transform(chunk, encoding, done) {
|
_transform(chunk, encoding, done) {
|
||||||
|
|
|
@ -7,6 +7,7 @@ if (https.globalAgent.maxSockets < 300) {
|
||||||
https.globalAgent.maxSockets = 300
|
https.globalAgent.maxSockets = 300
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Metrics = require('@overleaf/metrics')
|
||||||
const AbstractPersistor = require('./AbstractPersistor')
|
const AbstractPersistor = require('./AbstractPersistor')
|
||||||
const PersistorHelper = require('./PersistorHelper')
|
const PersistorHelper = require('./PersistorHelper')
|
||||||
|
|
||||||
|
@ -32,7 +33,6 @@ module.exports = class S3Persistor extends AbstractPersistor {
|
||||||
// egress from us to S3
|
// egress from us to S3
|
||||||
const observeOptions = {
|
const observeOptions = {
|
||||||
metric: 's3.egress',
|
metric: 's3.egress',
|
||||||
Metrics: this.settings.Metrics,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const observer = new PersistorHelper.ObserverStream(observeOptions)
|
const observer = new PersistorHelper.ObserverStream(observeOptions)
|
||||||
|
@ -120,7 +120,6 @@ module.exports = class S3Persistor extends AbstractPersistor {
|
||||||
// ingress from S3 to us
|
// ingress from S3 to us
|
||||||
const observer = new PersistorHelper.ObserverStream({
|
const observer = new PersistorHelper.ObserverStream({
|
||||||
metric: 's3.ingress',
|
metric: 's3.ingress',
|
||||||
Metrics: this.settings.Metrics,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const pass = new PassThrough()
|
const pass = new PassThrough()
|
||||||
|
@ -228,9 +227,7 @@ module.exports = class S3Persistor extends AbstractPersistor {
|
||||||
return md5
|
return md5
|
||||||
}
|
}
|
||||||
// etag is not in md5 format
|
// etag is not in md5 format
|
||||||
if (this.settings.Metrics) {
|
Metrics.inc('s3.md5Download')
|
||||||
this.settings.Metrics.inc('s3.md5Download')
|
|
||||||
}
|
|
||||||
return await PersistorHelper.calculateStreamMd5(
|
return await PersistorHelper.calculateStreamMd5(
|
||||||
await this.getObjectStream(bucketName, key)
|
await this.getObjectStream(bucketName, key)
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,9 +1,28 @@
|
||||||
const SandboxedModule = require('sandboxed-module')
|
const SandboxedModule = require('sandboxed-module')
|
||||||
const chai = require('chai')
|
const chai = require('chai')
|
||||||
|
const sinon = require('sinon')
|
||||||
|
|
||||||
chai.use(require('sinon-chai'))
|
chai.use(require('sinon-chai'))
|
||||||
chai.use(require('chai-as-promised'))
|
chai.use(require('chai-as-promised'))
|
||||||
|
|
||||||
SandboxedModule.configure({
|
SandboxedModule.configure({
|
||||||
|
requires: {
|
||||||
|
'@overleaf/logger': {
|
||||||
|
debug() {},
|
||||||
|
log() {},
|
||||||
|
info() {},
|
||||||
|
warn() {},
|
||||||
|
error() {},
|
||||||
|
err() {},
|
||||||
|
},
|
||||||
|
'@overleaf/metrics': {
|
||||||
|
inc: sinon.stub(),
|
||||||
|
count: sinon.stub(),
|
||||||
|
histogram: sinon.stub(),
|
||||||
|
Timer: class Timer {
|
||||||
|
done() {}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
globals: { Buffer, Math, console, process, URL },
|
globals: { Buffer, Math, console, process, URL },
|
||||||
})
|
})
|
||||||
|
|
|
@ -41,9 +41,6 @@ describe('GcsPersistorTests', function () {
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
Settings = {
|
Settings = {
|
||||||
directoryKeyRegex: /^[0-9a-fA-F]{24}\/[0-9a-fA-F]{24}/,
|
directoryKeyRegex: /^[0-9a-fA-F]{24}\/[0-9a-fA-F]{24}/,
|
||||||
Metrics: {
|
|
||||||
count: sinon.stub(),
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
files = [
|
files = [
|
||||||
|
|
7
package-lock.json
generated
7
package-lock.json
generated
|
@ -293,6 +293,8 @@
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@google-cloud/storage": "^6.10.1",
|
"@google-cloud/storage": "^6.10.1",
|
||||||
|
"@overleaf/logger": "*",
|
||||||
|
"@overleaf/metrics": "*",
|
||||||
"@overleaf/o-error": "*",
|
"@overleaf/o-error": "*",
|
||||||
"aws-sdk": "^2.718.0",
|
"aws-sdk": "^2.718.0",
|
||||||
"fast-crc32c": "overleaf/node-fast-crc32c#aae6b2a4c7a7a159395df9cc6c38dfde702d6f51",
|
"fast-crc32c": "overleaf/node-fast-crc32c#aae6b2a4c7a7a159395df9cc6c38dfde702d6f51",
|
||||||
|
@ -301,7 +303,6 @@
|
||||||
"tiny-async-pool": "^1.1.0"
|
"tiny-async-pool": "^1.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@overleaf/logger": "*",
|
|
||||||
"chai": "^4.3.6",
|
"chai": "^4.3.6",
|
||||||
"chai-as-promised": "^7.1.1",
|
"chai-as-promised": "^7.1.1",
|
||||||
"mocha": "^10.2.0",
|
"mocha": "^10.2.0",
|
||||||
|
@ -311,9 +312,6 @@
|
||||||
"sinon": "^9.2.4",
|
"sinon": "^9.2.4",
|
||||||
"sinon-chai": "^3.7.0",
|
"sinon-chai": "^3.7.0",
|
||||||
"typescript": "^5.0.4"
|
"typescript": "^5.0.4"
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"@overleaf/logger": "*"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"libraries/object-persistor/node_modules/bson": {
|
"libraries/object-persistor/node_modules/bson": {
|
||||||
|
@ -52317,6 +52315,7 @@
|
||||||
"requires": {
|
"requires": {
|
||||||
"@google-cloud/storage": "^6.10.1",
|
"@google-cloud/storage": "^6.10.1",
|
||||||
"@overleaf/logger": "*",
|
"@overleaf/logger": "*",
|
||||||
|
"@overleaf/metrics": "*",
|
||||||
"@overleaf/o-error": "*",
|
"@overleaf/o-error": "*",
|
||||||
"aws-sdk": "^2.718.0",
|
"aws-sdk": "^2.718.0",
|
||||||
"chai": "^4.3.6",
|
"chai": "^4.3.6",
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
const settings = require('@overleaf/settings')
|
const settings = require('@overleaf/settings')
|
||||||
|
|
||||||
const persistorSettings = settings.filestore
|
const persistorSettings = settings.filestore
|
||||||
persistorSettings.Metrics = require('@overleaf/metrics')
|
|
||||||
persistorSettings.paths = settings.path
|
persistorSettings.paths = settings.path
|
||||||
|
|
||||||
const ObjectPersistor = require('@overleaf/object-persistor')
|
const ObjectPersistor = require('@overleaf/object-persistor')
|
||||||
|
|
Loading…
Reference in a new issue