├── test ├── data │ └── .keep ├── memory-basic-test.js ├── memory-low-order-test.js ├── memory-high-order-test.js ├── fs-high-order-test.js ├── fs-basic-test.js ├── fs-partition-test.js ├── fs-largedata-benchmark.js ├── fs-basic-benchmark.js └── helpers.coffee ├── .npmignore ├── bench-data ├── read.png ├── compact.png ├── write.png ├── compact.bench ├── write.bench └── read.bench ├── .gitignore ├── package.json ├── Cakefile ├── lib ├── index │ ├── utils.coffee │ ├── memory-storage.coffee │ ├── core │ │ ├── compact.coffee │ │ ├── unset.coffee │ │ ├── get.coffee │ │ ├── set.coffee │ │ └── bulk.coffee │ └── file-storage.coffee └── index.coffee └── README.md /test/data/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | bench-data/* 2 | test/* 3 | -------------------------------------------------------------------------------- /bench-data/read.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/indutny/node-index/HEAD/bench-data/read.png -------------------------------------------------------------------------------- /bench-data/compact.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/indutny/node-index/HEAD/bench-data/compact.png -------------------------------------------------------------------------------- /bench-data/write.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/indutny/node-index/HEAD/bench-data/write.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | test/data/* 3 | !test/data/.keep 4 | node_modules 5 | node_modules/* 6 | build/* 7 | npm-debug.log 8 | -------------------------------------------------------------------------------- /test/memory-basic-test.js: -------------------------------------------------------------------------------- 1 | var vows = require('vows'), 2 | assert = require('assert'), 3 | coffee = require('coffee-script'), 4 | helpers = require('./helpers'); 5 | 6 | var index = require('../lib/index'); 7 | 8 | var options = {}; 9 | 10 | var suite = vows.describe('Node index/memory basic test'); 11 | 12 | helpers.memoryTest(suite, {}, options).export(module); 13 | -------------------------------------------------------------------------------- /test/memory-low-order-test.js: -------------------------------------------------------------------------------- 1 | var vows = require('vows'), 2 | assert = require('assert'), 3 | coffee = require('coffee-script'), 4 | helpers = require('./helpers'); 5 | 6 | var index = require('../lib/index'); 7 | 8 | var options = {}; 9 | 10 | var suite = vows.describe('Node index/memory basic test'); 11 | 12 | helpers.memoryTest(suite, {order: 4}, options).export(module); 13 | -------------------------------------------------------------------------------- /test/memory-high-order-test.js: -------------------------------------------------------------------------------- 1 | var vows = require('vows'), 2 | assert = require('assert'), 3 | coffee = require('coffee-script'), 4 | helpers = require('./helpers'); 5 | 6 | var index = require('../lib/index'); 7 | 8 | var options = {}; 9 | 10 | var suite = vows.describe('Node index/memory basic test'); 11 | 12 | helpers.memoryTest(suite, {order: 256}, options).export(module); 13 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "index", 3 | "description": "Append only B+ Tree Index engine for node.js", 4 | "version": "0.4.0", 5 | 6 | "author": "Fedor Indutny ", 7 | 8 | "dependencies": { 9 | "step": ">= 0.0.4" 10 | }, 11 | "devDependencies": { 12 | "coffee-script": ">= 1.0.1", 13 | "vows": ">= 0.5.8" 14 | }, 15 | 16 | "engine": ["node >= 0.6.0"], 17 | 18 | "main": "./build/index", 19 | 20 | "scripts": { 21 | "test": "vows --spec test/*-test.js" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /test/fs-high-order-test.js: -------------------------------------------------------------------------------- 1 | var vows = require('vows'), 2 | assert = require('assert'), 3 | step = require('step'), 4 | coffee = require('coffee-script'), 5 | fs = require('fs'); 6 | 7 | var helpers = require('./helpers'), 8 | index = require('../lib/index'), 9 | FileStorage = require('../lib/index/file-storage'); 10 | 11 | var options = {}; 12 | 13 | var suite = vows.describe('Node index/fs high order test'); 14 | 15 | helpers.fileTest(suite, {order: 256}, { 16 | filename: __dirname + '/data/fht.db' 17 | }, options).export(module); 18 | 19 | -------------------------------------------------------------------------------- /test/fs-basic-test.js: -------------------------------------------------------------------------------- 1 | var vows = require('vows'), 2 | assert = require('assert'), 3 | step = require('step'), 4 | coffee = require('coffee-script'), 5 | fs = require('fs'); 6 | 7 | var helpers = require('./helpers'), 8 | index = require('../lib/index'), 9 | FileStorage = require('../lib/index/file-storage'); 10 | 11 | var options = {}; 12 | 13 | var suite = vows.describe('Node index/fs basic test'); 14 | 15 | helpers.fileTest(suite, {}, { 16 | filename: __dirname + '/data/fbt.db' 17 | }, options); 18 | 19 | options.reopen = true; 20 | 21 | helpers.fileTest(suite, {}, { 22 | filename: __dirname +'/data/fbt.db', 23 | }, options).export(module); 24 | 25 | -------------------------------------------------------------------------------- /test/fs-partition-test.js: -------------------------------------------------------------------------------- 1 | var vows = require('vows'), 2 | assert = require('assert'), 3 | step = require('step'), 4 | coffee = require('coffee-script'), 5 | fs = require('fs'); 6 | 7 | var helpers = require('./helpers'), 8 | index = require('../lib/index'), 9 | FileStorage = require('../lib/index/file-storage'); 10 | 11 | var options = {}; 12 | 13 | var suite = vows.describe('Node index/fs partition test'); 14 | 15 | suite = helpers.fileTest(suite, {}, { 16 | filename: __dirname + '/data/fpt.db', 17 | partitionSize: 2 * 1024 * 1024 18 | }, options); 19 | 20 | options.reopen = true; 21 | 22 | helpers.fileTest(suite, {}, { 23 | filename: __dirname +'/data/fpt.db', 24 | partitionSize: 2 * 1024 * 1024 25 | }, options).export(module); 26 | 27 | -------------------------------------------------------------------------------- /Cakefile: -------------------------------------------------------------------------------- 1 | step = require 'step' 2 | {spawn, exec} = require 'child_process' 3 | 4 | run = (args, callback) -> 5 | proc = spawn 'coffee', args 6 | proc.stderr.on 'data', (buffer) -> console.log buffer.toString() 7 | proc.on 'exit', (status) -> 8 | unless status is 0 9 | return process.exit 1 10 | callback null 11 | 12 | build = (dir, files, callback) -> 13 | run ['-c', '-o', dir].concat(files), callback 14 | 15 | step (() -> 16 | console.log 'Starting building node-index' 17 | 18 | build 'build', ['lib/index.coffee'], @parallel() 19 | 20 | build 'build/index', [ 21 | 'lib/index/utils.coffee' 22 | 'lib/index/memory-storage.coffee' 23 | 'lib/index/file-storage.coffee' 24 | ], @parallel() 25 | 26 | build 'build/index/core', [ 27 | 'lib/index/core/get.coffee' 28 | 'lib/index/core/set.coffee' 29 | 'lib/index/core/unset.coffee' 30 | 'lib/index/core/compact.coffee' 31 | 'lib/index/core/bulk.coffee' 32 | ], @parallel() 33 | 34 | return 35 | ), (() -> 36 | console.log 'Done.' 37 | ) 38 | -------------------------------------------------------------------------------- /lib/index/utils.coffee: -------------------------------------------------------------------------------- 1 | ### 2 | Various utilities for Node index library 3 | 4 | This software is licensed under the MIT License. 5 | 6 | Copyright Fedor Indutny, 2011. 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a 9 | copy of this software and associated documentation files (the 10 | "Software"), to deal in the Software without restriction, including 11 | without limitation the rights to use, copy, modify, merge, publish, 12 | distribute, sublicense, and/or sell copies of the Software, and to permit 13 | persons to whom the Software is furnished to do so, subject to the 14 | following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included 17 | in all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 20 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 22 | NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 23 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 24 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 25 | USE OR OTHER DEALINGS IN THE SOFTWARE. 26 | ### 27 | 28 | crypto = require 'crypto' 29 | 30 | utils = exports 31 | 32 | ### 33 | Merge two objects 34 | ### 35 | 36 | merge = utils.merge = (a, b) -> 37 | unless a and b 38 | return a or b or {} 39 | 40 | c = {} 41 | for k,v of a 42 | unless a.hasOwnProperty k 43 | continue 44 | c[k] = v 45 | 46 | for k, v of b 47 | unless b.hasOwnProperty k 48 | continue 49 | 50 | c[k] = if typeof c[k] is 'object' 51 | merge c[k], v 52 | else 53 | v 54 | c 55 | 56 | ### 57 | Perform a binary search in following array 58 | [[key, value], [key, value], ...] 59 | 60 | @return value or undefined. 61 | ### 62 | utils.search = (index, sort, key) -> 63 | len = index.length - 1 64 | i = len 65 | 66 | while i >= 0 and sort(index[i][0], key) > 0 67 | i-- 68 | 69 | if i == len and len >= 0 and sort(index[i][0], key) == 0 70 | null 71 | if i < 0 72 | null 73 | else 74 | i 75 | 76 | ### 77 | Hash function wrapper 78 | ### 79 | utils.hash = (data) -> 80 | hash = crypto.createHash 'md5' 81 | hash.update data 82 | hash.digest 'hex' 83 | 84 | utils.hash.len = 32 85 | -------------------------------------------------------------------------------- /test/fs-largedata-benchmark.js: -------------------------------------------------------------------------------- 1 | var vows = require('vows'), 2 | assert = require('assert'), 3 | step = require('step'), 4 | coffee = require('coffee-script'), 5 | fs = require('fs'); 6 | 7 | var index = require('../lib/index'), 8 | FileStorage = require('../lib/index/file-storage'); 9 | 10 | var I, 11 | fileStorage, 12 | filename = __dirname + '/data/fldb.db', 13 | num = 100000, 14 | large_string = new Array(100).join('Eat that french bread carefully!'), 15 | start, 16 | end; 17 | 18 | vows.describe('Node index/fs basic benchmark').addBatch({ 19 | 'Creating new file storage': { 20 | topic: function() { 21 | try { 22 | fs.unlinkSync(filename); 23 | for (var i = 1; i < 100; i++) { 24 | fs.unlinkSync(filename + '.' + i); 25 | } 26 | } catch (e) { 27 | } 28 | 29 | FileStorage.createStorage({ 30 | filename: filename 31 | }, this.callback) 32 | }, 33 | 'should be successfull': function(_fileStorage) { 34 | fileStorage = _fileStorage; 35 | } 36 | } 37 | }).addBatch({ 38 | 'Creating new index': { 39 | topic: function() { 40 | return index.createIndex({ 41 | storage: fileStorage 42 | }); 43 | }, 44 | 'should create instance of Index': function(_I) { 45 | I = _I; 46 | assert.instanceOf(I, index.Index); 47 | } 48 | } 49 | }).addBatch({ 50 | 'Adding 100k items': { 51 | topic: function() { 52 | step(function() { 53 | var group = this.group(); 54 | 55 | start = +new Date; 56 | for (var i = 0; i < num; i++) { 57 | I.set(i, { 58 | value: i, 59 | extended: large_string 60 | }, group()); 61 | } 62 | }, this.callback); 63 | }, 64 | 'should be successfull': function() { 65 | end = +new Date; 66 | console.log('%d writes per second', 1000 * num / (end - start)); 67 | } 68 | } 69 | }).addBatch({ 70 | 'Getting 100k items': { 71 | topic: function() { 72 | step(function() { 73 | var group = this.group(); 74 | 75 | start = +new Date; 76 | for (var i = 0; i < num; i++) { 77 | (function(fn) { 78 | I.get(i, function(err) { 79 | fn(err); 80 | }); 81 | })(group()); 82 | } 83 | }, this.callback); 84 | }, 85 | 'should return correct values': function(values) { 86 | end = +new Date; 87 | console.log('%d reads per second', 1000 * num / (end - start)); 88 | } 89 | } 90 | }).export(module); 91 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Node index 2 | ========== 3 | 4 | This module is a implementation of a append-only B+ Tree fully written in 5 | [coffee-script](https://github.com/jashkenas/coffee-script). 6 | 7 | Benchmark 8 | --------- 9 | 10 | ![read benchmark](https://github.com/indutny/node-index/raw/master/bench-data/read.png) 11 | ![write benchmark](https://github.com/indutny/node-index/raw/master/bench-data/write.png) 12 | 13 | Basics 14 | ------ 15 | 16 | // Create basic B+ Tree index that will be stored 17 | // in memory (all settings are default, see below) 18 | var index = require('index').createIndex(); 19 | 20 | // Store value in storage (callback is optional) 21 | index.set('key', 'value', function(err) { 22 | // ... your code here ... 23 | }); 24 | 25 | // Get value from storage 26 | index.get('key', function(err, value) { 27 | // ... your code here ... 28 | }); 29 | 30 | // Remove value from storage 31 | index.unset('key', function(err) { 32 | }); 33 | 34 | // Bulk op 35 | index.bulk([ 36 | ['key', 'value', 1], // insert kv 37 | ['key'] // remove kv 38 | ], function(err, conflicts) { 39 | }); 40 | 41 | // Compaction 42 | index.compact(function(err) { 43 | }); 44 | 45 | Options 46 | ------- 47 | 48 | require('index').createIndex({ 49 | order: 32, // Maximum number of items in page 50 | // Tree's height depends on that 51 | storage: require('index').storage.memory // Place where all tree data will be stored 52 | .createStorage(), // (see more description below) 53 | sort: function(a, b) { 54 | return (a === null || a < b) ? -1 : a == b ? 0 : -1; 55 | } // Function that will be used to compare keys 56 | // Note that null is a system value and sort should always return negative 57 | // result if first argument is null 58 | }); 59 | 60 | License 61 | ------- 62 | 63 | This software is licensed under the MIT License. 64 | 65 | Copyright Fedor Indutny, 2011. 66 | 67 | Permission is hereby granted, free of charge, to any person obtaining a 68 | copy of this software and associated documentation files (the 69 | "Software"), to deal in the Software without restriction, including 70 | without limitation the rights to use, copy, modify, merge, publish, 71 | distribute, sublicense, and/or sell copies of the Software, and to permit 72 | persons to whom the Software is furnished to do so, subject to the 73 | following conditions: 74 | 75 | The above copyright notice and this permission notice shall be included 76 | in all copies or substantial portions of the Software. 77 | 78 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 79 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 80 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 81 | NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 82 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 83 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 84 | USE OR OTHER DEALINGS IN THE SOFTWARE. 85 | 86 | -------------------------------------------------------------------------------- /lib/index/memory-storage.coffee: -------------------------------------------------------------------------------- 1 | ### 2 | Memory storage for Node Index Module 3 | 4 | This software is licensed under the MIT License. 5 | 6 | Copyright Fedor Indutny, 2011. 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a 9 | copy of this software and associated documentation files (the 10 | "Software"), to deal in the Software without restriction, including 11 | without limitation the rights to use, copy, modify, merge, publish, 12 | distribute, sublicense, and/or sell copies of the Software, and to permit 13 | persons to whom the Software is furnished to do so, subject to the 14 | following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included 17 | in all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 20 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 22 | NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 23 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 24 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 25 | USE OR OTHER DEALINGS IN THE SOFTWARE. 26 | ### 27 | 28 | util = require 'util' 29 | step = require 'step' 30 | 31 | ### 32 | Class @constructor 33 | ### 34 | Storage = exports.Storage = (options) -> 35 | @data = [ 36 | [] 37 | ] 38 | @root_pos = new Position 0 39 | @ 40 | 41 | ### 42 | @constructor wrapper 43 | ### 44 | exports.createStorage = (options) -> 45 | return new Storage options 46 | 47 | Storage::isPosition = isPosition = (pos) -> 48 | return pos instanceof Position 49 | 50 | Storage::read = (pos, callback) -> 51 | unless isPosition pos 52 | return callback 'pos should be a valid position' 53 | 54 | process.nextTick => 55 | callback null, @data[pos.index] 56 | 57 | Storage::write = (data, callback) -> 58 | process.nextTick => 59 | callback null, new Position(@data.push(data) - 1) 60 | 61 | Storage::readRoot = (callback) -> 62 | process.nextTick => 63 | callback null, @data[@root_pos.index] 64 | 65 | Storage::writeRoot = (root_pos, callback) -> 66 | unless isPosition root_pos 67 | return callback 'pos should be a valid position' 68 | 69 | process.nextTick => 70 | @root_pos = root_pos 71 | callback null 72 | 73 | Storage::inspect = -> 74 | @data.forEach (line, i) -> 75 | util.puts i + ': ' + JSON.stringify line 76 | 77 | util.puts 'Root : ' + JSON.stringify @root_pos 78 | 79 | Position = exports.Position = (@index) -> 80 | @ 81 | 82 | ### 83 | Storage state 84 | ### 85 | Storage::getState = -> 86 | {} 87 | 88 | Storage::setState = (state) -> 89 | true 90 | 91 | ### 92 | Compaction flow 93 | ### 94 | 95 | Storage::beforeCompact = (callback) -> 96 | @_compactEdge = @data.push '--------' 97 | callback null 98 | 99 | Storage::afterCompact = (callback) -> 100 | @data[i] = 0 for i in [0...@_compactEdge] 101 | callback null 102 | 103 | -------------------------------------------------------------------------------- /lib/index.coffee: -------------------------------------------------------------------------------- 1 | ### 2 | Node index - Main Library file 3 | 4 | This software is licensed under the MIT License. 5 | 6 | Copyright Fedor Indutny, 2011. 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a 9 | copy of this software and associated documentation files (the 10 | "Software"), to deal in the Software without restriction, including 11 | without limitation the rights to use, copy, modify, merge, publish, 12 | distribute, sublicense, and/or sell copies of the Software, and to permit 13 | persons to whom the Software is furnished to do so, subject to the 14 | following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included 17 | in all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 20 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 22 | NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 23 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 24 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 25 | USE OR OTHER DEALINGS IN THE SOFTWARE. 26 | ### 27 | 28 | step = require 'step' 29 | utils = require './index/utils' 30 | 31 | DEFAULT_OPTIONS = 32 | sort: (a, b) -> 33 | if (a is null) or (a < b) then -1 else 34 | if a is b then 0 else 1 35 | order: 64 36 | 37 | 38 | ### 39 | Class @constructor 40 | ### 41 | Index = exports.Index = (options) -> 42 | options = utils.merge DEFAULT_OPTIONS, options 43 | 44 | @storage = options.storage || 45 | require('./index/memory-storage').createStorage() 46 | {@order, @sort, @conflictManager} = options 47 | @order-- 48 | 49 | @lockQueue = [] 50 | 51 | @ 52 | 53 | ### 54 | Wrapper for class @constructor 55 | ### 56 | exports.createIndex = (options) -> 57 | new Index options 58 | 59 | ### 60 | Get functionality 61 | ### 62 | Index::get = require('./index/core/get').get 63 | Index::traverse = require('./index/core/get').traverse 64 | Index::rangeGet = require('./index/core/get').rangeGet 65 | 66 | ### 67 | Set functionality 68 | ### 69 | Index::set = require('./index/core/set').set 70 | 71 | ### 72 | Unset functionality 73 | ### 74 | Index::unset = require('./index/core/unset').unset 75 | 76 | ### 77 | Compaction functionality 78 | ### 79 | Index::compact = require('./index/core/compact').compact 80 | 81 | ### 82 | Bulk functionality 83 | ### 84 | Index::bulk = require('./index/core/bulk').bulk 85 | 86 | ### 87 | Lock functionality 88 | ### 89 | Index::lock = (fn) -> 90 | if @locked 91 | @lockQueue.push fn 92 | return true 93 | 94 | @locked = true 95 | false 96 | 97 | ### 98 | Release lock functionality 99 | ### 100 | Index::releaseLock = -> 101 | unless @locked 102 | return 103 | 104 | @locked = false 105 | 106 | fn = @lockQueue.shift() 107 | 108 | if not fn and @lockQueue.length <= 0 109 | return 110 | 111 | process.nextTick fn 112 | 113 | ### 114 | Export storages 115 | ### 116 | exports.storage = 117 | memory: require('./index/memory-storage'), 118 | file: require('./index/file-storage') 119 | -------------------------------------------------------------------------------- /lib/index/core/compact.coffee: -------------------------------------------------------------------------------- 1 | ### 2 | Compaction for Node Index 3 | 4 | This software is licensed under the MIT License. 5 | 6 | Copyright Fedor Indutny, 2011. 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a 9 | copy of this software and associated documentation files (the 10 | "Software"), to deal in the Software without restriction, including 11 | without limitation the rights to use, copy, modify, merge, publish, 12 | distribute, sublicense, and/or sell copies of the Software, and to permit 13 | persons to whom the Software is furnished to do so, subject to the 14 | following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included 17 | in all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 20 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 22 | NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 23 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 24 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 25 | USE OR OTHER DEALINGS IN THE SOFTWARE. 26 | ### 27 | 28 | step = require 'step' 29 | 30 | utils = require '../../index/utils' 31 | 32 | exports.compact = (_callback) -> 33 | storage = @storage 34 | 35 | callback = (err, data) => 36 | @releaseLock() 37 | 38 | process.nextTick -> 39 | _callback and _callback err, data 40 | 41 | if @lock(=> @compact _callback) 42 | return 43 | 44 | iterate = (callback) -> 45 | (err, page) -> 46 | if err 47 | return callback err 48 | 49 | in_leaf = page[0] && page[0][2] 50 | fns = page.map (item) -> 51 | -> 52 | step -> 53 | storage.read item[1], @parallel(), in_leaf 54 | return 55 | , (err, data) -> 56 | if err 57 | throw err 58 | 59 | if in_leaf 60 | # data is actual value 61 | storage.write data, @parallel(), in_leaf 62 | return 63 | 64 | iterate(@parallel()) null, data 65 | return 66 | , (err, new_pos) -> 67 | if err 68 | throw err 69 | 70 | item[1] = new_pos 71 | @parallel() null 72 | return 73 | , @parallel() 74 | 75 | fns.push (err) -> 76 | if err 77 | throw err 78 | 79 | storage.write page, @parallel() 80 | return 81 | 82 | fns.push callback 83 | 84 | step.apply null, fns 85 | 86 | 87 | step -> 88 | # will allow storage controller 89 | # to prepare it for 90 | if storage.beforeCompact 91 | storage.beforeCompact @parallel() 92 | else 93 | @parallel() null 94 | return 95 | , (err) -> 96 | if err 97 | throw err 98 | 99 | storage.readRoot iterate @parallel() 100 | return 101 | , (err, new_root_pos) -> 102 | if err 103 | throw err 104 | 105 | storage.writeRoot new_root_pos, @parallel() 106 | return 107 | , (err) -> 108 | if err 109 | throw err 110 | 111 | # @will allow storage to finalize all actions 112 | if storage.afterCompact 113 | storage.afterCompact @parallel() 114 | else 115 | @parallel() null 116 | return 117 | , callback 118 | 119 | -------------------------------------------------------------------------------- /lib/index/core/unset.coffee: -------------------------------------------------------------------------------- 1 | ### 2 | Unset functionality for Node index module 3 | 4 | This software is licensed under the MIT License. 5 | 6 | Copyright Fedor Indutny, 2011. 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a 9 | copy of this software and associated documentation files (the 10 | "Software"), to deal in the Software without restriction, including 11 | without limitation the rights to use, copy, modify, merge, publish, 12 | distribute, sublicense, and/or sell copies of the Software, and to permit 13 | persons to whom the Software is furnished to do so, subject to the 14 | following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included 17 | in all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 20 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 22 | NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 23 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 24 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 25 | USE OR OTHER DEALINGS IN THE SOFTWARE. 26 | ### 27 | 28 | step = require 'step' 29 | 30 | utils = require '../../index/utils' 31 | 32 | exports.unset = (key, _callback) -> 33 | callback = (err, data) => 34 | @releaseLock() 35 | 36 | process.nextTick -> 37 | _callback and _callback err, data 38 | 39 | storage = @storage 40 | sort = @sort 41 | 42 | if @lock(=> @unset key, _callback) 43 | return 44 | 45 | iterate = (page, callback) -> 46 | item_index = utils.search page, sort, key 47 | item = page[item_index] 48 | 49 | if item_index is null 50 | # Not found 51 | # Even in that case unset should be successfull 52 | callback null 53 | return 54 | 55 | if item[2] 56 | # Leaf 57 | 58 | if sort(item[0], key) isnt 0 59 | # Actually key doesn't match 60 | # So do nothing 61 | callback null 62 | return 63 | 64 | # Delete from leaf and if one will be empty 65 | # remove leaf from parent 66 | page.splice item_index, 1 67 | if page.length > 0 68 | # If resulting page isn't empty 69 | step -> 70 | storage.write page, @parallel() 71 | , callback 72 | return 73 | 74 | # Notify that item should be removed from parent index 75 | callback null, false 76 | else 77 | # Index page 78 | step -> 79 | storage.read item[1], @parallel() 80 | , (err, page) -> 81 | if err 82 | throw err 83 | 84 | iterate page, @parallel() 85 | , (err, result) -> 86 | if err 87 | callback err 88 | return 89 | 90 | if result is false 91 | # Delete item from index page 92 | page.splice item_index, 1 93 | if page.length <= 1 94 | callback null, page[0][1] 95 | return 96 | else if storage.isPosition result 97 | page[item_index][1] = result 98 | else 99 | callback null 100 | return 101 | 102 | step -> 103 | storage.write page, @parallel() 104 | , callback 105 | 106 | 107 | step -> 108 | storage.readRoot @parallel() 109 | , (err, root) -> 110 | if err 111 | throw err 112 | 113 | iterate root, @parallel() 114 | , (err, result) -> 115 | if err 116 | throw err 117 | 118 | if result is false 119 | # Create new root 120 | storage.write [], @parallel() 121 | else if storage.isPosition(result) 122 | # Overwrite old root 123 | @parallel() null, result 124 | else 125 | @parallel() null 126 | , (err, position) -> 127 | if err 128 | throw err 129 | 130 | if storage.isPosition position 131 | storage.writeRoot position, @parallel() 132 | else 133 | @parallel() null 134 | , callback 135 | 136 | -------------------------------------------------------------------------------- /test/fs-basic-benchmark.js: -------------------------------------------------------------------------------- 1 | var vows = require('vows'), 2 | assert = require('assert'), 3 | step = require('step'), 4 | coffee = require('coffee-script'), 5 | fs = require('fs'), 6 | uuid = require('node-uuid'); 7 | 8 | var index = require('../lib/index'), 9 | FileStorage = require('../lib/index/file-storage'); 10 | 11 | var I, 12 | fileStorage, 13 | filename = __dirname + '/data/fbb.db', 14 | N = 15000000, 15 | dn = 1500, 16 | start, 17 | end; 18 | 19 | vows.describe('Node index/fs basic benchmark').addBatch({ 20 | 'Creating new file storage': { 21 | topic: function() { 22 | try { 23 | fs.unlinkSync(filename); 24 | } catch (e) { 25 | } 26 | 27 | FileStorage.createStorage({ 28 | filename: filename 29 | }, this.callback) 30 | }, 31 | 'should be successfull': function(_fileStorage) { 32 | fileStorage = _fileStorage; 33 | } 34 | } 35 | }).addBatch({ 36 | 'Creating new index': { 37 | topic: function() { 38 | return index.createIndex({ 39 | storage: fileStorage 40 | }); 41 | }, 42 | 'should create instance of Index': function(_I) { 43 | I = _I; 44 | assert.instanceOf(I, index.Index); 45 | } 46 | } 47 | }).addBatch({ 48 | 'Benchmark': { 49 | topic: function() { 50 | benchmark(this.callback); 51 | }, 52 | 'should be successfull': function(data) { 53 | } 54 | } 55 | }).export(module); 56 | 57 | function benchmark(callback) { 58 | var offset = 0, 59 | needsCompact = 0, 60 | results = { 61 | write: [], 62 | read: [] 63 | }, 64 | keys = [], 65 | all_keys = [], 66 | times = 0, 67 | readfd = fs.openSync(__dirname + '/data/read.bench', 'w'), 68 | writefd = fs.openSync(__dirname + '/data/write.bench', 'w'), 69 | compactfd = fs.openSync(__dirname + '/data/compact.bench', 'w'); 70 | 71 | function iterateWrite(callback) { 72 | var waiting = dn, 73 | i = -1; 74 | 75 | function next() { 76 | i++; 77 | if (i >= dn) return callback(); 78 | I.set(keys[i], { 79 | _rev: keys[i] 80 | }, next); 81 | } 82 | 83 | next(); 84 | }; 85 | 86 | function iterateRead(callback) { 87 | var waiting = dn; 88 | for (var i = 0; i < dn; i++) { 89 | I.get(keys[i], function() { 90 | if (--waiting == 0) callback(); 91 | }); 92 | } 93 | }; 94 | 95 | function iterate(callback) { 96 | var writeTotal, 97 | readTotal, 98 | compactTotal; 99 | 100 | keys = []; 101 | for (var i = 0; i < dn; i++) { 102 | var ind = uuid(); 103 | keys.push(ind); 104 | all_keys.push(ind); 105 | } 106 | 107 | var start = +new Date; 108 | iterateWrite(function() { 109 | writeTotal = +new Date - start; 110 | 111 | times++; 112 | fs.write(writefd, (offset + dn) + ',' + (1e3 * dn / writeTotal) + 113 | '\r\n'); 114 | 115 | keys = []; 116 | 117 | for (var i = 0; i < dn; i++) { 118 | keys.push(all_keys[i * times]); 119 | } 120 | 121 | start = +new Date; 122 | iterateRead(function() { 123 | readTotal = +new Date - start; 124 | 125 | fs.write(readfd, (offset + dn) + ',' + (1e3 * dn / readTotal) + 126 | '\r\n'); 127 | 128 | 129 | function next() { 130 | compactTotal = +new Date - start; 131 | 132 | fs.write(compactfd, (offset + dn) + ',' + compactTotal + '\r\n'); 133 | 134 | callback(); 135 | }; 136 | 137 | 138 | start = +new Date; 139 | if (++needsCompact > 20) { 140 | needsCompact = 0; 141 | I.compact(next); 142 | } else { 143 | next(); 144 | } 145 | }); 146 | }); 147 | }; 148 | 149 | function next() { 150 | offset += dn; 151 | 152 | console.log('Done: %d', offset); 153 | 154 | if (offset < N) { 155 | iterate(next); 156 | } else { 157 | fs.close(readfd); 158 | fs.close(writefd); 159 | callback(null, results); 160 | } 161 | }; 162 | 163 | iterate(next); 164 | 165 | }; 166 | 167 | -------------------------------------------------------------------------------- /lib/index/core/get.coffee: -------------------------------------------------------------------------------- 1 | ### 2 | Get functionality for Node Index module 3 | 4 | This software is licensed under the MIT License. 5 | 6 | Copyright Fedor Indutny, 2011. 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a 9 | copy of this software and associated documentation files (the 10 | "Software"), to deal in the Software without restriction, including 11 | without limitation the rights to use, copy, modify, merge, publish, 12 | distribute, sublicense, and/or sell copies of the Software, and to permit 13 | persons to whom the Software is furnished to do so, subject to the 14 | following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included 17 | in all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 20 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 22 | NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 23 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 24 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 25 | USE OR OTHER DEALINGS IN THE SOFTWARE. 26 | ### 27 | 28 | utils = require '../../index/utils' 29 | step = require 'step' 30 | 31 | ### 32 | Get value by key 33 | ### 34 | exports.get = (key, callback) -> 35 | sort = @sort 36 | storage = @storage 37 | 38 | iterate = (err, index) -> 39 | if err 40 | return callback err 41 | 42 | item_index = utils.search index, sort, key 43 | item = index[item_index] 44 | 45 | # Item not found 46 | unless item 47 | return callback 'Not found' 48 | 49 | value = item[1] 50 | 51 | if item[2] 52 | # Key Value - return value 53 | if sort(item[0], key) isnt 0 54 | return callback 'Not found' 55 | 56 | # Read actual value 57 | storage.read value, (err, value) -> 58 | if err 59 | return callback err 60 | 61 | # value = [value, link-to-previous-value] 62 | callback null, value[0] 63 | else 64 | # Key Pointer - go further 65 | storage.read value, iterate 66 | 67 | storage.readRoot iterate 68 | 69 | ### 70 | Traverse btree 71 | filter can call callback w/ following result: 72 | true - if `traverse` should go deeper 73 | undefined - if `traverse` should skip element 74 | false - if `traverse` should stop traversing and return to higher level 75 | 76 | filter can 77 | @return promise. 78 | ### 79 | 80 | exports.traverse = (filter) -> 81 | that = @ 82 | promise = new process.EventEmitter 83 | 84 | # If no filter were provided - match all 85 | filter = filter || (kp, callback) -> callback(null, true) 86 | 87 | process.nextTick => 88 | sort = @sort 89 | storage = @storage 90 | 91 | iterate = (callback) -> 92 | (err, page) -> 93 | if err 94 | promise.emit 'error', err 95 | promise.emit 'end' 96 | return 97 | 98 | index = -1 99 | pagelen = page.length 100 | 101 | asyncFilter = -> 102 | if ++index >= pagelen 103 | if callback 104 | callback null 105 | else 106 | promise.emit 'end' 107 | 108 | return 109 | 110 | current = page[index] 111 | 112 | filter.call that, current, (err, filter_value) -> 113 | if filter_value is true 114 | if current[2] 115 | # emit value 116 | storage.read current[1], (err, value) -> 117 | # value = [value, link-to-previous-value] 118 | promise.emit 'data', value[0], current 119 | asyncFilter() 120 | 121 | else 122 | # go deeper 123 | storage.read current[1], iterate asyncFilter 124 | 125 | else 126 | if filter_value is false 127 | index = pagelen 128 | 129 | asyncFilter() 130 | 131 | asyncFilter() 132 | 133 | storage.readRoot iterate() 134 | 135 | promise 136 | 137 | ### 138 | Get in range 139 | ### 140 | exports.rangeGet = (start_key, end_key) -> 141 | sort = @sort 142 | promise = new process.EventEmitter 143 | 144 | traverse_promise = @traverse (kp, callback) -> 145 | start_cmp = sort kp[0], start_key 146 | end_cmp = sort kp[0], end_key 147 | 148 | if kp[2] 149 | if start_cmp >= 0 and end_cmp <= 0 150 | return callback null, true 151 | 152 | if end_cmp > 0 153 | return callback null, false 154 | else 155 | if end_cmp <= 0 156 | return callback null, true 157 | if end_cmp > 0 158 | return callback null, false 159 | 160 | callback null 161 | 162 | traverse_promise.on 'data', (value) -> 163 | promise.emit 'data', value 164 | 165 | traverse_promise.on 'end', -> 166 | promise.emit 'end' 167 | 168 | promise 169 | 170 | -------------------------------------------------------------------------------- /lib/index/core/set.coffee: -------------------------------------------------------------------------------- 1 | ### 2 | Set functionality for Node index module 3 | 4 | This software is licensed under the MIT License. 5 | 6 | Copyright Fedor Indutny, 2011. 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a 9 | copy of this software and associated documentation files (the 10 | "Software"), to deal in the Software without restriction, including 11 | without limitation the rights to use, copy, modify, merge, publish, 12 | distribute, sublicense, and/or sell copies of the Software, and to permit 13 | persons to whom the Software is furnished to do so, subject to the 14 | following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included 17 | in all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 20 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 22 | NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 23 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 24 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 25 | USE OR OTHER DEALINGS IN THE SOFTWARE. 26 | ### 27 | 28 | step = require 'step' 29 | 30 | utils = require '../../index/utils' 31 | 32 | ### 33 | Set 34 | ### 35 | exports.set = (key, value, _callback) -> 36 | sort = @sort 37 | order = @order 38 | storage = @storage 39 | conflictManager = @conflictManager 40 | 41 | if @lock(=> @set key, value, _callback) 42 | return 43 | 44 | callback = (err, data) => 45 | @releaseLock() 46 | 47 | process.nextTick -> 48 | _callback and _callback err, data 49 | 50 | 51 | iterate = (page, callback) -> 52 | item_index = utils.search page, sort, key 53 | item = page[item_index] 54 | 55 | if item and not item[2] 56 | # Index 57 | 58 | # Read next page and try to insert kv in it 59 | step -> 60 | storage.read item[1], @parallel() 61 | , (err, page) -> 62 | if err 63 | throw err 64 | 65 | iterate page, @parallel() 66 | , (err, result) -> 67 | if err 68 | callback err 69 | return 70 | 71 | if storage.isPosition result 72 | # Page is just should be overwrited 73 | page[item_index][1] = result 74 | 75 | storage.write page, callback 76 | else 77 | # Result is = { 78 | # left_page: [...], 79 | # middle_key: ..., 80 | # right_page: [...] 81 | # } 82 | 83 | page[item_index][1] = result.left_page 84 | page.splice item_index + 1, 0, 85 | [result.middle_key, result.right_page] 86 | 87 | splitPage false, storage, order, page, callback 88 | 89 | else 90 | # Leaf 91 | step -> 92 | # Found dublicate 93 | if item and sort(item[0], key) is 0 94 | unless conflictManager 95 | throw 'Can\'t insert item w/ dublicate key' 96 | 97 | # Invoke conflictManager 98 | step -> 99 | storage.read item[1], @parallel() 100 | , (err, old_value) -> 101 | if err 102 | throw err 103 | 104 | @parallel() null, old_value 105 | conflictManager old_value, value, @parallel() 106 | , @parallel() 107 | 108 | return 109 | 110 | @parallel() null, value 111 | , (err, value, old_value) -> 112 | if err 113 | throw err 114 | 115 | # Value should be firstly written in storage 116 | item_index = if item_index is null then 0 else item_index + 1 117 | storage.write [value, old_value], @parallel() 118 | , (err, value) -> 119 | if err 120 | callback err 121 | return 122 | 123 | # Then inserted in leaf page 124 | page.splice item_index, 0, [key, value, 1] 125 | 126 | splitPage true, storage, order, page, callback 127 | 128 | 129 | step -> 130 | # Read initial data 131 | storage.readRoot @parallel() 132 | , (err, root) -> 133 | if err 134 | throw err 135 | 136 | # Initiate sequence 137 | iterate root, @parallel() 138 | , (err, result) -> 139 | if err 140 | throw err 141 | 142 | if storage.isPosition result 143 | # Write new root 144 | @parallel() null, result 145 | else 146 | # Split root 147 | storage.write [ 148 | [null, result.left_page], 149 | [result.middle_key, result.right_page] 150 | ], @parallel() 151 | , (err, new_root_pos) -> 152 | if err 153 | throw err 154 | 155 | storage.writeRoot new_root_pos, @parallel() 156 | , callback 157 | 158 | ### 159 | Check page length 160 | If exceed - split it into two and return left_page, right_page, middle_key 161 | ### 162 | splitPage = (in_leaf, storage, order, page, callback) -> 163 | # If item needs to be splitted 164 | if page.length > order 165 | mid_index = page.length >> 1 166 | mid_key = page[mid_index][0] 167 | 168 | # Write splitted pages 169 | step -> 170 | left_page = page[0...mid_index] 171 | storage.write left_page, @parallel() 172 | 173 | right_page = page[mid_index...] 174 | 175 | right_page[0][0] = null unless in_leaf 176 | 177 | storage.write right_page, @parallel() 178 | , (err, left_page, right_page) -> 179 | callback err, { 180 | left_page: left_page, 181 | middle_key: mid_key, 182 | right_page: right_page 183 | } 184 | 185 | else 186 | # Just overwrite it 187 | storage.write page, callback 188 | 189 | -------------------------------------------------------------------------------- /lib/index/core/bulk.coffee: -------------------------------------------------------------------------------- 1 | ### 2 | Bulk set/unset functionality for Node index module 3 | 4 | This software is licensed under the MIT License. 5 | 6 | Copyright Fedor Indutny, 2011. 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a 9 | copy of this software and associated documentation files (the 10 | "Software"), to deal in the Software without restriction, including 11 | without limitation the rights to use, copy, modify, merge, publish, 12 | distribute, sublicense, and/or sell copies of the Software, and to permit 13 | persons to whom the Software is furnished to do so, subject to the 14 | following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included 17 | in all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 20 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 22 | NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 23 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 24 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 25 | USE OR OTHER DEALINGS IN THE SOFTWARE. 26 | ### 27 | 28 | step = require 'step' 29 | 30 | utils = require '../../index/utils' 31 | 32 | ### 33 | Bulk set/unset 34 | 35 | kvs = [ 36 | [key, value, 1] - set 37 | [key] - unset 38 | ] 39 | ### 40 | exports.bulk = (kvs, _callback) -> 41 | that = @ 42 | sort = @sort 43 | order = @order 44 | storage = @storage 45 | conflictManager = @conflictManager 46 | 47 | if @lock(=> @bulk kvs, _callback) 48 | return 49 | 50 | callback = (err, data) => 51 | @releaseLock() 52 | 53 | process.nextTick -> 54 | _callback and _callback err, conflicts 55 | 56 | # conflicts 57 | conflicts = [] 58 | 59 | # clone kvs 60 | kvs = [].concat kvs 61 | # sort kvs 62 | kvs = kvs.sort (a, b) -> sort a[0], b[0] 63 | 64 | unless kvs.length > 0 65 | return callback null, [] 66 | 67 | # do bulk ops on page 68 | # kvs is part of original kvs that matches page range 69 | # (range is stored in parent page of course) 70 | # 71 | # return values 72 | # null, [array of kps] = [ [null, pos], [key1, pos], ...] 73 | # null, [] 74 | iterate = (callback, kvs) -> 75 | _iterate = (err, page) -> 76 | if err 77 | return callback err 78 | 79 | kv = kvs.shift() 80 | 81 | if not kv 82 | # all kvs has been processed split or delete page 83 | splitPage page, order, storage, callback 84 | return 85 | 86 | index = utils.search page, sort, kv[0] 87 | item = page[index] 88 | 89 | if item and not item[2] 90 | # index page 91 | 92 | kvs.unshift kv 93 | if page[index + 1] 94 | kv_index = utils.search kvs, sort, page[index + 1][0] 95 | 96 | if not kvs[kv_index] 97 | kv_index++ 98 | else if sort(kvs[kv_index][0], page[index + 1][0]) isnt 0 99 | kv_index++ 100 | _kvs = kvs.splice 0, kv_index 101 | else 102 | _kvs = kvs 103 | 104 | step () -> 105 | storage.read item[1], @parallel() 106 | , iterate( 107 | (err, kps) -> 108 | if err 109 | return callback err 110 | 111 | if kps.length == 0 112 | page.splice index, 1 113 | else 114 | kps[0][0] = item[0] 115 | page.splice.apply page, [index, 1].concat kps 116 | 117 | _iterate(null, page) 118 | , _kvs 119 | ) 120 | else 121 | # leaf page 122 | # so insert all kvs here 123 | # and remove 124 | if not item or (sort item[0], kv[0]) < 0 125 | if kv[2] 126 | # just insert item and continue iterating 127 | index = if index is null then 0 else index + 1 128 | page.splice index, 0, kv 129 | storage.write [kv[1],], (err, pos) -> 130 | kv[1] = pos 131 | process.nextTick -> 132 | _iterate null, page 133 | 134 | return 135 | else 136 | # do nothing, b/c item not found and we can't delete it 137 | else 138 | # ok, item exists 139 | if not kv[2] 140 | # if we're removing that's ok 141 | # just remove 142 | page.splice index, 1 143 | else 144 | # manage conflicts if inserting 145 | if conflictManager 146 | # TODO: write conflictManager 147 | conflicts.push kv[0] 148 | else 149 | conflicts.push kv[0] 150 | 151 | process.nextTick -> 152 | _iterate null, page 153 | 154 | step -> 155 | storage.readRoot @parallel() 156 | , iterate( 157 | recSplit = (err, page) -> 158 | if err 159 | return callback err 160 | 161 | if page.length == 0 162 | storage.write [], (err, pos) -> 163 | if err 164 | return callback err 165 | storage.writeRoot pos, callback 166 | else if page.length == 1 167 | storage.writeRoot page[0][1], callback 168 | else 169 | splitPage page, order, storage, recSplit 170 | , kvs 171 | ) 172 | 173 | splitPage = (page, order, storage, callback) -> 174 | if page.length == 0 175 | pages = [] 176 | else if page.length <= order 177 | pages = [page] 178 | else 179 | pages = [] 180 | i = 0 181 | len = page.length 182 | while len > order 183 | len = len >> 1 184 | 185 | while page.length > 0 186 | pages.push page.splice 0, len 187 | 188 | step -> 189 | group = @group() 190 | 191 | for page in pages 192 | pre_page = [].concat page 193 | if not page[0][2] 194 | pre_page[0] = [].concat pre_page[0] 195 | pre_page[0][0] = null 196 | storage.write pre_page, group() 197 | return 198 | , (err, pages_pos) -> 199 | if err 200 | return callback err 201 | 202 | pages = pages_pos.map (pos, i) -> 203 | if i == 0 204 | [null, pos] 205 | else 206 | [pages[i][0][0], pos] 207 | 208 | @parallel() null, pages 209 | , callback 210 | -------------------------------------------------------------------------------- /bench-data/compact.bench: -------------------------------------------------------------------------------- 1 | # N Compact_time 2 | 1500 0 3 | 3000 0 4 | 4500 0 5 | 6000 0 6 | 7500 1 7 | 9000 0 8 | 10500 7460 9 | 12000 0 10 | 13500 0 11 | 15000 0 12 | 16500 0 13 | 18000 0 14 | 19500 0 15 | 21000 4503 16 | 22500 0 17 | 24000 0 18 | 25500 0 19 | 27000 0 20 | 28500 0 21 | 30000 0 22 | 31500 12838 23 | 33000 0 24 | 34500 0 25 | 36000 0 26 | 37500 0 27 | 39000 0 28 | 40500 0 29 | 42000 10900 30 | 43500 0 31 | 45000 0 32 | 46500 0 33 | 48000 0 34 | 49500 0 35 | 51000 0 36 | 52500 30026 37 | 54000 0 38 | 55500 0 39 | 57000 0 40 | 58500 0 41 | 60000 0 42 | 61500 0 43 | 63000 36564 44 | 64500 0 45 | 66000 1 46 | 67500 0 47 | 69000 0 48 | 70500 0 49 | 72000 0 50 | 73500 82862 51 | 75000 0 52 | 76500 0 53 | 78000 0 54 | 79500 0 55 | 81000 0 56 | 82500 0 57 | 84000 54079 58 | 85500 0 59 | 87000 0 60 | 88500 0 61 | 90000 0 62 | 91500 0 63 | 93000 0 64 | 94500 44641 65 | 96000 0 66 | 97500 0 67 | 99000 0 68 | 100500 0 69 | 102000 0 70 | 103500 0 71 | 105000 50990 72 | 106500 0 73 | 108000 0 74 | 109500 0 75 | 111000 0 76 | 112500 0 77 | 114000 0 78 | 115500 59023 79 | 117000 0 80 | 118500 1 81 | 120000 0 82 | 121500 0 83 | 123000 0 84 | 124500 0 85 | 126000 20633 86 | 127500 0 87 | 129000 0 88 | 130500 0 89 | 132000 0 90 | 133500 0 91 | 135000 0 92 | 136500 81095 93 | 138000 0 94 | 139500 0 95 | 141000 0 96 | 142500 0 97 | 144000 0 98 | 145500 0 99 | 147000 76921 100 | 148500 0 101 | 150000 0 102 | 151500 0 103 | 153000 0 104 | 154500 0 105 | 156000 0 106 | 157500 82802 107 | 159000 0 108 | 160500 0 109 | 162000 0 110 | 163500 0 111 | 165000 0 112 | 166500 0 113 | 168000 116015 114 | 169500 0 115 | 171000 0 116 | 172500 0 117 | 174000 0 118 | 175500 0 119 | 177000 0 120 | 178500 112435 121 | 180000 0 122 | 181500 0 123 | 183000 0 124 | 184500 0 125 | 186000 0 126 | 187500 0 127 | 189000 93437 128 | 190500 0 129 | 192000 0 130 | 193500 0 131 | 195000 0 132 | 196500 0 133 | 198000 0 134 | 199500 98412 135 | 201000 0 136 | 202500 0 137 | 204000 0 138 | 205500 0 139 | 207000 0 140 | 208500 0 141 | 210000 98342 142 | 211500 0 143 | 213000 0 144 | 214500 0 145 | 216000 0 146 | 217500 0 147 | 219000 0 148 | 220500 103950 149 | 222000 0 150 | 223500 0 151 | 225000 0 152 | 226500 0 153 | 228000 0 154 | 229500 0 155 | 231000 118313 156 | 232500 0 157 | 234000 0 158 | 235500 0 159 | 237000 0 160 | 238500 0 161 | 240000 0 162 | 241500 132240 163 | 243000 0 164 | 244500 0 165 | 246000 0 166 | 247500 0 167 | 249000 0 168 | 250500 0 169 | 252000 116284 170 | 253500 0 171 | 255000 0 172 | 256500 0 173 | 258000 1 174 | 259500 0 175 | 261000 0 176 | 262500 174822 177 | 264000 0 178 | 265500 0 179 | 267000 0 180 | 268500 0 181 | 270000 0 182 | 271500 0 183 | 273000 88760 184 | 274500 0 185 | 276000 0 186 | 277500 0 187 | 279000 0 188 | 280500 0 189 | 282000 0 190 | 283500 138066 191 | 285000 0 192 | 286500 0 193 | 288000 0 194 | 289500 0 195 | 291000 0 196 | 292500 0 197 | 294000 325681 198 | 295500 0 199 | 297000 0 200 | 298500 0 201 | 300000 0 202 | 301500 0 203 | 303000 0 204 | 304500 376607 205 | 306000 0 206 | 307500 0 207 | 309000 0 208 | 310500 0 209 | 312000 0 210 | 313500 0 211 | 315000 188021 212 | 316500 0 213 | 318000 0 214 | 319500 0 215 | 321000 0 216 | 322500 0 217 | 324000 0 218 | 325500 292554 219 | 327000 0 220 | 328500 0 221 | 330000 0 222 | 331500 0 223 | 333000 0 224 | 334500 0 225 | 336000 306727 226 | 337500 0 227 | 339000 0 228 | 340500 0 229 | 342000 0 230 | 343500 0 231 | 345000 0 232 | 346500 335779 233 | 348000 0 234 | 349500 0 235 | 351000 0 236 | 352500 0 237 | 354000 0 238 | 355500 0 239 | 357000 184227 240 | 358500 0 241 | 360000 0 242 | 361500 0 243 | 363000 0 244 | 364500 0 245 | 366000 0 246 | 367500 326882 247 | 369000 0 248 | 370500 0 249 | 372000 0 250 | 373500 0 251 | 375000 0 252 | 376500 0 253 | 378000 249865 254 | 379500 0 255 | 381000 0 256 | 382500 0 257 | 384000 0 258 | 385500 0 259 | 387000 0 260 | 388500 153314 261 | 390000 0 262 | 391500 0 263 | 393000 1 264 | 394500 0 265 | 396000 0 266 | 397500 0 267 | 399000 223151 268 | 400500 0 269 | 402000 0 270 | 403500 0 271 | 405000 0 272 | 406500 0 273 | 408000 0 274 | 409500 282457 275 | 411000 0 276 | 412500 0 277 | 414000 0 278 | 415500 1 279 | 417000 0 280 | 418500 0 281 | 420000 281788 282 | 421500 0 283 | 423000 0 284 | 424500 0 285 | 426000 0 286 | 427500 0 287 | 429000 0 288 | 430500 295129 289 | 432000 0 290 | 433500 0 291 | 435000 0 292 | 436500 0 293 | 438000 0 294 | 439500 0 295 | 441000 298062 296 | 442500 1 297 | 444000 0 298 | 445500 0 299 | 447000 0 300 | 448500 0 301 | 450000 0 302 | 451500 349471 303 | 453000 0 304 | 454500 0 305 | 456000 0 306 | 457500 0 307 | 459000 0 308 | 460500 0 309 | 462000 402491 310 | 463500 0 311 | 465000 0 312 | 466500 0 313 | 468000 0 314 | 469500 0 315 | 471000 0 316 | 472500 433683 317 | 474000 0 318 | 475500 0 319 | 477000 0 320 | 478500 0 321 | 480000 0 322 | 481500 0 323 | 483000 408686 324 | 484500 0 325 | 486000 0 326 | 487500 0 327 | 489000 0 328 | 490500 0 329 | 492000 0 330 | 493500 389889 331 | 495000 0 332 | 496500 0 333 | 498000 0 334 | 499500 0 335 | 501000 0 336 | 502500 0 337 | 504000 277279 338 | 505500 0 339 | 507000 0 340 | 508500 0 341 | 510000 0 342 | 511500 1 343 | 513000 0 344 | 514500 321312 345 | 516000 0 346 | 517500 0 347 | 519000 0 348 | 520500 0 349 | 522000 0 350 | 523500 0 351 | 525000 413379 352 | 526500 0 353 | 528000 0 354 | 529500 0 355 | 531000 0 356 | 532500 0 357 | 534000 0 358 | 535500 425198 359 | 537000 0 360 | 538500 0 361 | 540000 0 362 | 541500 0 363 | 543000 0 364 | 544500 0 365 | 546000 482950 366 | 547500 0 367 | 549000 0 368 | 550500 0 369 | 552000 0 370 | 553500 0 371 | 555000 0 372 | 556500 275927 373 | 558000 0 374 | 559500 0 375 | 561000 0 376 | 562500 0 377 | 564000 0 378 | 565500 0 379 | 567000 291717 380 | 568500 0 381 | 570000 0 382 | 571500 0 383 | 573000 0 384 | 574500 0 385 | 576000 0 386 | 577500 394531 387 | 579000 0 388 | 580500 0 389 | 582000 0 390 | 583500 0 391 | 585000 0 392 | 586500 0 393 | 588000 290663 394 | 589500 0 395 | 591000 0 396 | 592500 1 397 | 594000 0 398 | 595500 0 399 | 597000 0 400 | 598500 340445 401 | 600000 0 402 | 601500 0 403 | 603000 0 404 | 604500 0 405 | 606000 0 406 | 607500 0 407 | 609000 298627 408 | 610500 0 409 | 612000 0 410 | 613500 0 411 | 615000 0 412 | 616500 0 413 | 618000 0 414 | 619500 644193 415 | 621000 0 416 | 622500 0 417 | 624000 0 418 | 625500 0 419 | 627000 0 420 | 628500 0 421 | 630000 202110 422 | 631500 0 423 | 633000 0 424 | 634500 0 425 | 636000 0 426 | 637500 0 427 | 639000 0 428 | 640500 277901 429 | 642000 0 430 | 643500 0 431 | 645000 0 432 | 646500 0 433 | 648000 0 434 | 649500 0 435 | 651000 464727 436 | 652500 0 437 | 654000 0 438 | 655500 0 439 | 657000 0 440 | 658500 0 441 | 660000 0 442 | 661500 331209 443 | 663000 1 444 | 664500 0 445 | 666000 0 446 | 667500 0 447 | 669000 0 448 | 670500 0 449 | 672000 460749 450 | 673500 0 451 | 675000 0 452 | 676500 0 453 | 678000 0 454 | 679500 0 455 | 681000 0 456 | 682500 495811 457 | 684000 0 458 | 685500 0 459 | 687000 0 460 | 688500 0 461 | 690000 0 462 | 691500 0 463 | 693000 574117 464 | 694500 0 465 | 696000 0 466 | 697500 0 467 | 699000 0 468 | 700500 0 469 | 702000 0 470 | 703500 522451 471 | 705000 0 472 | 706500 0 473 | 708000 0 474 | 709500 0 475 | 711000 0 476 | 712500 1 477 | 714000 595648 478 | 715500 0 479 | 717000 0 480 | 718500 0 481 | 720000 0 482 | 721500 0 483 | 723000 0 484 | 724500 887397 485 | 726000 0 486 | 727500 0 487 | 729000 0 488 | 730500 0 489 | 732000 0 490 | 733500 0 491 | 735000 849621 492 | 736500 0 493 | 738000 0 494 | 739500 0 495 | 741000 0 496 | 742500 0 497 | 744000 0 498 | 745500 817194 499 | 747000 0 500 | 748500 0 501 | 750000 0 502 | 751500 0 503 | 753000 0 504 | 754500 0 505 | 756000 621776 506 | 757500 0 507 | 759000 0 508 | 760500 0 509 | 762000 0 510 | 763500 0 511 | 765000 0 512 | 766500 980020 513 | 768000 0 514 | 769500 0 515 | 771000 0 516 | 772500 0 517 | 774000 0 518 | 775500 0 519 | 777000 727502 520 | 778500 0 521 | 780000 0 522 | 781500 0 523 | 783000 0 524 | 784500 0 525 | 786000 0 526 | 787500 654776 527 | 789000 0 528 | 790500 0 529 | 792000 0 530 | 793500 0 531 | 795000 0 532 | 796500 0 533 | 798000 521666 534 | 799500 0 535 | 801000 0 536 | 802500 0 537 | 804000 0 538 | 805500 0 539 | 807000 0 540 | 808500 527030 541 | 810000 0 542 | 811500 0 543 | 813000 0 544 | 814500 0 545 | 816000 0 546 | 817500 0 547 | 819000 566023 548 | 820500 0 549 | 822000 0 550 | 823500 0 551 | 825000 0 552 | 826500 0 553 | 828000 0 554 | 829500 701869 555 | 831000 0 556 | 832500 0 557 | 834000 0 558 | 835500 0 559 | 837000 0 560 | 838500 0 561 | 840000 805612 562 | 841500 0 563 | 843000 0 564 | 844500 0 565 | 846000 0 566 | 847500 0 567 | 849000 0 568 | 850500 856758 569 | 852000 0 570 | 853500 0 571 | 855000 0 572 | 856500 0 573 | 858000 0 574 | 859500 0 575 | 861000 657510 576 | 862500 0 577 | 864000 0 578 | 865500 0 579 | 867000 0 580 | 868500 0 581 | 870000 0 582 | 871500 453364 583 | 873000 0 584 | 874500 0 585 | 876000 0 586 | 877500 0 587 | 879000 0 588 | 880500 1 589 | 882000 414674 590 | 883500 0 591 | 885000 0 592 | 886500 0 593 | 888000 0 594 | 889500 0 595 | 891000 0 596 | 892500 288865 597 | 894000 0 598 | 895500 0 599 | 897000 0 600 | 898500 0 601 | 900000 0 602 | 901500 0 603 | 903000 611540 604 | 904500 0 605 | 906000 0 606 | 907500 1 607 | 909000 0 608 | 910500 0 609 | 912000 0 610 | 913500 900005 611 | 915000 0 612 | 916500 0 613 | 918000 0 614 | 919500 0 615 | 921000 0 616 | 922500 0 617 | 924000 407085 618 | 925500 0 619 | 927000 0 620 | 928500 0 621 | 930000 0 622 | 931500 0 623 | 933000 0 624 | 934500 501347 625 | 936000 0 626 | 937500 0 627 | 939000 0 628 | 940500 0 629 | 942000 0 630 | 943500 0 631 | 945000 508885 632 | 946500 0 633 | 948000 0 634 | 949500 0 635 | 951000 0 636 | 952500 0 637 | 954000 0 638 | 955500 577699 639 | 957000 0 640 | 958500 0 641 | 960000 0 642 | 961500 0 643 | 963000 0 644 | 964500 0 645 | 966000 555714 646 | 967500 0 647 | 969000 0 648 | 970500 0 649 | 972000 0 650 | 973500 0 651 | 975000 0 652 | 976500 307303 653 | 978000 0 654 | 979500 0 655 | 981000 0 656 | 982500 0 657 | 984000 0 658 | 985500 0 659 | 987000 790393 660 | 988500 0 661 | 990000 0 662 | 991500 0 663 | 993000 0 664 | 994500 0 665 | 996000 0 666 | 997500 875547 667 | 999000 0 668 | 1000500 0 669 | 1002000 0 670 | 1003500 0 671 | 1005000 0 672 | 1006500 0 673 | 1008000 934902 674 | 1009500 0 675 | 1011000 0 676 | 1012500 0 677 | 1014000 0 678 | 1015500 0 679 | 1017000 0 680 | -------------------------------------------------------------------------------- /test/helpers.coffee: -------------------------------------------------------------------------------- 1 | step = require 'step' 2 | fs = require 'fs' 3 | path = require 'path' 4 | vows = require 'vows' 5 | assert = require 'assert' 6 | 7 | index = require '../lib/index' 8 | 9 | few = [0, 1, 3, 4, 2, 6, -1] 10 | 11 | N = 1000 12 | half_of_N = N >> 1 13 | half_of_N_1 = half_of_N + 1 14 | 15 | bulk = [1..N] 16 | 17 | many = [1..N] 18 | left_half_many = [1..half_of_N] 19 | right_half_many = [half_of_N_1..N] 20 | unexist = [1..10] 21 | reversed = [N..1] 22 | 23 | setArray = (I, prefix, data, callback) -> 24 | step -> 25 | group = @group() 26 | I.set (prefix + i), (prefix + i), group() for i in data 27 | return 28 | , callback 29 | 30 | return 31 | 32 | getArray = (I, prefix, data, callback) -> 33 | step -> 34 | group = @group() 35 | fn = (key) -> 36 | _callback = group() 37 | (err, value) -> 38 | if err 39 | _callback err 40 | else 41 | _callback null, value is key 42 | 43 | I.get (prefix + i), fn(prefix + i) for i in data 44 | return 45 | , callback 46 | return 47 | 48 | notgetArray = (I, prefix, data, callback) -> 49 | step -> 50 | group = @group() 51 | fn = (key) -> 52 | _callback = group() 53 | (err, value) -> 54 | if err 55 | return _callback null 56 | _callback 'found!' 57 | 58 | I.get (prefix + i), fn() for i in data 59 | return 60 | , callback 61 | return 62 | 63 | unsetArray = (I, prefix, data, callback) -> 64 | step -> 65 | group = @group() 66 | I.unset (prefix + i), group() for i in data 67 | return 68 | , callback 69 | 70 | return 71 | 72 | exports.consistencyTest = (suite, options) -> 73 | I = null 74 | 75 | suite = suite 76 | .addBatch 77 | 'Unsetting not-existing values from empty tree': 78 | topic: -> 79 | I = options.I 80 | unsetArray I, 'unexist:', unexist, @callback 81 | 'should be still successfull': -> 82 | return 83 | 84 | suite = suite 85 | .addBatch 86 | 'Inserting kvs in reverse order': 87 | topic: -> 88 | setArray I, 'reverse:', reversed, @callback 89 | 'should be successfull': -> 90 | return 91 | .addBatch 92 | 'Getting any of them': 93 | topic: -> 94 | getArray I, 'reverse:', reversed, @callback 95 | 'should return right values': (oks) -> 96 | assert.ok oks.every (ok) -> ok is true 97 | .addBatch 98 | 'Unsetting all of them': 99 | topic: -> 100 | unsetArray I, 'reverse:', reversed, @callback 101 | 'should be successfull': -> 102 | return 103 | 104 | suite = suite 105 | .addBatch 106 | 'Running bulk insertion op': 107 | topic: -> 108 | I.bulk (bulk.map (i) -> 109 | ['bulk:' + i, 'bulk:' + i, 1] 110 | ), @callback 111 | 'should not have conflicts': (conflicts) -> 112 | assert.equal conflicts.length, 0 113 | .addBatch 114 | 'Getting items from that bulk set': 115 | topic: -> 116 | getArray I, 'bulk:', bulk, @callback 117 | 'should return right values': (oks) -> 118 | assert.ok oks.every (ok) -> ok is true 119 | .addBatch 120 | 'Running bulk insertion op again': 121 | topic: -> 122 | I.bulk (bulk.map (i) -> 123 | ['bulk:' + i, 'bulk:' + i, 1] 124 | ), @callback 125 | 'should have conflicts': (conflicts) -> 126 | assert.equal conflicts.length, bulk.length 127 | .addBatch 128 | 'Running bulk removal op': 129 | topic: -> 130 | I.bulk (bulk.map (i) -> 131 | ['bulk:' + i] 132 | ), @callback 133 | 'should be successfull': -> 134 | return 135 | .addBatch 136 | 'Getting items from that bulk set': 137 | topic: -> 138 | notgetArray I, 'bulk:', bulk, @callback 139 | 'should not return right values': (oks) -> 140 | assert.ok oks.every (ok) -> ok isnt true 141 | .addBatch 142 | 'Running bulk insertion for half': 143 | topic: -> 144 | arr = bulk.filter (i) -> i % 2 145 | arr = arr.map (i) -> ['bulk:' + i, 'bulk:' + i, 1] 146 | 147 | I.bulk arr, @callback 148 | 'should have no conflicts': (conflicts) -> 149 | assert.equal conflicts.length, 0 150 | .addBatch 151 | 'Running bulk mixed action (insertion/removal)': 152 | topic: -> 153 | arr = bulk.map (i) -> 154 | if i % 2 155 | ['bulk:' + i] 156 | else 157 | ['bulk:' + i, 'bulk:' + i, 1] 158 | 159 | I.bulk arr, @callback 160 | 'should have no conflicts': (conflicts) -> 161 | assert.equal conflicts.length, 0 162 | .addBatch 163 | 'Getting items from half of bulk set': 164 | topic: -> 165 | arr = bulk.filter (i) -> 166 | i % 2 == 0 167 | 168 | getArray I, 'bulk:', arr, @callback 169 | 'should return right values': (oks) -> 170 | assert.ok oks.every (ok) -> ok is true 171 | .addBatch 172 | 'Getting items from another half of bulk set': 173 | topic: -> 174 | arr = bulk.filter (i) -> 175 | i % 2 176 | 177 | notgetArray I, 'bulk:', arr, @callback 178 | 'should not return right values': (oks) -> 179 | assert.ok oks.every (ok) -> ok isnt true 180 | .addBatch 181 | 'Running bulk removal for another half': 182 | topic: -> 183 | arr = bulk.filter (i) -> 184 | i % 2 == 0 185 | arr = arr.map (i) -> 186 | ['bulk:' + i] 187 | 188 | I.bulk arr, @callback 189 | 'should have no conflicts': (conflicts) -> 190 | assert.equal conflicts.length, 0 191 | 192 | suite = suite 193 | .addBatch 194 | 'Setting': 195 | 'few key-values': 196 | topic: -> 197 | setArray I, 'few:', few, @callback 198 | 'should be successfull': -> 199 | return 200 | 'many values': 201 | topic: -> 202 | setArray I, 'many:', many, @callback 203 | 'should be successfull': -> 204 | return 205 | .addBatch 206 | 'Getting': 207 | 'few key-values': 208 | topic: -> 209 | getArray I, 'few:', few, @callback 210 | 'should return right values': (oks) -> 211 | assert.ok oks.every (ok) -> ok is true 212 | 'many values': 213 | topic: -> 214 | getArray I, 'many:', many, @callback 215 | 'should return right values': (oks) -> 216 | assert.ok oks.every (ok) -> ok is true 217 | .addBatch 218 | 'Running compaction': 219 | topic: -> 220 | I.compact @callback 221 | 'should be successfull': -> 222 | return 223 | .addBatch 224 | 'Getting few key-values': 225 | topic: -> 226 | getArray I, 'few:', few, @callback 227 | 'should return right values': (oks) -> 228 | assert.ok oks.every (ok) -> ok is true 229 | 'Getting many key-values': 230 | topic: -> 231 | getArray I, 'many:', many, @callback 232 | 'should return right values': (oks) -> 233 | assert.ok oks.every (ok) -> ok is true 234 | .addBatch 235 | 'Unsetting half of values': 236 | topic: -> 237 | unsetArray I, 'many:', left_half_many, @callback 238 | 'should be successfull': -> 239 | return 240 | .addBatch 241 | 'Getting values from that half': 242 | topic: -> 243 | notgetArray I, 'many:', left_half_many, @callback 244 | 'should be not successfull': (oks) -> 245 | assert.ok oks.every (ok) -> ok isnt true 246 | 'Getting values from another half': 247 | topic: -> 248 | getArray I, 'many:', right_half_many, @callback 249 | 'should be successfull': (oks) -> 250 | assert.ok oks.every (ok) -> ok is true 251 | .addBatch 252 | 'Unsetting another half of values': 253 | topic: -> 254 | unsetArray I, 'many:', right_half_many, @callback 255 | 'should be successfull': -> 256 | return 257 | .addBatch 258 | 'Getting any value from unsetted': 259 | topic: -> 260 | notgetArray I, 'many:', many, @callback 261 | 'should be not successfull': (oks) -> 262 | assert.ok oks.every (ok) -> ok isnt true 263 | .addBatch 264 | 'Inserting those values again': 265 | topic: -> 266 | setArray I, 'many:', many, @callback 267 | 'should be successfull': -> 268 | return 269 | .addBatch 270 | 'Getting any of them': 271 | topic: -> 272 | getArray I, 'many:', many, @callback 273 | 'should return right values': (oks) -> 274 | assert.ok oks.every (ok) -> ok is true 275 | 276 | suite = suite 277 | .addBatch 278 | 'Running compaction again': 279 | topic: -> 280 | I.compact @callback 281 | 'should be successfull': -> 282 | return 283 | .addBatch 284 | 'And all values': 285 | topic: -> 286 | getArray I, 'many:', many, @callback 287 | 'should be in place': (oks) -> 288 | assert.ok oks.every (ok) -> ok is true 289 | .addBatch 290 | 'Unsetting non-existant values': 291 | topic: -> 292 | unsetArray I, 'unexist', unexist, @callback 293 | 'should be still successfull': -> 294 | return 295 | 296 | exports.memoryTest = (suite, index_options, options) -> 297 | suite = suite.addBatch 298 | 'Creating new index': 299 | topic: -> 300 | index.createIndex(index_options) 301 | 'should create instance of Index': (I) -> 302 | options.I = I 303 | assert.instanceOf I, index.Index 304 | 305 | exports.consistencyTest suite, options 306 | 307 | exports.fileTest = (suite, index_options, fs_options, options) -> 308 | reopen = options.reopen 309 | if reopen 310 | suite = suite 311 | .addBatch 312 | 'Closing fds for old storage': 313 | topic: -> 314 | options.I.storage.close @callback 315 | 'should be successfull': -> 316 | return 317 | 318 | suite = suite 319 | .addBatch 320 | 'Creating new file-storage': 321 | topic: -> 322 | if not reopen 323 | try 324 | fs.unlinkSync fs_options.filename 325 | fs.unlinkSync (fs_options.filename + '.' + i) for i in [1..300] 326 | catch err 327 | true 328 | else 329 | # write few bytes to the end of file 330 | # (emulate) data corruption) 331 | 332 | getFilename = (i) -> 333 | fs_options.filename + if i then '.' + i else '' 334 | 335 | i = 0 336 | while path.existsSync getFilename i 337 | i++ 338 | i-- 339 | 340 | fd = fs.openSync (getFilename i), 'a+' 341 | 342 | buff = new Buffer '$$$random' 343 | fs.writeSync fd, buff, 0, buff.length, null 344 | 345 | fs.closeSync fd 346 | 347 | index.storage.file.createStorage fs_options, @callback 348 | return 349 | 'should be successfull': (storage) -> 350 | index_options.storage = storage 351 | .addBatch 352 | 'Creating new index': 353 | topic: -> 354 | index.createIndex(index_options) 355 | 'should create instance of Index': (I) -> 356 | options.I = I 357 | assert.instanceOf I, index.Index 358 | 359 | if reopen 360 | suite = suite 361 | .addBatch 362 | 'Unsetting all old values': 363 | 'many': 364 | topic: -> 365 | unsetArray options.I, 'many:', many, @callback 366 | 'should be successfull': -> 367 | return 368 | 'few': 369 | topic: -> 370 | unsetArray options.I, 'few:', few, @callback 371 | 'should be successfull': -> 372 | return 373 | 374 | exports.consistencyTest suite, options 375 | 376 | -------------------------------------------------------------------------------- /lib/index/file-storage.coffee: -------------------------------------------------------------------------------- 1 | ### 2 | File storage for Node Index Module 3 | 4 | This software is licensed under the MIT License. 5 | 6 | Copyright Fedor Indutny, 2011. 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a 9 | copy of this software and associated documentation files (the 10 | "Software"), to deal in the Software without restriction, including 11 | without limitation the rights to use, copy, modify, merge, publish, 12 | distribute, sublicense, and/or sell copies of the Software, and to permit 13 | persons to whom the Software is furnished to do so, subject to the 14 | following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included 17 | in all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 20 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 22 | NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 23 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 24 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 25 | USE OR OTHER DEALINGS IN THE SOFTWARE. 26 | ### 27 | 28 | utils = require './utils' 29 | step = require 'step' 30 | fs = require 'fs' 31 | path = require 'path' 32 | packer = 33 | pack: (data) -> 34 | data = JSON.stringify data 35 | size = Buffer.byteLength data 36 | result = new Buffer(size + 4) 37 | 38 | # Put size 39 | result.writeUInt32LE size, 0 40 | 41 | # Put data 42 | result.write data, 4 43 | 44 | # Return result 45 | result 46 | unpack: (data) -> 47 | size = data.readUInt32LE 0 48 | JSON.parse (data.slice 4, (4 + size)).toString() 49 | 50 | Buffer = require('buffer').Buffer 51 | 52 | ### 53 | Default storage options 54 | ### 55 | DEFAULT_OPTIONS = 56 | filename: '' 57 | padding: 8 58 | rootSize: 64 59 | partitionSize: 1024 * 1024 * 1024 60 | flushMinBytes: 4 * 1024 * 1024 61 | 62 | ### 63 | Class @constructor 64 | ### 65 | Storage = exports.Storage = (options, callback) -> 66 | options = utils.merge DEFAULT_OPTIONS, options 67 | 68 | unless options.filename 69 | return callback 'Filename is required' 70 | 71 | {@filename, @padding, 72 | @partitionSize, @rootSize, @flushMinBytes} = options 73 | 74 | @_init callback 75 | 76 | # Return instance of self 77 | @ 78 | 79 | ### 80 | Initializes storage 81 | May be used not from constructor 82 | ### 83 | Storage::_init = (callback) -> 84 | @files = [] 85 | @filesOffset = 0 86 | @buffers = [] 87 | @buffersBytes = 0 88 | @buffersHashmap = {} 89 | 90 | @checkCompaction (err) => 91 | if err 92 | return callback err 93 | 94 | @openFile (err) => 95 | if err 96 | return callback err 97 | 98 | if @files.length <= 0 99 | # If no files - create one 100 | @createFile callback 101 | else 102 | callback null, @ 103 | 104 | ### 105 | @constructor wrapper 106 | ### 107 | exports.createStorage = (options, callback) -> 108 | return new Storage options, callback 109 | 110 | ### 111 | pos = [ 112 | start-offset, 113 | length 114 | file-index or undefined 115 | ] 116 | ### 117 | Storage::isPosition = isPosition = (pos) -> 118 | pos? and Array.isArray(pos) and pos.length is 3 and true 119 | 120 | ### 121 | Adds index to the end of file 122 | and return next (unopened) filename 123 | ### 124 | Storage::nextFilename = (i, filename) -> 125 | index = i or @files.length 126 | index -= @filesOffset 127 | filename = filename or @filename 128 | if index > 0 129 | filename + '.' + index 130 | else 131 | filename 132 | 133 | ### 134 | Check if interrupted compaction is in place 135 | ### 136 | Storage::checkCompaction = (callback) -> 137 | filename = @filename 138 | 139 | nextFilename = @nextFilename.bind @ 140 | path.exists filename, (exists) => 141 | path.exists filename + '.compact', (compactExists) => 142 | if exists 143 | if compactExists 144 | # Probably compaction hasn't finished 145 | # Delete compaction files 146 | @iterateFiles filename + '.compact', (err, i) -> 147 | if err 148 | throw err 149 | 150 | if i isnt null 151 | compacted = nextFilename i, filename + '.compact' 152 | fs.unlink compacted, @parallel() 153 | else 154 | @parallel() null 155 | # Normal db file exists - use it 156 | callback null 157 | else 158 | if compactExists 159 | # Ok, compaction has finished 160 | # Move files 161 | @iterateFiles filename + '.compact', (err, i) -> 162 | if err 163 | throw err 164 | 165 | if i isnt null 166 | # still iterating 167 | compacted = nextFilename i, filename + '.compact' 168 | notcompacted = nextFilename i, filename 169 | _callback = @parallel() 170 | fs.unlink notcompacted, (err) -> 171 | if err and (not err.code or err.code isnt 'ENOENT') 172 | _callback err 173 | else 174 | fs.rename compacted, notcompacted, _callback 175 | else 176 | # finished iterating 177 | callback null 178 | else 179 | callback null 180 | 181 | 182 | ### 183 | Iterate through files in descending order 184 | filename.N 185 | filename.N - 1 186 | ... 187 | filename 188 | ### 189 | Storage::iterateFiles = (filename, callback) -> 190 | files = [] 191 | 192 | next = () => 193 | _filename = @nextFilename files.length, filename 194 | step -> 195 | _callback = @parallel() 196 | path.exists _filename, (exists) -> 197 | _callback null, exists 198 | return 199 | , (err, exists) -> 200 | unless exists 201 | process.nextTick iterate 202 | return 203 | 204 | files.push files.length 205 | process.nextTick next 206 | 207 | iterate = () -> 208 | file = files.pop() 209 | unless file? 210 | file = null 211 | 212 | step -> 213 | @parallel() null, file 214 | , callback 215 | , (err) -> 216 | if err 217 | callback err 218 | else if file isnt null 219 | process.nextTick iterate 220 | 221 | next() 222 | 223 | ### 224 | Add index to the end of filename, 225 | open it if exists and store size 226 | ### 227 | Storage::openFile = (callback) -> 228 | that = @ 229 | 230 | padding = @padding 231 | files = @files 232 | filename = @nextFilename() 233 | file = {} 234 | 235 | step -> 236 | _callback = @parallel() 237 | path.exists filename, (exists) -> 238 | _callback null, exists 239 | return 240 | , (err, exists) -> 241 | if err 242 | @paralell() err 243 | return 244 | 245 | unless exists 246 | @parallel() null 247 | return 248 | 249 | fs.open filename, 'a+', 0666, @parallel() 250 | fs.stat filename, @parallel() 251 | 252 | return 253 | , (err, fd, stat) -> 254 | if err 255 | throw err 256 | 257 | unless fd and stat 258 | @parallel() null 259 | return 260 | 261 | index = files.length 262 | 263 | 264 | file = 265 | filename: filename 266 | fd: fd 267 | size: stat.size 268 | index: index 269 | 270 | if file.size % padding 271 | paddBuff = new Buffer padding - file.size % padding 272 | fs.write fd, paddBuff, 0, paddBuff.length, 273 | null, @parallel() 274 | else 275 | @parallel() null, 0 276 | 277 | @parallel() null, file 278 | 279 | files.push file 280 | 281 | return 282 | , (err, bytesWritten, file) -> 283 | if err 284 | throw err 285 | 286 | if bytesWritten? 287 | if file.size % padding 288 | if bytesWritten != padding - file.size % padding 289 | @parallel() 'Can\'t add padding to db file' 290 | return 291 | file.size += padding - file.size % padding 292 | that.openFile @parallel() 293 | else 294 | @parallel() null 295 | , callback 296 | 297 | ### 298 | Create file 299 | ### 300 | Storage::createFile = (writeRoot, callback) -> 301 | filename = @nextFilename() 302 | 303 | unless callback? 304 | callback = writeRoot 305 | writeRoot = true 306 | 307 | fs.open filename, 'w+', 0666, (err, fd) => 308 | if err 309 | return callback err 310 | 311 | file = 312 | filename: filename 313 | fd: fd 314 | size: 0 315 | index: @files.length - @filesOffset 316 | 317 | @files.push file 318 | 319 | unless writeRoot 320 | return callback null, @ 321 | 322 | @root_pos = [ 323 | '0', 324 | '2', 325 | '0' 326 | ] 327 | @root_pos_data = null 328 | 329 | # Write new root 330 | @write [], (err, pos) => 331 | if err 332 | return callback err 333 | 334 | @writeRoot pos, (err) => 335 | if err 336 | return callback err 337 | 338 | callback null, @ 339 | 340 | ### 341 | Read data from position 342 | ### 343 | Storage::read = (pos, callback, raw) -> 344 | unless isPosition pos 345 | return callback 'pos should be a valid position (read)' 346 | 347 | s = pos[0] 348 | l = pos[1] 349 | f = pos[2] 350 | 351 | file = @files[f || 0] 352 | buff = new Buffer l 353 | 354 | if not file 355 | return callback 'pos is incorrect' 356 | 357 | cached = @buffersHashmap[pos.join '-'] 358 | 359 | if cached 360 | try 361 | unless raw 362 | cached = packer.unpack cached 363 | callback null, cached 364 | return 365 | catch e 366 | 367 | fs.read file.fd, buff, 0, l, s, (err, bytesRead) -> 368 | if err 369 | return callback err 370 | 371 | unless bytesRead == l 372 | return callback 'Read less bytes than expected' 373 | 374 | unless raw 375 | try 376 | buff = packer.unpack buff 377 | err = null 378 | catch e 379 | err = 'Data is not a valid json' 380 | 381 | callback err, buff 382 | 383 | ### 384 | Write data and return position 385 | ### 386 | Storage::write = (data, callback, raw) -> 387 | if raw and not Buffer.isBuffer data 388 | return callback 'In raw mode data should be a buffer' 389 | 390 | unless raw 391 | data = new Buffer packer.pack data 392 | @_fsWrite data, callback 393 | 394 | ### 395 | Read root page 396 | ### 397 | Storage::readRoot = (callback) -> 398 | if @root_pos_data 399 | callback null, @root_pos_data 400 | return 401 | 402 | cache_callback = (err, data) => 403 | if err 404 | return callback err 405 | @root_pos_data = data 406 | callback null, data 407 | return 408 | 409 | if @root_pos 410 | @read @root_pos, cache_callback 411 | return 412 | 413 | @readRootPos (err, pos) => 414 | if err 415 | return callback err 416 | @read pos, cache_callback 417 | 418 | ### 419 | Find last root in files and return it to callback 420 | 421 | it will be synchronous for now 422 | TODO: make it asynchronous 423 | ### 424 | Storage::readRootPos = (callback) -> 425 | iterate = (index, callback) => 426 | file = @files[index] 427 | unless file 428 | return callback 'root not found' 429 | 430 | buff = new Buffer @rootSize 431 | 432 | # If file has incorrect size (unpadded) 433 | # Substract difference from its size 434 | # B/c root can't be in that unpadded area 435 | offset = file.size - (file.size % @padding) - 436 | @rootSize + @padding 437 | 438 | while (offset -= @padding) >= 0 439 | bytesRead = fs.readSync file.fd, buff, 0, @rootSize, offset 440 | unless bytesRead == @rootSize 441 | # Header not found 442 | offset = -1 443 | break 444 | 445 | if data = checkHash buff 446 | root = data 447 | try 448 | root = packer.unpack root 449 | catch e 450 | # Header is not JSON 451 | # Try in previous file 452 | offset = -1 453 | break 454 | return callback null, root 455 | 456 | process.nextTick () -> 457 | iterate (index - 1), callback 458 | 459 | checkHash = (buff) -> 460 | hash = buff.slice(0, utils.hash.len).toString() 461 | rest = buff.slice(utils.hash.len) 462 | rest if hash == utils.hash rest 463 | 464 | iterate (@files.length - 1), callback 465 | 466 | ### 467 | Write root page 468 | ### 469 | Storage::writeRoot = (root_pos, callback) -> 470 | unless isPosition root_pos 471 | return callback 'pos should be a valid position (writeRoot)' 472 | 473 | _root_pos = packer.pack root_pos 474 | buff = new Buffer @rootSize 475 | _root_pos.copy buff, utils.hash.len 476 | 477 | hash = utils.hash buff.slice utils.hash.len 478 | buff.write hash, 0, 'binary' 479 | 480 | @_fsBuffer buff, true, (err) => 481 | if err 482 | return callback err 483 | 484 | @root_pos = root_pos 485 | @root_pos_data = null 486 | callback null 487 | 488 | 489 | ### 490 | Low-level write 491 | 492 | buff - is Buffer 493 | 494 | ### 495 | Storage::_fsWrite = (buff, callback) -> 496 | file = @currentFile() 497 | 498 | pos = [ 499 | file.size, 500 | buff.length, 501 | file.index 502 | ] 503 | 504 | file.size += buff.length 505 | 506 | @buffersHashmap[pos.join '-'] = buff 507 | 508 | @_fsBuffer buff, false, (err) -> 509 | callback err, pos 510 | 511 | ### 512 | Low-level bufferization 513 | ### 514 | Storage::_fsBuffer = (buff, isRoot, callback) -> 515 | file = @currentFile() 516 | fd = file.fd 517 | 518 | buffLen = buff.length 519 | 520 | if isRoot 521 | if file.size % @padding 522 | delta = @padding - (file.size % @padding) 523 | 524 | buffLen += delta 525 | @buffers.push new Buffer delta 526 | file.size += buffLen 527 | 528 | @buffers.push buff 529 | if (@buffersBytes += buffLen) < @flushMinBytes 530 | callback null 531 | return 532 | 533 | @_fsFlush callback 534 | 535 | ### 536 | Flush buffered data to disk 537 | ### 538 | Storage::_fsFlush = (callback, compacting) -> 539 | file = @currentFile() 540 | fd = file.fd 541 | 542 | buffLen = @buffersBytes 543 | buff = new Buffer buffLen 544 | offset = 0 545 | buffers = @buffers 546 | for i in [0...buffers.length] 547 | buffers[i].copy buff, offset 548 | offset += buffers[i].length 549 | 550 | @buffers = [] 551 | @buffersBytes = 0 552 | @buffersHashmap = {} 553 | 554 | fs.write fd, buff, 0, buffLen, null, (err, bytesWritten) => 555 | if err or (bytesWritten isnt buffLen) 556 | @_fsCheckSize (err2) -> 557 | callback err2 or err or 'Written less bytes than expected' 558 | return 559 | 560 | if file.size >= @partitionSize and not compacting 561 | @createFile false, callback 562 | else 563 | callback null 564 | 565 | ### 566 | Recheck current file's length 567 | ### 568 | Storage::_fsCheckSize = (callback)-> 569 | file = @currentFile() 570 | filename = @nextFilename @file.index 571 | fs.stat filename, (err, stat) => 572 | if err 573 | return callback err 574 | 575 | file.size = stat.size 576 | callback null 577 | 578 | ### 579 | Current file 580 | ### 581 | Storage::currentFile = -> 582 | @files[@files.length - 1] 583 | 584 | ### 585 | Close all fds 586 | ### 587 | Storage::close = (callback) -> 588 | files = @files 589 | 590 | @_fsFlush (err) -> 591 | if err 592 | return callback err 593 | 594 | step () -> 595 | group = @group() 596 | 597 | for i in [0...files.length] 598 | if files[i] 599 | fs.close files[i].fd, group() 600 | , callback 601 | 602 | ### 603 | Compaction flow actions 604 | ### 605 | Storage::beforeCompact = (callback) -> 606 | @filename += '.compact' 607 | @filesOffset = @files.length 608 | 609 | @_fsFlush (err) => 610 | if err 611 | return callback err 612 | 613 | @createFile false, callback 614 | , true 615 | 616 | Storage::afterCompact = (callback) -> 617 | that = @ 618 | filesOffset = @filesOffset 619 | files = @files 620 | @filename = @filename.replace /\.compact$/, '' 621 | 622 | @_fsFlush (err) => 623 | if err 624 | return callback err 625 | step () -> 626 | for i in [0...files.length] 627 | fs.close files[i].fd, @parallel() 628 | return 629 | , (err) -> 630 | if err 631 | throw err 632 | 633 | for i in [0...filesOffset] 634 | fs.unlink files[i].filename, @parallel() 635 | return 636 | , (err) -> 637 | if err 638 | throw err 639 | 640 | fnsQueue = [] 641 | compactedCount = files.length - filesOffset 642 | [0...compactedCount].forEach (i) -> 643 | compactedName = files[i + filesOffset].filename 644 | normalName = files[i].filename 645 | files[i] = files[i + filesOffset] 646 | files[i].filename = normalName 647 | 648 | fnsQueue.unshift (err) -> 649 | if err 650 | throw err 651 | 652 | fs.rename compactedName, normalName, @parallel() 653 | return 654 | 655 | fnsQueue.push @parallel() 656 | 657 | step.apply null, fnsQueue 658 | 659 | for i in [compactedCount...files.length] 660 | files.pop() 661 | 662 | that.filesOffset = 0 663 | return 664 | , (err) -> 665 | if err 666 | throw err 667 | 668 | [0...files.length].forEach (i) => 669 | file = files[i] 670 | fn = @parallel() 671 | fs.open file.filename, 'a+', 0666, (err, fd) -> 672 | file.fd = fd 673 | fn err, file 674 | 675 | return 676 | , (err) -> 677 | if err 678 | step -> 679 | for i in [0...files.length] 680 | fs.close files[i].fd, @parallel() 681 | return 682 | , -> 683 | that._init @parallel() 684 | return 685 | , @parallel() 686 | return 687 | null 688 | , callback 689 | -------------------------------------------------------------------------------- /bench-data/write.bench: -------------------------------------------------------------------------------- 1 | # N Writes 2 | 1500 1609.4420600858368 3 | 3000 1255.2301255230125 4 | 4500 1162.7906976744187 5 | 6000 1153.8461538461538 6 | 7500 1152.073732718894 7 | 9000 1124.4377811094453 8 | 10500 1078.3608914450035 9 | 12000 1178.318931657502 10 | 13500 1236.603462489695 11 | 15000 1087.7447425670775 12 | 16500 1160.092807424594 13 | 18000 1078.3608914450035 14 | 19500 1069.1375623663578 15 | 21000 1002.6737967914438 16 | 22500 1051.8934081346424 17 | 24000 939.8496240601504 18 | 25500 1083.8150289017342 19 | 27000 1118.5682326621925 20 | 28500 1021.1027910142955 21 | 30000 1063.0758327427357 22 | 31500 1089.3246187363834 23 | 33000 1054.8523206751054 24 | 34500 1077.5862068965516 25 | 36000 1033.0578512396694 26 | 37500 695.0880444856349 27 | 39000 724.9879168680523 28 | 40500 977.1986970684039 29 | 42000 993.3774834437086 30 | 43500 974.025974025974 31 | 45000 811.2493239588968 32 | 46500 984.2519685039371 33 | 48000 877.7062609713282 34 | 49500 808.6253369272238 35 | 51000 955.4140127388536 36 | 52500 993.3774834437086 37 | 54000 951.1731135066582 38 | 55500 957.2431397574984 39 | 57000 1015.572105619499 40 | 58500 932.2560596643879 41 | 60000 977.8357235984355 42 | 61500 980.3921568627451 43 | 63000 919.6811771919068 44 | 64500 964.6302250803858 45 | 66000 925.3547193090685 46 | 67500 953.5918626827718 47 | 69000 900.9009009009009 48 | 70500 945.179584120983 49 | 72000 894.9880668257756 50 | 73500 918.5548071034905 51 | 75000 894.4543828264758 52 | 76500 897.1291866028708 53 | 78000 871.5862870424172 54 | 79500 893.9213349225269 55 | 81000 846.979107848673 56 | 82500 900.3601440576231 57 | 84000 722.1954742416948 58 | 85500 787.8151260504202 59 | 87000 811.6883116883117 60 | 88500 829.1873963515754 61 | 90000 892.8571428571429 62 | 91500 853.7279453614115 63 | 93000 857.6329331046312 64 | 94500 879.2497069167644 65 | 96000 840.3361344537815 66 | 97500 850.8224617129893 67 | 99000 917.4311926605504 68 | 100500 854.2141230068337 69 | 102000 894.9880668257756 70 | 103500 914.6341463414634 71 | 105000 883.9127872716558 72 | 106500 946.969696969697 73 | 108000 610.2522375915379 74 | 109500 915.1921903599756 75 | 111000 689.0215893431327 76 | 112500 928.2178217821782 77 | 114000 921.3759213759214 78 | 115500 613.2461161079314 79 | 117000 931.6770186335403 80 | 118500 843.644544431946 81 | 120000 873.1082654249127 82 | 121500 904.1591320072333 83 | 123000 901.4423076923077 84 | 124500 920.8103130755064 85 | 126000 870.5745792222867 86 | 127500 881.8342151675485 87 | 129000 875.6567425569177 88 | 130500 882.8722778104767 89 | 132000 828.7292817679559 90 | 133500 874.6355685131196 91 | 135000 846.5011286681715 92 | 136500 834.2602892102336 93 | 138000 869.5652173913044 94 | 139500 880.7985907222549 95 | 141000 830.1051466519092 96 | 142500 863.0609896432682 97 | 144000 643.5006435006435 98 | 145500 806.0182697474476 99 | 147000 818.7772925764192 100 | 148500 739.6449704142012 101 | 150000 821.4676889375685 102 | 151500 814.3322475570033 103 | 153000 844.5945945945946 104 | 154500 734.2143906020558 105 | 156000 816.5487207403376 106 | 157500 802.9978586723769 107 | 159000 878.2201405152225 108 | 160500 782.8810020876826 109 | 162000 806.8854222700377 110 | 163500 797.8723404255319 111 | 165000 777.6049766718507 112 | 166500 782.8810020876826 113 | 168000 612.7450980392157 114 | 169500 816.5487207403376 115 | 171000 544.6623093681917 116 | 172500 751.8796992481203 117 | 174000 762.5826131164209 118 | 175500 797.0244420828906 119 | 177000 789.8894154818325 120 | 178500 598.3246908655764 121 | 180000 780.0312012480499 122 | 181500 772.3995880535531 123 | 183000 837.0535714285714 124 | 184500 749.6251874062968 125 | 186000 810.3727714748784 126 | 187500 747.7567298105683 127 | 189000 775.5946225439503 128 | 190500 781.657113079729 129 | 192000 746.6401194624191 130 | 193500 761.8080243778568 131 | 195000 785.751702462022 132 | 196500 771.2082262210797 133 | 198000 742.2068283028204 134 | 199500 780.0312012480499 135 | 201000 782.0646506777894 136 | 202500 690.9258406264395 137 | 204000 781.25 138 | 205500 510.8991825613079 139 | 207000 775.9958613554061 140 | 208500 777.2020725388601 141 | 210000 837.9888268156425 142 | 211500 840.8071748878924 143 | 213000 770.8119218910585 144 | 214500 888.6255924170616 145 | 216000 861.0792192881745 146 | 217500 896.5929468021518 147 | 219000 899.8200359928014 148 | 220500 869.0614136732329 149 | 222000 548.4460694698355 150 | 223500 632.1112515802781 151 | 225000 844.1193021947101 152 | 226500 861.5738081562321 153 | 228000 550.6607929515418 154 | 229500 590.5511811023622 155 | 231000 911.3001215066829 156 | 232500 700.2801120448179 157 | 234000 731.3505607020966 158 | 235500 862.0689655172414 159 | 237000 850.8224617129893 160 | 238500 774.3933918430563 161 | 240000 868.0555555555555 162 | 241500 888.0994671403197 163 | 243000 793.2310946589106 164 | 244500 914.0767824497258 165 | 246000 749.6251874062968 166 | 247500 909.6422073984232 167 | 249000 857.1428571428571 168 | 250500 596.6587112171837 169 | 252000 867.5534991324465 170 | 253500 564.1218503196691 171 | 255000 855.188141391106 172 | 256500 694.7660954145438 173 | 258000 868.5581933989578 174 | 259500 847.9366873940079 175 | 261000 794.0709370037057 176 | 262500 837.5209380234506 177 | 264000 923.6453201970444 178 | 265500 566.8934240362812 179 | 267000 718.3908045977012 180 | 268500 867.0520231213873 181 | 270000 810.8108108108108 182 | 271500 817.4386920980926 183 | 273000 784.9293563579278 184 | 274500 938.6733416770963 185 | 276000 847.9366873940079 186 | 277500 822.8195282501372 187 | 279000 876.6803039158386 188 | 280500 934.5794392523364 189 | 282000 813.0081300813008 190 | 283500 842.2234699606962 191 | 285000 857.6329331046312 192 | 286500 941.0288582183186 193 | 288000 798.7220447284345 194 | 289500 838.9261744966443 195 | 291000 791.9746568109821 196 | 292500 864.5533141210375 197 | 294000 801.2820512820513 198 | 295500 864.0552995391705 199 | 297000 784.5188284518829 200 | 298500 864.0552995391705 201 | 300000 851.7887563884157 202 | 301500 801.7103153393907 203 | 303000 610.004066693778 204 | 304500 764.525993883792 205 | 306000 842.2234699606962 206 | 307500 837.9888268156425 207 | 309000 772.0020586721564 208 | 310500 818.7772925764192 209 | 312000 762.1951219512196 210 | 313500 753.7688442211055 211 | 315000 696.7022758941013 212 | 316500 819.2244675040961 213 | 318000 849.8583569405099 214 | 319500 784.9293563579278 215 | 321000 737.4631268436578 216 | 322500 733.8551859099804 217 | 324000 475.43581616481777 218 | 325500 783.6990595611285 219 | 327000 785.751702462022 220 | 328500 756.0483870967741 221 | 330000 537.8271782000717 222 | 331500 744.047619047619 223 | 333000 829.6460176991151 224 | 334500 780.4370447450573 225 | 336000 809.4981111710739 226 | 337500 649.63187527068 227 | 339000 834.7245409015025 228 | 340500 666.0746003552398 229 | 342000 618.5567010309278 230 | 343500 669.0454950936663 231 | 345000 734.5739471106758 232 | 346500 491.480996068152 233 | 348000 806.0182697474476 234 | 349500 783.289817232376 235 | 351000 584.1121495327103 236 | 352500 830.1051466519092 237 | 354000 545.8515283842795 238 | 355500 737.8258730939498 239 | 357000 889.1523414344991 240 | 358500 829.1873963515754 241 | 360000 833.3333333333334 242 | 361500 775.5946225439503 243 | 363000 823.271130625686 244 | 364500 818.7772925764192 245 | 366000 724.9879168680523 246 | 367500 634.5177664974619 247 | 369000 364.8747263439552 248 | 370500 779.2207792207793 249 | 372000 649.0696668109043 250 | 373500 676.5899864682003 251 | 375000 695.0880444856349 252 | 376500 789.0583903208837 253 | 378000 566.2514156285391 254 | 379500 480.46124279308134 255 | 381000 659.6306068601583 256 | 382500 545.0581395348837 257 | 384000 652.4575902566334 258 | 385500 743.6787307882995 259 | 387000 635.862653666808 260 | 388500 778.4120394395434 261 | 390000 782.4726134585289 262 | 391500 761.4213197969543 263 | 393000 780.0312012480499 264 | 394500 709.2198581560284 265 | 396000 682.4385805277525 266 | 397500 795.7559681697612 267 | 399000 791.5567282321899 268 | 400500 564.1218503196691 269 | 402000 686.4988558352403 270 | 403500 778.4120394395434 271 | 405000 588.9281507656066 272 | 406500 685.2444038373686 273 | 408000 785.3403141361257 274 | 409500 780.4370447450573 275 | 411000 795.3340402969247 276 | 412500 536.6726296958855 277 | 414000 770.0205338809035 278 | 415500 724.6376811594203 279 | 417000 790.3055848261328 280 | 418500 844.1193021947101 281 | 420000 634.7862886161658 282 | 421500 835.1893095768374 283 | 423000 664.0106241699867 284 | 424500 539.568345323741 285 | 426000 800.4268943436499 286 | 427500 854.7008547008547 287 | 429000 835.1893095768374 288 | 430500 831.9467554076539 289 | 432000 860.0917431192661 290 | 433500 818.7772925764192 291 | 435000 765.3061224489796 292 | 436500 744.4168734491315 293 | 438000 369.9136868064118 294 | 439500 800.4268943436499 295 | 441000 530.035335689046 296 | 442500 857.1428571428571 297 | 444000 868.5581933989578 298 | 445500 844.1193021947101 299 | 447000 520.2913631633714 300 | 448500 827.357970215113 301 | 450000 776.7995857068876 302 | 451500 834.7245409015025 303 | 453000 869.5652173913044 304 | 454500 560.5381165919282 305 | 456000 596.1844197138315 306 | 457500 762.1951219512196 307 | 459000 515.1098901098901 308 | 460500 821.4676889375685 309 | 462000 737.4631268436578 310 | 463500 847.9366873940079 311 | 465000 788.2291119285339 312 | 466500 575.5947812739831 313 | 468000 820.5689277899344 314 | 469500 852.7572484366117 315 | 471000 547.8451424397371 316 | 472500 935.1620947630922 317 | 474000 775.5946225439503 318 | 475500 792.3930269413629 319 | 477000 769.2307692307693 320 | 478500 790.3055848261328 321 | 480000 705.5503292568203 322 | 481500 805.1529790660226 323 | 483000 905.2504526252263 324 | 484500 835.1893095768374 325 | 486000 823.271130625686 326 | 487500 780.8433107756376 327 | 489000 770.8119218910585 328 | 490500 864.0552995391705 329 | 492000 841.2787436904094 330 | 493500 731.3505607020966 331 | 495000 883.9127872716558 332 | 496500 831.4855875831486 333 | 498000 825.9911894273127 334 | 499500 832.4084350721421 335 | 501000 546.448087431694 336 | 502500 827.8145695364238 337 | 504000 836.1204013377926 338 | 505500 882.3529411764706 339 | 507000 679.3478260869565 340 | 508500 804.289544235925 341 | 510000 852.2727272727273 342 | 511500 732.0644216691069 343 | 513000 816.5487207403376 344 | 514500 869.5652173913044 345 | 516000 942.2110552763819 346 | 517500 647.9481641468683 347 | 519000 809.9352051835854 348 | 520500 809.0614886731391 349 | 522000 840.3361344537815 350 | 523500 653.0256856769699 351 | 525000 825.0825082508251 352 | 526500 650.7592190889371 353 | 528000 812.5677139761647 354 | 529500 802.9978586723769 355 | 531000 788.2291119285339 356 | 532500 407.49796251018745 357 | 534000 562.2188905547226 358 | 535500 743.3102081268582 359 | 537000 843.1703204047218 360 | 538500 628.4038542103058 361 | 540000 813.4490238611713 362 | 541500 700.9345794392524 363 | 543000 780.8433107756376 364 | 544500 554.3237250554324 365 | 546000 797.8723404255319 366 | 547500 813.8903960933261 367 | 549000 851.305334846765 368 | 550500 732.421875 369 | 552000 867.5534991324465 370 | 553500 715.9904534606205 371 | 555000 749.6251874062968 372 | 556500 789.0583903208837 373 | 558000 805.5853920515575 374 | 559500 849.3771234428086 375 | 561000 719.0795781399809 376 | 562500 782.0646506777894 377 | 564000 588.4660651235779 378 | 565500 615.0061500615006 379 | 567000 705.2186177715091 380 | 568500 768.0491551459294 381 | 570000 776.7995857068876 382 | 571500 588.0047040376323 383 | 573000 664.3046944198405 384 | 574500 574.052812858783 385 | 576000 514.0507196710075 386 | 577500 805.5853920515575 387 | 579000 791.1392405063291 388 | 580500 512.9958960328318 389 | 582000 783.6990595611285 390 | 583500 566.465256797583 391 | 585000 560.5381165919282 392 | 586500 622.66500622665 393 | 588000 623.7006237006237 394 | 589500 790.3055848261328 395 | 591000 831.0249307479224 396 | 592500 594.7660586835844 397 | 594000 786.5757734661772 398 | 595500 583.8847800700662 399 | 597000 493.42105263157896 400 | 598500 836.5867261572783 401 | 600000 525.3940455341506 402 | 601500 466.56298600311044 403 | 603000 716.3323782234957 404 | 604500 475.43581616481777 405 | 606000 525.3940455341506 406 | 607500 772.3995880535531 407 | 609000 467.1441918405481 408 | 610500 588.4660651235779 409 | 612000 586.1664712778429 410 | 613500 473.7839545167404 411 | 615000 538.0200860832138 412 | 616500 618.0469715698393 413 | 618000 692.8406466512702 414 | 619500 587.0841487279844 415 | 621000 426.25745950554136 416 | 622500 396.5107057890563 417 | 624000 821.917808219178 418 | 625500 404.9676025917927 419 | 627000 493.7458854509546 420 | 628500 796.6011683483803 421 | 630000 505.3908355795148 422 | 631500 586.3956215793589 423 | 633000 756.0483870967741 424 | 634500 556.9996286669142 425 | 636000 327.36796158882584 426 | 637500 306.1224489795918 427 | 639000 556.5862708719851 428 | 640500 527.6116778051354 429 | 642000 831.4855875831486 430 | 643500 519.9306759098787 431 | 645000 818.3306055646481 432 | 646500 513.6986301369863 433 | 648000 743.3102081268582 434 | 649500 780.4370447450573 435 | 651000 505.7316250842886 436 | 652500 724.287783679382 437 | 654000 536.2888809438684 438 | 655500 522.284122562674 439 | 657000 488.28125 440 | 658500 567.7517032551098 441 | 660000 707.2135785007072 442 | 661500 549.2493592090809 443 | 663000 558.4512285927029 444 | 664500 695.4102920723227 445 | 666000 569.2599620493359 446 | 667500 749.6251874062968 447 | 669000 741.839762611276 448 | 670500 577.8120184899846 449 | 672000 780.4370447450573 450 | 673500 543.2814197754436 451 | 675000 731.7073170731708 452 | 676500 414.4791378833932 453 | 678000 552.0794994479205 454 | 679500 748.502994011976 455 | 681000 560.9573672400898 456 | 682500 746.2686567164179 457 | 684000 597.3715651135007 458 | 685500 487.17115946735953 459 | 687000 421.7036828788305 460 | 688500 335.345405767941 461 | 690000 672.0430107526881 462 | 691500 401.06951871657753 463 | 693000 525.9467040673212 464 | 694500 722.1954742416948 465 | 696000 507.2708826513358 466 | 697500 764.9158592554819 467 | 699000 484.3396835647401 468 | 700500 537.6344086021505 469 | 702000 731.7073170731708 470 | 703500 557.6208178438661 471 | 705000 586.8544600938967 472 | 706500 690.9258406264395 473 | 708000 515.6411137848057 474 | 709500 494.8861761794787 475 | 711000 666.3705019991115 476 | 712500 422.892585283338 477 | 714000 722.1954742416948 478 | 715500 594.2947702060222 479 | 717000 556.5862708719851 480 | 718500 432.4012683770539 481 | 720000 577.3672055427252 482 | 721500 516.7068549776094 483 | 723000 351.20580660266916 484 | 724500 729.2173067574137 485 | 726000 741.839762611276 486 | 727500 706.547338671691 487 | 729000 520.2913631633714 488 | 730500 533.997864008544 489 | 732000 742.5742574257425 490 | 733500 549.8533724340176 491 | 735000 674.1573033707865 492 | 736500 498.67021276595744 493 | 738000 794.0709370037057 494 | 739500 517.9558011049724 495 | 741000 771.2082262210797 496 | 742500 737.4631268436578 497 | 744000 506.5856129685917 498 | 745500 397.35099337748346 499 | 747000 483.09178743961354 500 | 748500 579.8221878623889 501 | 750000 548.6466715435259 502 | 751500 725.6894049346879 503 | 753000 522.466039707419 504 | 754500 436.17330619366095 505 | 756000 757.5757575757576 506 | 757500 782.0646506777894 507 | 759000 490.6771344455348 508 | 760500 463.1058968817536 509 | 762000 611.7455138662317 510 | 763500 511.5961800818554 511 | 765000 742.2068283028204 512 | 766500 523.7430167597765 513 | 768000 697.350069735007 514 | 769500 553.9143279172821 515 | 771000 745.8975634012929 516 | 772500 750.3751875937969 517 | 774000 576.7012687427913 518 | 775500 774.7933884297521 519 | 777000 534.75935828877 520 | 778500 366.9275929549902 521 | 780000 721.8479307025987 522 | 781500 682.7492034592626 523 | 783000 501.00200400801606 524 | 784500 608.5192697768763 525 | 786000 710.5637138796778 526 | 787500 374.4383424862706 527 | 789000 509.85723997280763 528 | 790500 668.4491978609626 529 | 792000 386.39876352395675 530 | 793500 403.117441547971 531 | 795000 441.43613890523835 532 | 796500 375.5633450175263 533 | 798000 721.1538461538462 534 | 799500 687.757909215956 535 | 801000 721.8479307025987 536 | 802500 501.84008029441287 537 | 804000 726.3922518159807 538 | 805500 529.1005291005291 539 | 807000 492.93460400920145 540 | 808500 750 541 | 810000 727.802037845706 542 | 811500 505.050505050505 543 | 813000 737.1007371007371 544 | 814500 443.7869822485207 545 | 816000 484.1833440929632 546 | 817500 733.1378299120234 547 | 819000 326.51284283848497 548 | 820500 497.677504976775 549 | 822000 743.6787307882995 550 | 823500 514.5797598627787 551 | 825000 441.9563936358279 552 | 826500 755.6675062972292 553 | 828000 741.839762611276 554 | 829500 468.1647940074906 555 | 831000 587.0841487279844 556 | 832500 485.59404337973456 557 | 834000 720.1152184349496 558 | 835500 444.04973357015984 559 | 837000 377.1687201408097 560 | 838500 583.4305717619603 561 | 840000 525.9467040673212 562 | 841500 416.8982768204558 563 | 843000 542.6917510853835 564 | 844500 470.5144291091593 565 | 846000 370.919881305638 566 | 847500 634.2494714587738 567 | 849000 757.9585649317837 568 | 850500 521.376433785193 569 | 852000 546.448087431694 570 | 853500 678.7330316742082 571 | 855000 786.5757734661772 572 | 856500 518.1347150259068 573 | 858000 793.6507936507936 574 | 859500 443.6557231588287 575 | 861000 455.65006075334145 576 | 862500 358.2517315500358 577 | 864000 498.8360492184902 578 | 865500 346.02076124567475 579 | 867000 432.6507066628209 580 | 868500 503.5246727089627 581 | 870000 394.7368421052632 582 | 871500 389.2060197197717 583 | 873000 499.001996007984 584 | 874500 755.6675062972292 585 | 876000 767.6560900716479 586 | 877500 506.5856129685917 587 | 879000 600.2400960384153 588 | 880500 628.1407035175879 589 | 882000 535.9056806002144 590 | 883500 494.0711462450593 591 | 885000 804.289544235925 592 | 886500 778.4120394395434 593 | 888000 425.531914893617 594 | 889500 664.599025254763 595 | 891000 470.6620646375902 596 | 892500 490.3563255966002 597 | 894000 835.1893095768374 598 | 895500 516.7068549776094 599 | 897000 406.06388738494854 600 | 898500 692.201199815413 601 | 900000 494.3968358602505 602 | 901500 467.5810473815461 603 | 903000 899.8200359928014 604 | 904500 620.0909466721786 605 | 906000 525.5781359495445 606 | 907500 738.5524372230428 607 | 909000 500 608 | 910500 789.0583903208837 609 | 912000 643.5006435006435 610 | 913500 453.5833081342607 611 | 915000 311.7206982543641 612 | 916500 741.4730598121602 613 | 918000 531.7263381779511 614 | 919500 530.9734513274336 615 | 921000 885.478158205431 616 | 922500 512.8205128205128 617 | 924000 800.854244527496 618 | 925500 387.0967741935484 619 | 927000 868.0555555555555 620 | 928500 417.8272980501393 621 | 930000 646.2731581214994 622 | 931500 429.0617848970252 623 | 933000 759.8784194528876 624 | 934500 763.7474541751527 625 | 936000 825.536598789213 626 | 937500 790.3055848261328 627 | 939000 505.050505050505 628 | 940500 770.0205338809035 629 | 942000 508.30227041680786 630 | 943500 759.493670886076 631 | 945000 781.657113079729 632 | 946500 791.1392405063291 633 | 948000 845.5467869222097 634 | 949500 471.6981132075472 635 | 951000 569.692366122294 636 | 952500 520.471894517696 637 | 954000 423.4895539243365 638 | 955500 791.5567282321899 639 | 957000 492.2874958976042 640 | 958500 654.7359231776517 641 | 960000 793.2310946589106 642 | 961500 337.30604902181244 643 | 963000 861.5738081562321 644 | 964500 758.7253414264036 645 | 966000 504.71063257065947 646 | 967500 254.6257002206756 647 | 969000 388.70173620108835 648 | 970500 173.25017325017325 649 | 972000 167.95431642593215 650 | 973500 162.99032924046506 651 | 975000 318.40373593716834 652 | 976500 231.33867982726713 653 | 978000 447.6275738585497 654 | 979500 545.6529647144416 655 | 981000 761.4213197969543 656 | 982500 481.54093097913324 657 | 984000 794.0709370037057 658 | 985500 418.2933630786392 659 | 987000 332.7417923691216 660 | 988500 434.65662126919733 661 | 990000 447.6275738585497 662 | 991500 667.5567423230974 663 | 993000 486.5390853065196 664 | 994500 693.1608133086876 665 | 996000 750 666 | 997500 331.71163202122955 667 | 999000 441.82621502209133 668 | 1000500 757.5757575757576 669 | 1002000 307.8185922429715 670 | 1003500 356.6333808844508 671 | 1005000 378.3102143757881 672 | 1006500 383.7298541826554 673 | 1008000 780.8433107756376 674 | 1009500 722.8915662650602 675 | 1011000 481.6955684007707 676 | 1012500 764.1365257259297 677 | 1014000 761.4213197969543 678 | 1015500 388.60103626943004 679 | 1017000 840.3361344537815 680 | 1018500 453.5833081342607 681 | -------------------------------------------------------------------------------- /bench-data/read.bench: -------------------------------------------------------------------------------- 1 | # Items Reads 2 | 1500 6787.330316742082 3 | 3000 4966.887417218543 4 | 4500 4451.038575667656 5 | 6000 4731.86119873817 6 | 7500 4464.285714285715 7 | 9000 3926.7015706806283 8 | 10500 3348.214285714286 9 | 12000 3740.648379052369 10 | 13500 3605.769230769231 11 | 15000 3865.979381443299 12 | 16500 3947.3684210526317 13 | 18000 3614.4578313253014 14 | 19500 3282.2757111597375 15 | 21000 3676.470588235294 16 | 22500 3333.3333333333335 17 | 24000 4000 18 | 25500 5000 19 | 27000 4643.962848297214 20 | 28500 3731.3432835820895 21 | 30000 3886.0103626943005 22 | 31500 3597.122302158273 23 | 33000 3131.5240083507306 24 | 34500 3667.481662591687 25 | 36000 3311.2582781456954 26 | 37500 3759.3984962406016 27 | 39000 2929.6875 28 | 40500 3024.1935483870966 29 | 42000 3694.5812807881775 30 | 43500 3836.317135549872 31 | 45000 3112.033195020747 32 | 46500 3768.8442211055276 33 | 48000 3472.222222222222 34 | 49500 3978.7798408488065 35 | 51000 3865.979381443299 36 | 52500 3092.783505154639 37 | 54000 3750 38 | 55500 3448.2758620689656 39 | 57000 3740.648379052369 40 | 58500 3722.0843672456576 41 | 60000 3211.9914346895075 42 | 61500 3456.221198156682 43 | 63000 3480.278422273782 44 | 64500 3571.4285714285716 45 | 66000 3529.4117647058824 46 | 67500 3054.989816700611 47 | 69000 3112.033195020747 48 | 70500 3409.090909090909 49 | 72000 3480.278422273782 50 | 73500 3554.5023696682465 51 | 75000 3125 52 | 76500 3488.3720930232557 53 | 78000 3496.5034965034965 54 | 79500 3614.4578313253014 55 | 81000 3432.4942791762014 56 | 82500 3000 57 | 84000 3579.9522673031024 58 | 85500 2824.858757062147 59 | 87000 2941.176470588235 60 | 88500 3042.5963488843813 61 | 90000 2946.9548133595285 62 | 91500 3112.033195020747 63 | 93000 3694.5812807881775 64 | 94500 3432.4942791762014 65 | 96000 3239.7408207343415 66 | 97500 3488.3720930232557 67 | 99000 3937.0078740157483 68 | 100500 2819.5488721804513 69 | 102000 2692.998204667864 70 | 103500 2946.9548133595285 71 | 105000 2840.909090909091 72 | 106500 2788.104089219331 73 | 108000 2835.538752362949 74 | 109500 2819.5488721804513 75 | 111000 2358.490566037736 76 | 112500 2808.9887640449438 77 | 114000 2840.909090909091 78 | 115500 2525.252525252525 79 | 117000 2538.0710659898477 80 | 118500 2604.1666666666665 81 | 120000 2262.443438914027 82 | 121500 2717.391304347826 83 | 123000 2300.6134969325153 84 | 124500 2626.970227670753 85 | 126000 2568.4931506849316 86 | 127500 2752.293577981651 87 | 129000 2521.0084033613443 88 | 130500 2377.179080824089 89 | 132000 2617.801047120419 90 | 133500 2673.7967914438505 91 | 135000 2529.510961214165 92 | 136500 2722.323049001815 93 | 138000 2727.2727272727275 94 | 139500 2577.319587628866 95 | 141000 2351.0971786833857 96 | 142500 2259.0361445783133 97 | 144000 2654.8672566371683 98 | 145500 2196.193265007321 99 | 147000 2504.1736227045076 100 | 148500 2351.0971786833857 101 | 150000 2712.4773960217 102 | 151500 2559.726962457338 103 | 153000 2722.323049001815 104 | 154500 2380.9523809523807 105 | 156000 2757.3529411764707 106 | 157500 2757.3529411764707 107 | 159000 2092.050209205021 108 | 160500 2202.643171806167 109 | 162000 2177.0682148040637 110 | 163500 2115.6558533145276 111 | 165000 2347.417840375587 112 | 166500 2373.4177215189875 113 | 168000 2164.5021645021643 114 | 169500 2183.406113537118 115 | 171000 2521.0084033613443 116 | 172500 2479.3388429752067 117 | 174000 2373.4177215189875 118 | 175500 2590.6735751295337 119 | 177000 2727.2727272727275 120 | 178500 2467.1052631578946 121 | 180000 2245.508982035928 122 | 181500 2235.469448584203 123 | 183000 2504.1736227045076 124 | 184500 2235.469448584203 125 | 186000 2403.846153846154 126 | 187500 1875 127 | 189000 1984.126984126984 128 | 190500 2245.508982035928 129 | 192000 2354.7880690737834 130 | 193500 2407.704654895666 131 | 195000 2358.490566037736 132 | 196500 2133.7126600284496 133 | 198000 2358.490566037736 134 | 199500 2435.064935064935 135 | 201000 2232.1428571428573 136 | 202500 2300.6134969325153 137 | 204000 2304.147465437788 138 | 205500 1635.7688113413303 139 | 207000 2329.192546583851 140 | 208500 2286.5853658536585 141 | 210000 1822.6002430133658 142 | 211500 2788.104089219331 143 | 213000 2664.298401420959 144 | 214500 2581.7555938037867 145 | 216000 2732.24043715847 146 | 217500 2427.1844660194174 147 | 219000 2521.0084033613443 148 | 220500 2697.8417266187053 149 | 222000 2851.71102661597 150 | 223500 2683.3631484794278 151 | 225000 1592.3566878980891 152 | 226500 2757.3529411764707 153 | 228000 2617.801047120419 154 | 229500 2343.75 155 | 231000 2384.737678855326 156 | 232500 2450.9803921568628 157 | 234000 2626.970227670753 158 | 235500 2435.064935064935 159 | 237000 2697.8417266187053 160 | 238500 2762.4309392265195 161 | 240000 2450.9803921568628 162 | 241500 2431.118314424635 163 | 243000 2640.845070422535 164 | 244500 2340.0936037441497 165 | 246000 2542.3728813559323 166 | 247500 2407.704654895666 167 | 249000 2772.6432532347503 168 | 250500 2669.039145907473 169 | 252000 2354.7880690737834 170 | 253500 2431.118314424635 171 | 255000 2923.9766081871344 172 | 256500 2127.659574468085 173 | 258000 2463.054187192118 174 | 259500 2403.846153846154 175 | 261000 2407.704654895666 176 | 262500 2846.2998102466795 177 | 264000 2459.0163934426228 178 | 265500 2868.0688336520075 179 | 267000 2533.7837837837837 180 | 268500 2504.1736227045076 181 | 270000 2525.252525252525 182 | 271500 2613.240418118467 183 | 273000 2167.6300578034684 184 | 274500 2373.4177215189875 185 | 276000 2626.970227670753 186 | 277500 2423.263327948304 187 | 279000 2347.417840375587 188 | 280500 2568.4931506849316 189 | 282000 2170.767004341534 190 | 283500 2222.222222222222 191 | 285000 2678.5714285714284 192 | 286500 2362.2047244094488 193 | 288000 2358.490566037736 194 | 289500 2307.6923076923076 195 | 291000 2483.4437086092717 196 | 292500 2542.3728813559323 197 | 294000 2538.0710659898477 198 | 295500 2475.2475247524753 199 | 297000 2640.845070422535 200 | 298500 2533.7837837837837 201 | 300000 2279.6352583586627 202 | 301500 2008.0321285140562 203 | 303000 2106.741573033708 204 | 304500 2038.0434782608695 205 | 306000 1971.090670170828 206 | 307500 2057.61316872428 207 | 309000 2089.136490250696 208 | 310500 2262.443438914027 209 | 312000 1989.3899204244033 210 | 313500 2354.7880690737834 211 | 315000 2063.273727647868 212 | 316500 2021.5633423180593 213 | 318000 2071.8232044198894 214 | 319500 1976.2845849802372 215 | 321000 2189.78102189781 216 | 322500 2332.814930015552 217 | 324000 2459.0163934426228 218 | 325500 1968.5039370078741 219 | 327000 1928.0205655526993 220 | 328500 1963.3507853403141 221 | 330000 2152.080344332855 222 | 331500 2068.9655172413795 223 | 333000 2248.8755622188905 224 | 334500 2508.361204013378 225 | 336000 1923.076923076923 226 | 337500 2286.5853658536585 227 | 339000 2351.0971786833857 228 | 340500 1937.984496124031 229 | 342000 2533.7837837837837 230 | 343500 2508.361204013378 231 | 345000 2329.192546583851 232 | 346500 2362.2047244094488 233 | 348000 2365.930599369085 234 | 349500 2559.726962457338 235 | 351000 2164.5021645021643 236 | 352500 2446.982055464927 237 | 354000 2265.861027190332 238 | 355500 2318.3925811437402 239 | 357000 2467.1052631578946 240 | 358500 2038.0434782608695 241 | 360000 2051.983584131327 242 | 361500 2109.7046413502107 243 | 363000 2272.7272727272725 244 | 364500 1932.9896907216496 245 | 366000 2115.6558533145276 246 | 367500 2094.972067039106 247 | 369000 2380.9523809523807 248 | 370500 2212.3893805309735 249 | 372000 1400.5602240896358 250 | 373500 2259.0361445783133 251 | 375000 2183.406113537118 252 | 376500 2362.2047244094488 253 | 378000 2177.0682148040637 254 | 379500 2225.519287833828 255 | 381000 2340.0936037441497 256 | 382500 2248.8755622188905 257 | 384000 1923.076923076923 258 | 385500 2265.861027190332 259 | 387000 2124.6458923512746 260 | 388500 2115.6558533145276 261 | 390000 2325.5813953488373 262 | 391500 2262.443438914027 263 | 393000 2293.577981651376 264 | 394500 1937.984496124031 265 | 396000 2463.054187192118 266 | 397500 2403.846153846154 267 | 399000 2018.8425302826379 268 | 400500 2124.6458923512746 269 | 402000 2516.778523489933 270 | 403500 2148.997134670487 271 | 405000 2124.6458923512746 272 | 406500 2209.1310751104565 273 | 408000 2613.240418118467 274 | 409500 2054.794520547945 275 | 411000 2380.9523809523807 276 | 412500 2403.846153846154 277 | 414000 2369.6682464454975 278 | 415500 2459.0163934426228 279 | 417000 2192.9824561403507 280 | 418500 2479.3388429752067 281 | 420000 2362.2047244094488 282 | 421500 1968.5039370078741 283 | 423000 2189.78102189781 284 | 424500 2373.4177215189875 285 | 426000 2521.0084033613443 286 | 427500 2177.0682148040637 287 | 429000 2559.726962457338 288 | 430500 2435.064935064935 289 | 432000 2688.1720430107525 290 | 433500 2546.6893039049237 291 | 435000 2446.982055464927 292 | 436500 2654.8672566371683 293 | 438000 2551.0204081632655 294 | 439500 2471.169686985173 295 | 441000 2622.377622377622 296 | 442500 2155.1724137931033 297 | 444000 2613.240418118467 298 | 445500 2559.726962457338 299 | 447000 2057.61316872428 300 | 448500 2487.5621890547263 301 | 450000 2546.6893039049237 302 | 451500 2415.458937198068 303 | 453000 2551.0204081632655 304 | 454500 2400 305 | 456000 2664.298401420959 306 | 457500 2640.845070422535 307 | 459000 2232.1428571428573 308 | 460500 2086.230876216968 309 | 462000 2199.4134897360705 310 | 463500 2307.6923076923076 311 | 465000 2568.4931506849316 312 | 466500 2491.6943521594685 313 | 468000 2435.064935064935 314 | 469500 2542.3728813559323 315 | 471000 1861.0421836228288 316 | 472500 2516.778523489933 317 | 474000 2435.064935064935 318 | 475500 2650.1766784452298 319 | 477000 2538.0710659898477 320 | 478500 2516.778523489933 321 | 480000 2380.9523809523807 322 | 481500 1968.5039370078741 323 | 483000 2109.7046413502107 324 | 484500 2388.5350318471337 325 | 486000 2521.0084033613443 326 | 487500 2595.1557093425604 327 | 489000 2568.4931506849316 328 | 490500 2604.1666666666665 329 | 492000 2640.845070422535 330 | 493500 2051.983584131327 331 | 495000 2343.75 332 | 496500 2475.2475247524753 333 | 498000 2415.458937198068 334 | 499500 1879.6992481203008 335 | 501000 2336.448598130841 336 | 502500 2475.2475247524753 337 | 504000 1992.03187250996 338 | 505500 2564.102564102564 339 | 507000 2388.5350318471337 340 | 508500 2380.9523809523807 341 | 510000 2500 342 | 511500 2238.805970149254 343 | 513000 2060.4395604395604 344 | 514500 2192.9824561403507 345 | 516000 2336.448598130841 346 | 517500 2354.7880690737834 347 | 519000 2604.1666666666665 348 | 520500 2336.448598130841 349 | 522000 2529.510961214165 350 | 523500 2384.737678855326 351 | 525000 2516.778523489933 352 | 526500 2248.8755622188905 353 | 528000 2373.4177215189875 354 | 529500 2463.054187192118 355 | 531000 2265.861027190332 356 | 532500 2057.61316872428 357 | 534000 2262.443438914027 358 | 535500 2332.814930015552 359 | 537000 2300.6134969325153 360 | 538500 2183.406113537118 361 | 540000 2304.147465437788 362 | 541500 2384.737678855326 363 | 543000 2400 364 | 544500 2215.6573116691284 365 | 546000 1216.54501216545 366 | 547500 1838.235294117647 367 | 549000 1851.851851851852 368 | 550500 1716.2471395881007 369 | 552000 1867.9950186799501 370 | 553500 2276.176024279211 371 | 555000 1981.5059445178335 372 | 556500 1918.158567774936 373 | 558000 1835.985312117503 374 | 559500 1840.4907975460123 375 | 561000 2092.050209205021 376 | 562500 1889.168765743073 377 | 564000 1955.671447196871 378 | 565500 2235.469448584203 379 | 567000 1932.9896907216496 380 | 568500 1879.6992481203008 381 | 570000 2290.0763358778627 382 | 571500 2089.136490250696 383 | 573000 1940.4915912031047 384 | 574500 1766.7844522968198 385 | 576000 2145.922746781116 386 | 577500 2512.5628140703516 387 | 579000 2369.6682464454975 388 | 580500 1746.2165308498254 389 | 582000 2158.273381294964 390 | 583500 1805.0541516245487 391 | 585000 2118.64406779661 392 | 586500 2516.778523489933 393 | 588000 2276.176024279211 394 | 589500 1898.73417721519 395 | 591000 1896.3337547408344 396 | 592500 1800.7202881152461 397 | 594000 1965.923984272608 398 | 595500 2192.9824561403507 399 | 597000 1965.923984272608 400 | 598500 2232.1428571428573 401 | 600000 2097.902097902098 402 | 601500 1094.890510948905 403 | 603000 2300.6134969325153 404 | 604500 2083.3333333333335 405 | 606000 1290.8777969018934 406 | 607500 2483.4437086092717 407 | 609000 2304.147465437788 408 | 610500 2005.3475935828876 409 | 612000 1152.9592621060722 410 | 613500 1981.5059445178335 411 | 615000 2248.8755622188905 412 | 616500 2235.469448584203 413 | 618000 2411.575562700965 414 | 619500 2340.0936037441497 415 | 621000 2343.75 416 | 622500 1655.6291390728477 417 | 624000 1893.939393939394 418 | 625500 2283.10502283105 419 | 627000 2407.704654895666 420 | 628500 2373.4177215189875 421 | 630000 1746.2165308498254 422 | 631500 1706.4846416382252 423 | 633000 2380.9523809523807 424 | 634500 2040.8163265306123 425 | 636000 1820.388349514563 426 | 637500 2071.8232044198894 427 | 639000 1461.9883040935672 428 | 640500 981.0333551340746 429 | 642000 2021.5633423180593 430 | 643500 1527.4949083503054 431 | 645000 2018.8425302826379 432 | 646500 1928.0205655526993 433 | 648000 1126.9722013523667 434 | 649500 1693.002257336343 435 | 651000 1698.7542468856172 436 | 652500 2142.8571428571427 437 | 654000 1920.6145966709346 438 | 655500 2415.458937198068 439 | 657000 2180.232558139535 440 | 658500 1540.041067761807 441 | 660000 2136.7521367521367 442 | 661500 1835.985312117503 443 | 663000 1965.923984272608 444 | 664500 2269.2889561270804 445 | 666000 1997.3368841544607 446 | 667500 1128.6681715575621 447 | 669000 2109.7046413502107 448 | 670500 1796.4071856287426 449 | 672000 2209.1310751104565 450 | 673500 1976.2845849802372 451 | 675000 2212.3893805309735 452 | 676500 2205.8823529411766 453 | 678000 2121.6407355021215 454 | 679500 1822.6002430133658 455 | 681000 1992.03187250996 456 | 682500 2396.1661341853037 457 | 684000 1316.9446883230905 458 | 685500 1512.0967741935483 459 | 687000 1560.8740894901146 460 | 688500 1789.9761336515512 461 | 690000 2040.8163265306123 462 | 691500 1920.6145966709346 463 | 693000 1515.1515151515152 464 | 694500 1845.018450184502 465 | 696000 2016.1290322580646 466 | 697500 1748.2517482517483 467 | 699000 1849.5684340320593 468 | 700500 1835.985312117503 469 | 702000 2343.75 470 | 703500 1997.3368841544607 471 | 705000 910.7468123861566 472 | 706500 2145.922746781116 473 | 708000 1824.8175182481752 474 | 709500 929.368029739777 475 | 711000 2021.5633423180593 476 | 712500 1960.7843137254902 477 | 714000 791.5567282321899 478 | 715500 2245.508982035928 479 | 717000 1963.3507853403141 480 | 718500 1965.923984272608 481 | 720000 1712.3287671232877 482 | 721500 2145.922746781116 483 | 723000 2057.61316872428 484 | 724500 870.5745792222867 485 | 726000 993.3774834437086 486 | 727500 2225.519287833828 487 | 729000 1908.3969465648854 488 | 730500 1015.572105619499 489 | 732000 1728.110599078341 490 | 733500 1908.3969465648854 491 | 735000 2066.115702479339 492 | 736500 1968.5039370078741 493 | 738000 1818.1818181818182 494 | 739500 2152.080344332855 495 | 741000 916.8704156479217 496 | 742500 2152.080344332855 497 | 744000 1978.891820580475 498 | 745500 1805.0541516245487 499 | 747000 1088.5341074020319 500 | 748500 2276.176024279211 501 | 750000 1884.4221105527638 502 | 751500 2170.767004341534 503 | 753000 2074.688796680498 504 | 754500 1564.1293013555787 505 | 756000 2189.78102189781 506 | 757500 1689.1891891891892 507 | 759000 1981.5059445178335 508 | 760500 966.4948453608248 509 | 762000 2013.4228187919464 510 | 763500 2538.0710659898477 511 | 765000 2016.1290322580646 512 | 766500 1661.1295681063123 513 | 768000 2127.659574468085 514 | 769500 2232.1428571428573 515 | 771000 1005.3619302949062 516 | 772500 2189.78102189781 517 | 774000 1815.9806295399517 518 | 775500 1882.0577164366373 519 | 777000 1920.6145966709346 520 | 778500 1504.5135406218656 521 | 780000 873.1082654249127 522 | 781500 1986.7549668874171 523 | 783000 1452.081316553727 524 | 784500 988.793671720501 525 | 786000 1903.5532994923858 526 | 787500 1798.5611510791366 527 | 789000 1664.8168701442842 528 | 790500 2255.6390977443607 529 | 792000 1794.2583732057417 530 | 793500 1626.8980477223427 531 | 795000 1399.2537313432836 532 | 796500 1807.2289156626507 533 | 798000 2180.232558139535 534 | 799500 823.7232289950576 535 | 801000 1989.3899204244033 536 | 802500 1820.388349514563 537 | 804000 1482.2134387351778 538 | 805500 1679.7312430011198 539 | 807000 1789.9761336515512 540 | 808500 1994.6808510638298 541 | 810000 1809.4089264173704 542 | 811500 1831.5018315018315 543 | 813000 2205.8823529411766 544 | 814500 1973.6842105263158 545 | 816000 1777.2511848341233 546 | 817500 2145.922746781116 547 | 819000 2063.273727647868 548 | 820500 1835.985312117503 549 | 822000 817.8844056706652 550 | 823500 1905.972045743329 551 | 825000 1840.4907975460123 552 | 826500 805.1529790660226 553 | 828000 1564.1293013555787 554 | 829500 1770.956316410862 555 | 831000 1898.73417721519 556 | 832500 1698.7542468856172 557 | 834000 1945.5252918287938 558 | 835500 2471.169686985173 559 | 837000 1943.0051813471503 560 | 838500 914.0767824497258 561 | 840000 1923.076923076923 562 | 841500 1820.388349514563 563 | 843000 2024.2914979757086 564 | 844500 1923.076923076923 565 | 846000 2145.922746781116 566 | 847500 854.2141230068337 567 | 849000 2038.0434782608695 568 | 850500 1898.73417721519 569 | 852000 1657.4585635359117 570 | 853500 991.4077990746861 571 | 855000 2027.027027027027 572 | 856500 1845.018450184502 573 | 858000 1968.5039370078741 574 | 859500 1940.4915912031047 575 | 861000 1435.4066985645934 576 | 862500 1870.3241895261845 577 | 864000 2161.383285302594 578 | 865500 2029.7699594046007 579 | 867000 2127.659574468085 580 | 868500 2538.0710659898477 581 | 870000 953.5918626827718 582 | 871500 807.3196986006459 583 | 873000 1792.1146953405018 584 | 874500 837.5209380234506 585 | 876000 2089.136490250696 586 | 877500 1762.6321974148061 587 | 879000 828.2716731087796 588 | 880500 2283.10502283105 589 | 882000 2097.902097902098 590 | 883500 1953.125 591 | 885000 803.8585209003215 592 | 886500 2167.6300578034684 593 | 888000 2002.6702269692923 594 | 889500 857.1428571428571 595 | 891000 2136.7521367521367 596 | 892500 2100.840336134454 597 | 894000 1607.717041800643 598 | 895500 1893.939393939394 599 | 897000 1872.6591760299625 600 | 898500 2161.383285302594 601 | 900000 2060.4395604395604 602 | 901500 2384.737678855326 603 | 903000 1948.051948051948 604 | 904500 2066.115702479339 605 | 906000 1943.0051813471503 606 | 907500 2000 607 | 909000 1958.2245430809398 608 | 910500 866.5511265164645 609 | 912000 2170.767004341534 610 | 913500 1811.5942028985507 611 | 915000 1802.8846153846155 612 | 916500 954.1984732824427 613 | 918000 2049.1803278688526 614 | 919500 1845.018450184502 615 | 921000 1955.671447196871 616 | 922500 1831.5018315018315 617 | 924000 896.0573476702509 618 | 925500 1827.0401948842875 619 | 927000 858.1235697940504 620 | 928500 875.6567425569177 621 | 930000 2094.972067039106 622 | 931500 1989.3899204244033 623 | 933000 849.3771234428086 624 | 934500 2650.1766784452298 625 | 936000 754.906894816306 626 | 937500 1994.6808510638298 627 | 939000 1750.291715285881 628 | 940500 1893.939393939394 629 | 942000 1851.851851851852 630 | 943500 810.8108108108108 631 | 945000 2177.0682148040637 632 | 946500 830.1051466519092 633 | 948000 1935.483870967742 634 | 949500 1971.090670170828 635 | 951000 2242.152466367713 636 | 952500 2063.273727647868 637 | 954000 1940.4915912031047 638 | 955500 2767.5276752767527 639 | 957000 1766.7844522968198 640 | 958500 876.6803039158386 641 | 960000 2103.786816269285 642 | 961500 1811.5942028985507 643 | 963000 751.1266900350525 644 | 964500 1920.6145966709346 645 | 966000 1820.388349514563 646 | 967500 862.0689655172414 647 | 969000 931.6770186335403 648 | 970500 1971.090670170828 649 | 972000 1910.8280254777071 650 | 973500 1920.6145966709346 651 | 975000 1968.5039370078741 652 | 976500 1960.7843137254902 653 | 978000 1702.61066969353 654 | 979500 828.2716731087796 655 | 981000 2000 656 | 982500 1856.4356435643565 657 | 984000 2803.7383177570096 658 | 985500 1918.158567774936 659 | 987000 1958.2245430809398 660 | 988500 1928.0205655526993 661 | 990000 1847.2906403940888 662 | 991500 2180.232558139535 663 | 993000 2051.983584131327 664 | 994500 804.289544235925 665 | 996000 2192.9824561403507 666 | 997500 1879.6992481203008 667 | 999000 1937.984496124031 668 | 1000500 2245.508982035928 669 | 1002000 2063.273727647868 670 | 1003500 1932.9896907216496 671 | 1005000 1840.4907975460123 672 | 1006500 1955.671447196871 673 | 1008000 2170.767004341534 674 | 1009500 1800.7202881152461 675 | 1011000 2043.5967302452316 676 | 1012500 916.8704156479217 677 | 1014000 2083.3333333333335 678 | 1015500 2024.2914979757086 679 | 1017000 2077.562326869806 680 | 1018500 2000 681 | --------------------------------------------------------------------------------