-
Notifications
You must be signed in to change notification settings - Fork 14
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
[WIP] feat: delete unconfirmed users #414
[WIP] feat: delete unconfirmed users #414
Conversation
e4dc806
to
38daae5
Compare
Codecov Report
@@ Coverage Diff @@
## master #414 +/- ##
==========================================
- Coverage 94.57% 94.48% -0.09%
==========================================
Files 130 130
Lines 2341 2341
==========================================
- Hits 2214 2212 -2
- Misses 127 129 +2
Continue to review full report at Codecov.
|
Codecov Report
@@ Coverage Diff @@
## master #414 +/- ##
==========================================
+ Coverage 94.53% 94.59% +0.06%
==========================================
Files 130 132 +2
Lines 2377 2422 +45
==========================================
+ Hits 2247 2291 +44
- Misses 130 131 +1
Continue to review full report at Codecov.
|
38daae5
to
0dc98f2
Compare
doc/rfcs/inactive_users_cleanup.md
Outdated
|
||
### addInactiveUser(id, audience) | ||
Adds corresponding id record into `USERS_ACTIVATE` with score eq current time. Sets `USERS_ACTIVATE_AUDIENCE` hash to track audieces provided in registration process | ||
|
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.
User may have multiple audiences associated with it and right now its one of the problems during removal of such users. We may need to add tracking of used audiences as well when updateMetadata methods are used so that we have references to them and cleanup the data in them as well
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.
0dc98f2 audience tracking and cleanup already in commit. Possible solution, describes pre logic. Current logic in pre text
doc/rfcs/inactive_users_cleanup.md
Outdated
- Create additional set with id's of inactive users (`users-activate`). | ||
* Change activation process: remove successfully activated user id from `users-activate`. | ||
* Change registration process: insert inactive user id into `users-activate`. | ||
- Add repeating task with interval `config.incativeCleanupInterval`. |
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'd like to explore another venue where we perform cleanup for all inactive users during each registration and/or activation - process needs to very quick and written in LUA so that to be atomic - then we dont need to scan database and sets of pointers will be quick to use
src/utils/inactiveUsers.js
Outdated
const lock = await this.dlock.once(lockKey); | ||
const inactiveAccounts = await getInactiveUsers(redis, deleteInactiveAccounts); | ||
|
||
inactiveAccounts.forEach(async (acc) => { |
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.
async/await doesnt work that way - this will launch a bunch of unhandled promises - if you need to actually await for top level promises - the way to do it is to have await on Promise.all calls
const jobs = inactiveAccounts.map(...)
await Promise.all(jobs)
But I'd just suggest using Promise.map/mapSeries util from bluebird for that case
src/utils/inactiveUsers.js
Outdated
* @returns {NodeJS.Timeout} | ||
*/ | ||
function getCleanerTask(service) { | ||
const { deleteInactiveAccountsInterval: interval } = service.config; |
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.
multiple instances would conflict - we want to scale this up easily and, even though, lock would generally prevent things from breaking - I'd suggest to avoid them if possible
0dc98f2
to
6d5c429
Compare
src/actions/activate.js
Outdated
@@ -192,6 +195,8 @@ function activateAction({ params }) { | |||
}; | |||
|
|||
return Promise | |||
.resolve(['users:cleanup']) | |||
.spread(this.hook.bind(this)) |
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.
Promise.bind(this,['users:cleanup']
.spread(this.hook)
src/actions/register.js
Outdated
@@ -348,7 +349,11 @@ module.exports = async function registerUser({ params }) { | |||
.disposer(lockDisposer); | |||
|
|||
return Promise | |||
.using({ service, params }, acquireLock, performRegistration) | |||
.resolve(['users:cleanup']) | |||
.spread(service.hook.bind(service)) |
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.
same thing with bind & use async/await when possible
scripts/deleteInactivated.lua
Outdated
@@ -148,6 +148,7 @@ local function cleanInactiveUsers(idList) | |||
end | |||
|
|||
-- Command body | |||
redis.replicate_commands(); |
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.
makes sense to use that in other scripts?
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.
makes sense to use that in other scripts?
A bit, it stabilizes script behavior for older Redis versions and forces them to use 5.0 version behavior. IMHO Lua evaluation and processing on replica nodes bring additional overhead.
src/actions/activate.js
Outdated
@@ -126,6 +127,8 @@ function activateAccount(data, metadata) { | |||
.persist(userKey) | |||
.sadd(USERS_INDEX, userId); | |||
|
|||
removeInactiveUser(pipeline, userId); |
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.
не очень понятное название переменной, removeFromInactiveUsers
будет понятнее
а то кажется что юзера удаляем вообще
src/actions/register.js
Outdated
@@ -208,7 +209,7 @@ async function performRegistration({ service, params }) { | |||
pipeline.hset(USERS_USERNAME_TO_ID, username, userId); | |||
|
|||
if (activate === false && config.deleteInactiveAccounts >= 0) { | |||
pipeline.expire(userDataKey, config.deleteInactiveAccounts); | |||
addInactiveUser(pipeline, userId, audience); |
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.
тут тоже лучше addToInactiveUsers
src/actions/register.js
Outdated
@@ -348,7 +349,11 @@ module.exports = async function registerUser({ params }) { | |||
.disposer(lockDisposer); | |||
|
|||
return Promise | |||
.using({ service, params }, acquireLock, performRegistration) | |||
.bind(this, ['users:cleanup']) |
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.
клинап не стоит проводить каждый раз когда вызывается регистрация, лучше непосредственно внутри цикла когда взяли лок, операция потенциально дорогая, лучше делать до проверки есть ли такой юзер уже
src/utils/updateMetadata.js
Outdated
} | ||
|
||
async function setAudience(redis, userId, audience) { | ||
return redis.hset(USERS_AUDIENCE, userId, JSONStringify(audience)); |
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.
если 2 параллельных запроса будут обновлять audience, то одно может перезаписать другое
такие обновления нужно делать в lua (где нужно get+set) и как сайд эффект обновления данных внутри
соответственно это предполагает что метод updateMetadata будет обновлен и то где мы добавляем/удаляем поля будет происходить через lua, в конце выполнения луа мы будем проверять сколько полей по обновляемому audience у хэша, и если полей 0 - удаляем из сета audience по конкретному юзеру, а если > 0 , то добавляем
402c67b
to
acf50bb
Compare
b43a1f4
to
e1c48a0
Compare
rfcs/inactive_users_cleanup.md
Outdated
When Activation action executes, the hook starts before all actions. This strategy saves from inactive users | ||
that hit TTL but tried to pass the activation process. | ||
When Registration action executed, hook executed after `username` exists check. | ||
Handler not breaking general logic and not throwing errors, logs error into a logfile. |
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.
Hook handler doesn't breake general logic or throw errors, just logs it. (🤔 ?)
rfcs/inactive_users_cleanup.md
Outdated
|
||
## Registration and activation | ||
On Activation and Registration request event `users:cleanup` is emitted, and executed as a hook. | ||
When Activation action executes, the hook starts before all actions. This strategy saves from inactive users |
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.
When Activation action is called, the hook is invoked/fired/called first.
src/utils/inactiveUsers/index.js
Outdated
try { | ||
deletedUsers = await deleteInactiveUsers(redis, deleteInactiveAccounts); | ||
} catch (e) { | ||
this.log.error(e); |
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.
никогда не будет выбрасывать ошибку, это проблема - если нам нужно сапрессить ошибку, то следует это давать делать в функции вне этой
более того, если мы используем логгер, то корректно делать так:
log.error({ err: e }, message)
иначе ошибка не будет нормально десериализована
src/utils/updateMetadata.js
Outdated
// Stabilizes Lua script response | ||
function mapUpdateResponse(jsonStr) { | ||
const decodedData = JSONParse(jsonStr); | ||
const result = ld.map(decodedData, (audienceProcessResult) => { |
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.
можем ли мы использовать for ... of Object.enries(..)
?
); | ||
} | ||
return [...prev, ...delTokenActions]; | ||
}, []); |
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.
- const work = userIds.reduce((prev, id) => {
- const delTokenActions = [];
- for (const action of actions) {
- delTokenActions.push(
- this.tokenManager.remove({ id, action })
- );
- }
- return [...prev, ...delTokenActions];
- }, []);
+ const work = userIds.reduce((accum, id) => {
+ for (const action of actions) {
+ accum.push(this.tokenManager.remove({ id, action }));
+ }
+ return accum;
+ }, []);
* doc lex and typo fixes
* cluster bug fix
* updateMetadata no lodash * doc addons * cleanUsers supperess error parameter
* organization action tokens remove * user action tokens remove
* fs.promises use * delete token array reassignment fix * doc update
* linter parantheses fix
9231318
to
9b773a5
Compare
25186e7
to
d9f0aad
Compare
Closed as duplicate #430 |
Please comment.