forked from elastic/beats
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This change replaces the ACK handler functions with a single interface that makes it easier to combine ACK handlers. The global ACK handler is removed from the pipeline, requiring Beats to wrap and compose per input ACK handlers with their own ones. Although the PR is quite big, the main difference is that the `ACKCount`, `ACKEvents`, and `ACKLastEvents` handlers have been replaced by a single interface (`beat.ACKer`). The original ACKer implementations from `libbeat/publisher/pipeline/acker.go` and `libbeat/publisher/pipeline/client_acker.go` have been moved `libbeat/common/acker`. The former private implementation is now exposed as Helpers for writing and combining ACK handlers. Support for global ACK handlers has been removed. The `acker.Combine` and `acker.ConnectionOnly` are the only new additions to the code base. The global ACK handler support was introduced for filebeat, that did require some support for combine events from multiple inputs before applying state updates. With the introduction of the v2 input API this requirement will go away, as per input type managers are responsible for handling state update and ACKs. In order to run old and new architecture in parallel, we need to combine ACK handling from input managers, existing input, custom registrar ACKer in filebeat, and event counting support (also via ACK handling) for shutdown. Exposing the interface and providing combinators (acker.Combine) for merging ACK handlers into one helps with the integration. The v2 Input API gives implementors more flexibility in how to handle event publishing, coordination, and state handling shall be implemented. With the original ACK support the callbacks have been deregistered the moment inputs are stopped automatically. But for cursor based inputs we need to continue handling ACKs, even after the input is gone. The interface and helpers provide greater control over ACK handling after shutdown, which is required for the journald, winlog, and file/log inputs.
Steffen Siering
authored
Jul 6, 2020
1 parent
e88743f
commit bb89344
Showing
34 changed files
with
955 additions
and
1,480 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,341 @@ | ||
// Licensed to Elasticsearch B.V. under one or more contributor | ||
// license agreements. See the NOTICE file distributed with | ||
// this work for additional information regarding copyright | ||
// ownership. Elasticsearch B.V. licenses this file to you under | ||
// the Apache License, Version 2.0 (the "License"); you may | ||
// not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, | ||
// software distributed under the License is distributed on an | ||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
// KIND, either express or implied. See the License for the | ||
// specific language governing permissions and limitations | ||
// under the License. | ||
|
||
package acker | ||
|
||
import ( | ||
"sync" | ||
|
||
"github.com/elastic/beats/v7/libbeat/beat" | ||
"github.com/elastic/beats/v7/libbeat/common/atomic" | ||
) | ||
|
||
// Nil creates an ACKer that does nothing. | ||
func Nil() beat.ACKer { | ||
return nilACKer{} | ||
} | ||
|
||
type nilACKer struct{} | ||
|
||
func (nilACKer) AddEvent(event beat.Event, published bool) {} | ||
func (nilACKer) ACKEvents(n int) {} | ||
func (nilACKer) Close() {} | ||
|
||
// RawCounting reports the number of ACKed events as has been reported by the outputs or queue. | ||
// The ACKer does not keep track of dropped events. Events after the client has | ||
// been closed will still be reported. | ||
func RawCounting(fn func(int)) beat.ACKer { | ||
return countACKer(fn) | ||
} | ||
|
||
type countACKer func(int) | ||
|
||
func (countACKer) AddEvent(_ beat.Event, _ bool) {} | ||
func (fn countACKer) ACKEvents(n int) { fn(n) } | ||
func (countACKer) Close() {} | ||
|
||
// TrackingCounter keeps track of published and dropped events. It reports | ||
// the number of acked events from the queue in the 'acked' argument and the | ||
// total number of events published via the Client in the 'total' argument. | ||
// The TrackingCountACKer keeps track of the order of events being send and events being acked. | ||
// If N events have been acked by the output, then `total` will include all events dropped in between | ||
// the last forwarded N events and the 'tail' of dropped events. For example (X = send, D = dropped): | ||
// | ||
// index: 0 1 2 3 4 5 6 7 8 9 10 11 | ||
// event: X X D D X D D X D X X X | ||
// | ||
// If the output ACKs 3 events, then all events from index 0 to 6 will be reported because: | ||
// - the drop sequence for events 2 and 3 is inbetween the number of forwarded and ACKed events | ||
// - events 5-6 have been dropped as well, but event 7 is not ACKed yet | ||
// | ||
// If there is no event currently tracked by this ACKer and the next event is dropped by the processors, | ||
// then `fn` will be called immediately with acked=0 and total=1. | ||
func TrackingCounter(fn func(acked, total int)) beat.ACKer { | ||
a := &trackingACKer{fn: fn} | ||
init := &gapInfo{} | ||
a.lst.head = init | ||
a.lst.tail = init | ||
return a | ||
} | ||
|
||
// Counting returns an ACK count for all events a client has tried to publish. | ||
// The ACKer keeps track of dropped events as well, and adjusts the ACK from the outputs accordingly. | ||
func Counting(fn func(n int)) beat.ACKer { | ||
return TrackingCounter(func(_ int, total int) { | ||
fn(total) | ||
}) | ||
} | ||
|
||
type trackingACKer struct { | ||
fn func(acked, total int) | ||
events atomic.Uint32 | ||
lst gapList | ||
} | ||
|
||
type gapList struct { | ||
sync.Mutex | ||
head, tail *gapInfo | ||
} | ||
|
||
type gapInfo struct { | ||
sync.Mutex | ||
next *gapInfo | ||
send, dropped int | ||
} | ||
|
||
func (a *trackingACKer) AddEvent(_ beat.Event, published bool) { | ||
a.events.Inc() | ||
if published { | ||
a.addPublishedEvent() | ||
} else { | ||
a.addDropEvent() | ||
} | ||
} | ||
|
||
// addPublishedEvent increments the 'send' counter in the current gapInfo | ||
// element in the tail of the list. If events have been dropped, we append a | ||
// new empty gapInfo element. | ||
func (a *trackingACKer) addPublishedEvent() { | ||
a.lst.Lock() | ||
|
||
current := a.lst.tail | ||
current.Lock() | ||
if current.dropped > 0 { | ||
tmp := &gapInfo{} | ||
tmp.Lock() | ||
|
||
a.lst.tail.next = tmp | ||
a.lst.tail = tmp | ||
current.Unlock() | ||
current = tmp | ||
} | ||
a.lst.Unlock() | ||
|
||
current.send++ | ||
current.Unlock() | ||
} | ||
|
||
// addDropEvent increments the 'dropped' counter in the gapInfo element in the | ||
// tail of the list. The callback will be run with total=1 and acked=0 if the | ||
// acker state is empty and no events have been send yet. | ||
func (a *trackingACKer) addDropEvent() { | ||
a.lst.Lock() | ||
|
||
current := a.lst.tail | ||
current.Lock() | ||
|
||
if current.send == 0 && current.next == nil { | ||
// send can only be 0 if no no events/gaps present yet | ||
if a.lst.head != a.lst.tail { | ||
panic("gap list expected to be empty") | ||
} | ||
|
||
a.fn(0, 1) | ||
a.lst.Unlock() | ||
current.Unlock() | ||
|
||
a.events.Dec() | ||
return | ||
} | ||
|
||
a.lst.Unlock() | ||
current.dropped++ | ||
current.Unlock() | ||
} | ||
|
||
func (a *trackingACKer) ACKEvents(n int) { | ||
var ( | ||
total = 0 | ||
acked = n | ||
emptyLst bool | ||
) | ||
|
||
for n > 0 { | ||
if emptyLst { | ||
panic("too many events acked") | ||
} | ||
|
||
a.lst.Lock() | ||
current := a.lst.head | ||
current.Lock() | ||
|
||
// advance list if we detect that the current head will be completely consumed | ||
// by this ACK event. | ||
if n >= current.send { | ||
next := current.next | ||
emptyLst = next == nil | ||
if !emptyLst { | ||
// advance list all event in current entry have been send and list as | ||
// more then 1 gapInfo entry. If only 1 entry is present, list item will be | ||
// reset and reused | ||
a.lst.head = next | ||
} | ||
} | ||
// hand over lock list-entry, so ACK handler and producer can operate | ||
// on potentially different list ends | ||
a.lst.Unlock() | ||
|
||
if n < current.send { | ||
current.send -= n | ||
total += n | ||
n = 0 | ||
} else { | ||
total += current.send + current.dropped | ||
n -= current.send | ||
current.dropped = 0 | ||
current.send = 0 | ||
} | ||
current.Unlock() | ||
} | ||
|
||
a.events.Sub(uint32(total)) | ||
a.fn(acked, total) | ||
} | ||
|
||
func (a *trackingACKer) Close() {} | ||
|
||
// EventPrivateReporter reports all private fields from all events that have | ||
// been published or removed. | ||
// | ||
// The EventPrivateFieldsACKer keeps track of the order of events being send | ||
// and events being acked. If N events have been acked by the output, then | ||
// `total` will include all events dropped in between the last forwarded N | ||
// events and the 'tail' of dropped events. For example (X = send, D = | ||
// dropped): | ||
// | ||
// index: 0 1 2 3 4 5 6 7 8 9 10 11 | ||
// event: X X D D X D D X D X X X | ||
// | ||
// If the output ACKs 3 events, then all events from index 0 to 6 will be reported because: | ||
// - the drop sequence for events 2 and 3 is inbetween the number of forwarded and ACKed events | ||
// - events 5-6 have been dropped as well, but event 7 is not ACKed yet | ||
func EventPrivateReporter(fn func(acked int, data []interface{})) beat.ACKer { | ||
a := &eventDataACKer{fn: fn} | ||
a.ACKer = TrackingCounter(a.onACK) | ||
return a | ||
} | ||
|
||
type eventDataACKer struct { | ||
beat.ACKer | ||
mu sync.Mutex | ||
data []interface{} | ||
fn func(acked int, data []interface{}) | ||
} | ||
|
||
func (a *eventDataACKer) AddEvent(event beat.Event, published bool) { | ||
a.mu.Lock() | ||
a.data = append(a.data, event.Private) | ||
a.mu.Unlock() | ||
a.ACKer.AddEvent(event, published) | ||
} | ||
|
||
func (a *eventDataACKer) onACK(acked, total int) { | ||
if total == 0 { | ||
return | ||
} | ||
|
||
a.mu.Lock() | ||
data := a.data[:total] | ||
a.data = a.data[total:] | ||
a.mu.Unlock() | ||
|
||
if len(data) > 0 { | ||
a.fn(acked, data) | ||
} | ||
} | ||
|
||
// LastEventPrivateReporter reports only the 'latest' published and acked | ||
// event if a batch of events have been ACKed. | ||
func LastEventPrivateReporter(fn func(acked int, data interface{})) beat.ACKer { | ||
ignored := 0 | ||
return EventPrivateReporter(func(acked int, data []interface{}) { | ||
for i := len(data) - 1; i >= 0; i-- { | ||
if d := data[i]; d != nil { | ||
fn(ignored+acked, d) | ||
ignored = 0 | ||
return | ||
} | ||
} | ||
|
||
// complete batch has been ignored due to missing data -> add count | ||
ignored += acked | ||
}) | ||
} | ||
|
||
// Combine forwards events to a list of ackers. | ||
func Combine(as ...beat.ACKer) beat.ACKer { | ||
return ackerList(as) | ||
} | ||
|
||
type ackerList []beat.ACKer | ||
|
||
func (l ackerList) AddEvent(event beat.Event, published bool) { | ||
for _, a := range l { | ||
a.AddEvent(event, published) | ||
} | ||
} | ||
|
||
func (l ackerList) ACKEvents(n int) { | ||
for _, a := range l { | ||
a.ACKEvents(n) | ||
} | ||
} | ||
|
||
func (l ackerList) Close() { | ||
for _, a := range l { | ||
a.Close() | ||
} | ||
} | ||
|
||
// ConnectionOnly ensures that the given ACKer is only used for as long as the | ||
// pipeline Client is active. Once the Client is closed, the ACKer will drop | ||
// its internal state and no more ACK events will be processed. | ||
func ConnectionOnly(a beat.ACKer) beat.ACKer { | ||
return &clientOnlyACKer{acker: a} | ||
} | ||
|
||
type clientOnlyACKer struct { | ||
mu sync.Mutex | ||
acker beat.ACKer | ||
} | ||
|
||
func (a *clientOnlyACKer) AddEvent(event beat.Event, published bool) { | ||
a.mu.Lock() | ||
defer a.mu.Unlock() | ||
if sub := a.acker; sub != nil { | ||
sub.AddEvent(event, published) | ||
} | ||
} | ||
|
||
func (a *clientOnlyACKer) ACKEvents(n int) { | ||
a.mu.Lock() | ||
sub := a.acker | ||
a.mu.Unlock() | ||
if sub != nil { | ||
sub.ACKEvents(n) | ||
} | ||
} | ||
|
||
func (a *clientOnlyACKer) Close() { | ||
a.mu.Lock() | ||
sub := a.acker | ||
a.acker = nil // drop the internal ACKer on Close and allow the runtime to gc accumulated state. | ||
a.mu.Unlock() | ||
if sub != nil { | ||
sub.Close() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,250 @@ | ||
// Licensed to Elasticsearch B.V. under one or more contributor | ||
// license agreements. See the NOTICE file distributed with | ||
// this work for additional information regarding copyright | ||
// ownership. Elasticsearch B.V. licenses this file to you under | ||
// the Apache License, Version 2.0 (the "License"); you may | ||
// not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, | ||
// software distributed under the License is distributed on an | ||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
// KIND, either express or implied. See the License for the | ||
// specific language governing permissions and limitations | ||
// under the License. | ||
|
||
package acker | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/stretchr/testify/require" | ||
|
||
"github.com/elastic/beats/v7/libbeat/beat" | ||
) | ||
|
||
type fakeACKer struct { | ||
AddEventFunc func(event beat.Event, published bool) | ||
ACKEventsFunc func(n int) | ||
CloseFunc func() | ||
} | ||
|
||
func TestNil(t *testing.T) { | ||
acker := Nil() | ||
require.NotNil(t, acker) | ||
|
||
// check acker can be used without panic: | ||
acker.AddEvent(beat.Event{}, false) | ||
acker.AddEvent(beat.Event{}, true) | ||
acker.ACKEvents(3) | ||
acker.Close() | ||
} | ||
|
||
func TestCounting(t *testing.T) { | ||
t.Run("ack count is passed through", func(t *testing.T) { | ||
var n int | ||
acker := RawCounting(func(acked int) { n = acked }) | ||
acker.ACKEvents(3) | ||
require.Equal(t, 3, n) | ||
}) | ||
} | ||
|
||
func TestTracking(t *testing.T) { | ||
t.Run("dropped event is acked immediately if empty", func(t *testing.T) { | ||
var acked, total int | ||
TrackingCounter(func(a, t int) { acked, total = a, t }).AddEvent(beat.Event{}, false) | ||
require.Equal(t, 0, acked) | ||
require.Equal(t, 1, total) | ||
}) | ||
|
||
t.Run("no dropped events", func(t *testing.T) { | ||
var acked, total int | ||
acker := TrackingCounter(func(a, t int) { acked, total = a, t }) | ||
acker.AddEvent(beat.Event{}, true) | ||
acker.AddEvent(beat.Event{}, true) | ||
acker.ACKEvents(2) | ||
require.Equal(t, 2, acked) | ||
require.Equal(t, 2, total) | ||
}) | ||
|
||
t.Run("acking published includes dropped events in middle", func(t *testing.T) { | ||
var acked, total int | ||
acker := TrackingCounter(func(a, t int) { acked, total = a, t }) | ||
acker.AddEvent(beat.Event{}, true) | ||
acker.AddEvent(beat.Event{}, false) | ||
acker.AddEvent(beat.Event{}, false) | ||
acker.AddEvent(beat.Event{}, true) | ||
acker.ACKEvents(2) | ||
require.Equal(t, 2, acked) | ||
require.Equal(t, 4, total) | ||
}) | ||
|
||
t.Run("acking published includes dropped events at end of ACK interval", func(t *testing.T) { | ||
var acked, total int | ||
acker := TrackingCounter(func(a, t int) { acked, total = a, t }) | ||
acker.AddEvent(beat.Event{}, true) | ||
acker.AddEvent(beat.Event{}, true) | ||
acker.AddEvent(beat.Event{}, false) | ||
acker.AddEvent(beat.Event{}, false) | ||
acker.AddEvent(beat.Event{}, true) | ||
acker.ACKEvents(2) | ||
require.Equal(t, 2, acked) | ||
require.Equal(t, 4, total) | ||
}) | ||
|
||
t.Run("partial ACKs", func(t *testing.T) { | ||
var acked, total int | ||
acker := TrackingCounter(func(a, t int) { acked, total = a, t }) | ||
acker.AddEvent(beat.Event{}, true) | ||
acker.AddEvent(beat.Event{}, true) | ||
acker.AddEvent(beat.Event{}, true) | ||
acker.AddEvent(beat.Event{}, true) | ||
acker.AddEvent(beat.Event{}, false) | ||
acker.AddEvent(beat.Event{}, true) | ||
acker.AddEvent(beat.Event{}, true) | ||
|
||
acker.ACKEvents(2) | ||
require.Equal(t, 2, acked) | ||
require.Equal(t, 2, total) | ||
|
||
acker.ACKEvents(2) | ||
require.Equal(t, 2, acked) | ||
require.Equal(t, 3, total) | ||
}) | ||
} | ||
|
||
func TestEventPrivateReporter(t *testing.T) { | ||
t.Run("dropped event is acked immediately if empty", func(t *testing.T) { | ||
var acked int | ||
var data []interface{} | ||
acker := EventPrivateReporter(func(a int, d []interface{}) { acked, data = a, d }) | ||
acker.AddEvent(beat.Event{Private: 1}, false) | ||
require.Equal(t, 0, acked) | ||
require.Equal(t, []interface{}{1}, data) | ||
}) | ||
|
||
t.Run("no dropped events", func(t *testing.T) { | ||
var acked int | ||
var data []interface{} | ||
acker := EventPrivateReporter(func(a int, d []interface{}) { acked, data = a, d }) | ||
acker.AddEvent(beat.Event{Private: 1}, true) | ||
acker.AddEvent(beat.Event{Private: 2}, true) | ||
acker.AddEvent(beat.Event{Private: 3}, true) | ||
acker.ACKEvents(3) | ||
require.Equal(t, 3, acked) | ||
require.Equal(t, []interface{}{1, 2, 3}, data) | ||
}) | ||
|
||
t.Run("private of dropped events is included", func(t *testing.T) { | ||
var acked int | ||
var data []interface{} | ||
acker := EventPrivateReporter(func(a int, d []interface{}) { acked, data = a, d }) | ||
acker.AddEvent(beat.Event{Private: 1}, true) | ||
acker.AddEvent(beat.Event{Private: 2}, false) | ||
acker.AddEvent(beat.Event{Private: 3}, true) | ||
acker.ACKEvents(2) | ||
require.Equal(t, 2, acked) | ||
require.Equal(t, []interface{}{1, 2, 3}, data) | ||
}) | ||
} | ||
|
||
func TestLastEventPrivateReporter(t *testing.T) { | ||
t.Run("dropped event with private is acked immediately if empty", func(t *testing.T) { | ||
var acked int | ||
var datum interface{} | ||
acker := LastEventPrivateReporter(func(a int, d interface{}) { acked, datum = a, d }) | ||
acker.AddEvent(beat.Event{Private: 1}, false) | ||
require.Equal(t, 0, acked) | ||
require.Equal(t, 1, datum) | ||
}) | ||
|
||
t.Run("dropped event without private is ignored", func(t *testing.T) { | ||
var called bool | ||
acker := LastEventPrivateReporter(func(_ int, _ interface{}) { called = true }) | ||
acker.AddEvent(beat.Event{Private: nil}, false) | ||
require.False(t, called) | ||
}) | ||
|
||
t.Run("no dropped events", func(t *testing.T) { | ||
var acked int | ||
var data interface{} | ||
acker := LastEventPrivateReporter(func(a int, d interface{}) { acked, data = a, d }) | ||
acker.AddEvent(beat.Event{Private: 1}, true) | ||
acker.AddEvent(beat.Event{Private: 2}, true) | ||
acker.AddEvent(beat.Event{Private: 3}, true) | ||
acker.ACKEvents(3) | ||
require.Equal(t, 3, acked) | ||
require.Equal(t, 3, data) | ||
}) | ||
} | ||
|
||
func TestCombine(t *testing.T) { | ||
t.Run("AddEvent distributes", func(t *testing.T) { | ||
var a1, a2 int | ||
acker := Combine(countACKerOps(&a1, nil, nil), countACKerOps(&a2, nil, nil)) | ||
acker.AddEvent(beat.Event{}, true) | ||
require.Equal(t, 1, a1) | ||
require.Equal(t, 1, a2) | ||
}) | ||
|
||
t.Run("ACKEvents distributes", func(t *testing.T) { | ||
var a1, a2 int | ||
acker := Combine(countACKerOps(nil, &a1, nil), countACKerOps(nil, &a2, nil)) | ||
acker.ACKEvents(1) | ||
require.Equal(t, 1, a1) | ||
require.Equal(t, 1, a2) | ||
}) | ||
|
||
t.Run("Close distributes", func(t *testing.T) { | ||
var c1, c2 int | ||
acker := Combine(countACKerOps(nil, nil, &c1), countACKerOps(nil, nil, &c2)) | ||
acker.Close() | ||
require.Equal(t, 1, c1) | ||
require.Equal(t, 1, c2) | ||
}) | ||
} | ||
|
||
func TestConnectionOnly(t *testing.T) { | ||
t.Run("passes ACKs if not closed", func(t *testing.T) { | ||
var n int | ||
acker := ConnectionOnly(RawCounting(func(acked int) { n = acked })) | ||
acker.ACKEvents(3) | ||
require.Equal(t, 3, n) | ||
}) | ||
|
||
t.Run("ignores ACKs after close", func(t *testing.T) { | ||
var n int | ||
acker := ConnectionOnly(RawCounting(func(acked int) { n = acked })) | ||
acker.Close() | ||
acker.ACKEvents(3) | ||
require.Equal(t, 0, n) | ||
}) | ||
} | ||
|
||
func countACKerOps(add, acked, close *int) beat.ACKer { | ||
return &fakeACKer{ | ||
AddEventFunc: func(_ beat.Event, _ bool) { *add++ }, | ||
ACKEventsFunc: func(_ int) { *acked++ }, | ||
CloseFunc: func() { *close++ }, | ||
} | ||
} | ||
|
||
func (f *fakeACKer) AddEvent(event beat.Event, published bool) { | ||
if f.AddEventFunc != nil { | ||
f.AddEventFunc(event, published) | ||
} | ||
} | ||
|
||
func (f *fakeACKer) ACKEvents(n int) { | ||
if f.ACKEventsFunc != nil { | ||
f.ACKEventsFunc(n) | ||
} | ||
} | ||
|
||
func (f *fakeACKer) Close() { | ||
if f.CloseFunc != nil { | ||
f.CloseFunc() | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters