123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854 |
- "use strict";
- const tap = require("tap");
- const createPool = require("../").createPool;
- const utils = require("./utils");
- const ResourceFactory = utils.ResourceFactory;
- // tap.test('Pool expands only to max limit', function (t) {
- // const resourceFactory = new ResourceFactory()
- // const config = {
- // max: 1
- // }
- // const pool = createPool(resourceFactory, config)
- // // NOTES:
- // // - request a resource
- // // - once we have it, request another and check the pool is fool
- // pool.acquire(function (err, obj) {
- // t.error(err)
- // const poolIsFull = !pool.acquire(function (err, obj) {
- // t.error(err)
- // t.equal(1, resourceFactory.created)
- // pool.release(obj)
- // utils.stopPool(pool)
- // t.end()
- // })
- // t.ok(poolIsFull)
- // t.equal(1, resourceFactory.created)
- // pool.release(obj)
- // })
- // })
- // tap.test('Pool respects min limit', function (t) {
- // const resourceFactory = new ResourceFactory()
- // const config
- // min: 1,
- // max: 2
- // }
- // const pool = createPool(resourceFactory, config)
- // // FIXME: this logic only works because we know it takes ~1ms to create a resource
- // // we need better hooks into the pool probably to observe this...
- // setTimeout(function () {
- // t.equal(resourceFactory.created, 1)
- // utils.stopPool(pool)
- // t.end()
- // }, 10)
- // })
- tap.test("min and max limit defaults", function(t) {
- const resourceFactory = new ResourceFactory();
- const pool = createPool(resourceFactory);
- t.equal(1, pool.max);
- t.equal(0, pool.min);
- utils.stopPool(pool);
- t.end();
- });
- tap.test("malformed min and max limits are ignored", function(t) {
- const resourceFactory = new ResourceFactory();
- const config = {
- min: "asf",
- max: []
- };
- const pool = createPool(resourceFactory, config);
- t.equal(1, pool.max);
- t.equal(0, pool.min);
- utils.stopPool(pool);
- t.end();
- });
- tap.test("min greater than max sets to max", function(t) {
- const resourceFactory = new ResourceFactory();
- const config = {
- min: 5,
- max: 3
- };
- const pool = createPool(resourceFactory, config);
- t.equal(3, pool.max);
- t.equal(3, pool.min);
- utils.stopPool(pool);
- t.end();
- });
- tap.test("supports priority on borrow", function(t) {
- // NOTE: this test is pretty opaque about what it's really testing/expecting...
- let borrowTimeLow = 0;
- let borrowTimeHigh = 0;
- let borrowCount = 0;
- const resourceFactory = new ResourceFactory();
- const config = {
- max: 1,
- priorityRange: 2
- };
- const pool = createPool(resourceFactory, config);
- function lowPriorityOnFulfilled(obj) {
- const time = Date.now();
- if (time > borrowTimeLow) {
- borrowTimeLow = time;
- }
- borrowCount++;
- pool.release(obj);
- }
- function highPriorityOnFulfilled(obj) {
- const time = Date.now();
- if (time > borrowTimeHigh) {
- borrowTimeHigh = time;
- }
- borrowCount++;
- pool.release(obj);
- }
- const operations = [];
- for (let i = 0; i < 10; i++) {
- const op = pool.acquire(1).then(lowPriorityOnFulfilled);
- operations.push(op);
- }
- for (let i = 0; i < 10; i++) {
- const op = pool.acquire(0).then(highPriorityOnFulfilled);
- operations.push(op);
- }
- Promise.all(operations)
- .then(function() {
- t.equal(20, borrowCount);
- t.equal(true, borrowTimeLow >= borrowTimeHigh);
- utils.stopPool(pool);
- t.end();
- })
- .catch(t.threw);
- });
- // FIXME: bad test!
- // pool.destroy makes no obligations to user about when it will destroy the resource
- // we should test that "destroyed" objects are not acquired again instead
- // tap.test('removes correct object on reap', function (t) {
- // const resourceFactory = new ResourceFactory()
- // const config
- // max: 2
- // }
- // const pool = createPool(resourceFactory, config)
- // const op1 = pool.acquire()
- // .then(function (client) {
- // return new Promise(function (resolve, reject) {
- // // should be removed second
- // setTimeout(function () {
- // pool.destroy(client)
- // resolve()
- // }, 5)
- // })
- // })
- // const op2 = pool.acquire()
- // .then(function (client) {
- // pool.destroy(client)
- // })
- // Promise.all([op1, op2]).then(function () {
- // t.equal(1, resourceFactory.bin[0].id)
- // t.equal(0, resourceFactory.bin[1].id)
- // utils.stopPool(pool)
- // t.end()
- // })
- // .catch(t.threw)
- // })
- tap.test("evictor removes instances on idletimeout", function(t) {
- const resourceFactory = new ResourceFactory();
- const config = {
- min: 2,
- max: 2,
- idleTimeoutMillis: 50,
- evictionRunIntervalMillis: 10
- };
- const pool = createPool(resourceFactory, config);
- setTimeout(function() {
- pool
- .acquire()
- .then(res => {
- t.ok(res.id > 1);
- return pool.release(res);
- })
- .then(() => {
- utils.stopPool(pool);
- t.end();
- });
- }, 120);
- });
- tap.test("tests drain", function(t) {
- const count = 5;
- let acquired = 0;
- const resourceFactory = new ResourceFactory();
- const config = {
- max: 2,
- idletimeoutMillis: 300000
- };
- const pool = createPool(resourceFactory, config);
- const operations = [];
- function onAcquire(client) {
- acquired += 1;
- t.equal(typeof client.id, "number");
- setTimeout(function() {
- pool.release(client);
- }, 250);
- }
- // request 5 resources that release after 250ms
- for (let i = 0; i < count; i++) {
- const op = pool.acquire().then(onAcquire);
- operations.push(op);
- }
- // FIXME: what does this assertion prove?
- t.notEqual(count, acquired);
- Promise.all(operations)
- .then(function() {
- return pool.drain();
- })
- .then(function() {
- t.equal(count, acquired);
- // short circuit the absurdly long timeouts above.
- pool.clear();
- })
- .then(function() {
- // subsequent calls to acquire should resolve an error.
- return pool.acquire().then(t.fail, function(e) {
- t.type(e, Error);
- });
- })
- .then(function() {
- t.end();
- });
- });
- tap.test("clear promise resolves with no value", function(t) {
- let resources = [];
- const factory = {
- create: function create() {
- return new Promise(function tryCreate(resolve, reject) {
- let resource = resources.shift();
- if (resource) {
- resolve(resource);
- } else {
- process.nextTick(tryCreate.bind(this, resolve, reject));
- }
- });
- },
- destroy: function() {
- return Promise.resolve();
- }
- };
- const pool = createPool(factory, { max: 3, min: 3 });
- Promise.all([pool.acquire(), pool.acquire(), pool.acquire()]).then(all => {
- all.forEach(resource => {
- process.nextTick(pool.release.bind(pool), resource);
- });
- });
- t.equal(pool.pending, 3, "all acquisitions pending");
- pool
- .drain()
- .then(() => pool.clear())
- .then(resolved => {
- t.equal(resolved, undefined, "clear promise resolves with no value");
- t.end();
- });
- process.nextTick(() => {
- resources.push("a");
- resources.push("b");
- resources.push("c");
- });
- });
- tap.test("handle creation errors", function(t) {
- let created = 0;
- const resourceFactory = {
- create: function() {
- created++;
- if (created < 5) {
- return Promise.reject(new Error("Error occurred."));
- } else {
- return Promise.resolve({ id: created });
- }
- },
- destroy: function(client) {}
- };
- const config = {
- max: 1
- };
- const pool = createPool(resourceFactory, config);
- // FIXME: this section no longer proves anything as factory
- // errors no longer bubble up through the acquire call
- // we need to make the Pool an Emitter
- // ensure that creation errors do not populate the pool.
- // for (const i = 0; i < 5; i++) {
- // pool.acquire(function (err, client) {
- // t.ok(err instanceof Error)
- // t.ok(client === null)
- // })
- // }
- let called = false;
- pool
- .acquire()
- .then(function(client) {
- t.equal(typeof client.id, "number");
- called = true;
- pool.release(client);
- })
- .then(function() {
- t.ok(called);
- t.equal(pool.pending, 0);
- utils.stopPool(pool);
- t.end();
- })
- .catch(t.threw);
- });
- tap.test("handle creation errors for delayed creates", function(t) {
- let attempts = 0;
- const resourceFactory = {
- create: function() {
- attempts++;
- if (attempts <= 5) {
- return Promise.reject(new Error("Error occurred."));
- } else {
- return Promise.resolve({ id: attempts });
- }
- },
- destroy: function(client) {
- return Promise.resolve();
- }
- };
- const config = {
- max: 1
- };
- const pool = createPool(resourceFactory, config);
- let errorCount = 0;
- pool.on("factoryCreateError", function(err) {
- t.ok(err instanceof Error);
- errorCount++;
- });
- let called = false;
- pool
- .acquire()
- .then(function(client) {
- t.equal(typeof client.id, "number");
- called = true;
- pool.release(client);
- })
- .then(function() {
- t.ok(called);
- t.equal(errorCount, 5);
- t.equal(pool.pending, 0);
- utils.stopPool(pool);
- t.end();
- })
- .catch(t.threw);
- });
- tap.test("getPoolSize", function(t) {
- let assertionCount = 0;
- const resourceFactory = new ResourceFactory();
- const config = {
- max: 2
- };
- const pool = createPool(resourceFactory, config);
- const borrowedResources = [];
- t.equal(pool.size, 0);
- assertionCount += 1;
- pool
- .acquire()
- .then(function(obj) {
- borrowedResources.push(obj);
- t.equal(pool.size, 1);
- assertionCount += 1;
- })
- .then(function() {
- return pool.acquire();
- })
- .then(function(obj) {
- borrowedResources.push(obj);
- t.equal(pool.size, 2);
- assertionCount += 1;
- })
- .then(function() {
- pool.release(borrowedResources.shift());
- pool.release(borrowedResources.shift());
- })
- .then(function() {
- return pool.acquire();
- })
- .then(function(obj) {
- // should still be 2
- t.equal(pool.size, 2);
- assertionCount += 1;
- pool.release(obj);
- })
- .then(function() {
- t.equal(assertionCount, 4);
- utils.stopPool(pool);
- t.end();
- })
- .catch(t.threw);
- });
- tap.test("availableObjectsCount", function(t) {
- let assertionCount = 0;
- const resourceFactory = new ResourceFactory();
- const config = {
- max: 2
- };
- const pool = createPool(resourceFactory, config);
- const borrowedResources = [];
- t.equal(pool.available, 0);
- assertionCount += 1;
- pool
- .acquire()
- .then(function(obj) {
- borrowedResources.push(obj);
- t.equal(pool.available, 0);
- assertionCount += 1;
- })
- .then(function() {
- return pool.acquire();
- })
- .then(function(obj) {
- borrowedResources.push(obj);
- t.equal(pool.available, 0);
- assertionCount += 1;
- })
- .then(function() {
- pool.release(borrowedResources.shift());
- t.equal(pool.available, 1);
- assertionCount += 1;
- pool.release(borrowedResources.shift());
- t.equal(pool.available, 2);
- assertionCount += 1;
- })
- .then(function() {
- return pool.acquire();
- })
- .then(function(obj) {
- t.equal(pool.available, 1);
- assertionCount += 1;
- pool.release(obj);
- t.equal(pool.available, 2);
- assertionCount += 1;
- })
- .then(function() {
- t.equal(assertionCount, 7);
- utils.stopPool(pool);
- t.end();
- })
- .catch(t.threw);
- });
- // FIXME: bad test!
- // pool.destroy makes no obligations to user about when it will destroy the resource
- // we should test that "destroyed" objects are not acquired again instead
- // tap.test('removes from available objects on destroy', function (t) {
- // let destroyCalled = false
- // const factory = {
- // create: function () { return Promise.resolve({}) },
- // destroy: function (client) { destroyCalled = true; return Promise.resolve() }
- // }
- // const config
- // max: 2
- // }
- // const pool = createPool(factory, config)
- // pool.acquire().then(function (obj) {
- // pool.destroy(obj)
- // })
- // .then(function () {
- // t.equal(destroyCalled, true)
- // t.equal(pool.available, 0)
- // utils.stopPool(pool)
- // t.end()
- // })
- // .catch(t.threw)
- // })
- // FIXME: bad test!
- // pool.destroy makes no obligations to user about when it will destroy the resource
- // we should test that "destroyed" objects are not acquired again instead
- // tap.test('removes from available objects on validation failure', function (t) {
- // const destroyCalled = false
- // const validateCalled = false
- // const count = 0
- // const factory = {
- // create: function () { return Promise.resolve({count: count++}) },
- // destroy: function (client) { destroyCalled = client.count },
- // validate: function (client) {
- // validateCalled = true
- // return Promise.resolve(client.count > 0)
- // }
- // }
- // const config
- // max: 2,
- // testOnBorrow: true
- // }
- // const pool = createPool(factory, config)
- // pool.acquire()
- // .then(function (obj) {
- // pool.release(obj)
- // t.equal(obj.count, 0)
- // })
- // .then(function () {
- // return pool.acquire()
- // })
- // .then(function (obj2) {
- // pool.release(obj2)
- // t.equal(obj2.count, 1)
- // })
- // .then(function () {
- // t.equal(validateCalled, true)
- // t.equal(destroyCalled, 0)
- // t.equal(pool.available, 1)
- // utils.stopPool(pool)
- // t.end()
- // })
- // .catch(t.threw)
- // })
- tap.test(
- "do schedule again if error occured when creating new Objects async",
- function(t) {
- // NOTE: we're simulating the first few resource attempts failing
- let resourceCreationAttempts = 0;
- const factory = {
- create: function() {
- resourceCreationAttempts++;
- if (resourceCreationAttempts < 2) {
- return Promise.reject(new Error("Create Error"));
- }
- return Promise.resolve({});
- },
- destroy: function(client) {}
- };
- const config = {
- max: 1
- };
- const pool = createPool(factory, config);
- // pool.acquire(function () {})
- pool
- .acquire()
- .then(function(obj) {
- t.equal(pool.available, 0);
- pool.release(obj);
- utils.stopPool(pool);
- t.end();
- })
- .catch(t.threw);
- }
- );
- tap.test("returns only valid object to the pool", function(t) {
- const pool = createPool(new ResourceFactory(), { max: 1 });
- pool
- .acquire()
- .then(function(obj) {
- t.equal(pool.available, 0);
- t.equal(pool.borrowed, 1);
- // Invalid release
- pool
- .release({})
- .catch(function(err) {
- t.match(err.message, /Resource not currently part of this pool/);
- })
- .then(function() {
- t.equal(pool.available, 0);
- t.equal(pool.borrowed, 1);
- // Valid release
- pool.release(obj).catch(t.error);
- t.equal(pool.available, 1);
- t.equal(pool.borrowed, 0);
- utils.stopPool(pool);
- t.end();
- });
- })
- .catch(t.threw);
- });
- tap.test("validate acquires object from the pool", function(t) {
- const pool = createPool(new ResourceFactory(), { max: 1 });
- pool
- .acquire()
- .then(function(obj) {
- t.equal(pool.available, 0);
- t.equal(pool.borrowed, 1);
- pool.release(obj);
- utils.stopPool(pool);
- t.end();
- })
- .catch(t.threw);
- });
- tap.test("release to pool should work", function(t) {
- const pool = createPool(new ResourceFactory(), { max: 1 });
- pool
- .acquire()
- .then(function(obj) {
- t.equal(pool.available, 0);
- t.equal(pool.borrowed, 1);
- t.equal(pool.pending, 1);
- pool.release(obj);
- })
- .catch(t.threw);
- pool
- .acquire()
- .then(function(obj) {
- t.equal(pool.available, 0);
- t.equal(pool.borrowed, 1);
- t.equal(pool.pending, 0);
- pool.release(obj);
- utils.stopPool(pool);
- t.end();
- })
- .catch(t.threw);
- });
- tap.test(
- "isBorrowedResource should return true for borrowed resource",
- function(t) {
- const pool = createPool(new ResourceFactory(), { max: 1 });
- pool
- .acquire()
- .then(function(obj) {
- t.equal(pool.isBorrowedResource(obj), true);
- pool.release(obj);
- utils.stopPool(pool);
- t.end();
- })
- .catch(t.threw);
- }
- );
- tap.test(
- "isBorrowedResource should return false for released resource",
- function(t) {
- const pool = createPool(new ResourceFactory(), { max: 1 });
- pool
- .acquire()
- .then(function(obj) {
- pool.release(obj);
- return obj;
- })
- .then(function(obj) {
- t.equal(pool.isBorrowedResource(obj), false);
- utils.stopPool(pool);
- t.end();
- })
- .catch(t.threw);
- }
- );
- tap.test("destroy should redispense", function(t) {
- const pool = createPool(new ResourceFactory(), { max: 1 });
- pool
- .acquire()
- .then(function(obj) {
- t.equal(pool.available, 0);
- t.equal(pool.borrowed, 1);
- t.equal(pool.pending, 1);
- pool.destroy(obj);
- })
- .catch(t.threw);
- pool
- .acquire()
- .then(function(obj) {
- t.equal(pool.available, 0);
- t.equal(pool.borrowed, 1);
- t.equal(pool.pending, 0);
- pool.release(obj);
- utils.stopPool(pool);
- t.end();
- })
- .catch(t.threw);
- });
- tap.test("evictor start with acquire when autostart is false", function(t) {
- const pool = createPool(new ResourceFactory(), {
- evictionRunIntervalMillis: 10000,
- autostart: false
- });
- t.equal(pool._scheduledEviction, null);
- pool
- .acquire()
- .then(function(obj) {
- t.notEqual(pool._scheduledEviction, null);
- pool.release(obj);
- utils.stopPool(pool);
- t.end();
- })
- .catch(t.threw);
- });
- tap.test("use method", function(t) {
- const pool = createPool(new ResourceFactory());
- const result = pool.use(function(resource) {
- t.equal(0, resource.id);
- return Promise.resolve();
- });
- result.then(function() {
- t.end();
- });
- });
- tap.test("use method should resolve after fn promise is resolved", function(t) {
- const pool = createPool(new ResourceFactory());
- let done_with_resource = false;
- const result = pool.use(function(resource) {
- return new Promise(function(resolve, reject) {
- setImmediate(function() {
- done_with_resource = true;
- resolve("value");
- });
- });
- });
- result.then(val => {
- t.equal(done_with_resource, true);
- t.equal(val, "value");
- t.end();
- });
- });
- tap.test("evictor should not run when softIdleTimeoutMillis is -1", function(
- t
- ) {
- const resourceFactory = new ResourceFactory();
- const pool = createPool(resourceFactory, {
- evictionRunIntervalMillis: 10
- });
- pool
- .acquire()
- .then(res => pool.release(res))
- .then(() => {
- return new Promise(res => setTimeout(res, 30));
- })
- .then(() => t.equal(resourceFactory.destroyed, 0))
- .then(() => {
- utils.stopPool(pool);
- t.end();
- });
- });
- tap.test("should respect when maxWaitingClients is set to 0 ", function(t) {
- let assertionCount = 0;
- const resourceFactory = new ResourceFactory();
- const config = {
- max: 2,
- maxWaitingClients: 0
- };
- const pool = createPool(resourceFactory, config);
- const borrowedResources = [];
- t.equal(pool.size, 0);
- assertionCount += 1;
- pool
- .acquire()
- .then(function(obj) {
- borrowedResources.push(obj);
- t.equal(pool.size, 1);
- assertionCount += 1;
- })
- .then(function() {
- return pool.acquire();
- })
- .then(function(obj) {
- borrowedResources.push(obj);
- t.equal(pool.size, 2);
- assertionCount += 1;
- })
- .then(function() {
- return pool.acquire();
- })
- .then(function(obj) {
- // should not go in here
- t.equal(1, 2);
- })
- .catch(error => {
- t.equal(error.message, "max waitingClients count exceeded");
- t.end();
- });
- });
|