-
-
Notifications
You must be signed in to change notification settings - Fork 198
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: implement connection class to manage and monitor pool resources
- Loading branch information
1 parent
c8713e9
commit 69b366a
Showing
6 changed files
with
421 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
DB=mysql | ||
MYSQL_HOST=0.0.0.0 | ||
MYSQL_PORT=3306 | ||
DB_NAME=lucid | ||
MYSQL_USER=virk | ||
MYSQL_PASSWORD=password |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,18 +1,14 @@ | ||
version: '2' | ||
version: '3.3' | ||
services: | ||
mysql: | ||
db: | ||
image: mysql:5.7 | ||
expose: | ||
- '3306' | ||
restart: always | ||
environment: | ||
MYSQL_DATABASE: 'lucid' | ||
MYSQL_USER: 'virk' | ||
MYSQL_PASSWORD: 'password' | ||
MYSQL_ROOT_PASSWORD: 'password' | ||
ports: | ||
- '3306:3306' | ||
restart: 'always' | ||
environment: | ||
MYSQL_ALLOW_EMPTY_PASSWORD: '' | ||
MYSQL_ROOT_PASSWORD: 'root' | ||
MYSQL_USER: 'travis' | ||
MYSQL_PASSWORD: '' | ||
MYSQL_ROOT_HOST: '%' | ||
MYSQL_DATABASE: 'testing_lucid' | ||
volumes: | ||
- ./mysqldata:/var/lib/mysql | ||
expose: | ||
- '3306' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
// import * as Knex from 'knex' | ||
// // import { join } from 'path' | ||
// // import { DatabaseQueryBuilderContract } from '@ioc:Adonis/Addons/Database' | ||
|
||
// const knex = Knex({ | ||
// client: 'pg', | ||
// connection: { | ||
// host: '0.0.0.0', | ||
// user: 'virk', | ||
// password: '', | ||
// database: 'directory-service', | ||
// }, | ||
// pool: { | ||
// min: 0, | ||
// max: 5, | ||
// idleTimeoutMillis: 30000, | ||
// }, | ||
// useNullAsDefault: true, | ||
// }) | ||
|
||
// // let i = 0; | ||
// knex['_context'].client.pool.on('destroySuccess', _eventId => { | ||
// // i++ | ||
// console.log( | ||
// knex['_context'].client.pool.numUsed(), | ||
// knex['_context'].client.pool.numFree(), | ||
// knex['_context'].client.pool.numPendingAcquires(), | ||
// ) | ||
|
||
// // if (i === 3) { | ||
// // knex['_context'].client.pool.destroy() | ||
// // } | ||
// }); | ||
|
||
// knex['_context'].client.pool.on('poolDestroySuccess', _resource => { | ||
// console.log('poolDestroySuccess>>>') | ||
// }); | ||
|
||
// // setInterval(() => { | ||
// // console.log('ping') | ||
// // }, 1000) | ||
|
||
// // type User = { | ||
// // id: number, | ||
// // } | ||
|
||
// // console.log(knex.raw(['10']).toQuery()) | ||
|
||
// // knex.schema.createTable('users', (table) => { | ||
// // table.increments('id') | ||
// // table.string('username') | ||
// // table.integer('age') | ||
// // table.timestamps() | ||
// // }).then(() => console.log('created')) | ||
|
||
// // knex.table('users').insert([ | ||
// // { username: 'virk', age: 29 }, { username: 'nikk', age: 28 }, { username: 'prasan', age: 29 }, | ||
// // ]).then(console.log) | ||
|
||
// Promise.all([ | ||
// knex | ||
// .select('*') | ||
// .from('users') | ||
// .debug(true) | ||
// .then((result) => { | ||
// console.log(result) | ||
// }), | ||
// knex | ||
// .select('*') | ||
// .from('users') | ||
// .debug(true) | ||
// .then((result) => { | ||
// console.log(result) | ||
// }), | ||
// knex | ||
// .select('*') | ||
// .from('users') | ||
// .debug(true) | ||
// .then((result) => { | ||
// console.log(result) | ||
// }), | ||
// knex | ||
// .select('*') | ||
// .from('users') | ||
// .debug(true) | ||
// .then((result) => { | ||
// console.log(result) | ||
// }), | ||
// knex | ||
// .select('*') | ||
// .from('users') | ||
// .debug(true) | ||
// .then((result) => { | ||
// console.log(result) | ||
// }), | ||
// ]).then(() => { | ||
// }) | ||
|
||
// // knex.transaction().then((trx) => { | ||
// // }) | ||
|
||
// // console.log(query.toSQL()) | ||
|
||
// // type FilteredKeys<T> = { [P in keyof T]: T[P] extends Function ? never : P }[keyof T] | ||
|
||
// // type GetRefs<T extends typeof BaseModel> = T['refs'] extends object ? { | ||
// // [P in keyof T['refs']]: InstanceType<T>[P] | ||
// // } : { | ||
// // [P in FilteredKeys<InstanceType<T>>]: InstanceType<T>[P] | ||
// // } | ||
|
||
// // class BaseModel { | ||
// // public static refs: unknown | ||
|
||
// // public static query<T extends typeof BaseModel> ( | ||
// // this: T, | ||
// // ): DatabaseQueryBuilderContract<GetRefs<T>, InstanceType<T>> { | ||
// // return {} as DatabaseQueryBuilderContract<GetRefs<T>, InstanceType<T>> | ||
// // } | ||
// // } | ||
|
||
// // class User extends BaseModel { | ||
// // public username: string | ||
// // public age: number | ||
|
||
// // public castToInt (): number { | ||
// // return 22 | ||
// // } | ||
// // } | ||
|
||
// // class Post extends BaseModel { | ||
// // } | ||
|
||
// // const foo: DatabaseQueryBuilderContract<{ username: string, age: number }> | ||
// // const b = foo | ||
// // .select('*') | ||
// // .select({ | ||
// // 'a': 'username', | ||
// // 'age': 'age', | ||
// // }).first() | ||
|
||
// // async function foo () { | ||
// // const a = User.query().select(['username', 'age']).first() | ||
// // } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
/* | ||
* @adonisjs/lucid | ||
* | ||
* (c) Harminder Virk <virk@adonisjs.com> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
/// <reference path="../../adonis-typings/database.ts" /> | ||
|
||
import { Pool } from 'tarn' | ||
import * as knex from 'knex' | ||
import { EventEmitter } from 'events' | ||
import { ConnectionConfigContract, ConnectionContract } from '@ioc:Adonis/Addons/Database' | ||
|
||
/** | ||
* Connection class manages a given database connection. Internally it uses | ||
* knex to build the database connection with appropriate database | ||
* driver. | ||
*/ | ||
export class Connection extends EventEmitter implements ConnectionContract { | ||
/** | ||
* Reference to knex. The instance is created once the `open` | ||
* method is invoked | ||
*/ | ||
public client?: knex | ||
|
||
/** | ||
* List of events emitted by this class | ||
*/ | ||
public readonly EVENTS: ['open', 'close', 'close:error'] | ||
|
||
constructor (public name: string, public config: ConnectionConfigContract) { | ||
super() | ||
} | ||
|
||
/** | ||
* Does cleanup by removing knex reference and removing | ||
* all listeners | ||
*/ | ||
private _monitorPoolResources () { | ||
this.pool!.on('destroySuccess', () => { | ||
/** | ||
* Force close when `numUsed` and `numFree` both are zero. This happens | ||
* when `min` resources inside the pool are set to `0`. | ||
*/ | ||
if (this.pool!.numFree() === 0 && this.pool!.numUsed() === 0) { | ||
this.close() | ||
} | ||
}) | ||
|
||
/** | ||
* Pool has destroyed and hence we must cleanup resources | ||
* as well. | ||
*/ | ||
this.pool!.on('poolDestroySuccess', () => { | ||
this.client = undefined | ||
this.emit('close') | ||
this.removeAllListeners() | ||
}) | ||
} | ||
|
||
/** | ||
* Returns the pool instance for the given connection | ||
*/ | ||
public get pool (): null | Pool<any> { | ||
return this.client ? this.client['_context'].client.pool : null | ||
} | ||
|
||
/** | ||
* Opens the connection by creating knex instance | ||
*/ | ||
public open () { | ||
try { | ||
this.client = knex(this.config) | ||
this._monitorPoolResources() | ||
this.emit('open') | ||
} catch (error) { | ||
this.emit('error', error) | ||
throw error | ||
} | ||
} | ||
|
||
/** | ||
* Closes DB connection by destroying knex instance. The `connection` | ||
* object must be free for garbage collection. | ||
* | ||
* In case of error this method will emit `close:error` event followed | ||
* by the `close` event. | ||
*/ | ||
public async close (): Promise<void> { | ||
if (this.client) { | ||
try { | ||
await this.client!.destroy() | ||
} catch (error) { | ||
this.emit('close:error', error) | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
/* | ||
* @adonisjs/lucid | ||
* | ||
* (c) Harminder Virk <virk@adonisjs.com> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
/// <reference path="../adonis-typings/database.ts" /> | ||
|
||
import { join } from 'path' | ||
import * as dotenv from 'dotenv' | ||
import { Filesystem } from '@poppinss/dev-utils' | ||
import { ConnectionConfigContract } from '@ioc:Adonis/Addons/Database' | ||
|
||
export const fs = new Filesystem(join(__dirname, 'tmp')) | ||
dotenv.config() | ||
|
||
/** | ||
* Returns config based upon DB set in environment variables | ||
*/ | ||
export function getConfig (): ConnectionConfigContract { | ||
switch (process.env.DB) { | ||
case 'sqlite': | ||
return { | ||
client: 'sqlite', | ||
connection: { | ||
filename: join(fs.basePath, 'db.sqlite'), | ||
}, | ||
useNullAsDefault: true, | ||
} | ||
case 'mysql': | ||
return { | ||
client: 'mysql', | ||
connection: { | ||
host: process.env.MYSQL_HOST as string, | ||
port: Number(process.env.MYSQL_PORT), | ||
database: process.env.DB_NAME as string, | ||
user: process.env.MYSQL_USER as string, | ||
password: process.env.MYSQL_PASSWORD as string, | ||
}, | ||
useNullAsDefault: true, | ||
} | ||
default: | ||
throw new Error(`Missing test config for ${process.env.DB} connection`) | ||
} | ||
} | ||
|
||
/** | ||
* Does base setup by creating databases | ||
*/ | ||
export async function setup () { | ||
if (process.env.DB === 'sqlite') { | ||
await fs.ensureRoot() | ||
} | ||
} | ||
|
||
/** | ||
* Does cleanup removes database | ||
*/ | ||
export async function cleanup () { | ||
if (process.env.DB === 'sqlite') { | ||
await fs.cleanup() | ||
} | ||
} |
Oops, something went wrong.