Add document ETag optimistic concurrency (#16)

* Add ETag optimistic concurrency for document replace

Co-authored-by: Pijus Kamandulis <[email protected]>

* Expose precondition error code header

Co-authored-by: Pijus Kamandulis <[email protected]>

* Stop Badger GC before closing datastore

Co-authored-by: Pijus Kamandulis <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
This commit is contained in:
Pijus Kamandulis
2026-06-05 23:26:25 +03:00
committed by GitHub
co-authored by Cursor Agent
parent 05e8cd2842
commit 36fd7f48cc
5 changed files with 126 additions and 11 deletions
+22 -1
View File
@@ -47,6 +47,9 @@ func (h *Handlers) GetDocument(c *gin.Context) {
document, status := h.dataStore.GetDocument(databaseId, collectionId, documentId)
if status == datastore.StatusOk {
if etag, ok := document["_etag"].(string); ok {
c.Header(headers.ETag, etag)
}
c.IndentedJSON(http.StatusOK, document)
return
}
@@ -90,7 +93,25 @@ func (h *Handlers) ReplaceDocument(c *gin.Context) {
return
}
status := h.dataStore.DeleteDocument(databaseId, collectionId, documentId)
existingDocument, status := h.dataStore.GetDocument(databaseId, collectionId, documentId)
if status == datastore.StatusNotFound {
c.IndentedJSON(http.StatusNotFound, constants.NotFoundResponse)
return
}
if status != datastore.StatusOk {
c.IndentedJSON(http.StatusInternalServerError, constants.UnknownErrorResponse)
return
}
if ifMatch := c.GetHeader(headers.IfMatch); ifMatch != "" {
if existingDocument["_etag"] != ifMatch {
c.Header(headers.ErrorCode, "PreconditionFailed")
c.JSON(http.StatusPreconditionFailed, constants.PreconditionFailedResponse)
return
}
}
status = h.dataStore.DeleteDocument(databaseId, collectionId, documentId)
if status == datastore.StatusNotFound {
c.IndentedJSON(http.StatusNotFound, constants.NotFoundResponse)
return