Skip to content

Commit 39b2a36

Browse files
authored
fix: 409 conflict after auto accept of a shared drive (#4737)
## Summary - AddGroupsAndContacts adds Bob, saves doc (rev 1) - SendInvitations starts → SendShortcut → sends sharing request to Bob's cozy - Bob's cozy auto-accepts (trusted sender) → calls back POST /sharings/:id/answer on your cozy - ProcessAnswer on your cozy updates the sharing doc (rev 1 → 2) — member status becomes Ready - Meanwhile, SendInvitations finishes the goroutine and calls couchdb.UpdateDoc(inst, s) with stale rev 1 → CouchDB 409 The sharing actually worked, the member was added and accepted, but the response to the client is 409 because of the stale revision.
2 parents a7fa932 + d880057 commit 39b2a36

1 file changed

Lines changed: 36 additions & 1 deletion

File tree

model/sharing/invitation.go

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,12 +72,47 @@ func (s *Sharing) SendInvitations(inst *instance.Instance, perms *permission.Per
7272
})
7373
}
7474
errg := g.Wait()
75-
if err := couchdb.UpdateDoc(inst, s); err != nil {
75+
if err := s.updateMemberStatusesWithRetry(inst); err != nil {
7676
return err
7777
}
7878
return errg
7979
}
8080

81+
// updateMemberStatusesWithRetry persists the current member statuses to
82+
// CouchDB, retrying on conflict. A conflict can happen when ProcessAnswer
83+
// updates the sharing concurrently (e.g. during auto-accept).
84+
// On retry, the document is reloaded and statuses are re-applied, but
85+
// members already in Ready state are never downgraded.
86+
func (s *Sharing) updateMemberStatusesWithRetry(inst *instance.Instance) error {
87+
desiredStatuses := make(map[int]string, len(s.Members))
88+
for i, m := range s.Members {
89+
if i > 0 {
90+
desiredStatuses[i] = m.Status
91+
}
92+
}
93+
94+
maxRetries := 3
95+
for attempt := 0; ; attempt++ {
96+
err := couchdb.UpdateDoc(inst, s)
97+
if err == nil || !couchdb.IsConflictError(err) || attempt >= maxRetries {
98+
return err
99+
}
100+
s, err = FindSharing(inst, s.SID)
101+
if err != nil {
102+
return err
103+
}
104+
for i, status := range desiredStatuses {
105+
if i >= len(s.Members) {
106+
continue
107+
}
108+
if s.Members[i].Status == MemberStatusReady {
109+
continue
110+
}
111+
s.Members[i].Status = status
112+
}
113+
}
114+
}
115+
81116
// SendInvitationsToMembers sends mails from a recipient (open_sharing) to
82117
// their contacts to invite them
83118
func (s *Sharing) SendInvitationsToMembers(inst *instance.Instance, members []Member, states map[string]string) error {

0 commit comments

Comments
 (0)