npm.io
6.3.1 • Published 3 months ago

@ydbjs/core

Licence
Apache-2.0
Version
6.3.1
Deps
9
Size
134 kB
Vulns
0
Weekly
0
Stars
82

@ydbjs/core

The @ydbjs/core package provides the core driver and connection management for YDB in JavaScript/TypeScript. It is the foundation for all YDB client operations, handling connection pooling, service client creation, authentication, and middleware.

Features

  • Connection pooling and load balancing for YDB endpoints
  • Service client creation for any YDB gRPC API
  • Pluggable authentication via @ydbjs/auth providers
  • Automatic endpoint discovery and failover
  • TypeScript support with type definitions
  • Compatible with Node.js and modern runtimes

Installation

npm install @ydbjs/core

How It Works

  • Driver: The main entry point. Manages connections, endpoint discovery, and authentication.
  • Connection Pool: Maintains and balances gRPC channels to YDB endpoints.
  • Service Clients: Use driver.createClient(ServiceDefinition) to get a typed client for any YDB gRPC service (from @ydbjs/api).
  • Authentication: Pass a credentials provider from @ydbjs/auth to the driver for static, token, anonymous, or cloud metadata authentication.
  • Middleware: Internal middleware handles metadata, authentication, and debugging.

Usage

Basic Example
import { Driver } from '@ydbjs/core'
import { DiscoveryServiceDefinition } from '@ydbjs/api/discovery'

const driver = new Driver('grpc://localhost:2136/local')
await driver.ready()

const discovery = driver.createClient(DiscoveryServiceDefinition)
const endpoints = await discovery.listEndpoints({ database: '/local' })
console.log(endpoints)

await driver.close()
Using Authentication Providers
import { Driver } from '@ydbjs/core'
import { StaticCredentialsProvider } from '@ydbjs/auth/static'

const driver = new Driver('grpc://localhost:2136/local', {
  credentialsProvider: new StaticCredentialsProvider({
    username: 'user',
    password: 'pass',
  }),
})
await driver.ready()
// ...

You can also use AccessTokenCredentialsProvider, AnonymousCredentialsProvider, or MetadataCredentialsProvider from @ydbjs/auth.

Closing the Driver

Always close the driver when done to release resources:

driver.close()

Observability via node:diagnostics_channel

@ydbjs/core publishes domain events over node:diagnostics_channel so external subscribers (@ydbjs/telemetry, OpenTelemetry, custom loggers) can build traces, metrics, and logs without the driver knowing anything about them.

Two primitives are used:

  • channel.publish — point-in-time state changes (gauges, counters, structured logs).
  • tracingChannel.tracePromise — bracketed operations with duration and possible error (spans, latency histograms).
Conventions

All payloads share the same identity envelope, so multi-driver consumers can disambiguate:

type DriverIdentity = {
  address: string // host:port the driver was constructed with
  port: number | undefined
  database: string // YDB database path
  registeredAt: number // Date.now() at Driver construction
}

Time values follow Node.js conventions:

  • Durations are in milliseconds (performance.now() deltas).
  • Timestamps are in epoch milliseconds (Date.now()).
  • Subscribers that target OTel attributes / instruments (whose canonical unit is seconds) divide by 1000 at the mapping layer — @ydbjs/telemetry does this for you.
Channels
Driver lifecycle
Channel Type Payload
ydb:driver.ready publish { driver: DriverIdentity, duration: number } (ms since init)
ydb:driver.failed publish { driver: DriverIdentity, duration: number, error: unknown } (ms)
ydb:driver.closed publish { driver: DriverIdentity, uptime: number } (ms since ready)
Discovery
Channel Type Payload
tracing:ydb:driver.discovery tracing { driver: DriverIdentity }
ydb:driver.discovery.completed publish { driver: DriverIdentity, addedCount: number, removedCount: number, totalCount: number, duration: number } (ms)
Connection pool

All connection-pool channels carry { driver: DriverIdentity, nodeId: bigint, address: string, location: string } plus the extra field listed below.

Channel Type Extra fields
ydb:driver.connection.added publish (none)
ydb:driver.connection.pessimized publish until: number — epoch ms when the pessimization window ends
ydb:driver.connection.unpessimized publish duration: number — ms the connection actually stayed pessimized
ydb:driver.connection.retired publish reason: 'stale_active' | 'stale_pessimized'
ydb:driver.connection.removed publish reason: 'replaced' | 'idle' | 'pool_close'

The pool exposes two distinct teardown events for connections:

  • retired — the connection was removed from active routing (e.g. its endpoint disappeared from discovery), but its gRPC channel is left open so in-flight streams can drain.
  • removed — the gRPC channel was physically closed. The reason field distinguishes whether it was a replacement, an idle teardown, or a pool shutdown.

A gauge of "alive channels" can be reconstructed from the delta between connection.added and connection.removed. A gauge of "routable connections" should also subtract retired. @ydbjs/telemetry does this with an in-memory Map<DriverIdentity, ConnectionState>.

Subscribing
import { channel, tracingChannel } from 'node:diagnostics_channel'

channel('ydb:driver.ready').subscribe((msg) => {
  console.log('driver ready', msg)
})

tracingChannel('tracing:ydb:driver.discovery').subscribe({
  start(ctx) {
    // ctx.driver.database === '/local'
  },
  asyncEnd(ctx) {
    // discovery round succeeded
  },
  error(ctx) {
    // ctx.error is the failure
  },
})
Subscribers must be safe

node:diagnostics_channel invokes subscribers synchronously, on the publishing thread. Any exception thrown inside a subscriber propagates up the call stack and will disrupt the SDK — a buggy subscriber can break a Driver.ready(), abort a discovery round, or leak a gRPC channel.

@ydbjs/core does not wrap your subscribers. It is your responsibility to keep them safe:

channel('ydb:driver.ready').subscribe((msg) => {
  try {
    metrics.driverReady.add(1, { database: msg.driver.database })
  } catch (err) {
    // Never let a metrics failure escape — log it locally and move on.
    console.error('telemetry subscriber failed', err)
  }
})

The same applies to tracingChannel handlers (start, asyncEnd, error, etc.) — each must be self-contained and never throw.

Stability

Channel names and payload field names follow semantic versioning. Adding new optional fields is a minor change; renaming or removing fields is a major change. Treat the channel names and payload shapes as a public API surface.

Development

Building the Package
npm run build
Running Tests
npm test

For watch mode during development:

npm run test:watch

License

This project is licensed under the Apache 2.0 License.

Keywords