|
@@ -3,9 +3,9 @@ const fs = require('fs')
|
|
|
const fsPromises = require('fs/promises')
|
|
const fsPromises = require('fs/promises')
|
|
|
const globCallbacks = require('glob')
|
|
const globCallbacks = require('glob')
|
|
|
const Path = require('path')
|
|
const Path = require('path')
|
|
|
|
|
+const { PassThrough } = require('stream')
|
|
|
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')
|
|
@@ -63,6 +63,10 @@ module.exports = class FSPersistor extends AbstractPersistor {
|
|
|
|
|
|
|
|
// opts may be {start: Number, end: Number}
|
|
// opts may be {start: Number, end: Number}
|
|
|
async getObjectStream(location, name, opts = {}) {
|
|
async getObjectStream(location, name, opts = {}) {
|
|
|
|
|
+ const observer = new PersistorHelper.ObserverStream({
|
|
|
|
|
+ metric: 'fs.ingress', // ingress to us from disk
|
|
|
|
|
+ bucket: location,
|
|
|
|
|
+ })
|
|
|
const fsPath = this._getFsPath(location, name)
|
|
const fsPath = this._getFsPath(location, name)
|
|
|
|
|
|
|
|
try {
|
|
try {
|
|
@@ -76,7 +80,11 @@ module.exports = class FSPersistor extends AbstractPersistor {
|
|
|
)
|
|
)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- return fs.createReadStream(null, opts)
|
|
|
|
|
|
|
+ const stream = fs.createReadStream(null, opts)
|
|
|
|
|
+ // Return a PassThrough stream with a minimal interface. It will buffer until the caller starts reading. It will emit errors from the source stream (Stream.pipeline passes errors along).
|
|
|
|
|
+ const pass = new PassThrough()
|
|
|
|
|
+ pipeline(stream, observer, pass).catch(() => {})
|
|
|
|
|
+ return pass
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
async getRedirectUrl() {
|
|
async getRedirectUrl() {
|
|
@@ -221,22 +229,25 @@ module.exports = class FSPersistor extends AbstractPersistor {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
async _writeStreamToTempFile(location, stream, opts = {}) {
|
|
async _writeStreamToTempFile(location, stream, opts = {}) {
|
|
|
|
|
+ const observerOptions = {
|
|
|
|
|
+ metric: 'fs.egress', // egress from us to disk
|
|
|
|
|
+ bucket: location,
|
|
|
|
|
+ }
|
|
|
|
|
+ const observer = new PersistorHelper.ObserverStream(observerOptions)
|
|
|
|
|
+
|
|
|
const tempDirPath = await fsPromises.mkdtemp(Path.join(location, 'tmp-'))
|
|
const tempDirPath = await fsPromises.mkdtemp(Path.join(location, 'tmp-'))
|
|
|
const tempFilePath = Path.join(tempDirPath, 'uploaded-file')
|
|
const tempFilePath = Path.join(tempDirPath, 'uploaded-file')
|
|
|
|
|
|
|
|
- const transforms = []
|
|
|
|
|
|
|
+ const transforms = [observer]
|
|
|
let md5Observer
|
|
let md5Observer
|
|
|
if (opts.sourceMd5) {
|
|
if (opts.sourceMd5) {
|
|
|
md5Observer = createMd5Observer()
|
|
md5Observer = createMd5Observer()
|
|
|
transforms.push(md5Observer.transform)
|
|
transforms.push(md5Observer.transform)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- const timer = new 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)
|
|
|
- timer.done()
|
|
|
|
|
} catch (err) {
|
|
} catch (err) {
|
|
|
await this._cleanupTempFile(tempFilePath)
|
|
await this._cleanupTempFile(tempFilePath)
|
|
|
throw new WriteError(
|
|
throw new WriteError(
|