1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-10-27 11:02:16 +01:00

chore: new method that considers users updated at the same time (#10723)

We could have users updated at the exact same time, so we need to
include that timestamp in the next fetch to avoid missing users, but
also include the latest user id so we can exclude the ones already
fetched.

The following test shows how to process pages with this new method:

c03df86ee0/src/lib/features/users/user-updates-read-model.test.ts (L39-L65)
This commit is contained in:
Gastón Fournier 2025-10-03 09:24:53 +02:00 committed by GitHub
parent 1e5de5b8b7
commit a927f1f42b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 82 additions and 1 deletions

View File

@ -0,0 +1,65 @@
import dbInit, {
type ITestDb,
} from '../../../test/e2e/helpers/database-init.js';
let db: ITestDb;
const TABLE = 'users';
const INSERT_INSTANT = new Date();
beforeAll(async () => {
db = await dbInit();
for (let i = 0; i < 10; i += 1) {
await db.rawDatabase(TABLE).insert({
name: `test-user-${i}`,
username: `test-user-${i}`,
email: `test-user-${i}@example.com`,
created_at: INSERT_INSTANT,
updated_at: INSERT_INSTANT,
});
}
});
test('returns all users if page is large enough', async () => {
const userUpdatesReadModel = db.stores.userUpdatesReadModel;
const users =
await userUpdatesReadModel.getUsersUpdatedAfterOrEqual(INSERT_INSTANT);
expect(users).toHaveLength(10);
});
test('returns 0 if no users updated after timestamp', async () => {
const userUpdatesReadModel = db.stores.userUpdatesReadModel;
const users = await userUpdatesReadModel.getUsersUpdatedAfterOrEqual(
new Date(INSERT_INSTANT.getTime() + 1),
);
expect(users).toHaveLength(0);
});
test('returns the users in pages', async () => {
const userUpdatesReadModel = db.stores.userUpdatesReadModel;
const pageSize = 4;
const usersPage1 = await userUpdatesReadModel.getUsersUpdatedAfterOrEqual(
INSERT_INSTANT,
pageSize,
);
expect(usersPage1).toHaveLength(4);
expect(usersPage1[0].username).toBe('test-user-0');
const usersPage2 = await userUpdatesReadModel.getUsersUpdatedAfterOrEqual(
INSERT_INSTANT,
pageSize,
usersPage1[usersPage1.length - 1].id,
);
expect(usersPage2).toHaveLength(4);
expect(usersPage2[0].username).toBe('test-user-4');
const usersPage3 = await userUpdatesReadModel.getUsersUpdatedAfterOrEqual(
INSERT_INSTANT,
pageSize,
usersPage2[usersPage2.length - 1].id,
);
expect(usersPage3).toHaveLength(2);
expect(usersPage3[1].username).toBe('test-user-9');
});

View File

@ -50,9 +50,18 @@ export class UserUpdatesReadModel {
return result ? result.last_updated_at : null;
}
/** @deprecated */
async getUsersUpdatedAfter(
date: Date,
limit: number = 100,
): Promise<UpdatedUser[]> {
return this.getUsersUpdatedAfterOrEqual(date, limit, 0);
}
async getUsersUpdatedAfterOrEqual(
date: Date,
limit: number = 100,
afterId: number = 0,
): Promise<UpdatedUser[]> {
const result = await this.db(USERS_TABLE)
.where({
@ -60,8 +69,15 @@ export class UserUpdatesReadModel {
is_system: false,
is_service: false,
})
.where('updated_at', '>', date)
.where((builder) => {
builder.where('updated_at', '>', date).orWhere((subBuilder) => {
subBuilder
.where('updated_at', '=', date)
.where('id', '>', afterId);
});
})
.orderBy('updated_at', 'asc')
.orderBy('id', 'asc')
.select([
...USER_COLUMNS_PUBLIC,
'created_at',