FIX: Gap Of Uploading The Same File Key Twice
Build, Push and Deploy CMS / build-push-deploy (push) Successful in 2m19s

This commit is contained in:
FahdShalhoub
2026-08-27 18:56:08 +03:00
parent b68086eea4
commit 88c64adf38
8 changed files with 62 additions and 29 deletions
+7 -1
View File
@@ -84,6 +84,12 @@ const docTemplate = `{
"$ref": "#/definitions/api.ProblemDetails"
}
},
"409": {
"description": "key has already been registered by an earlier request",
"schema": {
"$ref": "#/definitions/api.ProblemDetails"
}
},
"422": {
"description": "title was empty, key was missing or not a key issued by this API, or categoryIds was empty",
"schema": {
@@ -91,7 +97,7 @@ const docTemplate = `{
}
},
"500": {
"description": "Categories could not be read, the transcoding job could not be queued, or the video record could not be saved",
"description": "Categories could not be read, the key could not be checked, the transcoding job could not be queued, or the video record could not be saved",
"schema": {
"$ref": "#/definitions/api.ProblemDetails"
}
+7 -1
View File
@@ -77,6 +77,12 @@
"$ref": "#/definitions/api.ProblemDetails"
}
},
"409": {
"description": "key has already been registered by an earlier request",
"schema": {
"$ref": "#/definitions/api.ProblemDetails"
}
},
"422": {
"description": "title was empty, key was missing or not a key issued by this API, or categoryIds was empty",
"schema": {
@@ -84,7 +90,7 @@
}
},
"500": {
"description": "Categories could not be read, the transcoding job could not be queued, or the video record could not be saved",
"description": "Categories could not be read, the key could not be checked, the transcoding job could not be queued, or the video record could not be saved",
"schema": {
"$ref": "#/definitions/api.ProblemDetails"
}
+7 -2
View File
@@ -190,14 +190,19 @@ paths:
description: categoryIds referenced a category that does not exist
schema:
$ref: '#/definitions/api.ProblemDetails'
"409":
description: key has already been registered by an earlier request
schema:
$ref: '#/definitions/api.ProblemDetails'
"422":
description: title was empty, key was missing or not a key issued by this
API, or categoryIds was empty
schema:
$ref: '#/definitions/api.ProblemDetails'
"500":
description: Categories could not be read, the transcoding job could not
be queued, or the video record could not be saved
description: Categories could not be read, the key could not be checked,
the transcoding job could not be queued, or the video record could not
be saved
schema:
$ref: '#/definitions/api.ProblemDetails'
summary: Register an uploaded video
+13
View File
@@ -14,6 +14,19 @@ type VideoRepository struct {
var VideoRepo VideoRepository
// VideoExistsWithStorageKey reports whether a video has already been
// registered under a storage key. It is served by videos_storage_key_key, the
// index Postgres builds for the column's UNIQUE constraint.
func (svc VideoRepository) VideoExistsWithStorageKey(ctx context.Context, storageKey string) (bool, error) {
var exists bool
err := svc.SQLDB.QueryRowContext(ctx,
`SELECT EXISTS (SELECT 1 FROM videos WHERE storage_key = $1)`, storageKey).Scan(&exists)
if err != nil {
return false, err
}
return exists, nil
}
func (svc VideoRepository) CreateVideo(ctx context.Context, v models.Video) (models.Video, error) {
tx, err := svc.SQLDB.BeginTx(ctx, nil)
if err != nil {
+20 -1
View File
@@ -113,8 +113,9 @@ func PresignVideoUpload(w http.ResponseWriter, r *http.Request) {
// @Success 201 {object} api.CompleteResponse
// @Failure 400 {object} api.ProblemDetails "Request body was not valid JSON"
// @Failure 404 {object} api.ProblemDetails "categoryIds referenced a category that does not exist"
// @Failure 409 {object} api.ProblemDetails "key has already been registered by an earlier request"
// @Failure 422 {object} api.ProblemDetails "title was empty, key was missing or not a key issued by this API, or categoryIds was empty"
// @Failure 500 {object} api.ProblemDetails "Categories could not be read, the transcoding job could not be queued, or the video record could not be saved"
// @Failure 500 {object} api.ProblemDetails "Categories could not be read, the key could not be checked, the transcoding job could not be queued, or the video record could not be saved"
// @Router /api/videos [post]
func CompleteVideoUpload(w http.ResponseWriter, r *http.Request) {
r.Body = http.MaxBytesReader(w, r.Body, maxJSONBodySize)
@@ -166,6 +167,24 @@ func CompleteVideoUpload(w http.ResponseWriter, r *http.Request) {
return
}
// Ahead of queueing, because a key that is already registered would
// otherwise get a second transcoding job before the insert failed on the
// storage_key UNIQUE constraint. That constraint is still the backstop:
// this check and the insert are not one atomic step, so two concurrent
// requests carrying the same key can both get past here.
alreadyRegistered, err := repositories.VideoRepo.VideoExistsWithStorageKey(r.Context(), key)
if err != nil {
log.Printf("Something Went Wrong Checking Whether The Video Was Already Registered: %s", err)
writeProblem(w, http.StatusInternalServerError, "Something Went Wrong Checking Whether The Video Was Already Registered",
"The submitted key could not be checked against the existing video records, so no video record was created and no transcoding job was queued. This is a server-side fault; the uploaded file is still in storage, so retry this request with the same 'key'.")
return
}
if alreadyRegistered {
writeProblem(w, http.StatusConflict, "Video Already Registered",
fmt.Sprintf("The 'key' %q has already been registered, so a video record and a transcoding job exist for that file. Registering it a second time would queue a duplicate job. If you meant to add a different video, request a fresh key from POST /api/videos/presign and upload the file to it.", key))
return
}
jobID, err := services.MediaConvertClient.QueueEncodingJob(r.Context(), key)
if err != nil {
log.Printf("Something Went Wrong On Creation Of Transcoding Job: %s", err)