Skip to content

Commit 19065ee

Browse files
authored
[3.2.4 Backport] CBG-4556: Detect loading non-xattr config document in xattr bootstrap mode (#7468)
1 parent 2d3b8f6 commit 19065ee

File tree

4 files changed

+72
-2
lines changed

4 files changed

+72
-2
lines changed

base/config_persistence.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,8 @@ func (xbp *XattrBootstrapPersistence) loadRawConfig(ctx context.Context, c *gocb
107107
// config
108108
xattrContErr := res.ContentAt(0, &rawValue)
109109
if xattrContErr != nil {
110-
DebugfCtx(ctx, KeyCRUD, "No xattr config found for key=%s, path=%s: %v", key, cfgXattrConfigPath, xattrContErr)
110+
SyncGatewayStats.GlobalStats.ConfigStat.XattrFormatMismatches.Add(1)
111+
DebugfCtx(ctx, KeyCRUD, "Found config document but No xattr config found for key=%s, path=%s: %v", key, cfgXattrConfigPath, xattrContErr)
111112
return rawValue, 0, ErrNotFound
112113
}
113114
return rawValue, res.Cas(), nil
@@ -181,7 +182,8 @@ func (xbp *XattrBootstrapPersistence) loadConfig(ctx context.Context, c *gocb.Co
181182
// config
182183
xattrContErr := res.ContentAt(0, valuePtr)
183184
if xattrContErr != nil {
184-
DebugfCtx(ctx, KeyCRUD, "No xattr config found for key=%s, path=%s: %v", key, cfgXattrConfigPath, xattrContErr)
185+
SyncGatewayStats.GlobalStats.ConfigStat.XattrFormatMismatches.Add(1)
186+
DebugfCtx(ctx, KeyCRUD, "Found config document but No xattr config found for key=%s, path=%s: %v", key, cfgXattrConfigPath, xattrContErr)
185187
return 0, ErrNotFound
186188
}
187189
casOut := res.Cas()

base/config_persistence_test.go

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
"github.com/stretchr/testify/require"
1717
)
1818

19+
// TestConfigPersistence ensures that all implementations of ConfigPersistence behave as expected for common operations.
1920
func TestConfigPersistence(t *testing.T) {
2021

2122
if UnitTestUrlIsWalrus() {
@@ -198,3 +199,63 @@ func TestXattrConfigPersistence(t *testing.T) {
198199
assert.Equal(t, configBody["sampleConfig"], loadedConfig["sampleConfig"])
199200

200201
}
202+
203+
// TestConfigPersistenceXattrFormatMismatches ensures that the XattrFormatMismatches stat is incremented when loading a config of a different format.
204+
func TestConfigPersistenceXattrFormatMismatches(t *testing.T) {
205+
206+
if UnitTestUrlIsWalrus() {
207+
t.Skip("This test only works against Couchbase Server")
208+
}
209+
210+
ctx := TestCtx(t)
211+
bucket := GetTestBucket(t)
212+
defer bucket.Close(ctx)
213+
214+
dataStore := bucket.GetSingleDataStore()
215+
216+
sgCollection, ok := dataStore.(*Collection)
217+
require.True(t, ok)
218+
c := sgCollection.Collection
219+
220+
nonXattrConfigPersistence := &DocumentBootstrapPersistence{}
221+
nonXattrConfigKey := "testNonXattrConfigKey"
222+
nonXattrConfigBody := map[string]interface{}{
223+
"sampleConfig": "value",
224+
}
225+
226+
xattrConfigKey := "testXattrConfigKey"
227+
xattrConfigPersistence := &XattrBootstrapPersistence{}
228+
xattrConfigBody := map[string]interface{}{
229+
"sampleConfig": "value",
230+
}
231+
232+
// create config without xattrs
233+
_, marshalErr := JSONMarshal(nonXattrConfigBody)
234+
require.NoError(t, marshalErr)
235+
_, insertErr := nonXattrConfigPersistence.insertConfig(c, nonXattrConfigKey, nonXattrConfigBody)
236+
require.NoError(t, insertErr)
237+
238+
existingStat := SyncGatewayStats.GlobalStats.ConfigStat.XattrFormatMismatches.Value()
239+
240+
// load config in xattr mode
241+
data, _, err := xattrConfigPersistence.loadRawConfig(ctx, c, nonXattrConfigKey)
242+
require.Error(t, err)
243+
assert.Nil(t, data)
244+
assert.Equal(t, existingStat+1, SyncGatewayStats.GlobalStats.ConfigStat.XattrFormatMismatches.Value())
245+
246+
// we can try the other way around (xattr config loading in non-xattr mode) - but it won't have any effect on the stat. This works in one way only.
247+
_, marshalErr = JSONMarshal(xattrConfigBody)
248+
require.NoError(t, marshalErr)
249+
_, insertErr = xattrConfigPersistence.insertConfig(c, xattrConfigKey, xattrConfigBody)
250+
require.NoError(t, insertErr)
251+
252+
existingStat = SyncGatewayStats.GlobalStats.ConfigStat.XattrFormatMismatches.Value()
253+
254+
// load config in non-xattr mode
255+
data, _, err = nonXattrConfigPersistence.loadRawConfig(ctx, c, xattrConfigKey)
256+
require.NoError(t, err)
257+
assert.Equal(t, []byte(`{"cfgVersion":1}`), data) // whitespace stripped cfgXattrBody
258+
259+
// this stat isn't effective since it only works when loading a non-xattr config in xattr mode
260+
assert.Equal(t, existingStat, SyncGatewayStats.GlobalStats.ConfigStat.XattrFormatMismatches.Value())
261+
}

base/stats.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,10 @@ func (g *GlobalStat) initConfigStats() error {
204204
if err != nil {
205205
return err
206206
}
207+
configStat.XattrFormatMismatches, err = NewIntStat(ConfigSubsystem, "xattr_format_mismatches", StatUnitBytes, XattrFormatMismatchesDesc, StatAddedVersion3dot2dot4, StatDeprecatedVersionNotDeprecated, StatStabilityCommitted, nil, nil, prometheus.CounterValue, 0)
208+
if err != nil {
209+
return err
210+
}
207211
g.ConfigStat = configStat
208212
return nil
209213
}
@@ -403,6 +407,8 @@ type ConfigStat struct {
403407
DatabaseBucketMismatches *SgwIntStat `json:"database_config_bucket_mismatches"`
404408
// The number of times the config was rolled back to an invalid state (conflicting collections)
405409
DatabaseRollbackCollectionCollisions *SgwIntStat `json:"database_config_rollback_collection_collisions"`
410+
// The number of times a non-xattr config or registry document was loaded in xattr mode
411+
XattrFormatMismatches *SgwIntStat `json:"xattr_format_mismatches"`
406412
}
407413

408414
type AuditStat struct {

base/stats_descriptions.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ const (
7474
const (
7575
DatabaseBucketMismatchesDesc = "The total number of times a database config is polled from a bucket that doesn't match the bucket specified in the database config."
7676
DatabaseCollectionConflictDesc = "The total number of times a database config is rolled back to an invalid state (collection conflicts)."
77+
XattrFormatMismatchesDesc = "The total number of times a non-xattr config or registry document was loaded in xattr mode."
7778
)
7879

7980
// audit stat

0 commit comments

Comments
 (0)