-
Notifications
You must be signed in to change notification settings - Fork 333
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Redis: Backfill last acknowledged #1288
Redis: Backfill last acknowledged #1288
Conversation
All (the pull request submitter and all commit authors) CLAs are signed, but one or more commits were authored or co-authored by someone other than the pull request submitter. We need to confirm that all authors are ok with their commits being contributed to this project. Please have them confirm that by leaving a comment that contains only Note to project maintainer: There may be cases where the author cannot leave a comment, or the comment is not properly detected as consent. In those cases, you can manually confirm consent of the commit author(s), and set the ℹ️ Googlers: Go here for more info. |
Hey @aLekSer, look at this. "Please have them confirm that by leaving a comment that contains only |
All (the pull request submitter and all commit authors) CLAs are signed, but one or more commits were authored or co-authored by someone other than the pull request submitter. We need to confirm that all authors are ok with their commits being contributed to this project. Please have them confirm that by leaving a comment that contains only Note to project maintainer: There may be cases where the author cannot leave a comment, or the comment is not properly detected as consent. In those cases, you can manually confirm consent of the commit author(s), and set the ℹ️ Googlers: Go here for more info. |
@hsorellana that would not be an issue once Alexey's PR would be merged. Then this PR would have only my changes. |
967f4d7
to
8aa7ad8
Compare
8aa7ad8
to
bc567a4
Compare
internal/statestore/backfill.go
Outdated
} | ||
|
||
// DeleteExpiredBackfillIDs - delete expired BackfillIDs from a sorted set | ||
func (rb *redisBackend) DeleteExpiredBackfillIDs(ctx context.Context, backfillIDs []string) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not clear to me how this will be used?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be used by a cleanup service:
1st step backfillIds, err := GetExpiredBackfills()
2nd - Return associated tickets for all Backfills
back to the pool.
Some AcknowledgeBackfill() could occur in between
3rd step DeleteExpiredBackfillIDs(backfillIds)
, note that there could be more expired Backfills but we only delete thos e processed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not just use the same mechanics as the delete method? Reduces implementation complexity and possible bugs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some DeleteBackfill() request can have some issues, others no, so Backend have an opportunity to select on its own.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got an idea now. This can be done as part of DeleteBackfill
internal/statestore/backfill.go
Outdated
defer handleConnectionClose(&redisConn) | ||
|
||
// the same TTL is used for both Backfills and pendingRelease Tickets | ||
ttl := rb.cfg.GetDuration("pendingReleaseTimeout") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be something like 80% to 90% of ttl, since the backfill should expire before the tickets waiting on it, as the tickets timing out but the backfill NOT timing out but being acknowledged would be a problem.
} | ||
defer handleConnectionClose(&redisConn) | ||
|
||
currentTime := time.Now().UnixNano() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Laremere is there an agreement of not using UTC()? time.Now()
is used everywhere.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Assumption is all computers in a cluster will have clocks that are synchronized enough.
internal/statestore/backfill.go
Outdated
} | ||
defer handleConnectionClose(&redisConn) | ||
|
||
cmds := make([]interface{}, 0, len(backfillIDs)+1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good one
internal/statestore/backfill_test.go
Outdated
@@ -301,3 +302,54 @@ func TestDeleteBackfill(t *testing.T) { | |||
require.Equal(t, codes.Unavailable.String(), status.Convert(err).Code().String()) | |||
require.Contains(t, status.Convert(err).Message(), "DeleteBackfill, id: 12345, failed to connect to redis:") | |||
} | |||
|
|||
func TestAcknowledgeBackfill(t *testing.T) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, it's not an AcknowledgeBackfill unit test now. You test here the whole acknowledge scenario. This is nice, but I would call this test something like TestAcknowledgeLifecycle.
To write a unit test you need to test exactly one single unit (method) under the different conditions.
Talking about backfill, it's enough to check that after calling AcknowledgeBackfill the passed ID is added to backfill_last_ack_time sorted set.
So I suggest you to rename this method and add TestAcknowledgeBackfill where you will test only AcknowledgeBackfill method. What do you think?
@@ -84,6 +84,15 @@ type Service interface { | |||
|
|||
// NewMutex returns an interface of a new distributed mutex with given name | |||
NewMutex(key string) RedisLocker | |||
|
|||
// AcknowledgeBackfill - store Backfill's last accessed time | |||
AcknowledgeBackfill(ctx context.Context, id string) error |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: I suggest sticking to the current comments format like:
AcknowledgeBackfill stores Backfill's last accessed time
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// AcknowledgeBackfill - stores Backfill's last acknowledgement time
internal/statestore/backfill.go
Outdated
_, err = redisConn.Do("ZADD", cmds...) | ||
if err != nil { | ||
err = errors.Wrap(err, "failed to store backfill's last acknowledgement time") | ||
return status.Error(codes.Internal, err.Error()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: I think here you can use status.Errorf(...., err) as you do above. Probably we need to elaborate the same error logging format across this file.
internal/statestore/backfill.go
Outdated
endTimeInt := curTime.Add(-ttl).UnixNano() | ||
startTimeInt := 0 | ||
|
||
// Filter out tickets that are fetched but not assigned within TTL time (ms). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Filter out tickets backfill ids ...
@Laremere Scott, note that I would add config parameter for Backfill Time in a separate PR to reduce complexity of this PR. It is ready to review, hope that I got all comments right. |
Store Backfills in a sorted set to retrieve all expired backfills by time.
It has the same input as output of GetExpiredBackfillIDs function. Some backfillIDs can be acknowledged in between,But this should not affect the work of cleanup service.
b722c97
to
fbc7ff6
Compare
Force push is adding more work to re-review. Friendly reminder to not do that (otherwise "see changes since your last view" breaks.) |
@Laremere Do you mean that we should not perform rebase on a new master also? I am used to do it on Agones. |
Read and update Redis content to make them independent of other functions.
internal/statestore/backfill.go
Outdated
} | ||
|
||
// deleteExpiredBackfillID - delete expired BackfillID from a sorted set | ||
func (rb *redisBackend) deleteExpiredBackfillID(ctx context.Context, backfillID string) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you can pass redis connection here from a caller method. what do you think?
internal/statestore/backfill.go
Outdated
_, err := conn.Do("ZREM", cmds...) | ||
if err != nil { | ||
return status.Errorf(codes.Internal, "failed to delete expired backfill ID %v", | ||
errors.Wrap(err, "failed to delete expired backfill ID from Sorted Set")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You use the same two error messages here, please clean it up.
internal/statestore/backfill_test.go
Outdated
} | ||
|
||
// test that Backfill also deleted from last acknowledged sorted set | ||
_, err = redis.Int64(conn.Do("ZSCORE", backfillLastAckTime, tc.backfillID)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
there is no point to perform this check if you are testing the case with an empty backfill id. I think it's better to put it under if tc.backfillID != "" statement.
We squash merge all PRs anyways, so rebasing isn't necessary. |
internal/statestore/backfill_test.go
Outdated
// test that Backfill last acknowledged is in a sorted set | ||
ts, err := redis.Int64(conn.Do("ZSCORE", backfillLastAckTime, tc.backfillID)) | ||
require.NoError(t, err) | ||
require.True(t, ts > 0, "timestamp is valid") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
timestamp is NOT valid
Fixing error returned and text in Test failure.
} | ||
defer handleConnectionClose(&redisConn) | ||
|
||
// Use a fraction 80% of pendingRelease Tickets TTL |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will update this in this or the next PR - Create a new config parameter.
internal/statestore/backfill.go
Outdated
cmds := make([]interface{}, 0) | ||
cmds = append(cmds, backfillLastAckTime, currentTime, id) | ||
|
||
_, err = redisConn.Do("ZADD", cmds...) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's no need for the oddness with cmds, just do:
_, err = redisConn.Do("ZADD", backfillLastAckTime, currentTime, id)
@@ -28,7 +29,8 @@ import ( | |||
) | |||
|
|||
const ( | |||
allBackfills = "allBackfills" | |||
backfillLastAckTime = "backfill_last_ack_time" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This also needs to be set during backfill creation, so that a server which never starts up can be cleaned up.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, that's true, made a TODO for this I wanted to make this as part of separate PR.
But I think can be added here also.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added additional call acknowledgeBackfill to StateStore CreateBackfill.
internal/statestore/backfill.go
Outdated
cmds := make([]interface{}, 0, 2) | ||
cmds = append(cmds, backfillLastAckTime, backfillID) | ||
|
||
_, err := conn.Do("ZREM", cmds...) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here too.
internal/statestore/backfill_test.go
Outdated
require.NoError(t, err) | ||
err = service.AcknowledgeBackfill(ctx, bf2) | ||
require.NoError(t, err) | ||
// Sleep until the pending release expired and verify we still have all the tickets |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After acknowledging, but before sleeping, verify the expired backfills.
Fixed tests and Redis commands.
The better place to do updates on CreateBackfill. Reuse one connection, added a helper function for that.
internal/statestore/backfill.go
Outdated
@@ -59,6 +61,8 @@ func (rb *redisBackend) CreateBackfill(ctx context.Context, backfill *pb.Backfil | |||
if res.(int64) == 0 { | |||
return status.Errorf(codes.AlreadyExists, "backfill already exists, id: %s", backfill.GetId()) | |||
} | |||
|
|||
acknowledgeBackfill(redisConn, backfill.GetId()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you are ignoring an error here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, thanks for noticing, will update
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TestCreateBackfill should be updated according to this new behavior.
Don't CreateBackfill prior to running AckBackfill it is covered in CreateBackfill.
af6645c
to
a494c69
Compare
@Laremere all comments got resolved. |
What this PR does / Why we need it:
Add SortedSet to store Backfill in a sorted order to retrieve all expired Backfills.
Which issue(s) this PR fixes:
Work on #1240
Special notes for your reviewer:
This PR is dependent on StateStore changes.
We can select a specific TTL config variable later during implementation of Cleanup Service.