Skip to content

Commit dd81b5c

Browse files
authored
CBG-5327: Wire up REST API to document channel history compaction (#8275)
1 parent 2cc594d commit dd81b5c

8 files changed

Lines changed: 431 additions & 107 deletions

File tree

db/crud.go

Lines changed: 63 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,62 @@ func (c *DatabaseCollection) GetDocSyncData(ctx context.Context, docid string) (
222222

223223
}
224224

225+
type ChannelHistory map[string]map[uint64]struct{}
226+
227+
func (ch ChannelHistory) addChannelHistoryEntry(name string, seq uint64) {
228+
if _, ok := ch[name]; !ok {
229+
ch[name] = make(map[uint64]struct{})
230+
}
231+
if _, ok := ch[name][seq]; !ok {
232+
ch[name][seq] = struct{}{}
233+
}
234+
}
235+
236+
func (ch ChannelHistory) getChannelHistoryAsMap() map[string][]uint64 {
237+
response := make(map[string][]uint64)
238+
for chanName, chanEntry := range ch {
239+
response[chanName] = make([]uint64, 0)
240+
for seq, _ := range chanEntry {
241+
response[chanName] = append(response[chanName], seq)
242+
}
243+
slices.Sort(response[chanName])
244+
slices.Reverse(response[chanName])
245+
}
246+
return response
247+
}
248+
249+
// GetDocChannelHistory returns the channel revocation history for the given document as a map
250+
// from channel name to the sequences at which the document was removed from that channel.
251+
// It collects revocation sequences from the active Channels map, the ChannelSet, and the
252+
// ChannelSetHistory (overflow). Only channels that have been revoked at least once appear in
253+
// the result; active memberships with no revocation history are omitted, even though a currently
254+
// assigned channel can still appear if it was revoked and later re-added.
255+
func (c *DatabaseCollection) GetDocChannelHistory(ctx context.Context, docid string) (map[string][]uint64, error) {
256+
257+
chanHistory := make(ChannelHistory)
258+
syncData, err := c.GetDocSyncData(ctx, docid)
259+
if err != nil {
260+
return nil, err
261+
}
262+
for chanName, chanVal := range syncData.Channels {
263+
if chanVal != nil && chanVal.Seq != 0 {
264+
chanHistory.addChannelHistoryEntry(chanName, chanVal.Seq)
265+
}
266+
}
267+
for _, chanSetEntry := range syncData.ChannelSet {
268+
if chanSetEntry.End != 0 {
269+
chanHistory.addChannelHistoryEntry(chanSetEntry.Name, chanSetEntry.End)
270+
}
271+
}
272+
for _, chanSetEntry := range syncData.ChannelSetHistory {
273+
if chanSetEntry.End != 0 {
274+
chanHistory.addChannelHistoryEntry(chanSetEntry.Name, chanSetEntry.End)
275+
}
276+
}
277+
278+
return chanHistory.getChannelHistoryAsMap(), nil
279+
}
280+
225281
// CompactDocChannelHistory removes channel history entries that ended at or before the given sequence number.
226282
// This is used to prune stale channel assignment history to reduce storage overhead.
227283
func (c *DatabaseCollection) CompactDocChannelHistory(ctx context.Context, docid string, seq uint64) ([]string, error) {
@@ -258,27 +314,27 @@ func (c *DatabaseCollection) CompactDocChannelHistory(ctx context.Context, docid
258314
cas = doc.Cas
259315
}
260316

261-
compactedChannels := make([]string, 0)
317+
compactedChannels := make(base.Set)
262318

263319
doc.SyncData.ChannelSetHistory = slices.DeleteFunc(doc.SyncData.ChannelSetHistory, func(channel ChannelSetEntry) bool {
264320
del := channel.End <= seq
265321
if del {
266-
compactedChannels = append(compactedChannels, channel.Name)
322+
compactedChannels.Add(channel.Name)
267323
}
268324
return del
269325
})
270326

271327
doc.SyncData.ChannelSet = slices.DeleteFunc(doc.SyncData.ChannelSet, func(channel ChannelSetEntry) bool {
272328
del := channel.End != 0 && channel.End <= seq
273329
if del {
274-
compactedChannels = append(compactedChannels, channel.Name)
330+
compactedChannels.Add(channel.Name)
275331
}
276332
return del
277333
})
278334

279335
for chanName, chanEntry := range doc.SyncData.Channels {
280336
if chanEntry != nil && chanEntry.Seq <= seq {
281-
compactedChannels = append(compactedChannels, chanName)
337+
compactedChannels.Add(chanName)
282338
delete(doc.SyncData.Channels, chanName)
283339
}
284340
}
@@ -320,7 +376,9 @@ func (c *DatabaseCollection) CompactDocChannelHistory(ctx context.Context, docid
320376
base.MouXattrName: rawMouXattr,
321377
}
322378
_, err = c.dataStore.UpdateXattrs(ctx, key, 0, cas, updatedXattr, opts)
323-
return compactedChannels, err
379+
compactedChannelArray := compactedChannels.ToArray()
380+
slices.Sort(compactedChannelArray)
381+
return compactedChannelArray, err
324382
}
325383

326384
// unmarshalDocumentWithXattrs populates individual xattrs on unmarshalDocumentWithXattrs from a provided xattrs map

docs/api/admin.yaml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -125,10 +125,10 @@ paths:
125125
$ref: './paths/admin/db-_index_init.yaml'
126126
'/{keyspace}/_purge':
127127
$ref: './paths/admin/keyspace-_purge.yaml'
128-
'/{keyspace}/_history':
129-
$ref: './paths/admin/keyspace-_history.yaml'
130-
'/{keyspace}/_history/compact':
131-
$ref: './paths/admin/keyspace-_history-compact.yaml'
128+
'/{keyspace}/_channel_history/{docid}':
129+
$ref: './paths/admin/keyspace-_channel_history.yaml'
130+
'/{keyspace}/_channel_history/{docid}/compact':
131+
$ref: './paths/admin/keyspace-_channel_history-compact.yaml'
132132
'/{db}/_flush':
133133
$ref: './paths/admin/db-_flush.yaml'
134134
'/{db}/_online':

docs/api/paths/admin/keyspace-_history-compact.yaml renamed to docs/api/paths/admin/keyspace-_channel_history-compact.yaml

Lines changed: 12 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,15 @@
77
# the file licenses/APL2.txt.
88
parameters:
99
- $ref: ../../components/parameters.yaml#/keyspace
10+
- $ref: ../../components/parameters.yaml#/docid
1011
post:
1112
summary: Compact Channel History of Document
1213
description: |-
13-
Compacts inactive channel history entries from one or more documents before a specified sequence number.
14+
Compacts channel history for a specified document. Channel history older than the specified sequence will be removed.
1415
15-
This endpoint removes all inactive channel entries (for sequences before the specified sequence number where the document left the channel),
16+
This endpoint removes all channel entries (for sequences before the specified sequence number where the document left the channel),
1617
effectively cleaning up historical channel membership information while preserving active channels and recent changes.
17-
This is useful for reducing storage overhead when maintaining large document histories.
18+
This can be useful for reducing metadata size for documents that frequently gain and lose access to channels.
1819
1920
Required Sync Gateway RBAC roles:
2021
@@ -25,32 +26,22 @@ post:
2526
schema:
2627
type: object
2728
required:
28-
- doc_ids
2929
- seq
3030
properties:
31-
doc_ids:
32-
description: |-
33-
List of document IDs whose inactive channels should be compacted.
34-
type: array
35-
items:
36-
type: string
37-
example:
38-
- doc1
39-
- doc2
4031
seq:
4132
description: |-
42-
Channel history for inactive channels having end sequences earlier than this sequence will be removed from the specified document's metadata.
33+
Channel history having end sequences earlier than this sequence will be removed from the specified document's metadata.
4334
type: integer
4435
format: int64
45-
minimum: 0
36+
minimum: 1
4637
example: 12345
4738
responses:
4839
'200':
4940
description: |-
50-
Successfully compacted channel history from the specified documents.
51-
Returns a mapping of document IDs to the list of channels that were compacted.
41+
Successfully compacted channel history from the specified document.
42+
Returns a list of channels that were compacted.
5243
53-
If a document ID has an empty array, it means no channels were pruned for that document.
44+
If the response has an empty array, it means either no channels were compacted.
5445
5546
content:
5647
application/json:
@@ -64,16 +55,13 @@ post:
6455
description: |-
6556
Array of channel names that were compacted.
6657
description: |-
67-
A mapping of document IDs (keys) to arrays of compacted channels (values).
58+
A array of all the compacted channels
6859
example:
69-
doc1:
60+
compacted_channels:
7061
- channel1
7162
- channel2
72-
doc2:
73-
- channel2
74-
- channel3
7563
'400':
76-
description: 'Bad request. This could be due to invalid request parameters such as missing or malformed doc_ids array or invalid seq value.'
64+
description: 'Bad request. This could be due to invalid request parameters such as invalid seq value.'
7765
content:
7866
application/json:
7967
schema:
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# Copyright 2026-Present Couchbase, Inc.
2+
#
3+
# Use of this software is governed by the Business Source License included
4+
# in the file licenses/BSL-Couchbase.txt. As of the Change Date specified
5+
# in that file, in accordance with the Business Source License, use of this
6+
# software will be governed by the Apache License, Version 2.0, included in
7+
# the file licenses/APL2.txt.
8+
parameters:
9+
- $ref: ../../components/parameters.yaml#/keyspace
10+
- $ref: ../../components/parameters.yaml#/docid
11+
get:
12+
summary: Get Channel History of Document
13+
description: |-
14+
Returns the channel revocation history for the specified document as a map of channel
15+
names to the array of sequences at which the document was removed from each channel. Only channels
16+
that have been revoked at least once are included; channels the document is currently
17+
assigned may be included if they were previously revoked.
18+
19+
Multiple sequences for a given channel indicate that the document has lost access to that channel
20+
more than once — each sequence represents a point in time at which the document was removed from
21+
the channel.
22+
23+
Required Sync Gateway RBAC roles:
24+
25+
* Sync Gateway Application
26+
* Sync Gateway Application Read Only
27+
responses:
28+
'200':
29+
description: |-
30+
Successfully retrieved the channel revocation history for the specified document.
31+
Returns a JSON object mapping each channel name to an array of sequences at which
32+
the document was removed from that channel.
33+
content:
34+
application/json:
35+
schema:
36+
type: object
37+
additionalProperties:
38+
type: array
39+
items:
40+
type: integer
41+
format: int64
42+
minimum: 1
43+
description: Sequences at which the document was removed from this channel.
44+
description: Map of channel names to their revocation sequences.
45+
example:
46+
channel1:
47+
- 3
48+
- 7
49+
channel2:
50+
- 5
51+
'404':
52+
description: Document not found.
53+
content:
54+
application/json:
55+
schema:
56+
$ref: ../../components/schemas.yaml#/HTTP-Error
57+
'403':
58+
$ref: ../../components/responses.yaml#/Unauthorized-database
59+
60+
tags:
61+
- Document
62+
operationId: get_keyspace-_channel_history

docs/api/paths/admin/keyspace-_history.yaml

Lines changed: 0 additions & 72 deletions
This file was deleted.

0 commit comments

Comments
 (0)