mirror of
https://github.com/pikami/cosmium.git
synced 2026-11-12 21:42:15 +00:00
Honor If-Match when deleting documents (#21)
Compare the document ETag and delete it in one datastore operation so a stale If-Match returns 412 and leaves the newer document in place. Co-authored-by: Cursor Agent <[email protected]>
This commit is contained in:
1 parent
af271092c3
commit
2fddaf7498
6 files changed
+185
-14
No files matched your search
@@ -64,20 +64,36 @@ func (r *BadgerDataStore) GetDocument(databaseId string, collectionId string, do
|
||||
}
|
||||
|
||||
func (r *BadgerDataStore) DeleteDocument(databaseId string, collectionId string, documentId string) datastore.DataStoreStatus {
|
||||
return r.deleteDocument(databaseId, collectionId, documentId, "")
|
||||
}
|
||||
|
||||
func (r *BadgerDataStore) DeleteDocumentIfMatch(databaseId string, collectionId string, documentId string, ifMatch string) datastore.DataStoreStatus {
|
||||
return r.deleteDocument(databaseId, collectionId, documentId, ifMatch)
|
||||
}
|
||||
|
||||
func (r *BadgerDataStore) deleteDocument(databaseId string, collectionId string, documentId string, ifMatch string) datastore.DataStoreStatus {
|
||||
documentKey := generateDocumentKey(databaseId, collectionId, documentId)
|
||||
|
||||
txn := r.db.NewTransaction(true)
|
||||
defer txn.Discard()
|
||||
|
||||
exists, err := keyExists(txn, documentKey)
|
||||
if err != nil {
|
||||
return datastore.Unknown
|
||||
}
|
||||
if !exists {
|
||||
var document datastore.Document
|
||||
status := getKey(txn, documentKey, &document)
|
||||
if status == datastore.StatusNotFound {
|
||||
return datastore.StatusNotFound
|
||||
}
|
||||
if status != datastore.StatusOk {
|
||||
return status
|
||||
}
|
||||
|
||||
err = txn.Delete([]byte(documentKey))
|
||||
if ifMatch != "" {
|
||||
etag, _ := document["_etag"].(string)
|
||||
if etag != ifMatch {
|
||||
return datastore.PreconditionFailed
|
||||
}
|
||||
}
|
||||
|
||||
err := txn.Delete([]byte(documentKey))
|
||||
if err != nil {
|
||||
logger.ErrorLn("Error while deleting document:", err)
|
||||
return datastore.Unknown
|
||||
|
||||
@@ -15,6 +15,11 @@ type DataStore interface {
|
||||
GetDocumentIterator(databaseId string, collectionId string) (DocumentIterator, DataStoreStatus)
|
||||
GetDocument(databaseId string, collectionId string, documentId string) (Document, DataStoreStatus)
|
||||
DeleteDocument(databaseId string, collectionId string, documentId string) DataStoreStatus
|
||||
// DeleteDocumentIfMatch removes a document when ifMatch is empty or equals its _etag.
|
||||
// The comparison and delete happen together, so a concurrent update cannot land between them.
|
||||
// A mismatched etag returns PreconditionFailed and leaves the document in place.
|
||||
// A missing document returns StatusNotFound.
|
||||
DeleteDocumentIfMatch(databaseId string, collectionId string, documentId string, ifMatch string) DataStoreStatus
|
||||
CreateDocument(databaseId string, collectionId string, document map[string]interface{}) (Document, DataStoreStatus)
|
||||
|
||||
GetAllTriggers(databaseId string, collectionId string) ([]Trigger, DataStoreStatus)
|
||||
|
||||
@@ -45,6 +45,14 @@ func (r *JsonDataStore) GetDocument(databaseId string, collectionId string, docu
|
||||
}
|
||||
|
||||
func (r *JsonDataStore) DeleteDocument(databaseId string, collectionId string, documentId string) datastore.DataStoreStatus {
|
||||
return r.deleteDocument(databaseId, collectionId, documentId, "")
|
||||
}
|
||||
|
||||
func (r *JsonDataStore) DeleteDocumentIfMatch(databaseId string, collectionId string, documentId string, ifMatch string) datastore.DataStoreStatus {
|
||||
return r.deleteDocument(databaseId, collectionId, documentId, ifMatch)
|
||||
}
|
||||
|
||||
func (r *JsonDataStore) deleteDocument(databaseId string, collectionId string, documentId string, ifMatch string) datastore.DataStoreStatus {
|
||||
r.storeState.Lock()
|
||||
defer r.storeState.Unlock()
|
||||
|
||||
@@ -56,10 +64,18 @@ func (r *JsonDataStore) DeleteDocument(databaseId string, collectionId string, d
|
||||
return datastore.StatusNotFound
|
||||
}
|
||||
|
||||
if _, ok := r.storeState.Documents[databaseId][collectionId][documentId]; !ok {
|
||||
document, ok := r.storeState.Documents[databaseId][collectionId][documentId]
|
||||
if !ok {
|
||||
return datastore.StatusNotFound
|
||||
}
|
||||
|
||||
if ifMatch != "" {
|
||||
etag, _ := document["_etag"].(string)
|
||||
if etag != ifMatch {
|
||||
return datastore.PreconditionFailed
|
||||
}
|
||||
}
|
||||
|
||||
delete(r.storeState.Documents[databaseId][collectionId], documentId)
|
||||
|
||||
return datastore.StatusOk
|
||||
|
||||
@@ -11,12 +11,13 @@ type Database struct {
|
||||
type DataStoreStatus int
|
||||
|
||||
const (
|
||||
StatusOk DataStoreStatus = 1
|
||||
StatusNotFound DataStoreStatus = 2
|
||||
Conflict DataStoreStatus = 3
|
||||
BadRequest DataStoreStatus = 4
|
||||
IterEOF DataStoreStatus = 5
|
||||
Unknown DataStoreStatus = 6
|
||||
StatusOk DataStoreStatus = 1
|
||||
StatusNotFound DataStoreStatus = 2
|
||||
Conflict DataStoreStatus = 3
|
||||
BadRequest DataStoreStatus = 4
|
||||
IterEOF DataStoreStatus = 5
|
||||
Unknown DataStoreStatus = 6
|
||||
PreconditionFailed DataStoreStatus = 7
|
||||
)
|
||||
|
||||
type TriggerOperation string
|
||||
|
||||
Reference in new issue
Block a user