Skip to content

Commit

Permalink
perf(client): Stop bundling @opentelemetry packages (#19039)
Browse files Browse the repository at this point in the history
Removes all @opentelemetry packages from client bundle.
Client would now interact with OTEL throgh type-only `TracingHelper`
dependency. Actual implementation is provided by
`@prisma/instrumentation` package. Technically it works like this:

- `@prisma/internals` package contains type definition for
  `TracingHelper` interface. This interface describes all OTEL functions
  client might need. `runInChildSpan`, `getTraceParent` and `createSpan`
  functions moved there.
- `@prisma/client` contains no-op `TracingHelper` implementation. It
  will be used if tracing is not active (either through disabled preview
  feature or through absence of instrumentation package).
- `@prisma/instrumentation` contains active implementation of
  `TracingHelper`. It is set on a global `PRISMA_INSTRUMENTATION`
  instead of the config. When active, client will use that
  implementation instead of no-op one.

Close prisma/client-planning#340
  • Loading branch information
SevInf committed May 5, 2023
1 parent 4ec6c05 commit 435bf81
Show file tree
Hide file tree
Showing 25 changed files with 1,008 additions and 852 deletions.
6 changes: 4 additions & 2 deletions packages/client/src/__tests__/binaryEngine.test.ts
@@ -1,8 +1,9 @@
import { ClientEngineType, getClientEngineType } from '@prisma/internals'
import { ClientEngineType, getClientEngineType, getQueryEngineProtocol } from '@prisma/internals'
import { EventEmitter } from 'events'
import path from 'path'

import { BinaryEngine } from '../runtime/core/engines'
import { disabledTracingHelper } from '../runtime/core/tracing/TracingHelper'

describe('BinaryEngine', () => {
test('should error correctly with invalid flags', async () => {
Expand All @@ -16,10 +17,11 @@ describe('BinaryEngine', () => {
const engine = new BinaryEngine({
flags: ['--flag-that-does-not-exist'],
datamodelPath: path.join(__dirname, './runtime-tests/blog/schema.prisma'),
tracingConfig: { enabled: false, middleware: false },
tracingHelper: disabledTracingHelper,
env: {},
cwd: process.cwd(),
logEmitter: new EventEmitter(),
engineProtocol: getQueryEngineProtocol(),
})
await engine.start()
} catch (e) {
Expand Down
12 changes: 4 additions & 8 deletions packages/client/src/runtime/RequestHandler.ts
Expand Up @@ -24,7 +24,6 @@ import { visitQueryResult } from './core/extensions/visitQueryResult'
import { dmmfToJSModelName } from './core/model/utils/dmmfToJSModelName'
import { ProtocolEncoder, ProtocolMessage } from './core/protocol/common'
import { PrismaPromiseInteractiveTransaction, PrismaPromiseTransaction } from './core/request/PrismaPromise'
import { getTraceParent, TracingConfig } from './core/tracing'
import { JsArgs } from './core/types/JsApi'
import { DataLoader } from './DataLoader'
import type { Client, Unpacker } from './getPrismaClient'
Expand Down Expand Up @@ -66,7 +65,6 @@ export type Request = {
transaction?: PrismaPromiseTransaction
otelParentCtx?: Context
otelChildCtx?: Context
tracingConfig?: TracingConfig
customDataProxyFetch?: (fetch: Fetch) => Fetch
}

Expand All @@ -87,10 +85,9 @@ export class RequestHandler {
this.client = client
this.dataloader = new DataLoader({
batchLoader: (requests) => {
const transaction = requests[0].transaction
const encoder = requests[0].protocolEncoder
const queries = encoder.createBatch(requests.map((r) => r.protocolMessage))
const traceparent = getTraceParent({ context: requests[0].otelParentCtx, tracingConfig: client._tracingConfig })
const { transaction, protocolEncoder, otelParentCtx } = requests[0]
const queries = protocolEncoder.createBatch(requests.map((r) => r.protocolMessage))
const traceparent = this.client._tracingHelper.getTraceParent(otelParentCtx)

// TODO: pass the child information to QE for it to issue links to queries
// const links = requests.map((r) => trace.getSpanContext(r.otelChildCtx!))
Expand All @@ -109,7 +106,7 @@ export class RequestHandler {
request.transaction?.kind === 'itx' ? getItxTransactionOptions(request.transaction) : undefined

return this.client._engine.request(request.protocolMessage.toEngineQuery(), {
traceparent: getTraceParent({ tracingConfig: request.tracingConfig }),
traceparent: this.client._tracingHelper.getTraceParent(),
interactiveTransaction,
isWrite: request.protocolMessage.isWrite(),
customDataProxyFetch: request.customDataProxyFetch,
Expand Down Expand Up @@ -148,7 +145,6 @@ export class RequestHandler {
transaction,
otelParentCtx,
otelChildCtx,
tracingConfig: this.client._tracingConfig,
customDataProxyFetch,
})
const data = response?.data
Expand Down
30 changes: 10 additions & 20 deletions packages/client/src/runtime/core/engines/binary/BinaryEngine.ts
Expand Up @@ -3,7 +3,7 @@ import { getEnginesPath } from '@prisma/engines'
import type { ConnectorType, DMMF, GeneratorConfig } from '@prisma/generator-helper'
import type { Platform } from '@prisma/get-platform'
import { getPlatform, platforms } from '@prisma/get-platform'
import { fixBinaryTargets, plusX, printGeneratorConfig } from '@prisma/internals'
import { EngineSpanEvent, fixBinaryTargets, plusX, printGeneratorConfig, TracingHelper } from '@prisma/internals'
import type { ChildProcess, ChildProcessByStdio } from 'child_process'
import { spawn } from 'child_process'
import execa from 'execa'
Expand All @@ -20,8 +20,6 @@ import { PrismaClientRustError } from '../../errors/PrismaClientRustError'
import { PrismaClientRustPanicError } from '../../errors/PrismaClientRustPanicError'
import { PrismaClientUnknownRequestError } from '../../errors/PrismaClientUnknownRequestError'
import { prismaGraphQLToJSError } from '../../errors/utils/prismaGraphQLToJSError'
import { createSpan, runInChildSpan } from '../../tracing'
import { TracingConfig } from '../../tracing/getTracingConfig'
import type {
BatchQueryEngineResult,
DatasourceOverwrite,
Expand All @@ -35,7 +33,7 @@ import type {
import { Engine } from '../common/Engine'
import { EventEmitter } from '../common/types/Events'
import { EngineMetricsOptions, Metrics, MetricsOptionsJson, MetricsOptionsPrometheus } from '../common/types/Metrics'
import type { EngineSpanEvent, QueryEngineResult } from '../common/types/QueryEngine'
import type { QueryEngineResult } from '../common/types/QueryEngine'
import type * as Tx from '../common/types/Transaction'
import { getBatchRequestPayload } from '../common/utils/getBatchRequestPayload'
import { getErrorMessageWithLink } from '../common/utils/getErrorMessageWithLink'
Expand Down Expand Up @@ -111,7 +109,7 @@ export class BinaryEngine extends Engine<undefined> {
private lastVersion?: string
private lastActiveProvider?: ConnectorType
private activeProvider?: string
private tracingConfig: TracingConfig
private tracingHelper: TracingHelper
/**
* exiting is used to tell the .on('exit') hook, if the exit came from our script.
* As soon as the Prisma binary returns a correct return code (like 1 or 0), we don't need this anymore
Expand All @@ -133,7 +131,7 @@ export class BinaryEngine extends Engine<undefined> {
allowTriggerPanic,
dirname,
activeProvider,
tracingConfig,
tracingHelper,
logEmitter,
}: EngineConfig) {
super()
Expand All @@ -147,7 +145,7 @@ export class BinaryEngine extends Engine<undefined> {
this.prismaPath = process.env.PRISMA_QUERY_ENGINE_BINARY ?? prismaPath
this.generator = generator
this.datasources = datasources
this.tracingConfig = tracingConfig
this.tracingHelper = tracingHelper
this.logEmitter = logEmitter
this.showColors = showColors ?? false
this.logQueries = logQueries ?? false
Expand Down Expand Up @@ -468,12 +466,11 @@ ${dim("In case we're mistaken, please report this to us 🙏.")}`)
}
}

const spanOptions = {
name: 'connect',
enabled: this.tracingConfig.enabled && !this.startPromise,
if (this.startPromise) {
return startFn()
}

return runInChildSpan(spanOptions, startFn)
return this.tracingHelper.runInChildSpan('connect', startFn)
}

private getEngineEnvVars() {
Expand Down Expand Up @@ -615,9 +612,7 @@ ${dim("In case we're mistaken, please report this to us 🙏.")}`)
// these logs can still include error logs
if (typeof json.is_panic === 'undefined') {
if (json.span === true) {
if (this.tracingConfig.enabled === true) {
void createSpan(json as EngineSpanEvent)
}
void this.tracingHelper.createEngineSpan(json as EngineSpanEvent)

return
}
Expand Down Expand Up @@ -770,12 +765,7 @@ You very likely have the wrong "binaryTarget" defined in the schema.prisma file.
return this.stopPromise
}

const spanOptions = {
name: 'disconnect',
enabled: this.tracingConfig.enabled,
}

return runInChildSpan(spanOptions, stopFn)
return this.tracingHelper.runInChildSpan('disconnect', stopFn)
}

/**
Expand Down
8 changes: 4 additions & 4 deletions packages/client/src/runtime/core/engines/common/Engine.ts
@@ -1,6 +1,6 @@
import type { DataSource, DMMF, GeneratorConfig } from '@prisma/generator-helper'
import { TracingHelper } from '@prisma/internals'

import { TracingConfig } from '../../tracing/getTracingConfig'
import { Fetch } from '../data-proxy/utils/request'
import { EventEmitter } from './types/Events'
import { JsonQuery } from './types/JsonProtocol'
Expand Down Expand Up @@ -146,10 +146,10 @@ export interface EngineConfig {
inlineSchemaHash?: string

/**
* The configuration object for enabling tracing
* @remarks enabling is determined by the client
* The helper for interaction with OTEL tracing
* @remarks enabling is determined by the client and @prisma/instrumentation package
*/
tracingConfig: TracingConfig
tracingHelper: TracingHelper

/**
* Information about whether we have not found a schema.prisma file in the
Expand Down
@@ -1,4 +1,5 @@
import type { DataSource, GeneratorConfig } from '@prisma/generator-helper'
import { EngineSpanEvent } from '@prisma/internals'

import { EngineProtocol } from '../Engine'
import { JsonBatchQuery } from './JsonProtocol'
Expand Down Expand Up @@ -35,23 +36,6 @@ export type QueryEnginePanicEvent = {
column: string
}

export type EngineSpanEvent = {
span: boolean
spans: EngineSpan[]
}

export type EngineSpan = {
span: boolean
name: string
trace_id: string
span_id: string
parent_span_id: string
start_time: [number, number]
end_time: [number, number]
attributes?: Record<string, string>
links?: { trace_id: string; span_id: string }[]
}

// Configuration
export type QueryEngineLogLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'off'

Expand Down
@@ -1,9 +1,9 @@
import Debug from '@prisma/debug'
import { DMMF } from '@prisma/generator-helper'
import { EngineSpan, TracingHelper } from '@prisma/internals'

import { PrismaClientUnknownRequestError } from '../../errors/PrismaClientUnknownRequestError'
import { prismaGraphQLToJSError } from '../../errors/utils/prismaGraphQLToJSError'
import { createSpan, getTraceParent, getTracingConfig, runInChildSpan, TracingConfig } from '../../tracing'
import type {
BatchQueryEngineResult,
EngineBatchQueries,
Expand All @@ -18,7 +18,7 @@ import type {
import { Engine } from '../common/Engine'
import { EventEmitter } from '../common/types/Events'
import { Metrics, MetricsOptionsJson, MetricsOptionsPrometheus } from '../common/types/Metrics'
import { EngineSpan, QueryEngineResult, QueryEngineResultBatchQueryResult } from '../common/types/QueryEngine'
import { QueryEngineResult, QueryEngineResultBatchQueryResult } from '../common/types/QueryEngine'
import type * as Tx from '../common/types/Transaction'
import { getBatchRequestPayload } from '../common/utils/getBatchRequestPayload'
import { LogLevel } from '../common/utils/log'
Expand Down Expand Up @@ -78,23 +78,23 @@ type HeaderBuilderOptions = {

class DataProxyHeaderBuilder {
readonly apiKey: string
readonly tracingConfig: TracingConfig
readonly tracingHelper: TracingHelper
readonly logLevel: EngineConfig['logLevel']
readonly logQueries: boolean | undefined

constructor({
apiKey,
tracingConfig,
tracingHelper,
logLevel,
logQueries,
}: {
apiKey: string
tracingConfig: TracingConfig
tracingHelper: TracingHelper
logLevel: EngineConfig['logLevel']
logQueries: boolean | undefined
}) {
this.apiKey = apiKey
this.tracingConfig = tracingConfig
this.tracingHelper = tracingHelper
this.logLevel = logLevel
this.logQueries = logQueries
}
Expand All @@ -104,8 +104,8 @@ class DataProxyHeaderBuilder {
Authorization: `Bearer ${this.apiKey}`,
}

if (this.tracingConfig.enabled) {
headers.traceparent = traceparent ?? getTraceParent({})
if (this.tracingHelper.isEnabled()) {
headers.traceparent = traceparent ?? this.tracingHelper.getTraceParent()
}

if (interactiveTransaction) {
Expand All @@ -124,7 +124,7 @@ class DataProxyHeaderBuilder {
private buildCaptureSettings() {
const captureTelemetry: string[] = []

if (this.tracingConfig.enabled) {
if (this.tracingHelper.isEnabled()) {
captureTelemetry.push('tracing')
}

Expand All @@ -148,7 +148,7 @@ export class DataProxyEngine extends Engine<DataProxyTxInfoPayload> {
private env: { [k in string]?: string }

private clientVersion: string
private tracingConfig: TracingConfig
private tracingHelper: TracingHelper
readonly remoteClientVersion: Promise<string>
readonly host: string
readonly headerBuilder: DataProxyHeaderBuilder
Expand All @@ -163,14 +163,14 @@ export class DataProxyEngine extends Engine<DataProxyTxInfoPayload> {
this.inlineSchemaHash = config.inlineSchemaHash ?? ''
this.clientVersion = config.clientVersion ?? 'unknown'
this.logEmitter = config.logEmitter
this.tracingConfig = getTracingConfig(this.config.previewFeatures || [])
this.tracingHelper = this.config.tracingHelper

const [host, apiKey] = this.extractHostAndApiKey()
this.host = host

this.headerBuilder = new DataProxyHeaderBuilder({
apiKey,
tracingConfig: this.tracingConfig,
tracingHelper: this.tracingHelper,
logLevel: config.logLevel,
logQueries: config.logQueries,
})
Expand All @@ -193,8 +193,6 @@ export class DataProxyEngine extends Engine<DataProxyTxInfoPayload> {
async stop() {}

private propagateResponseExtensions(extensions: DataProxyExtensions): void {
const tracingConfig = getTracingConfig(this.config.previewFeatures || [])

if (extensions?.logs?.length) {
extensions.logs.forEach((log) => {
switch (log.level) {
Expand All @@ -208,7 +206,7 @@ export class DataProxyEngine extends Engine<DataProxyTxInfoPayload> {
case 'query': {
let dbQuery = typeof log.attributes.query === 'string' ? log.attributes.query : ''

if (!tracingConfig.enabled) {
if (!this.tracingHelper.isEnabled()) {
// The engine uses tracing to consolidate logs
// - and so we should strip the generated traceparent
// - if tracing is disabled.
Expand All @@ -229,8 +227,8 @@ export class DataProxyEngine extends Engine<DataProxyTxInfoPayload> {
})
}

if (extensions?.traces?.length && tracingConfig.enabled) {
void createSpan({ span: true, spans: extensions.traces })
if (extensions?.traces?.length) {
void this.tracingHelper.createEngineSpan({ span: true, spans: extensions.traces })
}
}

Expand Down Expand Up @@ -260,10 +258,9 @@ export class DataProxyEngine extends Engine<DataProxyTxInfoPayload> {
const spanOptions = {
name: 'schemaUpload',
internal: true,
enabled: this.tracingConfig.enabled,
}

return runInChildSpan(spanOptions, async () => {
return this.tracingHelper.runInChildSpan(spanOptions, async () => {
const response = await request(await this.url('schema'), {
method: 'PUT',
headers: this.headerBuilder.build(),
Expand Down
Expand Up @@ -9,7 +9,6 @@ import os from 'os'
import path from 'path'

import { PrismaClientInitializationError } from '../../errors/PrismaClientInitializationError'
import { runInChildSpan } from '../../tracing'
import { EngineConfig } from '../common/Engine'
import { Library, LibraryLoader } from './types/Library'

Expand Down Expand Up @@ -86,9 +85,7 @@ export class DefaultLibraryLoader implements LibraryLoader {
debug(`loadEngine using ${this.libQueryEnginePath}`)
try {
const enginePath = this.libQueryEnginePath
return runInChildSpan({ name: 'loadLibrary', enabled: this.config.tracingConfig.enabled, internal: true }, () =>
load(enginePath),
)
return this.config.tracingHelper.runInChildSpan({ name: 'loadLibrary', internal: true }, () => load(enginePath))
} catch (e) {
const errorMessage = handleLibraryLoadingErrors({
e: e as Error,
Expand Down

0 comments on commit 435bf81

Please sign in to comment.