diff --git a/package.json b/package.json index 7dd9a58..9b40b29 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "canonical-json": "0.0.4", "chai": "^3.4.0", "coffeeify": "^1.1.0", + "coffee-script": "^1.7.1", "gulp": "git://github.com/gulpjs/gulp.git#4.0", "gulp-coffee": "^2.3.1", "gulp-concat": "^2.6.0", diff --git a/src/DataSource.coffee b/src/DataSource.coffee index 702c566..664826d 100644 --- a/src/DataSource.coffee +++ b/src/DataSource.coffee @@ -3,20 +3,6 @@ async = require 'async' # Fetches data for queries module.exports = class DataSource - # Gets the data for a lookup of queries - # e.g. { a: , b: } - # Calls cb with (null, { a: , b: } - # or (error) if there was an error - performQueries: (queries, cb) -> - async.map _.pairs(queries), (item, callback) => - @performQuery(item[1], (err, rows) => - callback(err, [item[0], rows]) - ) - , (err, items) => - if err - return cb(err) - else - cb(null, _.object(items)) # Performs a single query. Calls cb with rows performQuery: (query, cb) -> diff --git a/src/PriorityDataQueue.coffee b/src/PriorityDataQueue.coffee new file mode 100644 index 0000000..34a67b0 --- /dev/null +++ b/src/PriorityDataQueue.coffee @@ -0,0 +1,30 @@ +async = require 'async' + +PriorityDataSource = require './PriorityDataSource' + +# Creates PriorityDataSource from DataSource +module.exports = class PriorityDataQueue + + constructor: (dataSource, concurrency) -> + @dataSource = dataSource + # Creates a priorityQueue that calls performQuery + worker = (query, callback) -> + dataSource.performQuery(query, callback) + @performQueryPriorityQueue = new async.priorityQueue(worker, concurrency) + + # Creates a PriorityDataSource that will then be used like a DataSource but with a priority + createPriorityDataSource : (priority) -> + return new PriorityDataSource(this, priority) + + # Designed to be called by PriorityDataSource + performQuery: (query, cb, priority) -> + # Push to the priorityQueue + @performQueryPriorityQueue.push query, priority, cb + + # Simply call the dataSource since this is not an async function + getImageUrl: (imageId, height) -> + @dataSource.getImageUrl(imageId, height) + + kill: () -> + if @performQueryPriorityQueue? + @performQueryPriorityQueue.kill() diff --git a/src/PriorityDataSource.coffee b/src/PriorityDataSource.coffee new file mode 100644 index 0000000..1a6f7e2 --- /dev/null +++ b/src/PriorityDataSource.coffee @@ -0,0 +1,15 @@ + +# Behaves like a DataSource +# Created by a PriorityDataQueue +# Forwards performQuery call to the PriorityDataQueue that will forward them to the DataSource +module.exports = class PriorityDataSource + + constructor: (priorityDataQueue, priority) -> + @priorityDataQueue = priorityDataQueue + @priority = priority + + performQuery: (query, cb) -> + @priorityDataQueue.performQuery(query, cb, @priority) + + getImageUrl: (imageId, height) -> + @priorityDataQueue.getImageUrl(imageId, height) \ No newline at end of file diff --git a/src/index.coffee b/src/index.coffee index 6d6b8d1..b14fd21 100644 --- a/src/index.coffee +++ b/src/index.coffee @@ -1,5 +1,6 @@ exports.Schema = require './Schema' exports.DataSource = require './DataSource' +exports.PriorityDataQueue = require './PriorityDataQueue' exports.ExprCleaner = require './ExprCleaner' exports.ExprUtils = require './ExprUtils' diff --git a/test/PriorityDataQueueTests.coffee b/test/PriorityDataQueueTests.coffee new file mode 100644 index 0000000..5048a7f --- /dev/null +++ b/test/PriorityDataQueueTests.coffee @@ -0,0 +1,55 @@ +assert = require('chai').assert + +PriorityDataQueue = require '../src/PriorityDataQueue' +DataSource = require '../src/DataSource' + +# Very simple DataSource implementation used for testing +class TestDataSource extends DataSource + performQuery: (query, cb) -> + # Simply does an async callback passing back the query + call = () -> + cb(query) + setTimeout(call, 1) + +describe "PriorityDataQueue", -> + beforeEach -> + testDataSource = new TestDataSource() + @priorityDataQueue = new PriorityDataQueue(testDataSource, 1) + + it "calling performQuery reaches the DataSource", (testCallback) -> + priorityDataSource = @priorityDataQueue.createPriorityDataSource(1) + priorityDataSource.performQuery('test', (data) -> + # Make sure that TestDataSource called back with the right data + assert.equal('test', data) + testCallback() + ) + + it "priorityDataQueue properly prioritize the calls", (testCallback) -> + counter = 0 + callDone = (priority) -> + counter++ + # Make sure that the priority is called in the right order + assert.equal priority, counter + if counter == 4 + testCallback() + + priorityDataSource1 = @priorityDataQueue.createPriorityDataSource(1) + priorityDataSource2 = @priorityDataQueue.createPriorityDataSource(2) + priorityDataSource3 = @priorityDataQueue.createPriorityDataSource(3) + priorityDataSource4 = @priorityDataQueue.createPriorityDataSource(4) + # First one needs to be done first since the query is empty and the first call will have best priority + priorityDataSource1.performQuery(1, (data) -> + callDone(1) + ) + # Then call with 3 + priorityDataSource3.performQuery(3, (data) -> + callDone(3) + ) + # Then call with 4 + priorityDataSource4.performQuery(4, (data) -> + callDone(4) + ) + # Then with 2 + priorityDataSource2.performQuery(2, (data) -> + callDone(2) + )