feat(xo-server/file restore): dedupe mount/unmount (#4961)

This commit is contained in:
Julien Fontanet 2020-05-07 17:36:37 +02:00 committed by GitHub
parent 6555e2c440
commit 48ce7df43a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 99 additions and 40 deletions

View File

@ -0,0 +1,45 @@
import assert from 'assert'
import ensureArray from './_ensureArray'
import MultiKeyMap from './_MultiKeyMap'
function State() {
this.i = 0
this.value = undefined
}
export const dedupeUnmount = (fn, keyFn) => {
const states = new MultiKeyMap()
return function() {
const keys = ensureArray(keyFn.apply(this, arguments))
let state = states.get(keys)
if (state === undefined) {
state = new State()
states.set(keys, state)
const mount = async () => {
try {
const value = await fn.apply(this, arguments)
return {
__proto__: value,
async unmount() {
assert(state.i > 0)
if (--state.i === 0) {
states.delete(keys)
await value.unmount()
}
},
}
} catch (error) {
states.delete(keys)
throw error
}
}
state.value = mount()
}
++state.i
return state.value
}
}

View File

@ -6,9 +6,15 @@ import { normalize } from 'path'
import { readdir, rmdir, stat } from 'fs-extra' import { readdir, rmdir, stat } from 'fs-extra'
import { ZipFile } from 'yazl' import { ZipFile } from 'yazl'
import { decorateWith } from '../_decorateWith'
import { dedupeUnmount } from '../_dedupeUnmount'
import { lvs, pvs } from '../lvm' import { lvs, pvs } from '../lvm'
import { resolveSubpath, tmpDir } from '../utils' import { resolveSubpath, tmpDir } from '../utils'
const compose = (...fns) => value => fns.reduce((value, fn) => fn(value), value)
const dedupeUnmountWithArgs = fn => dedupeUnmount(fn, (...args) => args)
const IGNORED_PARTITION_TYPES = { const IGNORED_PARTITION_TYPES = {
// https://github.com/jhermsmeier/node-mbr/blob/master/lib/partition.js#L38 // https://github.com/jhermsmeier/node-mbr/blob/master/lib/partition.js#L38
0x05: true, 0x05: true,
@ -60,8 +66,10 @@ const parsePartxLine = createPairsParser({
: value, : value,
}) })
const listLvmLogicalVolumes = defer( const listLvmLogicalVolumes = compose(
async ($defer, devicePath, partition, results = []) => { defer,
dedupeUnmountWithArgs
)(async ($defer, devicePath, partition, results = []) => {
const pv = await mountLvmPhysicalVolume(devicePath, partition) const pv = await mountLvmPhysicalVolume(devicePath, partition)
$defer(pv.unmount) $defer(pv.unmount)
@ -78,10 +86,10 @@ const listLvmLogicalVolumes = defer(
} }
}) })
return results return results
} })
)
async function mountLvmPhysicalVolume(devicePath, partition) { const mountLvmPhysicalVolume = dedupeUnmountWithArgs(
async (devicePath, partition) => {
const args = [] const args = []
if (partition !== undefined) { if (partition !== undefined) {
args.push('-o', partition.start * 512) args.push('-o', partition.start * 512)
@ -101,9 +109,13 @@ async function mountLvmPhysicalVolume(devicePath, partition) {
} }
}, },
} }
} }
)
const mountPartition = defer(async ($defer, devicePath, partition) => { const mountPartition = compose(
defer,
dedupeUnmountWithArgs
)(async ($defer, devicePath, partition) => {
const options = ['loop', 'ro'] const options = ['loop', 'ro']
if (partition !== undefined) { if (partition !== undefined) {
@ -280,6 +292,7 @@ export default class BackupNgFileRestore {
return partitions return partitions
} }
@decorateWith(dedupeUnmountWithArgs)
@defer @defer
async _mountDisk($defer, remoteId, diskId) { async _mountDisk($defer, remoteId, diskId) {
const handler = await this._app.getRemoteHandler(remoteId) const handler = await this._app.getRemoteHandler(remoteId)
@ -321,6 +334,7 @@ export default class BackupNgFileRestore {
} }
} }
@decorateWith(dedupeUnmountWithArgs)
@defer @defer
async _mountPartition($defer, devicePath, partitionId) { async _mountPartition($defer, devicePath, partitionId) {
if (partitionId === undefined) { if (partitionId === undefined) {