Skip to content

Commit 9172384

Browse files
authored
Merge pull request #474 from SmartBFT-Go/request_pool
Add configuration to control request pool parameters
2 parents fe83545 + cd1c0b5 commit 9172384

File tree

6 files changed

+75
-1
lines changed

6 files changed

+75
-1
lines changed

internal/bft/requestpool.go

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,13 @@ import (
2020

2121
const (
2222
defaultRequestTimeout = 10 * time.Second // for unit tests only
23+
defaultMaxBytes = 100 * 1024 // default max request size would be of size 100Kb
2324
)
2425

2526
var (
2627
ErrReqAlreadyExists = fmt.Errorf("request already exists")
28+
ErrRequestTooBig = fmt.Errorf("submitted request is too big")
29+
ErrSubmitTimeout = fmt.Errorf("timeout submitting to request pool")
2730
)
2831

2932
//go:generate mockery -dir . -name RequestTimeoutHandler -case underscore -output ./mocks/
@@ -73,6 +76,8 @@ type PoolOptions struct {
7376
ForwardTimeout time.Duration
7477
ComplainTimeout time.Duration
7578
AutoRemoveTimeout time.Duration
79+
RequestMaxBytes uint64
80+
SubmitTimeout time.Duration
7681
}
7782

7883
// NewPool constructs new requests pool
@@ -86,6 +91,12 @@ func NewPool(log api.Logger, inspector api.RequestInspector, th RequestTimeoutHa
8691
if options.AutoRemoveTimeout == 0 {
8792
options.AutoRemoveTimeout = defaultRequestTimeout
8893
}
94+
if options.RequestMaxBytes == 0 {
95+
options.RequestMaxBytes = defaultMaxBytes
96+
}
97+
if options.SubmitTimeout == 0 {
98+
options.SubmitTimeout = defaultRequestTimeout
99+
}
89100

90101
return &Pool{
91102
timeoutHandler: th,
@@ -142,6 +153,10 @@ func (rp *Pool) Submit(request []byte) error {
142153
return errors.Errorf("pool closed, request rejected: %s", reqInfo)
143154
}
144155

156+
if uint64(len(request)) > rp.options.RequestMaxBytes {
157+
return ErrRequestTooBig
158+
}
159+
145160
rp.lock.RLock()
146161
_, alreadyExists := rp.existMap[reqInfo]
147162
rp.lock.RUnlock()
@@ -151,8 +166,10 @@ func (rp *Pool) Submit(request []byte) error {
151166
return ErrReqAlreadyExists
152167
}
153168

169+
ctx, cancel := context.WithTimeout(context.Background(), rp.options.SubmitTimeout)
170+
defer cancel()
154171
// do not wait for a semaphore with a lock, as it will prevent draining the pool.
155-
if err := rp.semaphore.Acquire(context.Background(), 1); err != nil {
172+
if err := rp.semaphore.Acquire(ctx, 1); err != nil {
156173
return errors.Wrapf(err, "acquiring semaphore for request: %s", reqInfo)
157174
}
158175

internal/bft/requestpool_test.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ package bft_test
77

88
import (
99
"bytes"
10+
"crypto/rand"
1011
"encoding/binary"
1112
"fmt"
1213
"sync"
@@ -291,6 +292,33 @@ func TestReqPoolTimeout(t *testing.T) {
291292
insp := &testRequestInspector{}
292293
submittedChan := make(chan struct{}, 1)
293294

295+
t.Run("request size too big", func(t *testing.T) {
296+
timeoutHandler := &mocks.RequestTimeoutHandler{}
297+
298+
timeoutHandler.On("OnRequestTimeout", byteReq1, insp.RequestID(byteReq1)).Return()
299+
timeoutHandler.On("OnLeaderFwdRequestTimeout", byteReq1, insp.RequestID(byteReq1)).Return()
300+
timeoutHandler.On("OnAutoRemoveTimeout", insp.RequestID(byteReq1)).Return()
301+
302+
pool := bft.NewPool(log, insp, timeoutHandler,
303+
bft.PoolOptions{
304+
QueueSize: 3,
305+
ForwardTimeout: 10 * time.Millisecond,
306+
ComplainTimeout: time.Hour,
307+
AutoRemoveTimeout: time.Hour,
308+
RequestMaxBytes: 1024,
309+
},
310+
nil,
311+
)
312+
defer pool.Close()
313+
314+
payload := make([]byte, 2048)
315+
rand.Read(payload)
316+
request := makeTestRequest("1", "1", string(payload))
317+
assert.Equal(t, 0, pool.Size())
318+
err = pool.Submit(request)
319+
assert.Equal(t, err, bft.ErrRequestTooBig)
320+
321+
})
294322
t.Run("request timeout", func(t *testing.T) {
295323
timeoutHandler := &mocks.RequestTimeoutHandler{}
296324

pkg/consensus/consensus.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,8 @@ func (c *Consensus) Start() error {
134134
ForwardTimeout: c.Config.RequestForwardTimeout,
135135
ComplainTimeout: c.Config.RequestComplainTimeout,
136136
AutoRemoveTimeout: c.Config.RequestAutoRemoveTimeout,
137+
RequestMaxBytes: c.Config.RequestMaxBytes,
138+
SubmitTimeout: c.Config.RequestPoolSubmitTimeout,
137139
}
138140
c.submittedChan = make(chan struct{}, 1)
139141
c.Pool = algorithm.NewPool(c.Logger, c.RequestInspector, c.controller, opts, c.submittedChan)
@@ -214,6 +216,8 @@ func (c *Consensus) reconfig(reconfig types.Reconfig) {
214216
ForwardTimeout: c.Config.RequestForwardTimeout,
215217
ComplainTimeout: c.Config.RequestComplainTimeout,
216218
AutoRemoveTimeout: c.Config.RequestAutoRemoveTimeout,
219+
RequestMaxBytes: c.Config.RequestMaxBytes,
220+
SubmitTimeout: c.Config.RequestPoolSubmitTimeout,
217221
}
218222
c.Pool.ChangeTimeouts(c.controller, opts) // TODO handle reconfiguration of queue size in the pool
219223
c.continueCreateComponents()

pkg/types/config.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,13 @@ type Configuration struct {
7777
LeaderRotation bool
7878
// DecisionsPerLeader is the number of decisions reached by a leader before there is a leader rotation.
7979
DecisionsPerLeader uint64
80+
81+
// RequestMaxBytes total allowed size of the single request
82+
RequestMaxBytes uint64
83+
84+
// RequestPoolSubmitTimeout the total amount of time client can wait for submission of single
85+
// request into request pool
86+
RequestPoolSubmitTimeout time.Duration
8087
}
8188

8289
// DefaultConfig contains reasonable values for a small cluster that resides on the same geography (or "Region"), but
@@ -102,6 +109,8 @@ var DefaultConfig = Configuration{
102109
SpeedUpViewChange: false,
103110
LeaderRotation: true,
104111
DecisionsPerLeader: 3,
112+
RequestMaxBytes: 10 * 1024,
113+
RequestPoolSubmitTimeout: 5 * time.Second,
105114
}
106115

107116
func (c Configuration) Validate() error {
@@ -167,5 +176,13 @@ func (c Configuration) Validate() error {
167176
return errors.Errorf("DecisionsPerLeader should be greater than zero when leader rotation is active")
168177
}
169178

179+
if !(c.RequestMaxBytes > 0) {
180+
return errors.Errorf("RequestMaxBytes should be greater than zero")
181+
}
182+
183+
if !(c.RequestPoolSubmitTimeout > 0) {
184+
return errors.Errorf("RequestPoolSubmitTimeout should be greater than zero")
185+
}
186+
170187
return nil
171188
}

test/reconfig.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ type Configuration struct {
2626
SpeedUpViewChange bool
2727
LeaderRotation bool
2828
DecisionsPerLeader int64
29+
RequestMaxBytes int64
30+
RequestPoolSubmitTimeout time.Duration
2931
}
3032

3133
type Reconfig struct {
@@ -58,6 +60,8 @@ func (r Reconfig) recconfigToUint(id uint64) types.Reconfig {
5860
SpeedUpViewChange: r.CurrentConfig.SpeedUpViewChange,
5961
LeaderRotation: r.CurrentConfig.LeaderRotation,
6062
DecisionsPerLeader: uint64(r.CurrentConfig.DecisionsPerLeader),
63+
RequestMaxBytes: uint64(r.CurrentConfig.RequestBatchMaxBytes),
64+
RequestPoolSubmitTimeout: r.CurrentConfig.RequestPoolSubmitTimeout,
6165
},
6266
}
6367
}
@@ -85,6 +89,8 @@ func recconfigToInt(reconfig types.Reconfig) Reconfig {
8589
SpeedUpViewChange: reconfig.CurrentConfig.SpeedUpViewChange,
8690
LeaderRotation: reconfig.CurrentConfig.LeaderRotation,
8791
DecisionsPerLeader: int64(reconfig.CurrentConfig.DecisionsPerLeader),
92+
RequestMaxBytes: int64(reconfig.CurrentConfig.RequestBatchMaxBytes),
93+
RequestPoolSubmitTimeout: reconfig.CurrentConfig.RequestPoolSubmitTimeout,
8894
},
8995
}
9096
}

test/test_app.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ var fastConfig = types.Configuration{
3838
NumOfTicksBehindBeforeSyncing: 10,
3939
CollectTimeout: 200 * time.Millisecond,
4040
LeaderRotation: false,
41+
RequestMaxBytes: 10 * 1024,
42+
RequestPoolSubmitTimeout: 5 * time.Second,
4143
}
4244

4345
// App implements all interfaces required by an application using this library

0 commit comments

Comments
 (0)