Skip to content

Commit

Permalink
fix: delete scheduled publish jobs when deleting documents (payloadcm…
Browse files Browse the repository at this point in the history
…s#10584)

### What?

When a document gets deleted we are not cleaning up jobs that would fail
if the document doesn't exist. This change makes an extra call to the DB
to delete any incomplete jobs for the document.

### Why?

The jobs queue will error and retry needlessly unless these are purged.

### How?

Adds a call to delete jobs from the delete operation.
  • Loading branch information
DanRibbens authored Jan 15, 2025
1 parent d4039f2 commit 05b9d94
Show file tree
Hide file tree
Showing 5 changed files with 178 additions and 3 deletions.
13 changes: 13 additions & 0 deletions packages/payload/src/collections/operations/delete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { commitTransaction } from '../../utilities/commitTransaction.js'
import { initTransaction } from '../../utilities/initTransaction.js'
import { killTransaction } from '../../utilities/killTransaction.js'
import { deleteCollectionVersions } from '../../versions/deleteCollectionVersions.js'
import { deleteScheduledPublishJobs } from '../../versions/deleteScheduledPublishJobs.js'
import { buildAfterOperation } from './utils.js'

export type Arguments = {
Expand Down Expand Up @@ -177,6 +178,18 @@ export const deleteOperation = async <
})
}

// /////////////////////////////////////
// Delete scheduled posts
// /////////////////////////////////////
if (collectionConfig.versions?.drafts && collectionConfig.versions.drafts.schedulePublish) {
await deleteScheduledPublishJobs({
id,
slug: collectionConfig.slug,
payload,
req,
})
}

// /////////////////////////////////////
// Delete document
// /////////////////////////////////////
Expand Down
13 changes: 13 additions & 0 deletions packages/payload/src/collections/operations/deleteByID.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { commitTransaction } from '../../utilities/commitTransaction.js'
import { initTransaction } from '../../utilities/initTransaction.js'
import { killTransaction } from '../../utilities/killTransaction.js'
import { deleteCollectionVersions } from '../../versions/deleteCollectionVersions.js'
import { deleteScheduledPublishJobs } from '../../versions/deleteScheduledPublishJobs.js'
import { buildAfterOperation } from './utils.js'

export type Arguments = {
Expand Down Expand Up @@ -155,6 +156,18 @@ export const deleteByIDOperation = async <TSlug extends CollectionSlug, TSelect
})
}

// /////////////////////////////////////
// Delete scheduled posts
// /////////////////////////////////////
if (collectionConfig.versions?.drafts && collectionConfig.versions.drafts.schedulePublish) {
await deleteScheduledPublishJobs({
id,
slug: collectionConfig.slug,
payload,
req,
})
}

// /////////////////////////////////////
// Delete document
// /////////////////////////////////////
Expand Down
7 changes: 4 additions & 3 deletions packages/payload/src/versions/deleteCollectionVersions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@ export const deleteCollectionVersions = async ({ id, slug, payload, req }: Args)
},
})
} catch (err) {
payload.logger.error(
`There was an error removing versions for the deleted ${slug} document with ID ${id}.`,
)
payload.logger.error({
err,
msg: `There was an error removing versions for the deleted ${slug} document with ID ${id}.`,
})
}
}
60 changes: 60 additions & 0 deletions packages/payload/src/versions/deleteScheduledPublishJobs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import type { PayloadRequest } from '../types/index.js'

import { type Payload } from '../index.js'

type Args = {
id?: number | string
payload: Payload
req?: PayloadRequest
slug: string
}

export const deleteScheduledPublishJobs = async ({
id,
slug,
payload,
req,
}: Args): Promise<void> => {
try {
await payload.db.deleteMany({
collection: 'payload-jobs',
req,
where: {
and: [
// only want to delete jobs have not run yet
{
completedAt: {
exists: false,
},
},
{
processing: {
equals: false,
},
},
{
'input.doc.value': {
equals: id,
},
},
{
'input.doc.relationTo': {
equals: slug,
},
},
// data.type narrows scheduled publish jobs in case of another job having input.doc.value
{
taskSlug: {
equals: 'schedulePublish',
},
},
],
},
})
} catch (err) {
payload.logger.error({
err,
msg: `There was an error deleting scheduled publish jobs from the queue for ${slug} document with ID ${id}.`,
})
}
}
88 changes: 88 additions & 0 deletions test/versions/int.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1944,6 +1944,94 @@ describe('Versions', () => {
expect(retrieved._status).toStrictEqual('draft')
})

it('should delete scheduled jobs after a document is deleted', async () => {
const draft = await payload.create({
collection: draftCollectionSlug,
data: {
title: 'my doc to publish in the future',
description: 'hello',
},
draft: true,
})

expect(draft._status).toStrictEqual('draft')

const currentDate = new Date()

await payload.jobs.queue({
task: 'schedulePublish',
waitUntil: new Date(currentDate.getTime() + 3000),
input: {
type: 'publish',
doc: {
relationTo: draftCollectionSlug,
value: draft.id,
},
},
})

await payload.delete({
collection: draftCollectionSlug,
where: {
id: { equals: draft.id },
},
})

const { docs } = await payload.find({
collection: 'payload-jobs',
where: {
'input.doc.value': {
equals: draft.id,
},
},
})

expect(docs[0]).toBeUndefined()
})

it('should delete scheduled jobs after a document is deleted by ID', async () => {
const draft = await payload.create({
collection: draftCollectionSlug,
data: {
title: 'my doc to publish in the future',
description: 'hello',
},
draft: true,
})

expect(draft._status).toStrictEqual('draft')

const currentDate = new Date()

await payload.jobs.queue({
task: 'schedulePublish',
waitUntil: new Date(currentDate.getTime() + 3000),
input: {
type: 'publish',
doc: {
relationTo: draftCollectionSlug,
value: draft.id,
},
},
})

await payload.delete({
collection: draftCollectionSlug,
id: draft.id,
})

const { docs } = await payload.find({
collection: 'payload-jobs',
where: {
'input.doc.value': {
equals: draft.id,
},
},
})

expect(docs[0]).toBeUndefined()
})

it('should allow global scheduled publish', async () => {
const draft = await payload.updateGlobal({
slug: draftGlobalSlug,
Expand Down

0 comments on commit 05b9d94

Please sign in to comment.