Files
payload/packages/db-mongodb/src/createVersion.ts
Dan Ribbens 631edd4c17 fix: latest: true version disappear on parallel writes (#9032)
What?
Fixes issue when on parallel writes in result you can have 0 latest:
true versions.

Why?
There must be always a version with latest: true

How?
Ensures that we always have a version with latest: true by adding a
filter on createdAt < createdVersion.createdAt.
Instead, this ponentially can lead to a situation where we have 2
versions with latest: true, if they were created at the exact same time,
but this shouldn't happen in a real world scenario and it's much less
problematic than not having a version with latest: true.

Fixes https://github.com/payloadcms/payload/issues/5895

Changes from #8986

---------

Co-authored-by: Sasha <64744993+r1tsuu@users.noreply.github.com>
2024-12-02 14:34:45 -05:00

103 lines
2.0 KiB
TypeScript

import { Types } from 'mongoose'
import {
buildVersionCollectionFields,
type CreateVersion,
type Document,
type PayloadRequest,
} from 'payload'
import type { MongooseAdapter } from './index.js'
import { sanitizeRelationshipIDs } from './utilities/sanitizeRelationshipIDs.js'
import { withSession } from './withSession.js'
export const createVersion: CreateVersion = async function createVersion(
this: MongooseAdapter,
{
autosave,
collectionSlug,
createdAt,
parent,
publishedLocale,
req = {} as PayloadRequest,
snapshot,
updatedAt,
versionData,
},
) {
const VersionModel = this.versions[collectionSlug]
const options = await withSession(this, req)
const data = sanitizeRelationshipIDs({
config: this.payload.config,
data: {
autosave,
createdAt,
latest: true,
parent,
publishedLocale,
snapshot,
updatedAt,
version: versionData,
},
fields: buildVersionCollectionFields(
this.payload.config,
this.payload.collections[collectionSlug].config,
),
})
const [doc] = await VersionModel.create([data], options, req)
const parentQuery = {
$or: [
{
parent: {
$eq: data.parent,
},
},
],
}
if (data.parent instanceof Types.ObjectId) {
parentQuery.$or.push({
parent: {
$eq: data.parent.toString(),
},
})
}
await VersionModel.updateMany(
{
$and: [
{
_id: {
$ne: doc._id,
},
},
parentQuery,
{
latest: {
$eq: true,
},
},
{
updatedAt: {
$lt: new Date(doc.updatedAt),
},
},
],
},
{ $unset: { latest: 1 } },
options,
)
const result: Document = JSON.parse(JSON.stringify(doc))
const verificationToken = doc._verificationToken
// custom id type reset
result.id = result._id
if (verificationToken) {
result._verificationToken = verificationToken
}
return result
}