@@ -9,20 +9,23 @@ import (
9
9
DefaultConcurrencyConfig sets the default maximum number of concurrent [HydrateFunc] calls.
10
10
11
11
Limit total concurrent hydrate calls:
12
- DefaultConcurrency: &plugin.DefaultConcurrencyConfig{
13
- TotalMaxConcurrency: 500,
14
- }
12
+
13
+ DefaultConcurrency: &plugin.DefaultConcurrencyConfig{
14
+ TotalMaxConcurrency: 500,
15
+ }
15
16
16
17
Limit concurrent hydrate calls to any single HydrateFunc which does not have a [HydrateConfig]:
17
- DefaultConcurrency: &plugin.DefaultConcurrencyConfig{
18
- DefaultMaxConcurrency: 100,
19
- }
20
-
18
+
19
+ DefaultConcurrency: &plugin.DefaultConcurrencyConfig{
20
+ DefaultMaxConcurrency: 100,
21
+ }
22
+
21
23
Do both:
22
- DefaultConcurrency: &plugin.DefaultConcurrencyConfig{
23
- TotalMaxConcurrency: 500,
24
- DefaultMaxConcurrency: 200,
25
- }
24
+
25
+ DefaultConcurrency: &plugin.DefaultConcurrencyConfig{
26
+ TotalMaxConcurrency: 500,
27
+ DefaultMaxConcurrency: 200,
28
+ }
26
29
27
30
Plugin examples:
28
31
- [hackernews]
@@ -31,14 +34,14 @@ Plugin examples:
31
34
*/
32
35
type DefaultConcurrencyConfig struct {
33
36
// sets how many HydrateFunc calls can run concurrently in total
34
- TotalMaxConcurrency int
37
+ TotalMaxConcurrency int
35
38
// sets the default for how many calls to each HydrateFunc can run concurrently
36
39
DefaultMaxConcurrency int
37
40
}
38
41
39
42
// concurrencyManager struct ensures that hydrate functions stay within concurrency limits
40
43
type concurrencyManager struct {
41
- mut sync.Mutex
44
+ mut sync.RWMutex
42
45
// the maximum number of all hydrate calls which can run concurrently
43
46
maxConcurrency int
44
47
// the maximum concurrency for a single hydrate call
@@ -80,25 +83,25 @@ func newConcurrencyManager(t *Table) *concurrencyManager {
80
83
// StartIfAllowed checks whether the named hydrate call is permitted to start
81
84
// based on the number of running instances of that call, and the total calls in progress
82
85
func (c * concurrencyManager ) StartIfAllowed (name string , maxCallConcurrency int ) (res bool ) {
83
- c .mut .Lock ()
84
- defer c .mut .Unlock ()
86
+ // acquire a Read lock
87
+ c .mut .RLock ()
88
+ // how many concurrent executions of this function are in progress right now?
89
+ currentExecutions := c .callMap [name ]
90
+ // ensure we unlock
91
+ c .mut .RUnlock ()
85
92
86
- // is the total call limit exceeded?
87
- if c .maxConcurrency > 0 && c .callsInProgress == c .maxConcurrency {
93
+ if ! c .canStart (currentExecutions , maxCallConcurrency ) {
88
94
return false
89
95
}
90
96
91
- // if there is no config or empty config, the maxCallConcurrency will be 0
92
- // - use defaultMaxConcurrencyPerCall set on the concurrencyManager
93
- if maxCallConcurrency == 0 {
94
- maxCallConcurrency = c .defaultMaxConcurrencyPerCall
95
- }
96
-
97
- // how many concurrent executions of this function are in progress right now?
98
- currentExecutions := c .callMap [name ]
97
+ // upgrade the mutex to a Write lock
98
+ c .mut .Lock ()
99
+ // ensure we unlock
100
+ defer c .mut .Unlock ()
99
101
100
- // if we at the call limit return
101
- if maxCallConcurrency > 0 && currentExecutions == maxCallConcurrency {
102
+ // check again in case another thread grabbed the Write lock before us
103
+ currentExecutions = c .callMap [name ]
104
+ if ! c .canStart (currentExecutions , maxCallConcurrency ) {
102
105
return false
103
106
}
104
107
@@ -113,6 +116,26 @@ func (c *concurrencyManager) StartIfAllowed(name string, maxCallConcurrency int)
113
116
if c .callsInProgress > c .maxCallsInProgress {
114
117
c .maxCallsInProgress = c .callsInProgress
115
118
}
119
+
120
+ return true
121
+ }
122
+
123
+ func (c * concurrencyManager ) canStart (currentExecutions int , maxCallConcurrency int ) bool {
124
+ // is the total call limit exceeded?
125
+ if c .maxConcurrency > 0 && c .callsInProgress == c .maxConcurrency {
126
+ return false
127
+ }
128
+
129
+ // if there is no config or empty config, the maxCallConcurrency will be 0
130
+ // - use defaultMaxConcurrencyPerCall set on the concurrencyManager
131
+ if maxCallConcurrency == 0 {
132
+ maxCallConcurrency = c .defaultMaxConcurrencyPerCall
133
+ }
134
+
135
+ // if we at the call limit return
136
+ if maxCallConcurrency > 0 && currentExecutions == maxCallConcurrency {
137
+ return false
138
+ }
116
139
return true
117
140
}
118
141
@@ -123,10 +146,11 @@ func (c *concurrencyManager) Finished(name string) {
123
146
log .Printf ("[WARN] %v" , r )
124
147
}
125
148
}()
149
+ // acquire a Write lock
126
150
c .mut .Lock ()
127
- defer c .mut .Unlock ()
128
151
c .callMap [name ]--
129
152
c .callsInProgress --
153
+ c .mut .Unlock ()
130
154
}
131
155
132
156
// Close executes when the query is complete and dumps out the concurrency stats
0 commit comments