Skip to content

Commit ea954fd

Browse files
authored
CBG-4413: Add logging capture to Sync Function diagnostic API (#7938)
1 parent 918df77 commit ea954fd

File tree

4 files changed

+288
-200
lines changed

4 files changed

+288
-200
lines changed

channels/sync_runner.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -116,12 +116,10 @@ type SyncRunner struct {
116116
expiry *uint32 // document expiry (in seconds) specified via expiry() callback
117117
}
118118

119-
func NewSyncRunner(ctx context.Context, funcSource string, timeout time.Duration) (*SyncRunner, error) {
119+
func NewSyncRunnerWithLogging(ctx context.Context, funcSource string, timeout time.Duration, errorLogFunc, infoLogFunc func(string)) (*SyncRunner, error) {
120120
funcSource = wrappedFuncSource(funcSource)
121121
runner := &SyncRunner{}
122-
err := runner.InitWithLogging(funcSource, timeout,
123-
func(s string) { base.ErrorfCtx(ctx, base.KeyJavascript.String()+": Sync %s", base.UD(s)) },
124-
func(s string) { base.InfofCtx(ctx, base.KeyJavascript, "Sync %s", base.UD(s)) })
122+
err := runner.InitWithLogging(funcSource, timeout, errorLogFunc, infoLogFunc)
125123
if err != nil {
126124
return nil, err
127125
}
@@ -212,6 +210,12 @@ func NewSyncRunner(ctx context.Context, funcSource string, timeout time.Duration
212210
return runner, nil
213211
}
214212

213+
func NewSyncRunner(ctx context.Context, funcSource string, timeout time.Duration) (*SyncRunner, error) {
214+
errorLogFunc := func(s string) { base.ErrorfCtx(ctx, base.KeyJavascript.String()+": Sync %s", base.UD(s)) }
215+
infoLogFunc := func(s string) { base.InfofCtx(ctx, base.KeyJavascript, "Sync %s", base.UD(s)) }
216+
return NewSyncRunnerWithLogging(ctx, funcSource, timeout, errorLogFunc, infoLogFunc)
217+
}
218+
215219
func (runner *SyncRunner) SetFunction(funcSource string) (bool, error) {
216220
funcSource = wrappedFuncSource(funcSource)
217221
return runner.JSRunner.SetFunction(funcSource)

db/crud.go

Lines changed: 19 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1697,8 +1697,7 @@ func (db *DatabaseCollectionWithUser) PutExistingRevWithBody(ctx context.Context
16971697
// SyncFnDryrun Runs the given document body through a sync function and returns expiry, channels doc was placed in,
16981698
// access map for users, roles, handler errors and sync fn exceptions.
16991699
// If syncFn is provided, it will be used instead of the one configured on the database.
1700-
func (db *DatabaseCollectionWithUser) SyncFnDryrun(ctx context.Context, newDoc, oldDoc *Document, syncFn string) (*channels.ChannelMapperOutput, error) {
1701-
1700+
func (db *DatabaseCollectionWithUser) SyncFnDryrun(ctx context.Context, newDoc, oldDoc *Document, syncFn string, errorLogFunc, infoLogFunc func(string)) (*channels.ChannelMapperOutput, error) {
17021701
mutableBody, metaMap, _, err := db.prepareSyncFn(oldDoc, newDoc)
17031702
if err != nil {
17041703
base.InfofCtx(ctx, base.KeyDiagnostic, "Failed to prepare to run sync function: %v", err)
@@ -1709,32 +1708,30 @@ func (db *DatabaseCollectionWithUser) SyncFnDryrun(ctx context.Context, newDoc,
17091708
if err != nil {
17101709
return nil, err
17111710
}
1712-
var output *channels.ChannelMapperOutput
1713-
var syncErr error
1714-
if syncFn == "" && db.ChannelMapper != nil {
1715-
output, err = db.ChannelMapper.MapToChannelsAndAccess(ctx, mutableBody, string(oldDoc._rawBody), metaMap, syncOptions)
1716-
if err != nil {
1717-
return nil, &base.SyncFnDryRunError{Err: err}
1718-
}
1719-
} else {
1720-
if syncFn == "" {
1711+
1712+
// fetch configured sync function if one is not provided
1713+
if syncFn == "" {
1714+
if db.ChannelMapper != nil {
1715+
syncFn = db.ChannelMapper.Function()
1716+
} else {
17211717
scopeAndCollectionName := db.ScopeAndCollectionName()
17221718
syncFn = channels.GetDefaultSyncFunction(scopeAndCollectionName.Scope, scopeAndCollectionName.Collection)
17231719
}
1724-
jsTimeout := time.Duration(base.DefaultJavascriptTimeoutSecs) * time.Second
1725-
syncRunner, err := channels.NewSyncRunner(ctx, syncFn, jsTimeout)
1726-
if err != nil {
1727-
return nil, fmt.Errorf("failed to create sync runner: %v", err)
1728-
}
1729-
jsOutput, err := syncRunner.Call(ctx, mutableBody, string(oldDoc._rawBody), metaMap, syncOptions)
1730-
if err != nil {
1720+
}
17311721

1732-
return nil, &base.SyncFnDryRunError{Err: err}
1733-
}
1734-
output = jsOutput.(*channels.ChannelMapperOutput)
1722+
// create new sync runner instance for this dry run
1723+
jsTimeout := time.Duration(base.DefaultJavascriptTimeoutSecs) * time.Second
1724+
syncRunner, err := channels.NewSyncRunnerWithLogging(ctx, syncFn, jsTimeout, errorLogFunc, infoLogFunc)
1725+
if err != nil {
1726+
return nil, fmt.Errorf("failed to create sync runner: %v", err)
1727+
}
1728+
1729+
jsOutput, err := syncRunner.Call(ctx, mutableBody, sgbucket.JSONString(oldDoc._rawBody), metaMap, syncOptions)
1730+
if err != nil {
1731+
return nil, &base.SyncFnDryRunError{Err: err}
17351732
}
17361733

1737-
return output, syncErr
1734+
return jsOutput.(*channels.ChannelMapperOutput), nil
17381735
}
17391736

17401737
// revTreeConflictCheck checks for conflicts in the rev tree history and returns the parent revid, currentRevIndex

rest/diagnostic_doc_api.go

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,18 @@ import (
2121
"github.com/couchbase/sync_gateway/db"
2222
)
2323

24+
type SyncFnDryRunLogging struct {
25+
Errors []string `json:"errors"`
26+
Info []string `json:"info"`
27+
}
28+
2429
type SyncFnDryRun struct {
25-
Channels base.Set `json:"channels"`
26-
Access channels.AccessMap `json:"access"`
27-
Roles channels.AccessMap `json:"roles"`
28-
Exception string `json:"exception"`
29-
Expiry *uint32 `json:"expiry,omitempty"`
30+
Channels base.Set `json:"channels"`
31+
Access channels.AccessMap `json:"access"`
32+
Roles channels.AccessMap `json:"roles"`
33+
Exception string `json:"exception,omitempty"`
34+
Expiry *uint32 `json:"expiry,omitempty"`
35+
Logging SyncFnDryRunLogging `json:"logging"`
3036
}
3137

3238
type ImportFilterDryRun struct {
@@ -83,7 +89,7 @@ func (h *handler) handleSyncFnDryRun() error {
8389
}
8490

8591
if syncDryRunPayload.Doc == nil && docid == "" {
86-
return base.HTTPErrorf(http.StatusBadRequest, "no docid or document provided")
92+
return base.HTTPErrorf(http.StatusBadRequest, "no doc_id or document provided")
8793
}
8894

8995
oldDoc := &db.Document{ID: docid}
@@ -144,7 +150,16 @@ func (h *handler) handleSyncFnDryRun() error {
144150
newRev := db.CreateRevIDWithBytes(generation, matchRev, rawDocBytes)
145151
newDoc.RevID = newRev
146152

147-
output, err := h.collection.SyncFnDryrun(h.ctx(), newDoc, oldDoc, syncDryRunPayload.Function)
153+
logErrors := make([]string, 0)
154+
logInfo := make([]string, 0)
155+
errorLogFn := func(s string) {
156+
logErrors = append(logErrors, s)
157+
}
158+
infoLogFn := func(s string) {
159+
logInfo = append(logInfo, s)
160+
}
161+
162+
output, err := h.collection.SyncFnDryrun(h.ctx(), newDoc, oldDoc, syncDryRunPayload.Function, errorLogFn, infoLogFn)
148163
if err != nil {
149164
var syncFnDryRunErr *base.SyncFnDryRunError
150165
if !errors.As(err, &syncFnDryRunErr) {
@@ -154,6 +169,7 @@ func (h *handler) handleSyncFnDryRun() error {
154169
errMsg := syncFnDryRunErr.Error()
155170
resp := SyncFnDryRun{
156171
Exception: errMsg,
172+
Logging: SyncFnDryRunLogging{Errors: logErrors, Info: logInfo},
157173
}
158174
h.writeJSON(resp)
159175
return nil
@@ -169,6 +185,7 @@ func (h *handler) handleSyncFnDryRun() error {
169185
output.Roles,
170186
errorMsg,
171187
output.Expiry,
188+
SyncFnDryRunLogging{Errors: logErrors, Info: logInfo},
172189
}
173190
h.writeJSON(resp)
174191
return nil

0 commit comments

Comments
 (0)