-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
17 changed files
with
468 additions
and
51 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
package services | ||
|
||
import ( | ||
"context" | ||
"time" | ||
|
||
"github.com/didil/inhooks/pkg/lib" | ||
"github.com/didil/inhooks/pkg/models" | ||
"github.com/pkg/errors" | ||
) | ||
|
||
type CleanupService interface { | ||
CleanupDoneQueue(ctx context.Context, f *models.Flow, sink *models.Sink, doneQueueCleanupDelay time.Duration) (int, error) | ||
} | ||
|
||
func NewCleanupService(redisStore RedisStore, timeSvc TimeService) CleanupService { | ||
return &cleanupService{ | ||
redisStore: redisStore, | ||
timeSvc: timeSvc, | ||
} | ||
} | ||
|
||
type cleanupService struct { | ||
redisStore RedisStore | ||
timeSvc TimeService | ||
} | ||
|
||
func (s *cleanupService) CleanupDoneQueue(ctx context.Context, f *models.Flow, sink *models.Sink, doneQueueCleanupDelay time.Duration) (int, error) { | ||
doneQueueKey := queueKey(f.ID, sink.ID, models.QueueStatusDone) | ||
|
||
cutOffTimeEpoch := s.timeSvc.Now().Add(-doneQueueCleanupDelay).Unix() | ||
mIDs, err := s.redisStore.ZRangeBelowScore(ctx, doneQueueKey, float64(cutOffTimeEpoch)) | ||
if err != nil { | ||
return 0, err | ||
} | ||
if err != nil { | ||
return 0, errors.Wrapf(err, "failed to zrange below score") | ||
} | ||
if len(mIDs) == 0 { | ||
// no messages do cleanup | ||
return 0, nil | ||
} | ||
|
||
// move message ids in chunks | ||
chunkSize := 50 | ||
mIDChunks := lib.ChunkSliceBy(mIDs, chunkSize) | ||
|
||
for i := 0; i < len(mIDChunks); i++ { | ||
messageKeys := make([]string, 0, len(mIDChunks[i])) | ||
for _, mId := range mIDChunks[i] { | ||
mKey := messageKey(f.ID, sink.ID, mId) | ||
messageKeys = append(messageKeys, mKey) | ||
} | ||
|
||
err := s.redisStore.ZRemDel(ctx, doneQueueKey, mIDChunks[i], messageKeys) | ||
if err != nil { | ||
return 0, errors.Wrapf(err, "failed to zremdel") | ||
} | ||
} | ||
|
||
return len(mIDs), nil | ||
} |
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,52 @@ | ||
package services | ||
|
||
import ( | ||
"context" | ||
"testing" | ||
"time" | ||
|
||
"github.com/didil/inhooks/pkg/models" | ||
"github.com/didil/inhooks/pkg/testsupport/mocks" | ||
"github.com/golang/mock/gomock" | ||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func TestCleanUpServiceCleanupDoneQueue(t *testing.T) { | ||
ctrl := gomock.NewController(t) | ||
defer ctrl.Finish() | ||
|
||
redisStore := mocks.NewMockRedisStore(ctrl) | ||
timeSvc := mocks.NewMockTimeService(ctrl) | ||
|
||
now := time.Date(2023, 05, 5, 8, 46, 20, 0, time.UTC) | ||
timeSvc.EXPECT().Now().Return(now) | ||
|
||
ctx := context.Background() | ||
|
||
flowId := "flow-1" | ||
sinkID := "sink-1" | ||
|
||
flow := &models.Flow{ | ||
ID: flowId, | ||
} | ||
sink := &models.Sink{ | ||
ID: sinkID, | ||
} | ||
|
||
queueKey := "f:flow-1:s:sink-1:q:done" | ||
|
||
doneQueueCleanupDelay := 30 * time.Minute | ||
cutoffTime := time.Date(2023, 05, 5, 8, 16, 20, 0, time.UTC) | ||
|
||
mIds := []string{"message-1", "message-2"} | ||
messageKeys := []string{"f:flow-1:s:sink-1:m:message-1", "f:flow-1:s:sink-1:m:message-2"} | ||
|
||
redisStore.EXPECT().ZRangeBelowScore(ctx, queueKey, float64(cutoffTime.Unix())).Return(mIds, nil) | ||
redisStore.EXPECT().ZRemDel(ctx, queueKey, mIds, messageKeys).Return(nil) | ||
|
||
s := NewCleanupService(redisStore, timeSvc) | ||
count, err := s.CleanupDoneQueue(ctx, flow, sink, doneQueueCleanupDelay) | ||
assert.NoError(t, err) | ||
|
||
assert.Equal(t, 2, count) | ||
} |
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,33 @@ | ||
package supervisor | ||
|
||
import ( | ||
"time" | ||
|
||
"github.com/didil/inhooks/pkg/models" | ||
"go.uber.org/zap" | ||
) | ||
|
||
func (s *Supervisor) HandleDoneQueue(f *models.Flow, sink *models.Sink) { | ||
logger := s.logger.With(zap.String("flowID", f.ID), zap.String("sinkID", sink.ID)) | ||
for { | ||
if s.appConf.Supervisor.DoneQueueCleanupEnabled { | ||
count, err := s.cleanupSvc.CleanupDoneQueue(s.ctx, f, sink, s.appConf.Supervisor.DoneQueueCleanupDelay) | ||
if err != nil { | ||
logger.Error("failed to cleanup done queue", zap.Error(err)) | ||
} | ||
if count > 0 { | ||
logger.Info("done queue cleanup ok. messages removed", zap.Int("messagesCount", count)) | ||
} | ||
} | ||
|
||
// wait before next check | ||
timer := time.NewTimer(s.appConf.Supervisor.DoneQueueCleanupInterval) | ||
|
||
select { | ||
case <-s.ctx.Done(): | ||
return | ||
case <-timer.C: | ||
continue | ||
} | ||
} | ||
} |
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,61 @@ | ||
package supervisor | ||
|
||
import ( | ||
"context" | ||
"testing" | ||
"time" | ||
|
||
"github.com/didil/inhooks/pkg/models" | ||
"github.com/didil/inhooks/pkg/testsupport" | ||
"github.com/didil/inhooks/pkg/testsupport/mocks" | ||
"github.com/golang/mock/gomock" | ||
"github.com/stretchr/testify/assert" | ||
"go.uber.org/zap" | ||
) | ||
|
||
func TestSupervisor_HandleDoneQueue(t *testing.T) { | ||
appConf, err := testsupport.InitAppConfig(context.Background()) | ||
assert.NoError(t, err) | ||
|
||
appConf.Supervisor.DoneQueueCleanupInterval = 45 * time.Second | ||
appConf.Supervisor.DoneQueueCleanupDelay = 5 * time.Hour | ||
appConf.Supervisor.DoneQueueCleanupEnabled = true | ||
|
||
ctrl := gomock.NewController(t) | ||
defer ctrl.Finish() | ||
|
||
flowId1 := "flow-1" | ||
sinkID1 := "sink-1" | ||
|
||
sink1 := &models.Sink{ | ||
ID: sinkID1, | ||
} | ||
|
||
flow1 := &models.Flow{ | ||
ID: flowId1, | ||
Sinks: []*models.Sink{sink1}, | ||
} | ||
|
||
cleanupSvc := mocks.NewMockCleanupService(ctrl) | ||
|
||
logger, err := zap.NewDevelopment() | ||
assert.NoError(t, err) | ||
|
||
s := NewSupervisor( | ||
WithCleanupService(cleanupSvc), | ||
WithAppConfig(appConf), | ||
WithLogger(logger), | ||
) | ||
|
||
count := 2 | ||
cleanupSvc.EXPECT(). | ||
CleanupDoneQueue(gomock.Any(), flow1, sink1, appConf.Supervisor.DoneQueueCleanupDelay). | ||
DoAndReturn(func(ctx context.Context, f *models.Flow, sink *models.Sink, doneQueueCleanupDelay time.Duration) (int, error) { | ||
s.Shutdown() | ||
|
||
return count, nil | ||
}) | ||
|
||
s.HandleDoneQueue(flow1, sink1) | ||
|
||
} |
Oops, something went wrong.