try to use copy_file_range for VHD merge

This commit is contained in:
Nicolas Raynaud
2020-10-15 04:38:17 +02:00
parent eed44156ae
commit d2b06f3ee7
3 changed files with 85 additions and 3 deletions

View File

@@ -36,6 +36,7 @@
"readable-stream": "^3.0.6",
"through2": "^4.0.2",
"tmp": "^0.2.1",
"syscall": "^0.2.0",
"xo-remote-parser": "^0.6.0"
},
"devDependencies": {

View File

@@ -1,14 +1,22 @@
import execa from 'execa'
import fs from 'fs-extra'
import { ignoreErrors } from 'promise-toolbox'
import { ignoreErrors, fromCallback } from 'promise-toolbox'
import { join } from 'path'
import { tmpdir } from 'os'
import LocalHandler from './local'
import { Syscall6 } from 'syscall'
import normalizePath from './_normalizePath'
import { randomBytes } from 'crypto'
const sudoExeca = (command, args, opts) =>
execa('sudo', [command, ...args], opts)
const computeRate = (hrtime: number[], size: number) => {
const seconds = hrtime[0] + hrtime[1] / 1e9
return size / seconds
}
export default class MountHandler extends LocalHandler {
constructor(
remote,
@@ -56,6 +64,71 @@ export default class MountHandler extends LocalHandler {
return this._realPath
}
async test(): Promise<Object> {
/**
* @returns the number of byte effectively copied, needs to be called in a loop!
*/
function copy_file_range(fdIn, offsetIn, fdOut, offsetOut, dataLen, flags = 0) {
// we are stuck on linux x86_64
function wrapOffset(offsetIn) {
if (offsetIn == null)
return 0
const offsetInBuffer = new Uint32Array(2)
new DataView(offsetInBuffer.buffer).setBigUint64(0, BigInt(offsetIn), true)
return offsetInBuffer
}
const SYS_copy_file_range = 326
const [ret, _, errno] = Syscall6(SYS_copy_file_range, fdIn, wrapOffset(offsetIn), fdOut, wrapOffset(offsetOut), data.byteLength, 0)
if (errno !== 0) {
throw new Error('Error no', errno)
}
return ret
}
const SIZE = 1024 * 1024 * 10
const testFileName = normalizePath(`${Date.now()}.test`)
const testFileName2 = normalizePath(`${Date.now()}__2.test`)
const data = await fromCallback(randomBytes, SIZE)
let step = 'write'
try {
const writeStart = process.hrtime()
await this._outputFile(testFileName, data, { flags: 'wx' })
const writeDuration = process.hrtime(writeStart)
step = 'duplicate'
const fd1 = await this._openFile(testFileName, 'r')
const fd2 = await this._openFile(testFileName2, 'w')
console.log('_openFile', fd1, fd2, data.byteLength)
const res = copy_file_range(fd1, 0, fd2, null, data.byteLength, 0)
console.log('copy_file_range', res)
await this._closeFile(fd2)
step = 'read'
const readStart = process.hrtime()
const read = await this._readFile(testFileName, { flags: 'r' })
const readDuration = process.hrtime(readStart)
if (!data.equals(read)) {
throw new Error('output and input did not match')
}
return {
success: true,
writeRate: computeRate(writeDuration, SIZE),
readRate: computeRate(readDuration, SIZE),
}
} catch (error) {
return {
success: false,
step,
file: testFileName,
error: error.message || String(error),
}
} finally {
ignoreErrors.call(this._unlink(testFileName))
}
}
async _sync() {
// in case of multiple `sync`s, ensure we properly close previous keeper
{

View File

@@ -3974,7 +3974,7 @@ bind-property-descriptor@^1.0.0:
dependencies:
lodash "^4.17.4"
bindings@^1.5.0:
bindings@^1.3.0, bindings@^1.5.0:
version "1.5.0"
resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df"
integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==
@@ -12784,7 +12784,7 @@ node-gyp-build@~4.1.0:
resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.1.1.tgz#d7270b5d86717068d114cc57fff352f96d745feb"
integrity sha512-dSq1xmcPDKPZ2EED2S6zw/b9NKsqzXRE6dVr8TVQnI3FJOTteUMuqF3Qqs6LZg+mLGYJWqQzMbIjMtJqTv87nQ==
node-gyp@^3.8.0:
node-gyp@^3.7.0, node-gyp@^3.8.0:
version "3.8.0"
resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-3.8.0.tgz#540304261c330e80d0d5edce253a68cb3964218c"
integrity sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA==
@@ -17313,6 +17313,14 @@ syntax-error@^1.1.1:
dependencies:
acorn-node "^1.2.0"
syscall@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/syscall/-/syscall-0.2.0.tgz#9308898495dfb5c062ea7a60c46f81f29f532ac4"
integrity sha512-MLlgaLAMbOGKUVlqsLVYnJ4dBZmeE1nza4BVgVgGUr2dPV17tgR79JUPIUybX/EqGm1jywsXSXUPXpNbIXDVCw==
dependencies:
bindings "^1.3.0"
node-gyp "^3.7.0"
table@^5.2.3:
version "5.4.6"
resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e"