-
Notifications
You must be signed in to change notification settings - Fork 2
/
index.js
110 lines (94 loc) · 3.65 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
var debug = require('debug')('block-sequence:mongo')
var _ = require('lodash')
var async = require('async')
var bigInt = require('big-integer')
module.exports = function init(config, cb) {
if (Number.MAX_SAFE_INTEGER === undefined) Number.MAX_SAFE_INTEGER = 9007199254740991
if (arguments.length === 1) return init({}, arguments[0])
if (!config.url) return cb(new Error('url is required'))
var MongoClient = config.client || require('mongodb').MongoClient
var client
var collection
function ensure(options, cb) {
if (options.name === null || options.name === undefined) return cb(new Error('name is required'))
var name = options.name.toLowerCase()
var value = options.value || 0
var metadata = options.metadata || {}
collection.findOneAndUpdate(
{ name: name },
{ $setOnInsert: { name: name, value: value, metadata: metadata } },
{ upsert: true, returnOriginal: false },
function(err, result) {
// Turns out there's no such thing as an atomic upsert in mongo
if (err && err.code === 11000) return ensure(options, cb)
if (err) return cb(err)
deserialize(result.value, function(err, sequence) {
if (err) return cb(err)
cb(null, _.chain({})
.defaultsDeep(sequence)
.omit(['_id'])
.value()
)
})
}
)
}
function allocate(options, cb) {
var size = options.size || 1
ensure(options, function(err, sequence) {
if (err) return cb(err)
collection.findOneAndUpdate(
{ name: sequence.name },
{ $inc: { value: size } },
{ upsert: true, returnOriginal: false },
function(err, result) {
if (err) return cb(err)
deserialize(result.value, function(err, sequence) {
if (err) return cb(err)
cb(null, _.chain({ next: sequence.value - size + 1, remaining: size })
.defaultsDeep(sequence)
.omit(['_id', 'value'])
.value()
)
})
}
)
})
}
function remove(options, cb) {
debug('Removing %s', options.name)
if (options.name === null || options.name === undefined) return cb(new Error('name is required'))
collection.findOneAndDelete({ name: options.name.toLowerCase() }, cb)
}
function deserialize(sequence, cb) {
var value = bigInt(sequence.value)
if (value.greater(Number.MAX_SAFE_INTEGER)) return cb(new Error('Sequence value exceeds Number.MAX_SAFE_INTEGER'))
cb(null, sequence)
}
function close(cb) {
client.close(cb)
}
function ensureCollection(cb) {
debug('Ensuring gs_block_sequence collection')
collection = client.db().collection('gs_block_sequence')
collection.createIndex({ name: 1 }, { unique: true, w: 1 }, cb)
}
function connect(cb) {
MongoClient.connect(config.url, config.options || {}, function(err, _client) {
if (err) return cb(err)
client = _client
return cb()
})
}
async.series([
connect,
ensureCollection
], function(err) {
cb(err, {
remove: remove,
allocate: allocate,
ensure: ensure,
close: close
})
})
}