-
Notifications
You must be signed in to change notification settings - Fork 325
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
Clean up SCIM-invited users with expired invitation #1264
Conversation
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 are no tests whatsoever. Not sure how to test this, and where, perhaps we can think about this together.
Sorry, lots of ideas how to change this. Do you want to go through them together?
else pure users' | ||
|
||
safeForever :: (MonadIO m, MonadLogger m, MonadCatch m) => String -> m () -> m () | ||
safeForever funName action = |
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.
I think this is duplicating existing code. Can we move the old function somewhere from where we can call it here as well? Or is it awkward to generalize? (Not that important.)
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 already duplicated 3 times. I created a separate issue for this https://wearezeta.atlassian.net/browse/SQSERVICES-146
services/brig/src/Brig/Run.hs
Outdated
isPendingInvitation <- (Just PendingInvitation ==) <$> lookupStatus uid | ||
invExpired <- isNothing <$> Data.lookupInvitation tid (coerce uid) | ||
when (isPendingInvitation && invExpired) $ do | ||
API.deleteUserNoVerify uid |
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.
Yeah, we should keep an eye on this.
I suggest we:
- add a function
deleteUsersNoVerify
that deletes many users - we can implement it for now using
mapM_
- but we should call it only once per page of
PendingActivationExpiration
- and keep a metric that tells us how often this is called
- ideally, we should also have a metric that tells us how much time & space deletion takes, but that may be too much work for now.
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.
To avoid scheduling too many deletions too fast, I added a throttling delay, which will schedule at most 2 items per second (170k per day).
Implemented suggestions 1-4
f744043
to
3e25585
Compare
d978ef4
to
d6b98fe
Compare
I think use deletions are idempotent; so you can attempt to delete the same user over and over again with the same result. It might be nice to (if not already done, I didn't look at the code, just replying to the previous comment), upon regular user deletions, as part of the deletion onEvent flow also delete the entry in this new expiry-tracking table (which will be a no-op if no entry under that key exists) |
If the GC loop cannot find a user then isPendingInvitation <- (Just PendingInvitation ==) <$> API.lookupStatus uid The user then skips deletion and the entry is removed from the expiry table. Currently there are no calls to remove an entry from the expiry table except for the loop. The GC loop removes entries from the expiry table by itself. I have slight prefrence for not adding it as a hook to the user deletion, because this creates two call sites that remove entries from the table, one of which is not even strictly necessary. I have another clarification question for @fisx : Is it possible that spar creates a second invitation for a user after the first invitation is expired? If so then I need to implement this: "every user has at most on entry in the expiry table, which will be overwritten with every invitation". |
If needed, this implementation can be achieved easily with the following: (
user uuid
, expires_at timestamp
- , primary key (user, expires_at)
+ , primary key (user)
) I believe at this point you don't need to have multiple entries for the same user, so you don't need a composite primary key. |
Thanks! Applied. |
@@ -38,7 +38,6 @@ data UserPendingActivation = UserPendingActivation | |||
} | |||
deriving stock (Eq, Show, Ord) | |||
|
|||
-- | Note: Call this function only after an invitation for the user has been created |
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 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.
ah, nice. Didn't see that.
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.
I've committed one more change, pls check whether this makes sense, and if you agree
This reverts commit 1d015e2.
Implements the clean up job discussed in #1238
https://wearezeta.atlassian.net/browse/SQSERVICES-26