From c60060977e91cd2c235d86d5ac27d194a7a9e3e3 Mon Sep 17 00:00:00 2001 From: hasezoey Date: Thu, 16 May 2024 12:18:08 +0200 Subject: [PATCH] fix(DryMongoBinary::locateBinary): check if download-lock still exists and treat it like the binary does not exists fixes #872 --- .../src/util/DryMongoBinary.ts | 20 ++++++++++++++++- .../src/util/__tests__/dryBinary.test.ts | 22 +++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/packages/mongodb-memory-server-core/src/util/DryMongoBinary.ts b/packages/mongodb-memory-server-core/src/util/DryMongoBinary.ts index 70bfcd2e4..ba1bc2e2c 100644 --- a/packages/mongodb-memory-server-core/src/util/DryMongoBinary.ts +++ b/packages/mongodb-memory-server-core/src/util/DryMongoBinary.ts @@ -1,6 +1,12 @@ import debug from 'debug'; import { DEFAULT_VERSION, envToBool, resolveConfig, ResolveConfigVariables } from './resolveConfig'; -import { assertion, checkBinaryPermissions, isNullOrUndefined, pathExists } from './utils'; +import { + assertion, + checkBinaryPermissions, + isNullOrUndefined, + lockfilePath, + pathExists, +} from './utils'; import * as path from 'path'; import { arch, homedir, platform } from 'os'; import findCacheDir from 'find-cache-dir'; @@ -100,6 +106,18 @@ export class DryMongoBinary { return undefined; } + // check for the race-condition of "extraction started, but not finished" + // or said differently, the file "exists" but is not fully extracted yet + // see https://github.com/nodkz/mongodb-memory-server/issues/872 + if ( + returnValue[0] && + (await pathExists(lockfilePath(path.dirname(returnValue[1]), useOpts.version))) + ) { + log('locateBinary: binary found, but also a download-lock, trying to resolve lock'); + + return undefined; + } + log(`locateBinary: found binary at "${returnValue[1]}"`); this.binaryCache.set(opts.version, returnValue[1]); diff --git a/packages/mongodb-memory-server-core/src/util/__tests__/dryBinary.test.ts b/packages/mongodb-memory-server-core/src/util/__tests__/dryBinary.test.ts index e8db90010..c3af0239f 100644 --- a/packages/mongodb-memory-server-core/src/util/__tests__/dryBinary.test.ts +++ b/packages/mongodb-memory-server-core/src/util/__tests__/dryBinary.test.ts @@ -428,6 +428,28 @@ describe('DryBinary', () => { expect(binary.DryMongoBinary.generateDownloadPath).toHaveBeenCalled(); }); + it('should return "undefined" if binary can be found but also a download-lock', async () => { + jest + .spyOn(binary.DryMongoBinary, 'generateDownloadPath') + .mockResolvedValue([true, '/tmp/1.1.1']); + + const originalPathExists = utils.pathExists; + + const utilsSpy = jest.spyOn(utils, 'pathExists').mockImplementation((path) => { + if (path === '/tmp/1.1.1.lock' || path === '/tmp/1.1.1') { + return Promise.resolve(true); + } + + return originalPathExists(path); + }); + + const returnValue = await binary.DryMongoBinary.locateBinary({ version: '1.1.1' }); + expect(returnValue).toBeUndefined(); + expect(binary.DryMongoBinary.binaryCache.size).toBe(0); + expect(binary.DryMongoBinary.generateDownloadPath).toHaveBeenCalled(); + expect(utilsSpy).toHaveBeenCalledWith('/tmp/1.1.1.lock'); + }); + it('should return cached version if exists', async () => { const mockBinary = '/custom/path'; binary.DryMongoBinary.binaryCache.set('1.1.1', mockBinary);