mirror of
https://github.com/rqlite/rqlite.git
synced 2026-01-25 04:16:26 +00:00
Correct batching queue flush
This commit is contained in:
@@ -19,6 +19,7 @@
|
|||||||
- [PR #2297](https://github.com/rqlite/rqlite/pull/2297): Record commit timestamp for CDC events.
|
- [PR #2297](https://github.com/rqlite/rqlite/pull/2297): Record commit timestamp for CDC events.
|
||||||
- [PR #2298](https://github.com/rqlite/rqlite/pull/2298), [PR #2299](https://github.com/rqlite/rqlite/pull/2299), [PR #2300](https://github.com/rqlite/rqlite/pull/2301), [PR #2301](https://github.com/rqlite/rqlite/pull/2301): CDC regex filtering for table names.
|
- [PR #2298](https://github.com/rqlite/rqlite/pull/2298), [PR #2299](https://github.com/rqlite/rqlite/pull/2299), [PR #2300](https://github.com/rqlite/rqlite/pull/2301), [PR #2301](https://github.com/rqlite/rqlite/pull/2301): CDC regex filtering for table names.
|
||||||
- [PR #2307](https://github.com/rqlite/rqlite/pull/2307): Add `SyncChannels` synchronization primitive.
|
- [PR #2307](https://github.com/rqlite/rqlite/pull/2307): Add `SyncChannels` synchronization primitive.
|
||||||
|
- [PR #2308](https://github.com/rqlite/rqlite/pull/2308): Correctly implement Batching Queue flushing.
|
||||||
|
|
||||||
## v8.43.4 (August 27th 2025)
|
## v8.43.4 (August 27th 2025)
|
||||||
### Implementation changes and bug fixes
|
### Implementation changes and bug fixes
|
||||||
|
|||||||
@@ -56,24 +56,25 @@ type queuedObjects[T any] struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func mergeQueued[T any](qs []*queuedObjects[T]) *Request[T] {
|
func mergeQueued[T any](qs []*queuedObjects[T]) *Request[T] {
|
||||||
var o *Request[T]
|
if len(qs) == 0 {
|
||||||
if len(qs) > 0 {
|
return nil
|
||||||
o = &Request[T]{
|
}
|
||||||
SequenceNumber: qs[0].SequenceNumber,
|
var req *Request[T]
|
||||||
flushChans: make([]FlushChannel, 0),
|
req = &Request[T]{
|
||||||
}
|
SequenceNumber: qs[0].SequenceNumber,
|
||||||
|
flushChans: make([]FlushChannel, 0),
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := range qs {
|
for i := range qs {
|
||||||
if o.SequenceNumber < qs[i].SequenceNumber {
|
if req.SequenceNumber < qs[i].SequenceNumber {
|
||||||
o.SequenceNumber = qs[i].SequenceNumber
|
req.SequenceNumber = qs[i].SequenceNumber
|
||||||
}
|
}
|
||||||
o.Objects = append(o.Objects, qs[i].Objects...)
|
req.Objects = append(req.Objects, qs[i].Objects...)
|
||||||
if qs[i].flushChan != nil {
|
if qs[i].flushChan != nil {
|
||||||
o.flushChans = append(o.flushChans, qs[i].flushChan)
|
req.flushChans = append(req.flushChans, qs[i].flushChan)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return o
|
return req
|
||||||
}
|
}
|
||||||
|
|
||||||
// Queue is a batching queue with a timeout.
|
// Queue is a batching queue with a timeout.
|
||||||
@@ -89,7 +90,6 @@ type Queue[T any] struct {
|
|||||||
|
|
||||||
done chan struct{}
|
done chan struct{}
|
||||||
closed chan struct{}
|
closed chan struct{}
|
||||||
flush chan struct{}
|
|
||||||
|
|
||||||
seqMu sync.Mutex
|
seqMu sync.Mutex
|
||||||
seqNum int64
|
seqNum int64
|
||||||
@@ -111,7 +111,6 @@ func New[T any](maxSize, batchSize int, t time.Duration) *Queue[T] {
|
|||||||
sendCh: make(chan *Request[T], 1),
|
sendCh: make(chan *Request[T], 1),
|
||||||
done: make(chan struct{}),
|
done: make(chan struct{}),
|
||||||
closed: make(chan struct{}),
|
closed: make(chan struct{}),
|
||||||
flush: make(chan struct{}),
|
|
||||||
seqNum: time.Now().UnixNano(),
|
seqNum: time.Now().UnixNano(),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,7 +160,7 @@ func (q *Queue[T]) WriteOne(object T, c FlushChannel) (int64, error) {
|
|||||||
|
|
||||||
// Flush flushes the queue
|
// Flush flushes the queue
|
||||||
func (q *Queue[T]) Flush() error {
|
func (q *Queue[T]) Flush() error {
|
||||||
q.flush <- struct{}{}
|
q.batchCh <- nil
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -212,6 +211,13 @@ func (q *Queue[T]) run() {
|
|||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case s := <-q.batchCh:
|
case s := <-q.batchCh:
|
||||||
|
if s == nil { // flush marker
|
||||||
|
stats.Add(numFlush, 1)
|
||||||
|
stopTimer(timer)
|
||||||
|
writeFn()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
qObjs = append(qObjs, s)
|
qObjs = append(qObjs, s)
|
||||||
if len(qObjs) == 1 {
|
if len(qObjs) == 1 {
|
||||||
if q.timeout != 0 {
|
if q.timeout != 0 {
|
||||||
@@ -228,10 +234,6 @@ func (q *Queue[T]) run() {
|
|||||||
stats.Add(numTimeout, 1)
|
stats.Add(numTimeout, 1)
|
||||||
q.numTimeouts++
|
q.numTimeouts++
|
||||||
writeFn()
|
writeFn()
|
||||||
case <-q.flush:
|
|
||||||
stats.Add(numFlush, 1)
|
|
||||||
stopTimer(timer)
|
|
||||||
writeFn()
|
|
||||||
case <-q.done:
|
case <-q.done:
|
||||||
stopTimer(timer)
|
stopTimer(timer)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -152,6 +152,46 @@ func Test_QueueWriteOne(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_QueueWriteOne_FlushEmpty(t *testing.T) {
|
||||||
|
q := New[*command.Statement](1024, 2, 60*time.Second)
|
||||||
|
defer q.Close()
|
||||||
|
|
||||||
|
if err := q.Flush(); err != nil {
|
||||||
|
t.Fatalf("failed to flush: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case req := <-q.C:
|
||||||
|
t.Fatalf("received unexpected request on empty flush: %v", req)
|
||||||
|
case <-time.After(100 * time.Millisecond):
|
||||||
|
// Expected, nothing to receive.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_QueueWriteOne_Flush(t *testing.T) {
|
||||||
|
q := New[*command.Statement](1024, 2, 60*time.Second)
|
||||||
|
defer q.Close()
|
||||||
|
|
||||||
|
if _, err := q.WriteOne(testStmtFoo, nil); err != nil {
|
||||||
|
t.Fatalf("failed to write: %s", err.Error())
|
||||||
|
}
|
||||||
|
if err := q.Flush(); err != nil {
|
||||||
|
t.Fatalf("failed to flush: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case req := <-q.C:
|
||||||
|
if exp, got := 1, len(req.Objects); exp != got {
|
||||||
|
t.Fatalf("received wrong length slice, exp %d, got %d", exp, got)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(req.Objects[0], testStmtFoo) {
|
||||||
|
t.Fatalf("received wrong statement, got: %v, want: %v", req.Objects[0], testStmtFoo)
|
||||||
|
}
|
||||||
|
case <-time.After(time.Second):
|
||||||
|
t.Fatalf("timed out waiting for statement")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func Test_QueueWriteBatchSizeSingle(t *testing.T) {
|
func Test_QueueWriteBatchSizeSingle(t *testing.T) {
|
||||||
q := New[*command.Statement](1024, 1, 60*time.Second)
|
q := New[*command.Statement](1024, 1, 60*time.Second)
|
||||||
defer q.Close()
|
defer q.Close()
|
||||||
@@ -495,5 +535,4 @@ func Test_QueueOrdering(t *testing.T) {
|
|||||||
case <-time.After(testTimeout):
|
case <-time.After(testTimeout):
|
||||||
t.Fatalf("timed out waiting for all indexes")
|
t.Fatalf("timed out waiting for all indexes")
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user