@@ -15,6 +15,7 @@ import (
1515 "maps"
1616 "math"
1717 "net/http"
18+ "slices"
1819 "strings"
1920 "time"
2021
@@ -218,46 +219,48 @@ func (c *DatabaseCollection) GetDocSyncData(ctx context.Context, docid string) (
218219
219220// CompactDocChannelHistory removes channel history entries that ended at or before the given sequence number.
220221// This is used to truncate stale channel assignment history to reduce storage overhead.
221- func (c * DatabaseCollection ) CompactDocChannelHistory (ctx context.Context , docid string , seq uint64 ) ( err error ) {
222+ func (c * DatabaseCollection ) CompactDocChannelHistory (ctx context.Context , docid string , seq uint64 ) error {
222223 key := realDocID (docid )
223224 if key == "" {
224225 return base .HTTPErrorf (400 , "Invalid doc ID" )
225226 }
226227
227- _ , xattrs , cas , err := c .dataStore .GetWithXattrs (ctx , key , c .syncGlobalSyncMouRevSeqNoAndUserXattrKeys ())
228+ xattrKeys := []string {base .SyncXattrName , base .VirtualXattrRevSeqNo , base .MouXattrName }
229+ _ , xattrs , cas , err := c .dataStore .GetWithXattrs (ctx , key , xattrKeys )
228230 if err != nil {
229- return
231+ return err
230232 }
231233
232- doc , err := c .unmarshalDocumentWithXattrs (ctx , docid , nil , xattrs , cas , DocUnmarshalSync )
234+ doc , err := c .unmarshalDocumentWithXattrs (ctx , key , nil , xattrs , cas , DocUnmarshalSync )
233235 if err != nil {
234- return
236+ return err
235237 }
236238
237- var compactedChannelSetHistory []ChannelSetEntry
238- for _ , channel := range doc .SyncData .ChannelSetHistory {
239- // Keep entries that are still active or that ended after the compaction point.
240- if channel .End > seq {
241- compactedChannelSetHistory = append (compactedChannelSetHistory , channel )
242- }
243- }
244- doc .SyncData .ChannelSetHistory = compactedChannelSetHistory
239+ // Store lengths before compaction to detect if any changes occur
240+ historyLenBefore := len (doc .SyncData .ChannelSetHistory )
241+ channelSetLenBefore := len (doc .SyncData .ChannelSet )
242+ channelsLenBefore := len (doc .SyncData .Channels )
245243
246- var compactedChannelSet [] ChannelSetEntry
247- for _ , channel := range doc . SyncData . ChannelSet {
248- if channel . End == 0 || channel . End > seq {
249- compactedChannelSet = append ( compactedChannelSet , channel )
250- }
251- }
252- doc . SyncData . ChannelSet = compactedChannelSet
244+ doc . SyncData . ChannelSetHistory = slices . DeleteFunc ( doc . SyncData . ChannelSetHistory , func ( channel ChannelSetEntry ) bool {
245+ return channel . End <= seq
246+ })
247+
248+ doc . SyncData . ChannelSet = slices . DeleteFunc ( doc . SyncData . ChannelSet , func ( channel ChannelSetEntry ) bool {
249+ return channel . End != 0 && channel . End <= seq
250+ })
253251
254- compactedChannels := make (channels.ChannelMap )
255252 for chanName , chanEntry := range doc .SyncData .Channels {
256- if chanEntry == nil || chanEntry .Seq > seq {
257- compactedChannels [ chanName ] = chanEntry
253+ if chanEntry != nil && chanEntry .Seq <= seq {
254+ delete ( doc . SyncData . Channels , chanName )
258255 }
259256 }
260- doc .SyncData .Channels = compactedChannels
257+
258+ // Exit early if no compaction occurred
259+ if len (doc .SyncData .ChannelSetHistory ) == historyLenBefore &&
260+ len (doc .SyncData .ChannelSet ) == channelSetLenBefore &&
261+ len (doc .SyncData .Channels ) == channelsLenBefore {
262+ return nil
263+ }
261264
262265 rawSyncXattr , err := base .JSONMarshal (doc .SyncData )
263266 if err != nil {
@@ -269,19 +272,19 @@ func (c *DatabaseCollection) CompactDocChannelHistory(ctx context.Context, docid
269272 base .InfofCtx (ctx , base .KeyCRUD , `Could not determine the revSeqNo when attempting to compact channel history for doc: %s. Error: %v` , base .UD (docid ), err )
270273 }
271274
272- metadataOnlyUpdate := & MetadataOnlyUpdate {
273- HexCAS : expandMacroCASValueString ,
274- PreviousHexCAS : doc .SyncData .Cas ,
275- PreviousRevSeqNo : revSeqNo ,
276- }
275+ metadataOnlyUpdate := computeMetadataOnlyUpdate (doc .Cas , revSeqNo , doc .MetadataOnlyUpdate )
276+
277277 rawMouXattr , err := base .JSONMarshal (metadataOnlyUpdate )
278278 if err != nil {
279279 return base .RedactErrorf ("failed to marshal _mou when attempting to compact channel history for doc: %s. Error: %v" , base .UD (docid ), err )
280280 }
281281
282282 // build macro expansion for sync data. This will avoid the update to xattrs causing an extra import event (i.e. sync cas will be == to doc cas)
283283 opts := & sgbucket.MutateInOptions {}
284- spec := append (macroExpandSpec (base .SyncXattrName ), sgbucket .NewMacroExpansionSpec (XattrMouCasPath (), sgbucket .MacroCas ))
284+ spec := []sgbucket.MacroExpansionSpec {
285+ sgbucket .NewMacroExpansionSpec (xattrCasPath (base .SyncXattrName ), sgbucket .MacroCas ),
286+ sgbucket .NewMacroExpansionSpec (XattrMouCasPath (), sgbucket .MacroCas ),
287+ }
285288 opts .MacroExpansion = spec
286289 opts .PreserveExpiry = true // if doc has expiry, we should preserve this
287290
0 commit comments