Firebase Authentication の匿名ユーザーを削除する
Firebase Authentication で匿名ユーザーを使う場合, プランに関わらずユーザーアカウントの上限は1億までになっている。
つまり1億PV以上になると新たに匿名ユーザーを作れず破滅する可能性があるので, 定期的にユーザーの削除が必要になってくる。
めちゃめちゃバズらないと1億PVはそうそう行かなそうだけど, 可能性はゼロではないので対策しておいたほうがよさそう。
匿名ユーザーの削除は Firebase 側では自動でやってくれたりはしないので、自動でスクリプトなどを用意する必要がある。
package main import ( "context" "fmt" "log" "os" "time" firebase "firebase.google.com/go/v4" "firebase.google.com/go/v4/auth" "google.golang.org/api/iterator" ) const maxDeletableUsersCount = 1000 func main() { ctx := context.Background() app, err := firebase.NewApp(ctx, nil) if err != nil { log.Fatalf("error new firebase app: %s\n", err) } auth, err := app.Auth(ctx) if err != nil { log.Fatalf("error create auth client: %s\n", err) } users, err := getInactiveAnonymousUsers(ctx, auth) if err != nil { log.Fatalf("error get users: %s\n", err) } fmt.Printf("%v個の匿名ユーザーアカウントが削除されます。よろしいですか? [y/n]:", len(users)) var answer string fmt.Scan(&answer) if answer != "y" && answer != "Y" { os.Exit(1) } successCount, failureCount, err := deleteUsers(ctx, auth, users) if err != nil { log.Fatalf("error delete users, error:%s, success:%v, failure:%v\n", err, successCount, failureCount) } fmt.Printf("delete user complete, success:%v, failure:%v\n", successCount, failureCount) } func getInactiveAnonymousUsers(ctx context.Context, auth *auth.Client) ([]string, error) { var users []string iter := auth.Users(ctx, "") for { user, err := iter.Next() if err == iterator.Done { break } if err != nil { return nil, err } // 匿名ユーザーではない場合削除しない // 匿名ユーザーしかいない場合は問答無用で削除してしまってよさそう if len(user.ProviderUserInfo) > 0 { continue } // LastLogInTimeStamp は ミリ秒 までを含めた値が返ってくるのに対し // time.Unix()は 秒 までを含めた値が返ってくるので, 比較できるように丸める // https://firebase.google.com/docs/reference/admin/java/reference/com/google/firebase/auth/UserMetadata#public-long-getlastsignintimestamp lastLogInTimestamp := user.UserMetadata.LastLogInTimestamp / time.Second.Milliseconds() // 1ヶ月以上ログインしていないユーザーを削除対象にする if lastLogInTimestamp < time.Now().AddDate(0, -1, 0).Unix() { users = append(users, user.UID) fmt.Printf("deletable user, uid:%s, last login:%v \n", user.UID, time.Unix(lastLogInTimestamp, 0)) } } return users, nil } func deleteUsers(ctx context.Context, auth *auth.Client, uIDs []string) (successCount int, failureCount int, err error) { successCount = 0 failureCount = 0 // auth.DeleteUsers で削除できるユーザーは1000件までなので, スライスを1000件に分割して繰り返し実行する for len(uIDs) > maxDeletableUsersCount { slice := uIDs[0:maxDeletableUsersCount] uIDs = uIDs[maxDeletableUsersCount:] result, err := auth.DeleteUsers(ctx, slice) if err != nil { return successCount, failureCount, err } successCount += result.SuccessCount failureCount += result.FailureCount } result, err := auth.DeleteUsers(ctx, uIDs) if err != nil { return successCount, failureCount, err } successCount += result.SuccessCount failureCount += result.FailureCount return successCount, failureCount, nil }
あとはこれを cron 等で定期的に実行してあげれば安心。