Don't stuck state when move transcoding job failed

This commit is contained in:
Chocobozzz 2021-12-23 11:09:31 +01:00
parent 482b26231b
commit dbd9fb44dd
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
9 changed files with 64 additions and 16 deletions

View File

@ -2,6 +2,10 @@
Transcoding failed, this video may not work properly. Transcoding failed, this video may not work properly.
</div> </div>
<div i18n class="alert alert-warning" *ngIf="isVideoMoveToObjectStorageFailed()">
Move to external storage failed, this video may not work properly.
</div>
<div i18n class="alert alert-warning" *ngIf="isVideoToImport()"> <div i18n class="alert alert-warning" *ngIf="isVideoToImport()">
The video is being imported, it will be available when the import is finished. The video is being imported, it will be available when the import is finished.
</div> </div>

View File

@ -18,6 +18,10 @@ export class VideoAlertComponent {
return this.video && this.video.state.id === VideoState.TRANSCODING_FAILED return this.video && this.video.state.id === VideoState.TRANSCODING_FAILED
} }
isVideoMoveToObjectStorageFailed () {
return this.video && this.video.state.id === VideoState.TO_MOVE_TO_EXTERNAL_STORAGE_FAILED
}
isVideoToImport () { isVideoToImport () {
return this.video && this.video.state.id === VideoState.TO_IMPORT return this.video && this.video.state.id === VideoState.TO_IMPORT
} }

View File

@ -179,6 +179,10 @@ export class VideoMiniatureComponent implements OnInit {
return $localize`Transcoding failed` return $localize`Transcoding failed`
} }
if (video.state.id === VideoState.TO_MOVE_TO_EXTERNAL_STORAGE_FAILED) {
return $localize`Move to external storage failed`
}
if (video.state.id === VideoState.TO_TRANSCODE && video.waitTranscoding === true) { if (video.state.id === VideoState.TO_TRANSCODE && video.waitTranscoding === true) {
return $localize`Waiting transcoding` return $localize`Waiting transcoding`
} }

View File

@ -4,7 +4,7 @@ registerTSPaths()
import { program } from 'commander' import { program } from 'commander'
import { VideoModel } from '@server/models/video/video' import { VideoModel } from '@server/models/video/video'
import { initDatabaseModels } from '@server/initializers/database' import { initDatabaseModels } from '@server/initializers/database'
import { VideoStorage } from '@shared/models' import { VideoState, VideoStorage } from '@shared/models'
import { moveToExternalStorageState } from '@server/lib/video-state' import { moveToExternalStorageState } from '@server/lib/video-state'
import { JobQueue } from '@server/lib/job-queue' import { JobQueue } from '@server/lib/job-queue'
import { CONFIG } from '@server/initializers/config' import { CONFIG } from '@server/initializers/config'
@ -62,6 +62,11 @@ async function run () {
process.exit(-1) process.exit(-1)
} }
if (video.state === VideoState.TO_MOVE_TO_EXTERNAL_STORAGE) {
console.error('This video is already being moved to external storage')
process.exit(-1)
}
ids.push(video.id) ids.push(video.id)
} else { } else {
ids = await VideoModel.listLocalIds() ids = await VideoModel.listLocalIds()

View File

@ -427,7 +427,8 @@ const VIDEO_STATES: { [ id in VideoState ]: string } = {
[VideoState.WAITING_FOR_LIVE]: 'Waiting for livestream', [VideoState.WAITING_FOR_LIVE]: 'Waiting for livestream',
[VideoState.LIVE_ENDED]: 'Livestream ended', [VideoState.LIVE_ENDED]: 'Livestream ended',
[VideoState.TO_MOVE_TO_EXTERNAL_STORAGE]: 'To move to an external storage', [VideoState.TO_MOVE_TO_EXTERNAL_STORAGE]: 'To move to an external storage',
[VideoState.TRANSCODING_FAILED]: 'Transcoding failed' [VideoState.TRANSCODING_FAILED]: 'Transcoding failed',
[VideoState.TO_MOVE_TO_EXTERNAL_STORAGE_FAILED]: 'External storage move failed'
} }
const VIDEO_IMPORT_STATES: { [ id in VideoImportState ]: string } = { const VIDEO_IMPORT_STATES: { [ id in VideoImportState ]: string } = {

View File

@ -7,7 +7,7 @@ import { CONFIG } from '@server/initializers/config'
import { P2P_MEDIA_LOADER_PEER_VERSION } from '@server/initializers/constants' import { P2P_MEDIA_LOADER_PEER_VERSION } from '@server/initializers/constants'
import { storeHLSFile, storeWebTorrentFile } from '@server/lib/object-storage' import { storeHLSFile, storeWebTorrentFile } from '@server/lib/object-storage'
import { getHLSDirectory, getHlsResolutionPlaylistFilename } from '@server/lib/paths' import { getHLSDirectory, getHlsResolutionPlaylistFilename } from '@server/lib/paths'
import { moveToNextState } from '@server/lib/video-state' import { moveToFailedMoveToObjectStorageState, moveToNextState } from '@server/lib/video-state'
import { VideoModel } from '@server/models/video/video' import { VideoModel } from '@server/models/video/video'
import { VideoJobInfoModel } from '@server/models/video/video-job-info' import { VideoJobInfoModel } from '@server/models/video/video-job-info'
import { MStreamingPlaylistVideo, MVideo, MVideoFile, MVideoWithAllFiles } from '@server/types/models' import { MStreamingPlaylistVideo, MVideo, MVideoFile, MVideoWithAllFiles } from '@server/types/models'
@ -24,18 +24,25 @@ export async function processMoveToObjectStorage (job: Job) {
return undefined return undefined
} }
if (video.VideoFiles) { try {
await moveWebTorrentFiles(video) if (video.VideoFiles) {
} await moveWebTorrentFiles(video)
}
if (video.VideoStreamingPlaylists) { if (video.VideoStreamingPlaylists) {
await moveHLSFiles(video) await moveHLSFiles(video)
} }
const pendingMove = await VideoJobInfoModel.decrease(video.uuid, 'pendingMove') const pendingMove = await VideoJobInfoModel.decrease(video.uuid, 'pendingMove')
if (pendingMove === 0) { if (pendingMove === 0) {
logger.info('Running cleanup after moving files to object storage (video %s in job %d)', video.uuid, job.id) logger.info('Running cleanup after moving files to object storage (video %s in job %d)', video.uuid, job.id)
await doAfterLastJob(video, payload.isNewVideo) await doAfterLastJob(video, payload.isNewVideo)
}
} catch (err) {
logger.error('Cannot move video %s to object storage.', video.url, { err })
await moveToFailedMoveToObjectStorageState(video)
await VideoJobInfoModel.abortAllTasks(video.uuid, 'pendingMove')
} }
return payload.videoUUID return payload.videoUUID

View File

@ -4,7 +4,7 @@ import { CONFIG } from '@server/initializers/config'
import { sequelizeTypescript } from '@server/initializers/database' import { sequelizeTypescript } from '@server/initializers/database'
import { VideoModel } from '@server/models/video/video' import { VideoModel } from '@server/models/video/video'
import { VideoJobInfoModel } from '@server/models/video/video-job-info' import { VideoJobInfoModel } from '@server/models/video/video-job-info'
import { MVideoFullLight, MVideoUUID } from '@server/types/models' import { MVideo, MVideoFullLight, MVideoUUID } from '@server/types/models'
import { VideoState } from '@shared/models' import { VideoState } from '@shared/models'
import { federateVideoIfNeeded } from './activitypub/videos' import { federateVideoIfNeeded } from './activitypub/videos'
import { Notifier } from './notifier' import { Notifier } from './notifier'
@ -79,18 +79,25 @@ async function moveToExternalStorageState (video: MVideoFullLight, isNewVideo: b
} }
} }
function moveToFailedTranscodingState (video: MVideoFullLight) { function moveToFailedTranscodingState (video: MVideo) {
if (video.state === VideoState.TRANSCODING_FAILED) return if (video.state === VideoState.TRANSCODING_FAILED) return
return video.setNewState(VideoState.TRANSCODING_FAILED, false, undefined) return video.setNewState(VideoState.TRANSCODING_FAILED, false, undefined)
} }
function moveToFailedMoveToObjectStorageState (video: MVideo) {
if (video.state === VideoState.TO_MOVE_TO_EXTERNAL_STORAGE_FAILED) return
return video.setNewState(VideoState.TO_MOVE_TO_EXTERNAL_STORAGE_FAILED, false, undefined)
}
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
export { export {
buildNextVideoState, buildNextVideoState,
moveToExternalStorageState, moveToExternalStorageState,
moveToFailedTranscodingState, moveToFailedTranscodingState,
moveToFailedMoveToObjectStorageState,
moveToNextState moveToNextState
} }

View File

@ -99,4 +99,19 @@ export class VideoJobInfoModel extends Model<Partial<AttributesOnly<VideoJobInfo
return pendingMove return pendingMove
} }
static async abortAllTasks (videoUUID: string, column: VideoJobInfoColumnType): Promise<void> {
const options = { type: QueryTypes.UPDATE as QueryTypes.UPDATE, bind: { videoUUID } }
await VideoJobInfoModel.sequelize.query(`
UPDATE
"videoJobInfo"
SET
"${column}" = 0,
"updatedAt" = NOW()
FROM "video"
WHERE
"video"."id" = "videoJobInfo"."videoId" AND "video"."uuid" = $videoUUID
`, options)
}
} }

View File

@ -5,5 +5,6 @@ export const enum VideoState {
WAITING_FOR_LIVE = 4, WAITING_FOR_LIVE = 4,
LIVE_ENDED = 5, LIVE_ENDED = 5,
TO_MOVE_TO_EXTERNAL_STORAGE = 6, TO_MOVE_TO_EXTERNAL_STORAGE = 6,
TRANSCODING_FAILED = 7 TRANSCODING_FAILED = 7,
TO_MOVE_TO_EXTERNAL_STORAGE_FAILED = 8
} }