mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-01-20 22:42:53 -05:00
🐛 Fixed post scheduling on restart (#10726)
no issue
- case: restart Ghost and while having a scheduled post
- caused by 4acc375fb6 (diff-4726ce3c4d18d41afad4b46cb0aa7dd3)
- the bug exists since 2.12
- Bookshelf added support (or better said fixed a bug) for accessing previous attributes
- `object.updated('published_at')` always returned "undefined", because the self-implementation < 2.12 only remembered previous attributes after update (see https://github.com/TryGhost/Ghost/blob/2.11.0/core/server/models/base/index.js#L234)
- but `object.previous('published_at')` returns the current value (object.get('published_at') === object.previous('published_at') -> and that's why rescheduling on bootstrap never worked
- might fix https://forum.ghost.org/t/scheduled-posts-never-publish/6873/10
- reduced timeouts on scheduling unit tests
This commit is contained in:
parent
90db78b369
commit
2447335ab1
3 changed files with 69 additions and 3 deletions
|
@ -74,9 +74,24 @@ SchedulingDefault.prototype.schedule = function (object) {
|
||||||
* oldTime: [Number] The previous published time.
|
* oldTime: [Number] The previous published time.
|
||||||
* }
|
* }
|
||||||
* }
|
* }
|
||||||
|
* @param {Object} options
|
||||||
|
* {
|
||||||
|
* bootstrap: [Boolean]
|
||||||
|
* }
|
||||||
*/
|
*/
|
||||||
SchedulingDefault.prototype.reschedule = function (object) {
|
SchedulingDefault.prototype.reschedule = function (object, options = {bootstrap: false}) {
|
||||||
this._deleteJob({time: object.extra.oldTime, url: object.url});
|
/**
|
||||||
|
* CASE:
|
||||||
|
* The post scheduling unit calls "reschedule" on bootstrap, because other custom scheduling implementations
|
||||||
|
* could use a database and we need to give the chance to update the job (delete + re-add).
|
||||||
|
*
|
||||||
|
* We receive a "bootstrap" variable to ensure that jobs are scheduled correctly for this scheduler implementation,
|
||||||
|
* because "object.extra.oldTime" === "object.time". If we mark the job as deleted, it won't get scheduled.
|
||||||
|
*/
|
||||||
|
if (!options.bootstrap) {
|
||||||
|
this._deleteJob({time: object.extra.oldTime, url: object.url});
|
||||||
|
}
|
||||||
|
|
||||||
this._addJob(object);
|
this._addJob(object);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -86,7 +86,9 @@ exports.init = function init(options = {}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
scheduledPosts.forEach((model) => {
|
scheduledPosts.forEach((model) => {
|
||||||
adapter.reschedule(_private.normalize({model, apiUrl, client}));
|
// NOTE: We are using reschedule, because custom scheduling adapter could use a database, which needs to be updated
|
||||||
|
// and not an in-process implementation!
|
||||||
|
adapter.reschedule(_private.normalize({model, apiUrl, client}), {bootstrap: true});
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
|
|
@ -60,6 +60,55 @@ describe('Scheduling Default Adapter', function () {
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('reschedule: default', function (done) {
|
||||||
|
sinon.stub(scope.adapter, '_pingUrl');
|
||||||
|
|
||||||
|
const time = moment().add(20, 'milliseconds').valueOf();
|
||||||
|
|
||||||
|
scope.adapter.schedule({
|
||||||
|
time: time,
|
||||||
|
url: 'something',
|
||||||
|
extra: {
|
||||||
|
oldTime: null,
|
||||||
|
method: 'PUT'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
scope.adapter.reschedule({
|
||||||
|
time: time,
|
||||||
|
url: 'something',
|
||||||
|
extra: {
|
||||||
|
oldTime: time,
|
||||||
|
method: 'PUT'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
scope.adapter._pingUrl.calledOnce.should.eql(true);
|
||||||
|
done();
|
||||||
|
}, 50);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('reschedule: simulate restart', function (done) {
|
||||||
|
sinon.stub(scope.adapter, '_pingUrl');
|
||||||
|
|
||||||
|
const time = moment().add(20, 'milliseconds').valueOf();
|
||||||
|
|
||||||
|
scope.adapter.reschedule({
|
||||||
|
time: time,
|
||||||
|
url: 'something',
|
||||||
|
extra: {
|
||||||
|
oldTime: time,
|
||||||
|
method: 'PUT'
|
||||||
|
}
|
||||||
|
}, {bootstrap: true});
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
scope.adapter._pingUrl.calledOnce.should.eql(true);
|
||||||
|
done();
|
||||||
|
}, 50);
|
||||||
|
});
|
||||||
|
|
||||||
it('run', function (done) {
|
it('run', function (done) {
|
||||||
// 1000 jobs, but only the number x are under 1 minute
|
// 1000 jobs, but only the number x are under 1 minute
|
||||||
var timestamps = _.map(_.range(1000), function (i) {
|
var timestamps = _.map(_.range(1000), function (i) {
|
||||||
|
|
Loading…
Add table
Reference in a new issue