Fixes https://github.com/payloadcms/payload/issues/13433. Testing release: `3.54.0-internal.90cf7d5` Previously, when calling `getPayload`, you would always use the same, cached payload instance within a single process, regardless of the arguments passed to the `getPayload` function. This resulted in the following issues - both are fixed by this PR: - If, in your frontend you're calling `getPayload` without `cron: true`, and you're hosting the Payload Admin Panel in the same process, crons will not be enabled even if you visit the admin panel which calls `getPayload` with `cron: true`. This will break jobs autorun depending on which page you visit first - admin panel or frontend - Within the same process, you are unable to use `getPayload` twice for different instances of payload with different Payload Configs. On postgres, you can get around this by manually calling new `BasePayload()` which skips the cache. This did not work on mongoose though, as mongoose was caching the models on a global singleton (this PR addresses this). In order to bust the cache for different Payload Config, this PR introduces a new, optional `key` property to `getPayload`. ## Mongoose - disable using global singleton This PR refactors the Payload Mongoose adapter to stop relying on the global mongoose singleton. Instead, each adapter instance now creates and manages its own scoped Connection object. ### Motivation Previously, calling `getPayload()` more than once in the same process would throw `Cannot overwrite model` errors because models were compiled into the global singleton. This prevented running multiple Payload instances side-by-side, even when pointing at different databases. ### Changes - Replace usage of `mongoose.connect()` / `mongoose.model()` with instance-scoped `createConnection()` and `connection.model()`. - Ensure models, globals, and versions are compiled per connection, not globally. - Added proper `close()` handling on `this.connection` instead of `mongoose.disconnect()`. --- - To see the specific tasks where the Asana app for GitHub is being used, see below: - https://app.asana.com/0/0/1211114366468745
86 lines
2.3 KiB
TypeScript
86 lines
2.3 KiB
TypeScript
import path from 'path'
|
|
import {
|
|
_internal_jobSystemGlobals,
|
|
_internal_resetJobSystemGlobals,
|
|
getPayload,
|
|
migrateCLI,
|
|
type SanitizedConfig,
|
|
} from 'payload'
|
|
import { wait } from 'payload/shared'
|
|
import { fileURLToPath } from 'url'
|
|
|
|
import { initPayloadInt } from '../helpers/initPayloadInt.js'
|
|
import { waitUntilAutorunIsDone } from './utilities.js'
|
|
|
|
const filename = fileURLToPath(import.meta.url)
|
|
const dirname = path.dirname(filename)
|
|
|
|
describe('Queues - CLI', () => {
|
|
let config: SanitizedConfig
|
|
beforeAll(async () => {
|
|
;({ config } = await initPayloadInt(dirname, undefined, false))
|
|
})
|
|
|
|
it('ensure consecutive getPayload call with cron: true will autorun jobs', async () => {
|
|
const payload = await getPayload({
|
|
config,
|
|
})
|
|
|
|
await payload.jobs.queue({
|
|
workflow: 'inlineTaskTest',
|
|
queue: 'autorunSecond',
|
|
input: {
|
|
message: 'hello!',
|
|
},
|
|
})
|
|
|
|
process.env.PAYLOAD_DROP_DATABASE = 'false'
|
|
|
|
// Second instance of payload with the only purpose of running cron jobs
|
|
const _payload2 = await getPayload({
|
|
config,
|
|
cron: true,
|
|
})
|
|
|
|
await waitUntilAutorunIsDone({
|
|
payload,
|
|
queue: 'autorunSecond',
|
|
})
|
|
|
|
const allSimples = await payload.find({
|
|
collection: 'simple',
|
|
limit: 100,
|
|
})
|
|
|
|
expect(allSimples.totalDocs).toBe(1)
|
|
expect(allSimples?.docs?.[0]?.title).toBe('hello!')
|
|
|
|
// Shut down safely:
|
|
// Ensure no new crons are scheduled
|
|
_internal_jobSystemGlobals.shouldAutoRun = false
|
|
_internal_jobSystemGlobals.shouldAutoSchedule = false
|
|
// Wait 3 seconds to ensure all currently-running crons are done. If we shut down the db while a function is running, it can cause issues
|
|
// Cron function runs may persist after a test has finished
|
|
await wait(3000)
|
|
// Now we can destroy the payload instance
|
|
await _payload2.destroy()
|
|
await payload.destroy()
|
|
_internal_resetJobSystemGlobals()
|
|
})
|
|
|
|
it('can run migrate CLI without jobs attempting to run', async () => {
|
|
await migrateCLI({
|
|
config,
|
|
parsedArgs: {
|
|
_: ['migrate'],
|
|
},
|
|
})
|
|
|
|
// Wait 3 seconds to let potential autorun crons trigger
|
|
await new Promise((resolve) => setTimeout(resolve, 3000))
|
|
|
|
// Expect no errors. Previously, this would throw an "error: relation "payload_jobs" does not exist" error
|
|
expect(true).toBe(true)
|
|
})
|
|
})
|