From 997cf67ae0824b849baad79923a0f847191e1216 Mon Sep 17 00:00:00 2001 From: William Luke Date: Tue, 27 Oct 2020 13:51:48 +0300 Subject: [PATCH 1/2] chore(engine-core): Improve binary search logic --- .../errors/missing-binary-native/test.ts | 9 +- .../integration/errors/missing-binary/test.ts | 9 +- src/packages/engine-core/src/NodeEngine.ts | 188 ++++++++---------- 3 files changed, 95 insertions(+), 111 deletions(-) diff --git a/src/packages/client/src/__tests__/integration/errors/missing-binary-native/test.ts b/src/packages/client/src/__tests__/integration/errors/missing-binary-native/test.ts index 6d0470090789..cf7042e90828 100644 --- a/src/packages/client/src/__tests__/integration/errors/missing-binary-native/test.ts +++ b/src/packages/client/src/__tests__/integration/errors/missing-binary-native/test.ts @@ -37,10 +37,13 @@ test('missing-binary-native', async () => { This probably happens, because you built Prisma Client on a different platform. (Prisma Client looked in "/client/src/__tests__/integration/errors/missing-binary-native/node_modules/@prisma/client/runtime/query-engine-TEST_PLATFORM") - Files in /client/src/__tests__/integration/errors/missing-binary-native/node_modules/@prisma/client/runtime: + Searched Locations: - index.d.ts - index.js + /client/src/__tests__/integration/errors/missing-binary-native/node_modules/.prisma/client + /client/src/__tests__/integration/errors/missing-binary-native/node_modules/@prisma/client/runtime + /client/src/__tests__/integration/errors/missing-binary-native/node_modules/@prisma/client + /client/src/__tests__/integration/errors/missing-binary-native/node_modules/.prisma/client + /client/src/__tests__/integration/errors/missing-binary-native You already added the platform "native" to the "generator" block in the "schema.prisma" file as described in https://pris.ly/d/client-generator, diff --git a/src/packages/client/src/__tests__/integration/errors/missing-binary/test.ts b/src/packages/client/src/__tests__/integration/errors/missing-binary/test.ts index 18aaa83fff68..9b00e4ae78b9 100644 --- a/src/packages/client/src/__tests__/integration/errors/missing-binary/test.ts +++ b/src/packages/client/src/__tests__/integration/errors/missing-binary/test.ts @@ -37,10 +37,13 @@ test('missing-binary', async () => { This probably happens, because you built Prisma Client on a different platform. (Prisma Client looked in "/client/src/__tests__/integration/errors/missing-binary/node_modules/@prisma/client/runtime/query-engine-TEST_PLATFORM") - Files in /client/src/__tests__/integration/errors/missing-binary/node_modules/@prisma/client/runtime: + Searched Locations: - index.d.ts - index.js + /client/src/__tests__/integration/errors/missing-binary/node_modules/.prisma/client + /client/src/__tests__/integration/errors/missing-binary/node_modules/@prisma/client/runtime + /client/src/__tests__/integration/errors/missing-binary/node_modules/@prisma/client + /client/src/__tests__/integration/errors/missing-binary/node_modules/.prisma/client + /client/src/__tests__/integration/errors/missing-binary To solve this problem, add the platform "TEST_PLATFORM" to the "generator" block in the "schema.prisma" file: diff --git a/src/packages/engine-core/src/NodeEngine.ts b/src/packages/engine-core/src/NodeEngine.ts index c5dce35fd122..45b116c84068 100644 --- a/src/packages/engine-core/src/NodeEngine.ts +++ b/src/packages/engine-core/src/NodeEngine.ts @@ -1,32 +1,31 @@ -import { - PrismaClientKnownRequestError, - PrismaClientUnknownRequestError, - RequestError, - PrismaClientInitializationError, - PrismaClientRustPanicError, - getMessage, - getErrorMessageWithLink, -} from './Engine' import { getEnginesPath } from '@prisma/engines' -import debugLib from 'debug' +import { DataSource, GeneratorConfig } from '@prisma/generator-helper' import { getPlatform, Platform } from '@prisma/get-platform' -import path from 'path' -import net from 'net' -import fs from 'fs' -import os from 'os' import chalk from 'chalk' -import { GeneratorConfig, DataSource } from '@prisma/generator-helper' -import { printGeneratorConfig } from './printGeneratorConfig' -import { fixBinaryTargets, plusX, getRandomString } from './util' -import { promisify } from 'util' +import { ChildProcessWithoutNullStreams, spawn } from 'child_process' +import debugLib from 'debug' import EventEmitter from 'events' -import { convertLog, RustLog, RustError } from './log' -import { spawn, ChildProcessWithoutNullStreams } from 'child_process' -import byline from './byline' -import pRetry from 'p-retry' import execa from 'execa' +import fs from 'fs' +import net from 'net' +import pRetry from 'p-retry' +import path from 'path' +import { promisify } from 'util' +import byline from './byline' +import { + getErrorMessageWithLink, + getMessage, + PrismaClientInitializationError, + PrismaClientKnownRequestError, + PrismaClientRustPanicError, + PrismaClientUnknownRequestError, + RequestError, +} from './Engine' +import { convertLog, RustError, RustLog } from './log' import { omit } from './omit' +import { printGeneratorConfig } from './printGeneratorConfig' import { Undici } from './undici' +import { fixBinaryTargets, getRandomString, plusX } from './util' const debug = debugLib('engine') const exists = promisify(fs.exists) @@ -189,7 +188,7 @@ export class NodeEngine { 'distinct', 'aggregations', 'insensitiveFilters', - 'atomicNumberOperations' + 'atomicNumberOperations', ] const filteredFlags = ['nativeTypes'] const removedFlagsUsed = this.enableExperimental.filter((e) => @@ -197,7 +196,9 @@ export class NodeEngine { ) if (removedFlagsUsed.length > 0) { console.log( - `${chalk.blueBright('info')} The preview flags \`${removedFlagsUsed.join( + `${chalk.blueBright( + 'info', + )} The preview flags \`${removedFlagsUsed.join( '`, `', )}\` were removed, you can now safely remove them from your schema.prisma.`, ) @@ -320,10 +321,15 @@ You may have to run ${chalk.greenBright( this.currentRequestPromise.cancel() } } - - private async resolvePrismaPath(): Promise { + // TODO Clean Up Logic + private async resolvePrismaPath(): Promise<{ + prismaPath: string + searchedLocations: string[] + }> { + const searchedLocations: string[] = [] + let enginePath if (this.prismaPath) { - return this.prismaPath + return { prismaPath: this.prismaPath, searchedLocations } } const platform = await this.getPlatform() @@ -335,81 +341,52 @@ You may have to run ${chalk.greenBright( if (__filename.includes('NodeEngine')) { // TODO: Use engines package here - return this.getQueryEnginePath( - this.platform, - getEnginesPath(), - ) - } else { - const dotPrismaPath = await this.getQueryEnginePath( - this.platform, - eval(`require('path').join(__dirname, '../../../.prisma/client')`), - ) - debug({ dotPrismaPath }) - if (fs.existsSync(dotPrismaPath)) { - return dotPrismaPath - } - const dirnamePath = await this.getQueryEnginePath( - this.platform, - eval('__dirname'), - ) - debug({ dirnamePath }) - if (fs.existsSync(dirnamePath)) { - return dirnamePath - } - const parentDirName = await this.getQueryEnginePath( - this.platform, - path.join(eval('__dirname'), '..'), - ) - debug({ parentDirName }) - if (fs.existsSync(parentDirName)) { - return parentDirName - } - const datamodelDirName = await this.getQueryEnginePath( - this.platform, - path.dirname(this.datamodelPath), - ) - if (fs.existsSync(datamodelDirName)) { - return datamodelDirName - } - const cwdPath = await this.getQueryEnginePath(this.platform, this.cwd) - if (fs.existsSync(cwdPath)) { - return cwdPath + enginePath = this.getQueryEnginePath(this.platform, getEnginesPath()) + return { prismaPath: enginePath, searchedLocations } + } + const searchLocations: string[] = [ + eval(`require('path').join(__dirname, '../../../.prisma/client')`), // Dot Prisma Path + this.generator?.output ?? eval('__dirname'), // Custom Generator Path + path.join(eval('__dirname'), '..'), // parentDirName + path.dirname(this.datamodelPath), // Datamodel Dir + this.cwd, //cwdPath + ] + + for (let i = 0; i < searchLocations.length; i++) { + const location = searchLocations[i] + searchedLocations.push(location) + debug(`Search for Query Engine in ${location}`) + enginePath = await this.getQueryEnginePath(this.platform, location) + if (fs.existsSync(enginePath)) { + return { prismaPath: enginePath, searchedLocations } } - const prismaPath = await this.getQueryEnginePath(this.platform) - debug({ prismaPath }) - return prismaPath } + enginePath = await this.getQueryEnginePath(this.platform) + + return { prismaPath: enginePath ?? '', searchedLocations } } // get prisma path private async getPrismaPath(): Promise { - const prismaPath = await this.resolvePrismaPath() - // console.log({ prismaPath }) + const { prismaPath, searchedLocations } = await this.resolvePrismaPath() const platform = await this.getPlatform() // If path to query engine doesn't exist, throw if (!(await exists(prismaPath))) { const pinnedStr = this.incorrectlyPinnedBinaryTarget ? `\nYou incorrectly pinned it to ${chalk.redBright.bold( - `${this.incorrectlyPinnedBinaryTarget}`, - )}\n` + `${this.incorrectlyPinnedBinaryTarget}`, + )}\n` : '' - const dir = path.dirname(prismaPath) - const dirExists = fs.existsSync(dir) - let files = [] - if (dirExists) { - files = await readdir(dir) - } let errorText = `Query engine binary for current platform "${chalk.bold( platform, )}" could not be found.${pinnedStr} This probably happens, because you built Prisma Client on a different platform. (Prisma Client looked in "${chalk.underline(prismaPath)}") -Files in ${dir}: - -${files.map((f) => ` ${f}`).join('\n')}\n` +Searched Locations: +${searchedLocations.map((f) => ` ${f}`).join('\n')}\n` // The generator should always be there during normal usage if (this.generator) { // The user already added it, but it still doesn't work 🤷‍♀️ @@ -419,26 +396,29 @@ ${files.map((f) => ` ${f}`).join('\n')}\n` this.generator.binaryTargets.includes('native') ) { errorText += ` -You already added the platform${this.generator.binaryTargets.length > 1 ? 's' : '' - } ${this.generator.binaryTargets - .map((t) => `"${chalk.bold(t)}"`) - .join(', ')} to the "${chalk.underline('generator')}" block +You already added the platform${ + this.generator.binaryTargets.length > 1 ? 's' : '' + } ${this.generator.binaryTargets + .map((t) => `"${chalk.bold(t)}"`) + .join(', ')} to the "${chalk.underline('generator')}" block in the "schema.prisma" file as described in https://pris.ly/d/client-generator, but something went wrong. That's suboptimal. Please create an issue at https://github.com/prisma/prisma-client-js/issues/new` + errorText += `` } else { // If they didn't even have the current running platform in the schema.prisma file, it's easy // Just add it - errorText += `\n\nTo solve this problem, add the platform "${this.platform - }" to the "${chalk.underline( - 'generator', - )}" block in the "schema.prisma" file: + errorText += `\n\nTo solve this problem, add the platform "${ + this.platform + }" to the "${chalk.underline( + 'generator', + )}" block in the "schema.prisma" file: ${chalk.greenBright(this.getFixedGenerator())} Then run "${chalk.greenBright( - 'prisma generate', - )}" for your changes to take effect. + 'prisma generate', + )}" for your changes to take effect. Read more about deploying Prisma Client: https://pris.ly/d/client-generator` } } else { @@ -567,8 +547,8 @@ ${chalk.dim("In case we're mistaken, please report this to us 🙏.")}`) const prismaPath = await this.getPrismaPath() const experimentalFlags = this.enableExperimental && - Array.isArray(this.enableExperimental) && - this.enableExperimental.length > 0 + Array.isArray(this.enableExperimental) && + this.enableExperimental.length > 0 ? [`--enable-experimental=${this.enableExperimental.join(',')}`] : [] @@ -715,7 +695,7 @@ ${chalk.dim("In case we're mistaken, please report this to us 🙏.")}`) err = new PrismaClientInitializationError( `Query engine process killed with signal ${this.child.signalCode} for unknown reason. Make sure that the engine binary at ${prismaPath} is not corrupt.\n` + - this.stderrLogs, + this.stderrLogs, this.clientVersion, ) } else { @@ -834,12 +814,12 @@ ${this.lastErrorLog.fields.file}:${this.lastErrorLog.fields.line}:${this.lastErr this.url = `http://localhost:${this.port}` - // don't wait for this - ; (async () => { - const engineVersion = await this.version() - debug(`Client Version ${this.clientVersion}`) - debug(`Engine Version ${engineVersion}`) - })() + // don't wait for this + ;(async () => { + const engineVersion = await this.version() + debug(`Client Version ${this.clientVersion}`) + debug(`Engine Version ${engineVersion}`) + })() this.stopPromise = undefined resolve() @@ -991,7 +971,6 @@ ${this.lastErrorLog.fields.file}:${this.lastErrorLog.fields.line}:${this.lastErr ) } - this.currentRequestPromise = this.undici.request( stringifyQuery(query), headers, @@ -1143,9 +1122,8 @@ ${this.lastErrorLog.fields.file}:${this.lastErrorLog.fields.line}:${this.lastErr (error.code === 'UND_ERR_SOCKET' && error.message.toLowerCase().includes('closed')) || error.message.toLowerCase().includes('client is destroyed') || - error.message.toLowerCase().includes('other side closed') || ( - error.code === 'UND_ERR_CLOSED' - ) + error.message.toLowerCase().includes('other side closed') || + error.code === 'UND_ERR_CLOSED' ) { if (this.globalKillSignalReceived && !this.child.connected) { throw new PrismaClientUnknownRequestError( From bc3d3875b999f5ddde5b9bcb5e1ae710c879d27f Mon Sep 17 00:00:00 2001 From: William Luke Date: Tue, 27 Oct 2020 14:21:40 +0300 Subject: [PATCH 2/2] tims suggestions --- src/packages/engine-core/src/NodeEngine.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/packages/engine-core/src/NodeEngine.ts b/src/packages/engine-core/src/NodeEngine.ts index 45b116c84068..0c053c61b898 100644 --- a/src/packages/engine-core/src/NodeEngine.ts +++ b/src/packages/engine-core/src/NodeEngine.ts @@ -321,7 +321,6 @@ You may have to run ${chalk.greenBright( this.currentRequestPromise.cancel() } } - // TODO Clean Up Logic private async resolvePrismaPath(): Promise<{ prismaPath: string searchedLocations: string[] @@ -340,7 +339,6 @@ You may have to run ${chalk.greenBright( this.platform = this.platform || platform if (__filename.includes('NodeEngine')) { - // TODO: Use engines package here enginePath = this.getQueryEnginePath(this.platform, getEnginesPath()) return { prismaPath: enginePath, searchedLocations } } @@ -351,9 +349,7 @@ You may have to run ${chalk.greenBright( path.dirname(this.datamodelPath), // Datamodel Dir this.cwd, //cwdPath ] - - for (let i = 0; i < searchLocations.length; i++) { - const location = searchLocations[i] + for (const location of searchLocations) { searchedLocations.push(location) debug(`Search for Query Engine in ${location}`) enginePath = await this.getQueryEnginePath(this.platform, location)