Fix retrying update on sql serialization conflict

pull/5615/head
Chocobozzz 2023-02-14 08:59:27 +01:00
parent 4c61660a0a
commit 4565774669
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
6 changed files with 14 additions and 33 deletions

View File

@ -273,7 +273,6 @@ async function addVideoChannel (req: express.Request, res: express.Response) {
async function updateVideoChannel (req: express.Request, res: express.Response) { async function updateVideoChannel (req: express.Request, res: express.Response) {
const videoChannelInstance = res.locals.videoChannel const videoChannelInstance = res.locals.videoChannel
const videoChannelFieldsSave = videoChannelInstance.toJSON()
const oldVideoChannelAuditKeys = new VideoChannelAuditView(videoChannelInstance.toFormattedJSON()) const oldVideoChannelAuditKeys = new VideoChannelAuditView(videoChannelInstance.toFormattedJSON())
const videoChannelInfoToUpdate = req.body as VideoChannelUpdate const videoChannelInfoToUpdate = req.body as VideoChannelUpdate
let doBulkVideoUpdate = false let doBulkVideoUpdate = false
@ -309,10 +308,9 @@ async function updateVideoChannel (req: express.Request, res: express.Response)
} catch (err) { } catch (err) {
logger.debug('Cannot update the video channel.', { err }) logger.debug('Cannot update the video channel.', { err })
// Force fields we want to update
// If the transaction is retried, sequelize will think the object has not changed // If the transaction is retried, sequelize will think the object has not changed
// So it will skip the SQL request, even if the last one was ROLLBACKed! // So we need to restore the previous fields
resetSequelizeInstance(videoChannelInstance, videoChannelFieldsSave) resetSequelizeInstance(videoChannelInstance)
throw err throw err
} }

View File

@ -210,7 +210,6 @@ async function addVideoPlaylist (req: express.Request, res: express.Response) {
async function updateVideoPlaylist (req: express.Request, res: express.Response) { async function updateVideoPlaylist (req: express.Request, res: express.Response) {
const videoPlaylistInstance = res.locals.videoPlaylistFull const videoPlaylistInstance = res.locals.videoPlaylistFull
const videoPlaylistFieldsSave = videoPlaylistInstance.toJSON()
const videoPlaylistInfoToUpdate = req.body as VideoPlaylistUpdate const videoPlaylistInfoToUpdate = req.body as VideoPlaylistUpdate
const wasPrivatePlaylist = videoPlaylistInstance.privacy === VideoPlaylistPrivacy.PRIVATE const wasPrivatePlaylist = videoPlaylistInstance.privacy === VideoPlaylistPrivacy.PRIVATE
@ -275,10 +274,9 @@ async function updateVideoPlaylist (req: express.Request, res: express.Response)
} catch (err) { } catch (err) {
logger.debug('Cannot update the video playlist.', { err }) logger.debug('Cannot update the video playlist.', { err })
// Force fields we want to update
// If the transaction is retried, sequelize will think the object has not changed // If the transaction is retried, sequelize will think the object has not changed
// So it will skip the SQL request, even if the last one was ROLLBACKed! // So we need to restore the previous fields
resetSequelizeInstance(videoPlaylistInstance, videoPlaylistFieldsSave) resetSequelizeInstance(videoPlaylistInstance)
throw err throw err
} }

View File

@ -45,7 +45,6 @@ export {
async function updateVideo (req: express.Request, res: express.Response) { async function updateVideo (req: express.Request, res: express.Response) {
const videoFromReq = res.locals.videoAll const videoFromReq = res.locals.videoAll
const videoFieldsSave = videoFromReq.toJSON()
const oldVideoAuditView = new VideoAuditView(videoFromReq.toFormattedDetailsJSON()) const oldVideoAuditView = new VideoAuditView(videoFromReq.toFormattedDetailsJSON())
const videoInfoToUpdate: VideoUpdate = req.body const videoInfoToUpdate: VideoUpdate = req.body
@ -151,10 +150,9 @@ async function updateVideo (req: express.Request, res: express.Response) {
isNewVideo isNewVideo
}) })
} catch (err) { } catch (err) {
// Force fields we want to update
// If the transaction is retried, sequelize will think the object has not changed // If the transaction is retried, sequelize will think the object has not changed
// So it will skip the SQL request, even if the last one was ROLLBACKed! // So we need to restore the previous fields
resetSequelizeInstance(videoFromReq, videoFieldsSave) resetSequelizeInstance(videoFromReq)
throw err throw err
} finally { } finally {

View File

@ -78,10 +78,8 @@ function updateInstanceWithAnother <M, T extends U, U extends Model<M>> (instanc
} }
} }
function resetSequelizeInstance (instance: Model<any>, savedFields: object) { function resetSequelizeInstance <T> (instance: Model<T>) {
Object.keys(savedFields).forEach(key => { instance.set(instance.previous())
instance[key] = savedFields[key]
})
} }
function filterNonExistingModels <T extends { hasSameUniqueKeysThan (other: T): boolean }> ( function filterNonExistingModels <T extends { hasSameUniqueKeysThan (other: T): boolean }> (

View File

@ -13,19 +13,12 @@ export class APActorUpdater {
private accountOrChannel: MAccount | MChannel private accountOrChannel: MAccount | MChannel
private readonly actorFieldsSave: object
private readonly accountOrChannelFieldsSave: object
constructor ( constructor (
private readonly actorObject: ActivityPubActor, private readonly actorObject: ActivityPubActor,
private readonly actor: MActorFull private readonly actor: MActorFull
) { ) {
this.actorFieldsSave = this.actor.toJSON()
if (this.actorObject.type === 'Group') this.accountOrChannel = this.actor.VideoChannel if (this.actorObject.type === 'Group') this.accountOrChannel = this.actor.VideoChannel
else this.accountOrChannel = this.actor.Account else this.accountOrChannel = this.actor.Account
this.accountOrChannelFieldsSave = this.accountOrChannel.toJSON()
} }
async update () { async update () {
@ -58,12 +51,12 @@ export class APActorUpdater {
logger.info('Remote account %s updated', this.actorObject.url) logger.info('Remote account %s updated', this.actorObject.url)
} catch (err) { } catch (err) {
if (this.actor !== undefined && this.actorFieldsSave !== undefined) { if (this.actor !== undefined) {
resetSequelizeInstance(this.actor, this.actorFieldsSave) resetSequelizeInstance(this.actor)
} }
if (this.accountOrChannel !== undefined && this.accountOrChannelFieldsSave !== undefined) { if (this.accountOrChannel !== undefined) {
resetSequelizeInstance(this.accountOrChannel, this.accountOrChannelFieldsSave) resetSequelizeInstance(this.accountOrChannel)
} }
// This is just a debug because we will retry the insert // This is just a debug because we will retry the insert

View File

@ -13,8 +13,6 @@ export class APVideoUpdater extends APVideoAbstractBuilder {
private readonly wasPrivateVideo: boolean private readonly wasPrivateVideo: boolean
private readonly wasUnlistedVideo: boolean private readonly wasUnlistedVideo: boolean
private readonly videoFieldsSave: any
private readonly oldVideoChannel: MChannelAccountLight private readonly oldVideoChannel: MChannelAccountLight
protected lTags: LoggerTagsFn protected lTags: LoggerTagsFn
@ -30,8 +28,6 @@ export class APVideoUpdater extends APVideoAbstractBuilder {
this.oldVideoChannel = this.video.VideoChannel this.oldVideoChannel = this.video.VideoChannel
this.videoFieldsSave = this.video.toJSON()
this.lTags = loggerTagsFactory('ap', 'video', 'update', video.uuid, video.url) this.lTags = loggerTagsFactory('ap', 'video', 'update', video.uuid, video.url)
} }
@ -156,8 +152,8 @@ export class APVideoUpdater extends APVideoAbstractBuilder {
} }
private catchUpdateError (err: Error) { private catchUpdateError (err: Error) {
if (this.video !== undefined && this.videoFieldsSave !== undefined) { if (this.video !== undefined) {
resetSequelizeInstance(this.video, this.videoFieldsSave) resetSequelizeInstance(this.video)
} }
// This is just a debug because we will retry the insert // This is just a debug because we will retry the insert