aboutsummaryrefslogtreecommitdiff
path: root/refstore/files/batch_apply.go
diff options
context:
space:
mode:
authorGravatar Runxi Yu2026-03-23 03:25:44 +0000
committerGravatar Runxi Yu2026-03-23 03:27:52 +0000
commit4a796e64ac576d6a3e3f2fe6174c4aa476ea0c5c (patch)
tree44d72a20076ceab0981d0b553693d26ca36cc0be /refstore/files/batch_apply.go
parentreceivepack: Lifecycle/ownership docs (diff)
signatureNo signature
refstore: Improve interfaces, errors, and make batch work v0.1.92
Diffstat (limited to 'refstore/files/batch_apply.go')
-rw-r--r--refstore/files/batch_apply.go142
1 files changed, 102 insertions, 40 deletions
diff --git a/refstore/files/batch_apply.go b/refstore/files/batch_apply.go
index 0c217c56..55224b36 100644
--- a/refstore/files/batch_apply.go
+++ b/refstore/files/batch_apply.go
@@ -1,73 +1,135 @@
package files
-import (
- "errors"
-
- "codeberg.org/lindenii/furgit/refstore"
-)
+import "codeberg.org/lindenii/furgit/refstore"
func (batch *Batch) Apply() ([]refstore.BatchResult, error) {
- if batch.closed {
- return nil, errors.New("refstore/files: batch already closed")
- }
-
results := make([]refstore.BatchResult, len(batch.ops))
- seen := make(map[string]struct{}, len(batch.ops))
+ remainingIdx := make([]int, 0, len(batch.ops))
+ remainingOps := make([]queuedUpdate, 0, len(batch.ops))
+ seenTargets := make(map[string]struct{}, len(batch.ops))
+ executor := &refUpdateExecutor{store: batch.store}
for i, op := range batch.ops {
results[i].Name = op.name
- if _, exists := seen[op.name]; exists {
- batch.closed = true
+ err := executor.validateQueuedUpdate(op)
+ if err != nil {
+ results[i].Status = refstore.BatchStatusRejected
+ results[i].Error = batchResultError(err)
+
+ continue
+ }
+
+ target, err := executor.resolveQueuedUpdateTarget(op)
+ if err != nil {
+ if isBatchRejected(err) {
+ results[i].Status = refstore.BatchStatusRejected
+ results[i].Error = batchResultError(err)
+
+ continue
+ }
+
+ results[i].Status = refstore.BatchStatusFatal
+ results[i].Error = batchResultError(err)
- err := errors.New("refstore/files: duplicate batch operation for " + `"` + op.name + `"`)
- for j := i; j < len(results); j++ {
+ for j := i + 1; j < len(results); j++ {
results[j].Name = batch.ops[j].name
- results[j].Error = err
+ results[j].Status = refstore.BatchStatusNotAttempted
+ results[j].Error = batchResultError(err)
}
return results, err
}
- seen[op.name] = struct{}{}
- }
+ targetKey := updateTargetKey(target.loc)
+ if _, exists := seenTargets[targetKey]; exists {
+ results[i].Status = refstore.BatchStatusRejected
+ results[i].Error = &refstore.DuplicateUpdateError{}
- for i, op := range batch.ops {
- tx := &Transaction{
- store: batch.store,
- ops: []txOp{op},
+ continue
}
- err := tx.validateOp(op)
+ seenTargets[targetKey] = struct{}{}
+ remainingIdx = append(remainingIdx, i)
+ remainingOps = append(remainingOps, op)
+ }
+
+ for len(remainingOps) > 0 {
+ prepared, err := executor.prepareUpdates(remainingOps)
if err != nil {
- results[i].Error = err
+ if isBatchRejected(err) {
+ name := batchResultName(err)
+ rejectedAt := -1
- continue
- }
+ for i, op := range remainingOps {
+ if op.name == name {
+ rejectedAt = i
- err = tx.Commit()
- if err == nil {
- continue
- }
+ break
+ }
+ }
- if isBatchRejected(err) {
- results[i].Error = err
+ if rejectedAt < 0 {
+ for _, idx := range remainingIdx {
+ results[idx].Status = refstore.BatchStatusNotAttempted
+ results[idx].Error = batchResultError(err)
+ }
- continue
+ return results, err
+ }
+
+ results[remainingIdx[rejectedAt]].Status = refstore.BatchStatusRejected
+ results[remainingIdx[rejectedAt]].Error = batchResultError(err)
+ remainingIdx = append(remainingIdx[:rejectedAt], remainingIdx[rejectedAt+1:]...)
+ remainingOps = append(remainingOps[:rejectedAt], remainingOps[rejectedAt+1:]...)
+
+ continue
+ }
+
+ fatalName := batchResultName(err)
+ fatalMarked := false
+ for i, idx := range remainingIdx {
+ if !fatalMarked && remainingOps[i].name == fatalName && fatalName != "" {
+ results[idx].Status = refstore.BatchStatusFatal
+ results[idx].Error = batchResultError(err)
+ fatalMarked = true
+
+ continue
+ }
+
+ results[idx].Status = refstore.BatchStatusNotAttempted
+ results[idx].Error = batchResultError(err)
+ }
+
+ return results, err
}
- batch.closed = true
- results[i].Error = err
+ err = executor.commitPreparedUpdates(prepared)
+ if err != nil {
+ fatalName := batchResultName(err)
+ fatalMarked := false
+ for i, idx := range remainingIdx {
+ if !fatalMarked && remainingOps[i].name == fatalName && fatalName != "" {
+ results[idx].Status = refstore.BatchStatusFatal
+ results[idx].Error = batchResultError(err)
+ fatalMarked = true
+
+ continue
+ }
+
+ results[idx].Status = refstore.BatchStatusNotAttempted
+ results[idx].Error = batchResultError(err)
+ }
- for j := i + 1; j < len(results); j++ {
- results[j].Name = batch.ops[j].name
- results[j].Error = err
+ return results, err
}
- return results, err
- }
+ for _, idx := range remainingIdx {
+ results[idx].Status = refstore.BatchStatusApplied
+ }
- batch.closed = true
+ return results, nil
+ }
return results, nil
}