1 |
|
2 | _ = require('lodash')
|
3 |
|
4 | defaults = {
|
5 | client: null
|
6 | prefix: 'redlocks:'
|
7 | timeout: 60
|
8 | retries: 2
|
9 | }
|
10 |
|
11 | RELEASE_LUA = """
|
12 | if redis.call("get",KEYS[1]) == ARGV[1] then
|
13 | return redis.call("del",KEYS[1])
|
14 | else
|
15 | return 0
|
16 | end
|
17 | """
|
18 |
|
19 | KEEPALIVE_LUA = """
|
20 | if redis.call("get",KEYS[1]) == ARGV[1] then
|
21 | return redis.call("expire",ARGV[2])
|
22 | else
|
23 | return 0
|
24 | end
|
25 | """
|
26 |
|
27 | redlock = (opts) ->
|
28 |
|
29 | opts = _.merge(defaults, opts)
|
30 |
|
31 | unless opts.client?
|
32 | throw new Error("You must provide a Redis client to redlock")
|
33 |
|
34 | return (name, timeout, callback) ->
|
35 | unless callback?
|
36 | callback = timeout
|
37 | timeout = opts.timeout
|
38 | unless callback?
|
39 | callback = _.noop
|
40 | }
|
41 |
|
42 | lockId = opts.prefix + name
|
43 | token = "#{Date.now()}-#{(Math.random() + 1).toString(36).substr(2, 9)}"
|
44 |
|
45 | lock = {
|
46 | release: (callback) ->
|
47 | callback ?= _.noop
|
48 | opts.client.eval([RELEASE_LUA, 1, lockId, token], callback)
|
49 | }
|
50 | keepAlive: (callback) ->
|
51 | callback ?= _.noop
|
52 | opts.client.eval([KEEPALIVE_LUA, 1, lockId, token, timeout], callback)
|
53 | }
|
54 | }
|
55 |
|
56 | opts.client.set([lockId, token, 'NX', 'EX', timeout], (err, value) ->
|
57 | if err
|
58 | return callback(err)
|
59 |
|
60 | if value
|
61 | callback(null, lock)
|
62 | else
|
63 | callback()
|
64 | )
|
65 |
|
66 | module.exports = redlock
|