Przeglądaj źródła

[web] switch migrations for fixing dates to batchedUpdate (#26582)

* [web] switch migration for fixing confirmedAt dates to batchedUpdate

* [web] switch migration for fixing assignedAt dates to batchedUpdate

* [web] make eslint happy

GitOrigin-RevId: d898d28dc2aa1084e8d3af20b98f49e3fda8a1c6
Jakob Ackermann 1 rok temu
rodzic
commit
19980b41b8

+ 27 - 4
services/web/migrations/20210726083523_convert_confirmedAt_strings_to_dates.mjs

@@ -1,12 +1,35 @@
-import updateStringDates from '../scripts/confirmed_at_to_dates.mjs'
+import { db } from '../app/src/infrastructure/mongodb.js'
+import { batchedUpdate } from '@overleaf/mongo-utils/batchedUpdate.js'
 
 const tags = ['saas']
 
-const migrate = async client => {
-  await updateStringDates()
+const migrate = async () => {
+  await batchedUpdate(
+    db.users,
+    { 'emails.confirmedAt': { $type: 'string' } },
+    async function (batch) {
+      for (const user of batch) {
+        for (const email of user.emails) {
+          if (typeof email.confirmedAt === 'string') {
+            await db.users.updateOne(
+              { _id: user._id, 'emails.email': email.email },
+              {
+                $set: {
+                  'emails.$.confirmedAt': new Date(
+                    email.confirmedAt.replace(/ UTC$/, '')
+                  ),
+                },
+              }
+            )
+          }
+        }
+      }
+    },
+    { emails: 1 }
+  )
 }
 
-const rollback = async client => {
+const rollback = async () => {
   /* nothing to do */
 }
 

+ 21 - 4
services/web/migrations/20210726083523_convert_split_tests_assigned_at_strings_to_dates.mjs

@@ -1,12 +1,29 @@
-import updateStringDates from '../scripts/split_tests_assigned_at_to_dates.mjs'
+import { db } from '../app/src/infrastructure/mongodb.js'
+import { batchedUpdate } from '@overleaf/mongo-utils/batchedUpdate.js'
 
 const tags = ['saas']
 
-const migrate = async client => {
-  await updateStringDates()
+const migrate = async () => {
+  await batchedUpdate(
+    db.users,
+    { splitTests: { $exists: true } },
+    async function (batch) {
+      for (const user of batch) {
+        const splitTests = user.splitTests
+        for (const splitTest of Object.values(user.splitTests)) {
+          for (const variant of splitTest) {
+            variant.assignedAt = new Date(variant.assignedAt)
+          }
+        }
+
+        await db.users.updateOne({ _id: user._id }, { $set: { splitTests } })
+      }
+    },
+    { splitTests: 1 }
+  )
 }
 
-const rollback = async client => {
+const rollback = async () => {
   /* nothing to do */
 }
 

+ 0 - 53
services/web/scripts/confirmed_at_to_dates.mjs

@@ -1,53 +0,0 @@
-import { db } from '../app/src/infrastructure/mongodb.js'
-import { fileURLToPath } from 'node:url'
-
-async function updateStringDates() {
-  const users = await db.users.aggregate([
-    { $unwind: { path: '$emails' } },
-    {
-      $match: { 'emails.confirmedAt': { $exists: true, $type: 'string' } },
-    },
-    {
-      $project: {
-        _id: 1,
-        'emails.email': 1,
-        'emails.confirmedAt': 1,
-      },
-    },
-  ])
-
-  let user
-  let count = 0
-  while ((user = await users.next())) {
-    count += 1
-    if (count % 10000 === 0) {
-      console.log(`processed ${count} users`)
-    }
-    const confirmedAt = user.emails.confirmedAt
-    const dateConfirmedAt = new Date(confirmedAt.replace(/ UTC$/, ''))
-    await db.users.updateOne(
-      {
-        _id: user._id,
-        'emails.email': user.emails.email,
-      },
-      {
-        $set: {
-          'emails.$.confirmedAt': dateConfirmedAt,
-        },
-      }
-    )
-  }
-  console.log(`Updated ${count} confirmedAt strings to dates!`)
-}
-
-if (fileURLToPath(import.meta.url) === process.argv[1]) {
-  try {
-    await updateStringDates()
-    process.exit(0)
-  } catch (error) {
-    console.error(error)
-    process.exit(1)
-  }
-}
-
-export default updateStringDates

+ 0 - 46
services/web/scripts/split_tests_assigned_at_to_dates.mjs

@@ -1,46 +0,0 @@
-import { db } from '../app/src/infrastructure/mongodb.js'
-import { fileURLToPath } from 'node:url'
-
-async function updateStringDates() {
-  const users = db.users.find({
-    splitTests: { $exists: true },
-  })
-
-  let user
-  let count = 0
-  while ((user = await users.next())) {
-    count += 1
-    if (count % 10000 === 0) {
-      console.log(`processed ${count} users...`)
-    }
-
-    const splitTests = user.splitTests
-    for (const splitTestKey of Object.keys(splitTests)) {
-      for (const variantIndex in splitTests[splitTestKey]) {
-        splitTests[splitTestKey][variantIndex].assignedAt = new Date(
-          splitTests[splitTestKey][variantIndex].assignedAt
-        )
-      }
-    }
-
-    await db.users.updateOne(
-      {
-        _id: user._id,
-      },
-      { $set: { splitTests } }
-    )
-  }
-  console.log(`Updated ${count} assignedAt strings to dates!`)
-}
-
-if (fileURLToPath(import.meta.url) === process.argv[1]) {
-  try {
-    await updateStringDates()
-    process.exit(0)
-  } catch (error) {
-    console.error(error)
-    process.exit(1)
-  }
-}
-
-export default updateStringDates

+ 69 - 0
services/web/test/acceptance/src/ConvertEmailConfirmedAtToDates.js

@@ -0,0 +1,69 @@
+import { expect } from 'chai'
+import { db } from '../../../app/src/infrastructure/mongodb.js'
+import { exec } from 'node:child_process'
+
+describe('ConvertEmailConfirmedAtToDates', function () {
+  beforeEach('insert data', async function () {
+    await db.users.insertMany([
+      { email: 'foo0@bar.com', emails: [{ email: 'foo0@bar.com' }] },
+      {
+        email: 'foo1@bar.com',
+        emails: [
+          { email: 'foo1@bar.com', confirmedAt: '2025-06-20 15:53:31 UTC' },
+        ],
+      },
+      {
+        email: 'foo2@bar.com',
+        emails: [
+          { email: 'foo2@bar.com', confirmedAt: '2025-06-20 15:53:32 UTC' },
+          { email: 'foo3@bar.com', confirmedAt: '2025-06-20 15:53:33 UTC' },
+          {
+            email: 'foo4@bar.com',
+            confirmedAt: new Date('2025-06-20T15:53:31.134Z'),
+          },
+        ],
+      },
+    ])
+  })
+
+  beforeEach('run migration', function (done) {
+    exec(
+      'east migrate -t saas --force 20210726083523_convert_confirmedAt_strings_to_dates',
+      done
+    )
+  })
+
+  it('should update the dates', async function () {
+    expect(
+      await db.users.find({}, { projection: { _id: 0 } }).toArray()
+    ).to.deep.equal([
+      { email: 'foo0@bar.com', emails: [{ email: 'foo0@bar.com' }] },
+      {
+        email: 'foo1@bar.com',
+        emails: [
+          {
+            email: 'foo1@bar.com',
+            confirmedAt: new Date('2025-06-20T15:53:31.000Z'),
+          },
+        ],
+      },
+      {
+        email: 'foo2@bar.com',
+        emails: [
+          {
+            email: 'foo2@bar.com',
+            confirmedAt: new Date('2025-06-20T15:53:32.000Z'),
+          },
+          {
+            email: 'foo3@bar.com',
+            confirmedAt: new Date('2025-06-20T15:53:33.000Z'),
+          },
+          {
+            email: 'foo4@bar.com',
+            confirmedAt: new Date('2025-06-20T15:53:31.134Z'),
+          },
+        ],
+      },
+    ])
+  })
+})

+ 134 - 0
services/web/test/acceptance/src/ConvertSplitTestAssignedAtToDates.js

@@ -0,0 +1,134 @@
+import { expect } from 'chai'
+import { db } from '../../../app/src/infrastructure/mongodb.js'
+import { exec } from 'node:child_process'
+
+describe('ConvertSplitTestAssignedAtToDates', function () {
+  beforeEach('insert data', async function () {
+    await db.users.insertMany([
+      {
+        email: 'foo0@bar.com',
+        splitTests: {
+          'split-test-1': [
+            {
+              variantName: 'enabled',
+              versionNumber: 4,
+              phase: 'release',
+              assignedAt: '2025-03-18T13:19:46.627Z',
+            },
+            {
+              variantName: 'default',
+              versionNumber: 5,
+              phase: 'release',
+              assignedAt: new Date('2025-04-30T08:52:13.783Z'),
+            },
+          ],
+          'split-test-2': [
+            {
+              variantName: 'active',
+              versionNumber: 5,
+              phase: 'release',
+              assignedAt: new Date('2025-02-14T09:08:30.190Z'),
+            },
+            {
+              variantName: 'active',
+              versionNumber: 7,
+              phase: 'release',
+              assignedAt: new Date('2025-03-11T11:05:13.640Z'),
+            },
+          ],
+        },
+      },
+      {
+        email: 'foo1@bar.com',
+        splitTests: {
+          'split-test-3': [
+            {
+              variantName: 'default',
+              versionNumber: 1,
+              phase: 'release',
+              assignedAt: '2025-02-11T14:55:38.470Z',
+            },
+            {
+              variantName: 'enabled',
+              versionNumber: 21,
+              phase: 'release',
+              assignedAt: '2025-03-18T13:19:46.826Z',
+            },
+          ],
+        },
+      },
+      {
+        email: 'foo2@bar.com',
+      },
+    ])
+  })
+
+  beforeEach('run migration', function (done) {
+    exec(
+      'east migrate -t saas --force 20210726083523_convert_split_tests_assigned_at_strings_to_dates',
+      done
+    )
+  })
+
+  it('should update the dates', async function () {
+    expect(
+      await db.users.find({}, { projection: { _id: 0 } }).toArray()
+    ).to.deep.equal([
+      {
+        email: 'foo0@bar.com',
+        splitTests: {
+          'split-test-1': [
+            {
+              variantName: 'enabled',
+              versionNumber: 4,
+              phase: 'release',
+              assignedAt: new Date('2025-03-18T13:19:46.627Z'),
+            },
+            {
+              variantName: 'default',
+              versionNumber: 5,
+              phase: 'release',
+              assignedAt: new Date('2025-04-30T08:52:13.783Z'),
+            },
+          ],
+          'split-test-2': [
+            {
+              variantName: 'active',
+              versionNumber: 5,
+              phase: 'release',
+              assignedAt: new Date('2025-02-14T09:08:30.190Z'),
+            },
+            {
+              variantName: 'active',
+              versionNumber: 7,
+              phase: 'release',
+              assignedAt: new Date('2025-03-11T11:05:13.640Z'),
+            },
+          ],
+        },
+      },
+      {
+        email: 'foo1@bar.com',
+        splitTests: {
+          'split-test-3': [
+            {
+              variantName: 'default',
+              versionNumber: 1,
+              phase: 'release',
+              assignedAt: new Date('2025-02-11T14:55:38.470Z'),
+            },
+            {
+              variantName: 'enabled',
+              versionNumber: 21,
+              phase: 'release',
+              assignedAt: new Date('2025-03-18T13:19:46.826Z'),
+            },
+          ],
+        },
+      },
+      {
+        email: 'foo2@bar.com',
+      },
+    ])
+  })
+})