Repository navigation
feat(common-contacts): reconcile the org instance with Sabre - #4977
Conversation
d445b70 to
bd95911
Compare
rezk2ll
left a comment
There was a problem hiding this comment.
Nice approach, the seen marker plus reconcile fits well, and going through DeleteDoc keeps share-group working.
Nothing blocking. My main worry is the guard, it only refuses when nothing at all was seen. Details inline.
| // stack, not that Sabre is empty. | ||
| if len(stale) > 0 && report.Kept == 0 { | ||
| return nil, fmt.Errorf("no contact was sent since %s, refusing to remove %d contacts", since.Format(time.RFC3339), len(stale)) | ||
| } |
There was a problem hiding this comment.
This only refuses when Kept is 0.
If the consumer is still behind, or Redis restarted mid republication, Kept is above 0 and everything not seen yet gets deleted. share-group then removes those people from their group sharings.
I tried it locally: republished 1 contact out of 3, the fixer deleted the other 2. The late ADDs then recreated them with new ids and no group ref.
Could it refuse above some removal ratio (or take a --max-removals) unless --force is passed?
There was a problem hiding this comment.
That's what --dry-run is for: it lists what would go and how many were kept, so your 2 out of 3 shows up before anything is deleted. A ratio would also refuse a real cleanup after a big offboarding, and then --force becomes the default.
|
|
||
| func seenKey(inst *instance.Instance, path string) string { | ||
| return "common-contacts-seen:" + inst.Domain + ":" + path | ||
| } |
There was a problem hiding this comment.
On Redis Cluster the MGET spans several slots, gets CROSSSLOT, and MultiGet returns all nil. So the fixer always refuses there.
A hash tag fixes it for free: "common-contacts-seen:{" + inst.Domain + "}:" + path
Low priority, the job scheduler doesn't run on a cluster today either.
| return err | ||
| } | ||
| return c.JSON(http.StatusOK, report) | ||
| } |
There was a problem hiding this comment.
nit: every refusal comes back as a 500 Unqualified error.
BadRequest / NotFound / Conflict would let a script tell misuse from a crash.
| var stale []*contact.Contact | ||
| for i, doc := range docs { | ||
| at, _ := strconv.ParseInt(string(seen[i]), 10, 64) | ||
| if at >= since.UnixMilli() { |
There was a problem hiding this comment.
nit: at comes from the stack's clock, since from the side service. A small skew deletes the first contacts.
One line in docs/admin.md saying to take since a few minutes earlier would cover it.
45c09a4 to
6231f0b
Compare
9584414 to
5b1a577
Compare
… Redis Cluster slot The reconciliation reads them with one MGET, which fails with CROSSSLOT on a cluster, so the fixer always refused.
…ixer A since too far back, an unknown organization, a missing organization instance and an organization instance that is not ready all came back as a 500.
The stack dates what the feed sends with its own clock, so a side service clock ahead of it removed the first contacts sent.
5b1a577 to
c4d5d7c
Compare
Summary
cozy-stack fix org-contacts <org-id>andPOST /instances/fixers/org-contacts/:org_idto delete feed-owned organization contacts absent from a completed source republication.since, the completed domain republication'sprocessedContactCountasexpected_contacts(CLI:--expected-contacts), and its exact CardDAV path list (CLI:--paths-file; API body:{"paths":[...]}). The side service currently supplies the count, so the corresponding paths must be exported separately from the source domain address books.common_contacts.carddavPath. Use normal contact deletion so sharing-group events retain their existing behavior. Dry runs report candidates, and repeating a successful cleanup removes nothing more.common_contactsindependently ofcentralized_org_directory, including while replication is retained. Organization-wide sharing updates remain tracked in Propagate organization group changes to member-instance sharings #5102.Part of linagora/twake-workplace-private#1781.