-
Notifications
You must be signed in to change notification settings - Fork 277
Optimize updateAllUserStatus Query #2598
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
base: develop
Are you sure you want to change the base?
Changes from all commits
6a88a00
09fe69e
d02b3fa
e6151c4
973eca1
4e013fa
9d0c873
961731b
25c35ae
8755926
6fde85f
5836861
ae35433
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -27,6 +27,7 @@ const usersCollection = firestore.collection("users"); | |
| const config = require("config"); | ||
| const DISCORD_BASE_URL = config.get("services.discordBot.baseUrl"); | ||
| const { generateAuthTokenForCloudflare } = require("../utils/discord-actions"); | ||
| const logger = require("../utils/logger"); | ||
| const { BATCH_SIZE_IN_CLAUSE } = require("../constants/firebase"); | ||
|
|
||
| // added this function here to avoid circular dependency | ||
|
|
@@ -265,10 +266,13 @@ const updateAllUserStatus = async () => { | |
| nonOooUsersUnaltered: 0, | ||
| }; | ||
| try { | ||
| const userStatusDocs = await userStatusModel.where("futureStatus.state", "in", ["ACTIVE", "IDLE", "OOO"]).get(); | ||
| summary.usersCount = userStatusDocs._size; | ||
| const today = Date.now(); | ||
| const userStatusDocs = await userStatusModel | ||
| .where("futureStatus.state", "in", ["ACTIVE", "IDLE", "OOO"]) | ||
| .where("futureStatus.from", "<=", today) | ||
| .get(); | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| summary.usersCount = userStatusDocs.size; | ||
| const batch = firestore.batch(); | ||
| const today = new Date().getTime(); | ||
| for (const document of userStatusDocs.docs) { | ||
| const doc = document.data(); | ||
| const docRef = document.ref; | ||
|
|
@@ -280,32 +284,28 @@ const updateAllUserStatus = async () => { | |
| const currentState = currentStatus?.state; | ||
| const currentUntil = currentStatus?.until; | ||
| if (futureState === "ACTIVE" || futureState === "IDLE") { | ||
| if (today >= futureStatus.from) { | ||
| // OOO period is over and we need to update their current status | ||
| newStatusData.currentStatus = { ...futureStatus, until: "", updatedAt: today }; | ||
| delete newStatusData.futureStatus; | ||
| const lastOooUntilUpdate = resolveLastOooUntil({ | ||
| previousState: currentState, | ||
| previousUntil: currentUntil, | ||
| nextState: futureState, | ||
| fallbackTimestamp: today, | ||
| }); | ||
| if (lastOooUntilUpdate !== undefined) { | ||
| newStatusData.lastOooUntil = lastOooUntilUpdate; | ||
| } | ||
| toUpdate = !toUpdate; | ||
| summary.oooUsersAltered++; | ||
| } else { | ||
| summary.oooUsersUnaltered++; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. previously there was
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, we are still using it check line 296 |
||
| // OOO period is over and we need to update their current status | ||
| newStatusData.currentStatus = { ...futureStatus, until: "", updatedAt: today }; | ||
| delete newStatusData.futureStatus; | ||
| const lastOooUntilUpdate = resolveLastOooUntil({ | ||
| previousState: currentState, | ||
| previousUntil: currentUntil, | ||
| nextState: futureState, | ||
| fallbackTimestamp: today, | ||
| }); | ||
| if (lastOooUntilUpdate !== undefined) { | ||
| newStatusData.lastOooUntil = lastOooUntilUpdate; | ||
| } | ||
| toUpdate = !toUpdate; | ||
| summary.oooUsersAltered++; | ||
| } else { | ||
| // futureState is OOO | ||
| if (today > futureStatus.until) { | ||
| // the OOO period is over | ||
| delete newStatusData.futureStatus; | ||
| toUpdate = !toUpdate; | ||
| summary.nonOooUsersAltered++; | ||
| } else if (today <= doc.futureStatus.until && today >= doc.futureStatus.from) { | ||
| } else { | ||
| // the current date i.e today lies in between the from and until so we need to swap the status | ||
| let newCurrentStatus = {}; | ||
| let newFutureStatus = {}; | ||
|
|
@@ -318,8 +318,6 @@ const updateAllUserStatus = async () => { | |
| newStatusData.lastOooUntil = null; | ||
| toUpdate = !toUpdate; | ||
| summary.nonOooUsersAltered++; | ||
| } else { | ||
| summary.nonOooUsersUnaltered++; | ||
| } | ||
| } | ||
| if (toUpdate) { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,16 +1,25 @@ | ||
| import { userFutureStatusData } from "../../fixtures/userFutureStatus/userFutureStatusData"; | ||
| const { userFutureStatusData } = require("../../fixtures/userFutureStatus/userFutureStatusData"); | ||
| const chai = require("chai"); | ||
| const sinon = require("sinon"); | ||
| const { NotFound, Forbidden } = require("http-errors"); | ||
| const { expect } = chai; | ||
| const firestore = require("../../../utils/firestore"); | ||
| const userStatusModel = firestore.collection("usersStatus"); | ||
| const tasksModel = firestore.collection("tasks"); | ||
| const { cancelOooStatus, addFutureStatus, getUserStatusForUserIds } = require("../../../models/userStatus"); | ||
| const { | ||
| cancelOooStatus, | ||
| addFutureStatus, | ||
| getUserStatusForUserIds, | ||
| updateAllUserStatus, | ||
| } = require("../../../models/userStatus"); | ||
| const cleanDb = require("../../utils/cleanDb"); | ||
| const addUser = require("../../utils/addUser"); | ||
| const { userState } = require("../../../constants/userStatus"); | ||
| const { generateStatusDataForCancelOOO, generateDefaultFutureStatus } = require("../../fixtures/userStatus/userStatus"); | ||
| const { | ||
| generateStatusDataForCancelOOO, | ||
| generateDefaultFutureStatus, | ||
| generateOooUserStatusDoc, | ||
| } = require("../../fixtures/userStatus/userStatus"); | ||
|
|
||
| describe("tasks", function () { | ||
| let userId; | ||
|
|
@@ -38,7 +47,7 @@ describe("tasks", function () { | |
|
|
||
| it("Should clear the future Status if the User cancels OOO", async function () { | ||
| const data = generateStatusDataForCancelOOO(userId, userState.OOO); | ||
| const from = new Date().getTime() + 24 * 60 * 60 * 1000; // 1 day offset from current time | ||
| const from = new Date().getTime() + 24 * 60 * 60 * 1000; | ||
| data.futureStatus = generateDefaultFutureStatus(userState.IDLE, from, ""); | ||
| await docRefUser0.set(data); | ||
| const response = await cancelOooStatus(userId); | ||
|
|
@@ -87,6 +96,86 @@ describe("tasks", function () { | |
| expect(response.data.futureStatus.state).to.equal("UPCOMING"); | ||
| }); | ||
|
|
||
| describe("updateAllUserStatus", function () { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| let clock; | ||
|
|
||
| beforeEach(async function () { | ||
| clock = sinon.useFakeTimers({ | ||
| now: new Date("2026-07-14T02:00:00.000Z").getTime(), | ||
| toFake: ["Date"], | ||
| }); | ||
| }); | ||
|
|
||
| afterEach(async function () { | ||
| clock.restore(); | ||
| await cleanDb(); | ||
| }); | ||
|
|
||
| it("Should update user status when futureStatus.from <= today (e.g. from is in the past)", async function () { | ||
| const today = Date.now(); | ||
| const docRef = userStatusModel.doc(); | ||
|
|
||
| const userStatusData = generateOooUserStatusDoc(userId, today, { | ||
| currentStatusFromOffset: -2 * 24 * 60 * 60 * 1000, | ||
| futureStatusFromOffset: -24 * 60 * 60 * 1000, | ||
| }); | ||
| await docRef.set(userStatusData); | ||
|
|
||
| const summary = await updateAllUserStatus(); | ||
| expect(summary.usersCount).to.equal(1); | ||
| expect(summary.oooUsersAltered).to.equal(1); | ||
|
|
||
| const doc = await docRef.get(); | ||
| const data = doc.data(); | ||
|
|
||
| expect(data.currentStatus.state).to.equal(userState.ACTIVE); | ||
| expect(data.currentStatus.from).to.equal(today - 24 * 60 * 60 * 1000); | ||
| expect(data.futureStatus).to.equal(undefined); | ||
| }); | ||
|
|
||
| it("Should update user status when futureStatus.from === today (boundary case)", async function () { | ||
| const today = Date.now(); | ||
| const docRef = userStatusModel.doc(); | ||
|
|
||
| const userStatusData = generateOooUserStatusDoc(userId, today, { | ||
| currentStatusUntilOffset: 24 * 60 * 60 * 1000, | ||
| futureStatusFromOffset: 0, | ||
| }); | ||
| await docRef.set(userStatusData); | ||
|
|
||
| const summary = await updateAllUserStatus(); | ||
| expect(summary.usersCount).to.equal(1); | ||
| expect(summary.oooUsersAltered).to.equal(1); | ||
|
|
||
| const doc = await docRef.get(); | ||
| const data = doc.data(); | ||
|
|
||
| expect(data.currentStatus.state).to.equal(userState.ACTIVE); | ||
| expect(data.currentStatus.from).to.equal(today); | ||
| expect(data.futureStatus).to.equal(undefined); | ||
| }); | ||
|
|
||
| it("Should not update user status when futureStatus.from > today (e.g. from is in the future)", async function () { | ||
| const today = Date.now(); | ||
| const docRef = userStatusModel.doc(); | ||
|
|
||
| const userStatusData = generateOooUserStatusDoc(userId, today, { | ||
| futureStatusFromOffset: 24 * 60 * 60 * 1000, | ||
| }); | ||
| await docRef.set(userStatusData); | ||
|
|
||
| const summary = await updateAllUserStatus(); | ||
| expect(summary.usersCount).to.equal(0); | ||
| expect(summary.oooUsersAltered).to.equal(0); | ||
|
|
||
| const doc = await docRef.get(); | ||
| const data = doc.data(); | ||
|
|
||
| expect(data.currentStatus.state).to.equal(userState.OOO); | ||
| expect(data.futureStatus.state).to.equal(userState.ACTIVE); | ||
| }); | ||
| }); | ||
|
|
||
| describe("getUserStatusForUserIds", function () { | ||
| it("returns statuses keyed by userId for the given ids", async function () { | ||
| await userStatusModel.add({ userId: "user-idle-1", currentStatus: { state: userState.IDLE } }); | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.