Skip to content

Commit 86db63b

Browse files
authored
DSET-4050 feat: make client shutdown timeout configurable. (#44)
* DSET-4050 fix indefinite retrials while shutdown of client - do not reset ExponentialBackOff - define separate timeout for shutdown * add serverHost getter * release 0.0.11
1 parent 0ef5e42 commit 86db63b

File tree

7 files changed

+92
-29
lines changed

7 files changed

+92
-29
lines changed

RELEASE_NOTES.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Release Notes
22

3+
## 0.0.11
4+
5+
* feat: make client shutdown timeout configurable.
6+
37
## 0.0.10
48

59
* feat: make `serverHost` explicit field of the `Event`.

pkg/buffer_config/buffer_config.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ type DataSetBufferSettings struct {
4343
RetryInitialInterval time.Duration
4444
RetryMaxInterval time.Duration
4545
RetryMaxElapsedTime time.Duration
46+
RetryShutdownTimeout time.Duration // defines timeout period (for which client will retry on failures) for processing events and sending buffers after shutdown of a client
4647
}
4748

4849
func NewDefaultDataSetBufferSettings() DataSetBufferSettings {
@@ -55,6 +56,7 @@ func NewDefaultDataSetBufferSettings() DataSetBufferSettings {
5556
RetryMaxElapsedTime: 300 * time.Second,
5657
RetryRandomizationFactor: backoff.DefaultRandomizationFactor,
5758
RetryMultiplier: backoff.DefaultMultiplier,
59+
RetryShutdownTimeout: 30 * time.Second,
5860
}
5961
}
6062

@@ -116,6 +118,13 @@ func WithRetryMaxElapsedTime(retryMaxElapsedTime time.Duration) DataSetBufferSet
116118
}
117119
}
118120

121+
func WithRetryShutdownTimeout(retryShutdownTimeout time.Duration) DataSetBufferSettingsOption {
122+
return func(c *DataSetBufferSettings) error {
123+
c.RetryShutdownTimeout = retryShutdownTimeout
124+
return nil
125+
}
126+
}
127+
119128
func New(opts ...DataSetBufferSettingsOption) (*DataSetBufferSettings, error) {
120129
cfg := &DataSetBufferSettings{}
121130
for _, opt := range opts {

pkg/client/add_events.go

Lines changed: 36 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -211,8 +211,10 @@ func (client *DataSetClient) isProcessingEvents() bool {
211211
return client.eventsEnqueued.Load() > client.eventsProcessed.Load()
212212
}
213213

214-
// Shutdown stops processing of new events and waits until all the events that are
215-
// being processed are really processed (sent to DataSet).
214+
// Shutdown takes care of shutdown of client. It does following steps
215+
// - stops processing of new events,
216+
// - tries (with 1st half of shutdownMaxTimeout period) to process (add into buffers) all the events,
217+
// - tries (with 2nd half of shutdownMaxTimeout period) to send processed events (buffers) into DataSet
216218
func (client *DataSetClient) Shutdown() error {
217219
client.Logger.Info("Shutting down - BEGIN")
218220
// mark as finished to prevent processing of further events
@@ -222,33 +224,25 @@ func (client *DataSetClient) Shutdown() error {
222224
client.logStatistics()
223225

224226
var lastError error = nil
227+
shutdownTimeout := minDuration(client.Config.BufferSettings.RetryMaxElapsedTime, client.Config.BufferSettings.RetryShutdownTimeout)
225228
expBackoff := backoff.ExponentialBackOff{
226229
InitialInterval: client.Config.BufferSettings.RetryInitialInterval,
227230
RandomizationFactor: client.Config.BufferSettings.RetryRandomizationFactor,
228231
Multiplier: client.Config.BufferSettings.RetryMultiplier,
229232
MaxInterval: client.Config.BufferSettings.RetryMaxInterval,
230-
MaxElapsedTime: client.Config.BufferSettings.RetryMaxElapsedTime,
233+
MaxElapsedTime: shutdownTimeout / 2,
231234
Stop: backoff.Stop,
232235
Clock: backoff.SystemClock,
233236
}
234237
expBackoff.Reset()
235238

236-
// first we wait until all the events in buffers are added into buffers
237-
// then we are waiting until all the buffers are processed
238-
// if some progress is made we restart the waiting times
239-
240-
// do wait for all events to be processed
239+
// try (with timeout) to process (add into buffers) events,
241240
retryNum := 0
242-
lastProcessed := client.eventsProcessed.Load()
241+
processingStart := time.Now()
243242
for client.isProcessingEvents() {
244243
// log statistics
245244
client.logStatistics()
246245

247-
// if some events were processed restart retry interval
248-
if client.eventsProcessed.Load() != lastProcessed {
249-
expBackoff.Reset()
250-
}
251-
lastProcessed = client.eventsProcessed.Load()
252246
backoffDelay := expBackoff.NextBackOff()
253247
client.Logger.Info(
254248
"Shutting down - processing events",
@@ -279,22 +273,26 @@ func (client *DataSetClient) Shutdown() error {
279273
client.Logger.Info("Shutting down - publishing all buffers")
280274
client.publishAllBuffers()
281275

282-
// do wait for all buffers to be processed
276+
// reinitialize expBackoff with MaxElapsedTime based on actually elapsed time of processing (previous phase)
277+
processingElapsed := time.Since(processingStart)
278+
remainingShutdownTimeout := maxDuration(shutdownTimeout-processingElapsed, shutdownTimeout/2)
279+
expBackoff = backoff.ExponentialBackOff{
280+
InitialInterval: client.Config.BufferSettings.RetryInitialInterval,
281+
RandomizationFactor: client.Config.BufferSettings.RetryRandomizationFactor,
282+
Multiplier: client.Config.BufferSettings.RetryMultiplier,
283+
MaxInterval: client.Config.BufferSettings.RetryMaxInterval,
284+
MaxElapsedTime: remainingShutdownTimeout,
285+
Stop: backoff.Stop,
286+
Clock: backoff.SystemClock,
287+
}
288+
// do wait (with timeout) for all buffers to be sent to the server
283289
retryNum = 0
284290
expBackoff.Reset()
285-
lastProcessed = client.buffersProcessed.Load()
286-
lastDropped := client.buffersDropped.Load()
287-
initialDropped := lastDropped
291+
initialDropped := client.buffersDropped.Load()
288292
for client.isProcessingBuffers() {
289293
// log statistics
290294
client.logStatistics()
291295

292-
// if some buffers were processed restart retry interval
293-
if client.buffersProcessed.Load()+lastDropped != lastProcessed+client.buffersDropped.Load() {
294-
expBackoff.Reset()
295-
}
296-
lastProcessed = client.buffersProcessed.Load()
297-
lastDropped = client.buffersDropped.Load()
298296
backoffDelay := expBackoff.NextBackOff()
299297
client.Logger.Info(
300298
"Shutting down - processing buffers",
@@ -476,3 +474,17 @@ func truncateText(text string, length int) string {
476474

477475
return text
478476
}
477+
478+
func minDuration(a, b time.Duration) time.Duration {
479+
if a <= b {
480+
return a
481+
}
482+
return b
483+
}
484+
485+
func maxDuration(a, b time.Duration) time.Duration {
486+
if a >= b {
487+
return a
488+
}
489+
return b
490+
}

pkg/client/add_events_test.go

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -582,7 +582,8 @@ func TestAddEventsLogResponseBodyOnInvalidJson(t *testing.T) {
582582
server := mockServer(t, 503, []byte("<html>not valid json</html>"))
583583
defer server.Close()
584584
config := newDataSetConfig(server.URL, *newBufferSettings(
585-
buffer_config.WithRetryMaxElapsedTime(time.Duration(3) * time.Second),
585+
buffer_config.WithRetryMaxElapsedTime(time.Duration(30)*time.Second),
586+
buffer_config.WithRetryShutdownTimeout(time.Duration(6)*time.Second),
586587
), server_host_config.NewDefaultDataSetServerHostSettings())
587588
sc, err := NewClient(config, &http.Client{}, zap.Must(zap.NewDevelopment()), nil)
588589
require.Nil(t, err)
@@ -605,6 +606,39 @@ func TestAddEventsLogResponseBodyOnInvalidJson(t *testing.T) {
605606
assert.GreaterOrEqual(t, attempt.Load(), int32(0))
606607
}
607608

609+
func TestShutdownFinishesWithinExpectedTimeout(t *testing.T) {
610+
// GIVEN
611+
attempt.Store(0)
612+
server := mockServer(t, http.StatusTooManyRequests, []byte("{}"))
613+
defer server.Close()
614+
retryShutdownTimeout := 6
615+
config := newDataSetConfig(server.URL, *newBufferSettings(
616+
buffer_config.WithRetryMaxElapsedTime(time.Duration(30)*time.Second),
617+
buffer_config.WithRetryShutdownTimeout(time.Duration(retryShutdownTimeout)*time.Second),
618+
), server_host_config.NewDefaultDataSetServerHostSettings())
619+
sc, err := NewClient(config, &http.Client{}, zap.Must(zap.NewDevelopment()), nil)
620+
require.Nil(t, err)
621+
622+
sessionInfo := &add_events.SessionInfo{ServerId: "a", ServerType: "b"}
623+
sc.SessionInfo = sessionInfo
624+
event1 := &add_events.Event{Thread: "5", Sev: 3, Ts: "0", Attrs: map[string]interface{}{"message": "test - 1"}}
625+
eventBundle1 := &add_events.EventBundle{Event: event1, Thread: &add_events.Thread{Id: "5", Name: "fred"}}
626+
err = sc.AddEvents([]*add_events.EventBundle{eventBundle1})
627+
assert.Nil(t, err)
628+
// WHEN
629+
shutdownStart := time.Now()
630+
err = sc.Shutdown()
631+
shutdownFinish := time.Since(shutdownStart)
632+
// THEN
633+
assert.LessOrEqual(t, shutdownFinish, time.Duration(retryShutdownTimeout+1)*time.Second)
634+
lastError := sc.LastError()
635+
assert.Nil(t, lastError)
636+
637+
assert.NotNil(t, err)
638+
assert.Errorf(t, err, "some buffers were dropped during finishing - 1")
639+
assert.GreaterOrEqual(t, attempt.Load(), int32(0))
640+
}
641+
608642
func TestAddEventsAreNotRejectedOncePreviousReqRetriesMaxLifetimeExpired(t *testing.T) {
609643
// GIVEN
610644
maxElapsedTime := 10

pkg/client/client.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -665,3 +665,7 @@ func (client *DataSetClient) lastErrorTimestamp() time.Time {
665665
func (client *DataSetClient) isLastRetryableErrorTimedOut() bool {
666666
return client.lastErrorTimestamp().Add(client.Config.BufferSettings.RetryMaxElapsedTime).Before(time.Now())
667667
}
668+
669+
func (client *DataSetClient) ServerHost() string {
670+
return client.serverHost
671+
}

pkg/version/version.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,6 @@
1717
package version
1818

1919
const (
20-
Version = "0.0.10"
21-
ReleasedDate = "2023-07-10"
20+
Version = "0.0.11"
21+
ReleasedDate = "2023-07-26"
2222
)

pkg/version/version_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,6 @@ import (
2323
)
2424

2525
func TestVersion(t *testing.T) {
26-
assert.Equal(t, "0.0.10", Version)
27-
assert.Equal(t, "2023-07-10", ReleasedDate)
26+
assert.Equal(t, "0.0.11", Version)
27+
assert.Equal(t, "2023-07-26", ReleasedDate)
2828
}

0 commit comments

Comments
 (0)