-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* rework on new MetaData
- Loading branch information
Showing
10 changed files
with
355 additions
and
70 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
# Inactive user cleanup | ||
|
||
## Overview and motivation | ||
For users who didn't pass the activation process, the service using TTL keyspace cleanup and only `Internal Data` deleted. | ||
A lot of linked keys remain in the database, and this leads to keyspace pollution. | ||
For better data handling and clean database structure, I introduce some changes in service logic: | ||
|
||
## General defs | ||
- `inactive-users` [Described here](#inactive-users-index) | ||
- `user-audiences` [Described here](user_and_organization_meta_update.md) | ||
|
||
## `inactive-users` index | ||
It's an Additional Redis Sorted Set which contains the list of IDs of the `inactive` users. | ||
Each item score is equal to the `timestamp` set on user creation. | ||
To avoid hardcoded SET name new `USERS_INACTIVATED` constant introduced. | ||
|
||
#### Registration process | ||
When the user succeeds registration but activation not requested, the new entry added to `inactive-users`. | ||
|
||
#### Activation process | ||
When the user succeeds activation the entry deleted from `inactive-users`. | ||
|
||
#### Index cleanup | ||
On `registration` cleanup method executes before user creation. | ||
On `activation` cleanup method executes before any checks performed by `activation` action. | ||
* Uses `dlock` to be sure that only one process runs. | ||
* Gets outdated user list and deletes them. | ||
|
||
## TODO Organization Members --- Need additional information | ||
The `organization add member` process doesn't have validation whether the user passed activation and allows | ||
inviting inactive users into an organization. | ||
|
||
* Should We Delete Organization Members? | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
const Promise = require('bluebird'); | ||
const RedisInactiveUser = require('./redis/inactive-user'); | ||
const User = require('../user/user'); | ||
|
||
class InactiveUser { | ||
constructor(service) { | ||
this.service = service; | ||
this.backend = new RedisInactiveUser(service.redis); | ||
} | ||
|
||
acquireLock(lockName) { | ||
const { dlock } = this.service; | ||
return dlock | ||
.once(lockName) | ||
.disposer((l) => { | ||
l.release().reflect(); | ||
}); | ||
} | ||
|
||
add(userId, created, pipeline = undefined) { | ||
return this.backend.add(userId, created, pipeline); | ||
} | ||
|
||
delete(userId) { | ||
return this.backend.delete(userId); | ||
} | ||
|
||
deleteInactive(userTTL) { | ||
return Promise | ||
.using(this.acquireLock('delete-inactive-users'), () => this._deleteInactive(userTTL)) | ||
.catch({ name: 'LockAcquisitionError' }, () => {}); | ||
} | ||
|
||
async _deleteInactive(userTTL) { | ||
const user = new User(this.service); | ||
const inactiveUsers = await this.backend.get(userTTL); | ||
const work = []; | ||
|
||
for (const userId of inactiveUsers) { | ||
work.push( | ||
user | ||
.delete(userId) | ||
.then(() => this.backend.delete(userId)) | ||
); | ||
} | ||
await Promise.all(work); | ||
|
||
return inactiveUsers.length; | ||
} | ||
} | ||
|
||
module.exports = InactiveUser; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
class InactiveUser { | ||
static REDIS_KEY = 'users-inactivated'; | ||
|
||
constructor(redis) { | ||
this.redis = redis; | ||
} | ||
|
||
get(interval) { | ||
const expire = Date.now() - (interval * 1000); | ||
return this.redis.zrangebyscore(InactiveUser.REDIS_KEY, '-inf', expire); | ||
} | ||
|
||
add(userId, createTime, redis = this.redis) { | ||
return redis.zadd(InactiveUser.REDIS_KEY, createTime, userId); | ||
} | ||
|
||
delete(userId, redis = this.redis) { | ||
return redis.zrem(InactiveUser.REDIS_KEY, userId); | ||
} | ||
} | ||
|
||
module.exports = InactiveUser; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
const Promise = require('bluebird'); | ||
const getInternalData = require('../../userData/get-internal-data'); | ||
|
||
const { | ||
USERS_ALIAS_TO_ID, | ||
USERS_SSO_TO_ID, | ||
USERS_USERNAME_TO_ID, | ||
USERS_INDEX, | ||
USERS_PUBLIC_INDEX, | ||
USERS_DATA, | ||
USERS_TOKENS, | ||
} = require('../../../constants'); | ||
|
||
class User { | ||
constructor(redis) { | ||
this.redis = redis; | ||
} | ||
|
||
static getUserDataKey(userId) { | ||
return `${userId}!${USERS_DATA}`; | ||
} | ||
|
||
flushCaches() { | ||
const now = Date.now(); | ||
return Promise.all([ | ||
this.redis.fsortBust(USERS_INDEX, now), | ||
this.redis.fsortBust(USERS_PUBLIC_INDEX, now), | ||
]); | ||
} | ||
|
||
getData(userId) { | ||
return getInternalData.call({ redis: this.redis }, userId); | ||
} | ||
|
||
delete({ id, username, alias, ssoIds }, redis = this.redis) { | ||
const scriptKeys = [ | ||
USERS_ALIAS_TO_ID, USERS_USERNAME_TO_ID, USERS_SSO_TO_ID, | ||
USERS_PUBLIC_INDEX, USERS_INDEX, USERS_TOKENS, | ||
User.getUserDataKey(id), | ||
]; | ||
const luaScript = ` | ||
${alias === undefined ? '' : `redis.call("HDEL", KEYS[1], '${alias}', '${alias.toLowerCase()}')`} | ||
redis.call("HDEL", KEYS[2], '${username}') | ||
${ssoIds.length > 0 ? `redis.call("HDEL", KEYS[3], ${ssoIds.map((uid) => `'${uid}'`).join(',')})` : ''} | ||
redis.call("SREM", KEYS[4], '${id}') | ||
redis.call("SREM", KEYS[5], '${id}') | ||
redis.call("HDEL", KEYS[6], '${id}') | ||
redis.call("DEL", KEYS[7]) | ||
`; | ||
return redis.eval(luaScript, scriptKeys.length, ...scriptKeys); | ||
} | ||
} | ||
|
||
module.exports = User; |
Oops, something went wrong.