-
Notifications
You must be signed in to change notification settings - Fork 453
Expand file tree
/
Copy pathLocalDatastore.coffee
More file actions
140 lines (114 loc) · 4.6 KB
/
LocalDatastore.coffee
File metadata and controls
140 lines (114 loc) · 4.6 KB
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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
parser = require "./parser"
BottleneckError = require "./BottleneckError"
class LocalDatastore
constructor: (@instance, @storeOptions, storeInstanceOptions) ->
@clientId = @instance._randomIndex()
parser.load storeInstanceOptions, storeInstanceOptions, @
@_nextRequest = @_lastReservoirRefresh = @_lastReservoirIncrease = Date.now()
@_running = 0
@_done = 0
@_unblockTime = 0
@ready = @Promise.resolve()
@clients = {}
@_startHeartbeat()
_startHeartbeat: ->
if !@heartbeat? and ((
@storeOptions.reservoirRefreshInterval? and @storeOptions.reservoirRefreshAmount?
) or (
@storeOptions.reservoirIncreaseInterval? and @storeOptions.reservoirIncreaseAmount?
))
(@heartbeat = setInterval =>
now = Date.now()
if @storeOptions.reservoirRefreshInterval? and now >= @_lastReservoirRefresh + @storeOptions.reservoirRefreshInterval
@_lastReservoirRefresh = now
@storeOptions.reservoir = @storeOptions.reservoirRefreshAmount
@instance._drainAll @computeCapacity()
if @storeOptions.reservoirIncreaseInterval? and now >= @_lastReservoirIncrease + @storeOptions.reservoirIncreaseInterval
{ reservoirIncreaseAmount: amount, reservoirIncreaseMaximum: maximum, reservoir } = @storeOptions
@_lastReservoirIncrease = now
incr = if maximum? then Math.min amount, maximum - reservoir else amount
if incr > 0
@storeOptions.reservoir += incr
@instance._drainAll @computeCapacity()
, @heartbeatInterval).unref?()
else clearInterval @heartbeat
__publish__: (message) ->
await @yieldLoop()
@instance.Events.trigger "message", message.toString()
__disconnect__: (flush) ->
await @yieldLoop()
clearInterval @heartbeat
@Promise.resolve()
yieldLoop: (t=0) -> new @Promise (resolve, reject) -> setTimeout resolve, t
computePenalty: -> @storeOptions.penalty ? ((15 * @storeOptions.minTime) or 5000)
__updateSettings__: (options) ->
await @yieldLoop()
parser.overwrite options, options, @storeOptions
@_startHeartbeat()
@instance._drainAll @computeCapacity()
true
__running__: ->
await @yieldLoop()
@_running
__queued__: ->
await @yieldLoop()
@instance.queued()
__done__: ->
await @yieldLoop()
@_done
__groupCheck__: (time) ->
await @yieldLoop()
(@_nextRequest + @timeout) < time
computeCapacity: ->
{ maxConcurrent, reservoir } = @storeOptions
if maxConcurrent? and reservoir? then Math.min((maxConcurrent - @_running), reservoir)
else if maxConcurrent? then maxConcurrent - @_running
else if reservoir? then reservoir
else null
conditionsCheck: (weight) ->
capacity = @computeCapacity()
not capacity? or weight <= capacity
__incrementReservoir__: (incr) ->
await @yieldLoop()
reservoir = @storeOptions.reservoir += incr
@instance._drainAll @computeCapacity()
reservoir
__currentReservoir__: ->
await @yieldLoop()
@storeOptions.reservoir
isBlocked: (now) -> @_unblockTime >= now
check: (weight, now) -> @conditionsCheck(weight) and (@_nextRequest - now) <= 0
__check__: (weight) ->
await @yieldLoop()
now = Date.now()
@check weight, now
__register__: (index, weight, expiration) ->
await @yieldLoop()
now = Date.now()
if @conditionsCheck weight
@_running += weight
if @storeOptions.reservoir? then @storeOptions.reservoir -= weight
wait = Math.max @_nextRequest - now, 0
@_nextRequest = now + wait + @storeOptions.minTime
{ success: true, wait, reservoir: @storeOptions.reservoir }
else { success: false }
strategyIsBlock: -> @storeOptions.strategy == 3
__submit__: (queueLength, weight) ->
await @yieldLoop()
if @storeOptions.maxConcurrent? and weight > @storeOptions.maxConcurrent
throw new BottleneckError("Impossible to add a job having a weight of #{weight} to a limiter having a maxConcurrent setting of #{@storeOptions.maxConcurrent}")
now = Date.now()
reachedHWM = @storeOptions.highWater? and queueLength == @storeOptions.highWater and not @check(weight, now)
blocked = @strategyIsBlock() and (reachedHWM or @isBlocked now)
if blocked
@_unblockTime = now + @computePenalty()
@_nextRequest = @_unblockTime + @storeOptions.minTime
@instance._dropAllQueued()
{ reachedHWM, blocked, strategy: @storeOptions.strategy }
__free__: (index, weight) ->
await @yieldLoop()
@_running -= weight
@_done += weight
@instance._drainAll @computeCapacity()
{ running: @_running }
module.exports = LocalDatastore