Skip to content

Commit

Permalink
refactor(*): Polish Node-API library implementation (#7898)
Browse files Browse the repository at this point in the history
Co-authored-by: Joël Galeran <Jolg42@users.noreply.github.com>
Co-authored-by: Pierre-Antoine Mills <pierreantoine.urvoy@gmail.com>
  • Loading branch information
3 people committed Jun 28, 2021
1 parent 0389d2e commit c8a179b
Show file tree
Hide file tree
Showing 15 changed files with 82 additions and 55 deletions.
2 changes: 1 addition & 1 deletion src/packages/cli/src/Version.ts
Expand Up @@ -84,7 +84,7 @@ export class Version implements Command {
['@prisma/client', prismaClientVersion ?? 'Not found'],
['Current platform', platform],
[
`${useNAPI ? 'N-API ' : ''}Query Engine`,
`Query Engine${useNAPI ? ' (Node-API)' : ''}`,
this.printBinaryInfo(queryEngine),
],
['Migration Engine', this.printBinaryInfo(migrationEngine)],
Expand Down
36 changes: 18 additions & 18 deletions src/packages/cli/src/__tests__/__snapshots__/version.test.ts.snap
@@ -1,15 +1,15 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`version basic version (N-API) 1`] = `
prisma : placeholder
@prisma/client : placeholder
Current platform : placeholder
N-API Query Engine : placeholder
Migration Engine : placeholder
Introspection Engine : placeholder
Format Binary : placeholder
Default Engines Hash : placeholder
Studio : placeholder
prisma : placeholder
@prisma/client : placeholder
Current platform : placeholder
Query Engine (Node-API) : placeholder
Migration Engine : placeholder
Introspection Engine : placeholder
Format Binary : placeholder
Default Engines Hash : placeholder
Studio : placeholder
`;

exports[`version basic version 1`] = `
Expand All @@ -25,15 +25,15 @@ Studio : placeholder
`;

exports[`version version with custom binaries (N-API) 1`] = `
prisma : placeholder
@prisma/client : placeholder
Current platform : placeholder
N-API Query Engine : placeholder
Migration Engine : placeholder
Introspection Engine : placeholder
Format Binary : placeholder
Default Engines Hash : placeholder
Studio : placeholder
prisma : placeholder
@prisma/client : placeholder
Current platform : placeholder
Query Engine (Node-API) : placeholder
Migration Engine : placeholder
Introspection Engine : placeholder
Format Binary : placeholder
Default Engines Hash : placeholder
Studio : placeholder
`;

exports[`version version with custom binaries 1`] = `
Expand Down
@@ -1,7 +1,7 @@
import { NodeEngine } from '@prisma/engine-core/dist/NodeEngine'
import { BinaryEngine } from '@prisma/engine-core/dist/BinaryEngine'
import path from 'path'

describe('NodeEngine', () => {
describe('BinaryEngine', () => {
test('should error correctly with invalid flags', async () => {

// Skip for Node-API library
Expand All @@ -11,7 +11,7 @@ describe('NodeEngine', () => {
}

try {
const engine = new NodeEngine({
const engine = new BinaryEngine({
flags: ['--flag-that-does-not-exist'],
datamodelPath: path.join(
__dirname,
Expand Down
9 changes: 6 additions & 3 deletions src/packages/client/src/generation/generateClient.ts
Expand Up @@ -12,6 +12,7 @@ import makeDir from 'make-dir'
import path from 'path'
import pkgUp from 'pkg-up'
import { promisify } from 'util'
import { BinaryType } from '@prisma/fetch-engine'
import { DMMF as PrismaClientDMMF } from '../runtime/dmmf-types'
import { Dictionary } from '../runtime/utils/common'
import { resolveDatasources } from '../utils/resolveDatasources'
Expand Down Expand Up @@ -279,11 +280,13 @@ export async function generateClient({
await copyFile(filePath, target)
continue
}

const binaryName = useNAPI
? BinaryType.libqueryEngineNapi
: BinaryType.queryEngine
// They must have an equal size now, let's check for the hash
const [sourceVersion, targetVersion] = await Promise.all([
getVersion(filePath).catch(() => null),
getVersion(target).catch(() => null),
getVersion(filePath, binaryName).catch(() => null),
getVersion(target, binaryName).catch(() => null),
])

if (sourceVersion && targetVersion && sourceVersion === targetVersion) {
Expand Down
8 changes: 4 additions & 4 deletions src/packages/client/src/runtime/getPrismaClient.ts
Expand Up @@ -5,8 +5,8 @@ import {
EngineConfig,
EngineEventType,
} from '@prisma/engine-core/dist/Engine'
import { NAPIEngine } from '@prisma/engine-core/dist/NAPIEngine'
import { NodeEngine } from '@prisma/engine-core/dist/NodeEngine'
import { LibraryEngine } from '@prisma/engine-core/dist/LibraryEngine'
import { BinaryEngine } from '@prisma/engine-core/dist/BinaryEngine'
import {
DataSource,
GeneratorConfig,
Expand Down Expand Up @@ -425,9 +425,9 @@ export function getPrismaClient(config: GetPrismaClientOptions): any {
this._previewFeatures.includes('nApi') ||
process.env.PRISMA_FORCE_NAPI === 'true'
) {
return new NAPIEngine(this._engineConfig)
return new LibraryEngine(this._engineConfig)
} else {
return new NodeEngine(this._engineConfig)
return new BinaryEngine(this._engineConfig)
}
}

Expand Down
Expand Up @@ -67,13 +67,13 @@ export type StopDeferred = {
reject: (err: Error) => void
}

const engines: NodeEngine[] = []
const engines: BinaryEngine[] = []
const socketPaths: string[] = []

const MAX_STARTS = process.env.PRISMA_CLIENT_NO_RETRY ? 1 : 2
const MAX_REQUEST_RETRIES = process.env.PRISMA_CLIENT_NO_RETRY ? 1 : 2

export class NodeEngine implements Engine {
export class BinaryEngine implements Engine {
private logEmitter: EventEmitter
private showColors: boolean
private logQueries: boolean
Expand Down Expand Up @@ -351,7 +351,7 @@ You may have to run ${chalk.greenBright(

this.platform = this.platform || platform

if (__filename.includes('NodeEngine')) {
if (__filename.includes('BinaryEngine')) {
enginePath = this.getQueryEnginePath(this.platform, getEnginesPath())
return { prismaPath: enginePath, searchedLocations }
}
Expand Down
Expand Up @@ -27,7 +27,7 @@ import {
} from './errors'
import {
ConfigMetaFormat,
NAPI,
Library,
QueryEngine,
QueryEngineBatchRequest,
QueryEngineConstructor,
Expand All @@ -39,7 +39,7 @@ import {
QueryEngineRequestHeaders,
RustRequestError,
SyncRustError,
} from './napi-types'
} from './NodeAPILibraryTypes'
import { printGeneratorConfig } from './printGeneratorConfig'
import { fixBinaryTargets } from './util'

Expand All @@ -53,7 +53,7 @@ function isPanicEvent(event: QueryEngineEvent): event is QueryEnginePanicEvent {
}

const knownPlatforms: Platform[] = [...platforms, 'native']
export class NAPIEngine implements Engine {
export class LibraryEngine implements Engine {
private engine?: QueryEngine
private libraryInstantiationPromise?: Promise<void>
private libraryStartingPromise?: Promise<void>
Expand All @@ -62,7 +62,7 @@ export class NAPIEngine implements Engine {
private executingQueryPromise?: Promise<any>
private config: EngineConfig
private QueryEngineConstructor?: QueryEngineConstructor
private library?: NAPI
private library?: Library
private logEmitter: EventEmitter
libQueryEnginePath?: string
platform?: Platform
Expand Down Expand Up @@ -170,7 +170,7 @@ You may have to run ${chalk.greenBright(
if (!this.QueryEngineConstructor) {
try {
// this require needs to be resolved at runtime, tell webpack to ignore it
this.library = eval('require')(this.libQueryEnginePath) as NAPI
this.library = eval('require')(this.libQueryEnginePath) as Library
this.QueryEngineConstructor = this.library.QueryEngine
} catch (e) {
if (fs.existsSync(this.libQueryEnginePath)) {
Expand Down Expand Up @@ -365,7 +365,7 @@ You may have to run ${chalk.greenBright(
return this.libraryStoppingPromise
}

async getConfig(): Promise<ConfigMetaFormat> {
getConfig(): Promise<ConfigMetaFormat> {
return this.library!.getConfig({
datamodel: this.datamodel,
datasourceOverrides: this.datasourceOverrides,
Expand Down Expand Up @@ -511,7 +511,7 @@ You may have to run ${chalk.greenBright(

this.platform = this.platform ?? (await getPlatform())

if (__filename.includes('NAPIEngine')) {
if (__filename.includes('LibraryEngine')) {
enginePath = path.join(getEnginesPath(), getNapiName(this.platform, 'fs'))
return { enginePath, searchedLocations }
}
Expand Down Expand Up @@ -648,7 +648,7 @@ Read more about deploying Prisma Client: https://pris.ly/d/client-generator`
}
}

function hookProcess(engine: NAPIEngine, handler: string, exit = false) {
function hookProcess(engine: LibraryEngine, handler: string, exit = false) {
process.once(handler as any, async () => {
debug(`hookProcess received: ${handler}`)
await engine.runBeforeExit()
Expand All @@ -660,7 +660,7 @@ function hookProcess(engine: NAPIEngine, handler: string, exit = false) {
})
}

function initHooks(engine: NAPIEngine) {
function initHooks(engine: LibraryEngine) {
hookProcess(engine, 'beforeExit')
hookProcess(engine, 'exit')
hookProcess(engine, 'SIGINT', true)
Expand Down
Expand Up @@ -99,7 +99,7 @@ export type ConfigMetaFormat = {
warnings: string[]
}
// Main
export type NAPI = {
export type Library = {
QueryEngine: QueryEngineConstructor
version: () => {
commit: string
Expand Down
7 changes: 4 additions & 3 deletions src/packages/engine-core/src/index.ts
Expand Up @@ -5,11 +5,12 @@ export {
PrismaClientUnknownRequestError,
} from './errors'
export { getInternalDatamodelJson } from './getInternalDatamodelJson'
export { NAPIEngine } from './NAPIEngine'
export { NodeEngine as Engine } from './NodeEngine'
export { LibraryEngine } from './LibraryEngine'
export { BinaryEngine } from './BinaryEngine'
export { Engine } from './Engine'
export {
printGeneratorConfig,
getOriginalBinaryTargetsValue,
} from './printGeneratorConfig'
export * as NApiEngineTypes from './napi-types'
export * as NodeAPILibraryTypes from './NodeAPILibraryTypes'
export { fixBinaryTargets } from './util'
7 changes: 4 additions & 3 deletions src/packages/fetch-engine/src/download.ts
Expand Up @@ -218,10 +218,11 @@ function getCollectiveBar(options: DownloadOptions): {
finishBar: () => void
setProgress: (sourcePath: string) => (progress: number) => void
} {
const hasNodeAPI = 'libquery-engine' in options.binaries
const bar = getBar(
`Downloading Prisma engines for ${options.binaryTargets
?.map((p) => chalk.bold(p))
.join(' and ')}`,
`Downloading Prisma engines${
hasNodeAPI ? ' for Node-API' : ''
} for ${options.binaryTargets?.map((p) => chalk.bold(p)).join(' and ')}`,
)

const progressMap: { [key: string]: number } = {}
Expand Down
5 changes: 3 additions & 2 deletions src/packages/sdk/src/engine-commands/getConfig.ts
@@ -1,5 +1,5 @@
import Debug from '@prisma/debug'
import { NApiEngineTypes } from '@prisma/engine-core'
import { NodeAPILibraryTypes } from '@prisma/engine-core'
import { BinaryType } from '@prisma/fetch-engine'
import { DataSource, GeneratorConfig } from '@prisma/generator-helper'
import chalk from 'chalk'
Expand All @@ -9,6 +9,7 @@ import tmpWrite from 'temp-write'
import { promisify } from 'util'
import { resolveBinary } from '../resolveBinary'
import { isNodeAPISupported } from '@prisma/get-platform'
import { load } from '../utils/load'

const debug = Debug('prisma:getConfig')

Expand Down Expand Up @@ -75,7 +76,7 @@ async function getConfigNAPI(
await isNodeAPISupported()
debug(`Using N-API Query Engine at: ${queryEnginePath}`)
try {
const NApiQueryEngine = require(queryEnginePath) as NApiEngineTypes.NAPI
const NApiQueryEngine = load<NodeAPILibraryTypes.Library>(queryEnginePath)
data = await NApiQueryEngine.getConfig({
datamodel: options.datamodel,
datasourceOverrides: {},
Expand Down
5 changes: 3 additions & 2 deletions src/packages/sdk/src/engine-commands/getDmmf.ts
@@ -1,5 +1,5 @@
import Debug from '@prisma/debug'
import { NApiEngineTypes } from '@prisma/engine-core'
import { NodeAPILibraryTypes } from '@prisma/engine-core'
import { BinaryType } from '@prisma/fetch-engine'
import { DataSource, DMMF, GeneratorConfig } from '@prisma/generator-helper'
import chalk from 'chalk'
Expand All @@ -9,6 +9,7 @@ import tmpWrite from 'temp-write'
import { promisify } from 'util'
import { resolveBinary } from '../resolveBinary'
import { isNodeAPISupported } from '@prisma/get-platform'
import { load } from '../utils/load'

const debug = Debug('prisma:getDMMF')

Expand Down Expand Up @@ -53,7 +54,7 @@ async function getDmmfNapi(options: GetDMMFOptions): Promise<DMMF.Document> {
await isNodeAPISupported()

debug(`Using N-API Query Engine at: ${queryEnginePath}`)
const NApiQueryEngine = require(queryEnginePath) as NApiEngineTypes.NAPI
const NApiQueryEngine = load<NodeAPILibraryTypes.Library>(queryEnginePath)
const datamodel =
options.datamodel ?? fs.readFileSync(options.datamodelPath!, 'utf-8')
let dmmf: DMMF.Document | undefined
Expand Down
14 changes: 11 additions & 3 deletions src/packages/sdk/src/engine-commands/getVersion.ts
@@ -1,23 +1,31 @@
import Debug from '@prisma/debug'
import { NApiEngineTypes } from '@prisma/engine-core'
import { NodeAPILibraryTypes } from '@prisma/engine-core'
import { BinaryType } from '@prisma/fetch-engine'
import { isNodeAPISupported } from '@prisma/get-platform'
import execa from 'execa'
import { resolveBinary } from '../resolveBinary'
import { load } from '../utils/load'

const debug = Debug('prisma:getVersion')

const MAX_BUFFER = 1_000_000_000

export async function getVersion(
enginePath?: string,
binaryName: BinaryType = BinaryType.queryEngine,
binaryName?: BinaryType,
): Promise<string> {
const useNapi = process.env.PRISMA_FORCE_NAPI === 'true'

if (!binaryName) {
binaryName = useNapi
? BinaryType.libqueryEngineNapi
: BinaryType.queryEngine
}
enginePath = await resolveBinary(binaryName, enginePath)
if (binaryName === BinaryType.libqueryEngineNapi) {
await isNodeAPISupported()

const QE = require(enginePath) as NApiEngineTypes.NAPI
const QE = load<NodeAPILibraryTypes.Library>(enginePath)
return `libquery-engine ${QE.version().commit}`
} else {
const result = await execa(enginePath, ['--version'], {
Expand Down
1 change: 1 addition & 0 deletions src/packages/sdk/src/index.ts
Expand Up @@ -68,6 +68,7 @@ export {
parseEnvValue,
} from './utils/parseEnvValue'
export { printConfigWarnings } from './utils/printConfigWarnings'
export { load } from './utils/load'
export {
Position,
trimBlocksFromSchema,
Expand Down
11 changes: 11 additions & 0 deletions src/packages/sdk/src/utils/load.ts
@@ -0,0 +1,11 @@
/**
* This is a wrapper around `require`
* This is to avoid eval and hide require away from bundlers
*/
export function load<T>(id: string): T {
try {
return require(id) as T
} catch (e) {
throw new Error(`Unable to require(\`${id}\`)\n ${e.message}`)
}
}

0 comments on commit c8a179b

Please sign in to comment.