Skip to content

Commit

Permalink
feat: emit db queries
Browse files Browse the repository at this point in the history
  • Loading branch information
thetutlage committed Apr 7, 2020
1 parent 297acd1 commit 1466b31
Show file tree
Hide file tree
Showing 28 changed files with 250 additions and 263 deletions.
22 changes: 5 additions & 17 deletions adonis-typings/database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ declare module '@ioc:Adonis/Lucid/Database' {
import { Pool } from 'tarn'
import { EventEmitter } from 'events'
import { MacroableConstructorContract } from 'macroable'
import { EmitterContract } from '@ioc:Adonis/Core/Event'
import { HealthReportEntry } from '@ioc:Adonis/Core/HealthCheck'
import { ProfilerRowContract, ProfilerContract } from '@ioc:Adonis/Core/Profiler'

Expand Down Expand Up @@ -59,6 +60,8 @@ declare module '@ioc:Adonis/Lucid/Database' {
* of query builder
*/
export interface QueryClientContract {
emitter: EmitterContract,

/**
* Custom profiler to time queries
*/
Expand Down Expand Up @@ -474,35 +477,20 @@ declare module '@ioc:Adonis/Lucid/Database' {
name: string,
config: ConnectionConfig,
connection?: ConnectionContract,
state: 'registered' | 'migrating' | 'open' | 'closed',
state: 'registered' | 'migrating' | 'open' | 'closing' | 'closed',
}

/**
* Connection manager to manage one or more database
* connections.
*/
export interface ConnectionManagerContract extends EventEmitter {
export interface ConnectionManagerContract {
/**
* List of registered connection. You must check the connection state
* to understand, if it is connected or not
*/
connections: Map<string, ConnectionNode>

/**
* Everytime a connection is created
*/
on (event: 'connect', callback: (connection: ConnectionContract) => void): this

/**
* Everytime a connection leaves
*/
on (event: 'disconnect', callback: (connection: ConnectionContract) => void): this

/**
* When error is received on a given connection
*/
on (event: 'error', callback: (error: Error, connection: ConnectionContract) => void): this

/**
* Add a new connection to the list of managed connection. You must call
* connect seperately to instantiate a connection instance
Expand Down
3 changes: 0 additions & 3 deletions src/Connection/Logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,14 @@ import { LoggerContract } from '@ioc:Adonis/Core/Logger'
*/
export class Logger {
public warn = function (message: any) {
this.addConnectionName(message)
this.adonisLogger.warn(message)
}.bind(this)

public error = function (message: any) {
this.addConnectionName(message)
this.adonisLogger.error(message)
}.bind(this)

public deprecate = function (message: any) {
this.addConnectionName(message)
this.adonisLogger.info(message)
}.bind(this)

Expand Down
105 changes: 53 additions & 52 deletions src/Connection/Manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@

/// <reference path="../../adonis-typings/index.ts" />

import { EventEmitter } from 'events'
import { Exception } from '@poppinss/utils'
import { EmitterContract } from '@ioc:Adonis/Core/Event'
import { LoggerContract } from '@ioc:Adonis/Core/Logger'
import { HealthReportEntry } from '@ioc:Adonis/Core/HealthCheck'

Expand All @@ -29,7 +29,10 @@ import { Connection } from './index'
* or connections by registering their config only once and then make use of `connect`
* and `close` methods to create and destroy db connections.
*/
export class ConnectionManager extends EventEmitter implements ConnectionManagerContract {
export class ConnectionManager implements ConnectionManagerContract {
/**
* List of managed connections
*/
public connections: ConnectionManagerContract['connections'] = new Map()

/**
Expand All @@ -38,66 +41,62 @@ export class ConnectionManager extends EventEmitter implements ConnectionManager
*/
private orphanConnections: Set<ConnectionContract> = new Set()

constructor (private logger: LoggerContract) {
super()
constructor (private logger: LoggerContract, private emitter: EmitterContract) {
}

/**
* Monitors a given connection by listening for lifecycle events
* Handles disconnection of a connection
*/
private monitorConnection (connection: ConnectionContract): void {
private handleDisconnect (connection: ConnectionContract) {
/**
* Listens for disconnect to set the connection state and cleanup
* memory
* We received the close event on the orphan connection and not the connection
* that is in use
*/
connection.on('disconnect', ($connection) => {
/**
* We received the close event on the orphan connection and not the connection
* that is in use
*/
if (this.orphanConnections.has($connection)) {
this.orphanConnections.delete($connection)

this.emit('disconnect', $connection)
this.logger.trace({ connection: $connection.name }, 'disconnecting connection inside manager')
return
}

const internalConnection = this.get($connection.name)

/**
* This will be false, when connection was released at the
* time of closing
*/
if (!internalConnection) {
return
}

this.emit('disconnect', $connection)
this.logger.trace({ connection: $connection.name }, 'disconnecting connection inside manager')

delete internalConnection.connection
internalConnection.state = 'closed'
})
if (this.orphanConnections.has(connection)) {
this.orphanConnections.delete(connection)
this.emitter.emit('db:connection:disconnect', connection)
this.logger.trace({ connection: connection.name }, 'disconnecting connection inside manager')
return
}

/**
* Listens for connect to set the connection state to open
*/
connection.on('connect', ($connection) => {
const internalConnection = this.get($connection.name)
if (!internalConnection) {
return
}

this.emit('connect', $connection)
internalConnection.state = 'open'
})
const internalConnection = this.get(connection.name)

/**
* Listens for error event to proxy it to the client
* This will be false, when connection was released at the
* time of closing
*/
if (!internalConnection) {
return
}

this.emitter.emit('db:connection:disconnect', connection)
this.logger.trace({ connection: connection.name }, 'disconnecting connection inside manager')

delete internalConnection.connection
internalConnection.state = 'closed'
}

/**
* Handles event when a new connection is added
*/
private handleConnect (connection: ConnectionContract) {
const internalConnection = this.get(connection.name)
if (!internalConnection) {
return
}

this.emitter.emit('db:connection:connect', connection)
internalConnection.state = 'open'
}

/**
* Monitors a given connection by listening for lifecycle events
*/
private monitorConnection (connection: ConnectionContract): void {
connection.on('disconnect', ($connection) => this.handleDisconnect($connection))
connection.on('connect', ($connection) => this.handleConnect($connection))
connection.on('error', (error, $connection) => {
this.emit('error', error, $connection)
this.emitter.emit('db:connection:error', [error, $connection])
})
}

Expand Down Expand Up @@ -222,7 +221,9 @@ export class ConnectionManager extends EventEmitter implements ConnectionManager
*/
public async close (connectionName: string, release: boolean = false): Promise<void> {
if (this.isConnected(connectionName)) {
await this.get(connectionName)!.connection!.disconnect()
const connection = this.get(connectionName)!
await connection.connection!.disconnect()
connection.state = 'closing'
}

if (release) {
Expand Down
12 changes: 4 additions & 8 deletions src/Database/QueryBuilder/Database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,15 +70,11 @@ export class DatabaseQueryBuilder extends Chainable implements DatabaseQueryBuil
/**
* Returns the profiler action
*/
private getProfilerAction () {
if (!this.client.profiler) {
return null
}

return this.client.profiler.profile('sql:query', Object.assign(this['toSQL'](), {
private getQueryData () {
return Object.assign(this.toSQL(), {
connection: this.client.connectionName,
inTransaction: this.client.isTransaction,
}))
})
}

/**
Expand Down Expand Up @@ -196,7 +192,7 @@ export class DatabaseQueryBuilder extends Chainable implements DatabaseQueryBuil
* Executes the query
*/
public async exec (): Promise<any> {
return executeQuery(this.knexQuery, this.client, this.getProfilerAction())
return executeQuery(this.knexQuery, this.client, this.getQueryData())
}

/**
Expand Down
12 changes: 4 additions & 8 deletions src/Database/QueryBuilder/Insert.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,11 @@ export class InsertQueryBuilder extends Macroable implements InsertQueryBuilderC
/**
* Returns the profiler action
*/
private getProfilerAction () {
if (!this.client.profiler) {
return null
}

return this.client.profiler.profile('sql:query', Object.assign(this['toSQL'](), {
private getQueryData () {
return Object.assign(this.toSQL(), {
connection: this.client.connectionName,
inTransaction: this.client.isTransaction,
}))
})
}

/**
Expand Down Expand Up @@ -118,7 +114,7 @@ export class InsertQueryBuilder extends Macroable implements InsertQueryBuilderC
* Executes the query
*/
public async exec (): Promise<any> {
return executeQuery(this.knexQuery, this.client, this.getProfilerAction())
return executeQuery(this.knexQuery, this.client, this.getQueryData())
}

/**
Expand Down
12 changes: 4 additions & 8 deletions src/Database/QueryBuilder/Raw.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,11 @@ export class RawQueryBuilder implements RawQueryBuilderContract {
/**
* Returns the profiler action
*/
private getProfilerAction () {
if (!this.client.profiler) {
return null
}

return this.client.profiler.profile('sql:query', Object.assign(this['toSQL'](), {
private getQueryData () {
return Object.assign(this.toSQL(), {
connection: this.client.connectionName,
inTransaction: this.client.isTransaction,
}))
})
}

/**
Expand Down Expand Up @@ -79,7 +75,7 @@ export class RawQueryBuilder implements RawQueryBuilderContract {
* Executes the query
*/
public async exec (): Promise<any> {
return executeQuery(this.knexQuery, this.client, this.getProfilerAction())
return executeQuery(this.knexQuery, this.client, this.getQueryData())
}

/**
Expand Down
21 changes: 13 additions & 8 deletions src/Database/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
/// <reference path="../../adonis-typings/index.ts" />

import { Exception } from '@poppinss/utils'
import { EmitterContract } from '@ioc:Adonis/Core/Event'
import { LoggerContract } from '@ioc:Adonis/Core/Logger'
import { ProfilerContract } from '@ioc:Adonis/Core/Profiler'

Expand Down Expand Up @@ -63,8 +64,9 @@ export class Database implements DatabaseContract {
private config: DatabaseConfig,
private logger: LoggerContract,
private profiler: ProfilerContract,
private emitter: EmitterContract,
) {
this.manager = new ConnectionManager(this.logger)
this.manager = new ConnectionManager(this.logger, this.emitter)
this.registerConnections()
}

Expand Down Expand Up @@ -120,7 +122,6 @@ export class Database implements DatabaseContract {
if (this.connectionGlobalTransactions.has(connection)) {
this.logger.trace({ connection }, 'using pre-existing global transaction connection')
const globalTransactionClient = this.connectionGlobalTransactions.get(connection)!
globalTransactionClient.profiler = options.profiler
return globalTransactionClient
}

Expand All @@ -135,8 +136,8 @@ export class Database implements DatabaseContract {
*/
this.logger.trace({ connection }, 'creating query client in %s mode', [options.mode || 'dual'])
const queryClient = options.mode
? new QueryClient(options.mode, rawConnection)
: new QueryClient('dual', rawConnection)
? new QueryClient(options.mode, rawConnection, this.emitter)
: new QueryClient('dual', rawConnection, this.emitter)

/**
* Passing profiler to the query client for profiling queries
Expand Down Expand Up @@ -282,8 +283,10 @@ export class Database implements DatabaseContract {
const trx = this.connectionGlobalTransactions.get(connectionName)

if (!trx) {
// eslint-disable-next-line max-len
throw new Exception('Cannot commit a non-existing global transaction. Make sure you are not calling "commitGlobalTransaction" twice')
throw new Exception([
'Cannot commit a non-existing global transaction.',
' Make sure you are not calling "commitGlobalTransaction" twice',
].join(''))
}

await trx.commit()
Expand All @@ -297,8 +300,10 @@ export class Database implements DatabaseContract {
const trx = this.connectionGlobalTransactions.get(connectionName)

if (!trx) {
// eslint-disable-next-line max-len
throw new Exception('Cannot rollback a non-existing global transaction. Make sure you are not calling "commitGlobalTransaction" twice')
throw new Exception([
'Cannot rollback a non-existing global transaction.',
' Make sure you are not calling "commitGlobalTransaction" twice',
].join(''))
}

await trx.rollback()
Expand Down
12 changes: 4 additions & 8 deletions src/Orm/QueryBuilder/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ export class ModelQueryBuilder extends Chainable implements ModelQueryBuilderCon
*/
private async execQuery () {
const isWriteQuery = ['update', 'del', 'insert'].includes(this.knexQuery['_method'])
const rows = await executeQuery(this.knexQuery, this.client, this.getProfilerAction())
const rows = await executeQuery(this.knexQuery, this.client, this.getQueryData())

/**
* Return the rows as it is when query is a write query
Expand Down Expand Up @@ -159,16 +159,12 @@ export class ModelQueryBuilder extends Chainable implements ModelQueryBuilderCon
* Returns the profiler action. Protected, since the class is extended
* by relationships
*/
protected getProfilerAction () {
if (!this.client.profiler) {
return null
}

return this.client.profiler.profile('sql:query', Object.assign(this['toSQL'](), {
protected getQueryData () {
return Object.assign(this.toSQL(), {
connection: this.client.connectionName,
inTransaction: this.client.isTransaction,
model: this.model.name,
}))
})
}

/**
Expand Down
Loading

0 comments on commit 1466b31

Please sign in to comment.