Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 05b8ba0

Browse files
wilfred-asomaniiStorj Robot
authored andcommitted
satellite/dbcleanup: cleanup users pending deletion
This change updates the pending deletion data cleanup chore to also delete users that are pending deletion and are not frozen and their data. This completes the abbreviated account deletion process. Issue: storj/storj-private#1299 Change-Id: I3003083672c15bb0341da935dd105cf23966869c
1 parent 35e47e0 commit 05b8ba0

File tree

5 files changed

+485
-85
lines changed

5 files changed

+485
-85
lines changed

satellite/console/dbcleanup/pendingdelete/chore.go

Lines changed: 188 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,11 @@ import (
2828

2929
var (
3030
// Error defines the pendingdelete chore errors class.
31-
Error = errs.Class("pendingdelete")
32-
mon = monkit.Package()
33-
userDataTask = "user-pending-deletion"
34-
projectDataTask = "project-pending-deletion"
31+
Error = errs.Class("pendingdelete")
32+
mon = monkit.Package()
33+
frozenDataTask = "frozen-user-deletion"
34+
projectDataTask = "project-pending-deletion"
35+
pendingDeleteUserDataTask = "user-pending-deletion"
3536
)
3637

3738
// Config contains configuration for pending deletion project cleanup.
@@ -42,6 +43,7 @@ type Config struct {
4243
DeleteConcurrency int `help:"how many delete workers to run at a time" default:"1"`
4344

4445
Project DeleteTypeConfig
46+
User DeleteTypeConfig
4547
ViolationFreeze DeleteTypeConfig
4648
BillingFreeze DeleteTypeConfig
4749
TrialFreeze DeleteTypeConfig
@@ -114,10 +116,19 @@ func (chore *Chore) Run(ctx context.Context) (err error) {
114116

115117
if len(chore.enabledFrozenDeleteTypes()) != 0 {
116118
group.Go(func() error {
117-
return chore.runDeleteUserData(ctx)
119+
return chore.runDeleteFrozenUsers(ctx)
118120
})
119121
}
120122

123+
if chore.config.User.Enabled {
124+
group.Go(func() error {
125+
return chore.runDeletePendingDeletionUsers(ctx)
126+
})
127+
} else {
128+
chore.log.Info("skipping deleting pending deletion users because it is disabled in config",
129+
zap.String("task", pendingDeleteUserDataTask))
130+
}
131+
121132
return group.Wait()
122133
})
123134
}
@@ -172,7 +183,7 @@ func (chore *Chore) runDeleteProjects(ctx context.Context) (err error) {
172183
}
173184

174185
if project.Status == nil || *project.Status != console.ProjectPendingDeletion {
175-
chore.log.Error("project not marked pending deletion, skipping",
186+
chore.log.Info("project not marked pending deletion, skipping",
176187
zap.String("task", projectDataTask),
177188
zap.String("projectID", p.ProjectID.String()),
178189
zap.String("userID", p.OwnerID.String()),
@@ -208,6 +219,115 @@ func (chore *Chore) runDeleteProjects(ctx context.Context) (err error) {
208219
return Error.Wrap(errGrp.Err())
209220
}
210221

222+
func (chore *Chore) runDeletePendingDeletionUsers(ctx context.Context) (err error) {
223+
defer mon.Task()(&ctx)(&err)
224+
225+
chore.log.Info("deleting pending deletion users", zap.String("task", pendingDeleteUserDataTask))
226+
227+
mu := new(sync.Mutex)
228+
var errGrp errs.Group
229+
230+
addErr := func(err error) {
231+
mu.Lock()
232+
errGrp.Add(err)
233+
mu.Unlock()
234+
}
235+
236+
errorLog := func(msg string, err2 error, args ...zap.Field) {
237+
chore.log.Error(msg,
238+
zap.String("task", pendingDeleteUserDataTask),
239+
zap.Error(err2),
240+
)
241+
}
242+
243+
var skippedUsers, deletedUsers, deletedProjects atomic.Int64
244+
hasNext := true
245+
for hasNext {
246+
idsPage, err := chore.store.Users().ListPendingDeletionBefore(
247+
ctx,
248+
chore.config.ListLimit, chore.nowFn().Add(-chore.config.User.BufferTime),
249+
)
250+
if err != nil {
251+
chore.log.Error("failed to get users for deletion",
252+
zap.String("task", pendingDeleteUserDataTask), zap.Error(err))
253+
return err
254+
}
255+
hasNext = idsPage.HasNext
256+
257+
if !hasNext && len(idsPage.IDs) == 0 {
258+
break
259+
}
260+
261+
limiter := sync2.NewLimiter(chore.config.DeleteConcurrency)
262+
263+
for _, userID := range idsPage.IDs {
264+
limiter.Go(ctx, func() {
265+
// confirm user still marked pending deletion
266+
user, err := chore.store.Users().Get(ctx, userID)
267+
if err != nil {
268+
chore.log.Error("failed to get user for deletion",
269+
zap.String("task", pendingDeleteUserDataTask),
270+
zap.String("userID", userID.String()),
271+
zap.Error(err),
272+
)
273+
addErr(err)
274+
return
275+
}
276+
277+
if user.Status != console.PendingDeletion {
278+
chore.log.Info("user not marked pending deletion, skipping",
279+
zap.String("task", pendingDeleteUserDataTask),
280+
zap.String("userID", userID.String()),
281+
)
282+
skippedUsers.Add(1)
283+
return
284+
}
285+
286+
projects, err := chore.store.Projects().GetActiveByUserID(ctx, userID)
287+
if err != nil {
288+
errorLog("failed to get projects for deletion", err, zap.String("userID", userID.String()))
289+
addErr(err)
290+
return
291+
}
292+
293+
for _, project := range projects {
294+
err := chore.deleteData(ctx, project.ID, userID, pendingDeleteUserDataTask)
295+
if err != nil {
296+
addErr(err)
297+
return
298+
}
299+
300+
err = chore.disableProject(ctx, project.ID, project.PublicID, userID, pendingDeleteUserDataTask)
301+
if err != nil {
302+
addErr(err)
303+
return
304+
}
305+
306+
deletedProjects.Add(1)
307+
}
308+
309+
err = chore.deactivateUser(ctx, userID, nil, pendingDeleteUserDataTask)
310+
if err != nil {
311+
addErr(err)
312+
return
313+
}
314+
deletedUsers.Add(1)
315+
})
316+
}
317+
318+
limiter.Wait()
319+
}
320+
321+
chore.log.Info("finished deleting users",
322+
zap.String("task", pendingDeleteUserDataTask),
323+
zap.Int64("skipped_users", skippedUsers.Load()),
324+
zap.Int64("deleted_users", deletedUsers.Load()),
325+
zap.Int64("deleted_projects", deletedProjects.Load()),
326+
)
327+
328+
return Error.Wrap(errGrp.Err())
329+
}
330+
211331
func (chore *Chore) enabledFrozenDeleteTypes() []console.EventTypeAndTime {
212332
var eventTypes []console.EventTypeAndTime
213333
if chore.config.ViolationFreeze.Enabled {
@@ -230,18 +350,18 @@ func (chore *Chore) enabledFrozenDeleteTypes() []console.EventTypeAndTime {
230350
}
231351
if len(eventTypes) == 0 {
232352
chore.log.Info("no freeze event types are enabled, skipping unpaid data deletion",
233-
zap.String("task", userDataTask),
353+
zap.String("task", frozenDataTask),
234354
)
235355
return nil
236356
}
237357

238358
return eventTypes
239359
}
240360

241-
func (chore *Chore) runDeleteUserData(ctx context.Context) (err error) {
361+
func (chore *Chore) runDeleteFrozenUsers(ctx context.Context) (err error) {
242362
defer mon.Task()(&ctx)(&err)
243363

244-
chore.log.Info("deleting pending deletion users and data", zap.String("task", userDataTask))
364+
chore.log.Info("deleting pending deletion users and data", zap.String("task", frozenDataTask))
245365

246366
mu := new(sync.Mutex)
247367
var errGrp errs.Group
@@ -254,7 +374,7 @@ func (chore *Chore) runDeleteUserData(ctx context.Context) (err error) {
254374

255375
errorLog := func(msg string, err2 error, args ...zap.Field) {
256376
chore.log.Error(msg,
257-
zap.String("task", userDataTask),
377+
zap.String("task", frozenDataTask),
258378
zap.Error(err2),
259379
)
260380
}
@@ -290,9 +410,9 @@ func (chore *Chore) runDeleteUserData(ctx context.Context) (err error) {
290410
}
291411

292412
if user.Status != console.PendingDeletion {
293-
chore.log.Error("user not marked pending deletion, skipping",
413+
chore.log.Info("user not marked pending deletion, skipping",
294414
zap.String("userID", event.UserID.String()),
295-
zap.String("task", userDataTask),
415+
zap.String("task", frozenDataTask),
296416
)
297417
skippedUsers.Add(1)
298418
return
@@ -306,13 +426,13 @@ func (chore *Chore) runDeleteUserData(ctx context.Context) (err error) {
306426
}
307427

308428
for _, project := range projects {
309-
err := chore.deleteData(ctx, project.ID, event.UserID, userDataTask)
429+
err := chore.deleteData(ctx, project.ID, event.UserID, frozenDataTask)
310430
if err != nil {
311431
addErr(err)
312432
return
313433
}
314434

315-
err = chore.disableProject(ctx, project.ID, project.PublicID, event.UserID, userDataTask)
435+
err = chore.disableProject(ctx, project.ID, project.PublicID, event.UserID, frozenDataTask)
316436
if err != nil {
317437
addErr(err)
318438
return
@@ -321,47 +441,20 @@ func (chore *Chore) runDeleteUserData(ctx context.Context) (err error) {
321441
deletedProjects.Add(1)
322442
}
323443

324-
// this is ideally part of chore.deactivateUser, but we cannot use a db object in
325-
// a transaction, which accounts.CreditCards().RemoveAll does internally.
326-
err = chore.accounts.CreditCards().RemoveAll(ctx, event.UserID)
327-
if err != nil {
328-
chore.log.Error("failed to remove user credit cards",
329-
zap.String("userID", event.UserID.String()),
330-
zap.Error(err),
331-
)
332-
addErr(err)
333-
return
334-
}
335-
336-
err = chore.store.WithTx(ctx, func(ctx context.Context, tx console.DBTx) error {
337-
err = chore.deactivateUser(ctx, tx, event)
338-
if err != nil {
339-
return err
340-
}
341-
342-
// remove the freeze event so this user is not retrieved by GetEscalatedEventsOlderThanToDelete
343-
err = tx.AccountFreezeEvents().DeleteByUserIDAndEvent(ctx, event.UserID, event.Type)
344-
if err != nil {
345-
chore.log.Error("failed to remove freeze event", zap.String("userID", event.UserID.String()), zap.Error(err))
346-
return err
347-
}
348-
349-
deletedUsers.Add(1)
350-
351-
return nil
352-
})
444+
err = chore.deactivateUser(ctx, event.UserID, &event.Type, frozenDataTask)
353445
if err != nil {
354446
addErr(err)
355447
return
356448
}
449+
deletedUsers.Add(1)
357450
})
358451
}
359452

360453
limiter.Wait()
361454
}
362455

363456
chore.log.Info("finished deleting pending deletion users and data",
364-
zap.String("task", userDataTask),
457+
zap.String("task", frozenDataTask),
365458
zap.Int64("skipped_users", skippedUsers.Load()),
366459
zap.Int64("deleted_users", deletedUsers.Load()),
367460
zap.Int64("deleted_projects", deletedProjects.Load()),
@@ -485,44 +578,66 @@ func (chore *Chore) disableProject(ctx context.Context, projectID, projectPublic
485578
})
486579
}
487580

488-
func (chore *Chore) deactivateUser(ctx context.Context, tx console.DBTx, event console.EventWithUser) (err error) {
489-
_, err = tx.WebappSessions().DeleteAllByUserID(ctx, event.UserID)
581+
func (chore *Chore) deactivateUser(ctx context.Context, userID uuid.UUID, freezeEventType *console.AccountFreezeEventType, task string) (err error) {
582+
err = chore.accounts.CreditCards().RemoveAll(ctx, userID)
490583
if err != nil {
491-
chore.log.Error("failed to remove webapp sessions for user",
492-
zap.String("task", userDataTask),
493-
zap.String("userID", event.UserID.String()),
584+
chore.log.Error("failed to remove user credit cards",
585+
zap.String("task", task),
586+
zap.String("userID", userID.String()),
494587
zap.Error(err),
495588
)
496589
return err
497590
}
498591

499-
deactivatedEmail := fmt.Sprintf("deactivated+%[email protected]", event.UserID.String())
500-
status := console.Deleted
592+
return chore.store.WithTx(ctx, func(ctx context.Context, tx console.DBTx) error {
593+
_, err = tx.WebappSessions().DeleteAllByUserID(ctx, userID)
594+
if err != nil {
595+
chore.log.Error("failed to remove webapp sessions for user",
596+
zap.String("task", task),
597+
zap.String("userID", userID.String()),
598+
zap.Error(err),
599+
)
600+
return err
601+
}
501602

502-
err = tx.Users().Update(ctx, event.UserID, console.UpdateUserRequest{
503-
FullName: new(string),
504-
ShortName: new(*string),
505-
Email: &deactivatedEmail,
506-
Status: &status,
507-
ExternalID: new(*string),
508-
EmailChangeVerificationStep: new(int),
509-
})
510-
if err != nil {
511-
chore.log.Error("failed to update user status to Deleted",
512-
zap.String("task", userDataTask),
513-
zap.String("userID", event.UserID.String()),
514-
zap.Error(err),
515-
)
516-
return err
517-
}
603+
deactivatedEmail := fmt.Sprintf("deactivated+%[email protected]", userID.String())
604+
status := console.Deleted
605+
err = tx.Users().Update(ctx, userID, console.UpdateUserRequest{
606+
FullName: new(string),
607+
ShortName: new(*string),
608+
Email: &deactivatedEmail,
609+
Status: &status,
610+
ExternalID: new(*string),
611+
EmailChangeVerificationStep: new(int),
612+
})
613+
if err != nil {
614+
chore.log.Error("failed to update user status to Deleted",
615+
zap.String("task", task),
616+
zap.String("userID", userID.String()),
617+
zap.Error(err),
618+
)
619+
return err
620+
}
518621

519-
chore.log.Info(
520-
"user deactivated",
521-
zap.String("task", userDataTask),
522-
zap.String("userID", event.UserID.String()),
523-
)
622+
if freezeEventType != nil {
623+
err = tx.AccountFreezeEvents().DeleteByUserIDAndEvent(ctx, userID, *freezeEventType)
624+
if err != nil {
625+
chore.log.Error("failed to remove freeze event",
626+
zap.String("task", task),
627+
zap.String("userID", userID.String()),
628+
zap.Error(err))
629+
return err
630+
}
631+
}
524632

525-
return nil
633+
chore.log.Info(
634+
"user deactivated",
635+
zap.String("task", task),
636+
zap.String("userID", userID.String()),
637+
)
638+
639+
return nil
640+
})
526641
}
527642

528643
// Close stops chore.

0 commit comments

Comments
 (0)