Skip to content

Commit daa6bdf

Browse files
authored
GODRIVER-1867 Add new Details field on WriteError for server v5.0 (#694)
1 parent 7e6f265 commit daa6bdf

File tree

7 files changed

+355
-43
lines changed

7 files changed

+355
-43
lines changed

mongo/collection.go

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -376,12 +376,8 @@ func (coll *Collection) InsertMany(ctx context.Context, documents []interface{},
376376
bwErrors := make([]BulkWriteError, 0, len(writeException.WriteErrors))
377377
for _, we := range writeException.WriteErrors {
378378
bwErrors = append(bwErrors, BulkWriteError{
379-
WriteError{
380-
Index: we.Index,
381-
Code: we.Code,
382-
Message: we.Message,
383-
},
384-
nil,
379+
WriteError: we,
380+
Request: nil,
385381
})
386382
}
387383

mongo/errors.go

Lines changed: 83 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -275,31 +275,39 @@ type WriteError struct {
275275

276276
Code int
277277
Message string
278+
Details bson.Raw
278279
}
279280

280-
func (we WriteError) Error() string { return we.Message }
281+
func (we WriteError) Error() string {
282+
msg := we.Message
283+
if len(we.Details) > 0 {
284+
msg = fmt.Sprintf("%s: %s", msg, we.Details.String())
285+
}
286+
return msg
287+
}
281288

282289
// WriteErrors is a group of write errors that occurred during execution of a write operation.
283290
type WriteErrors []WriteError
284291

285292
// Error implements the error interface.
286293
func (we WriteErrors) Error() string {
287-
var buf bytes.Buffer
288-
fmt.Fprint(&buf, "write errors: [")
289-
for idx, err := range we {
290-
if idx != 0 {
291-
fmt.Fprintf(&buf, ", ")
292-
}
293-
fmt.Fprintf(&buf, "{%s}", err)
294+
errs := make([]error, len(we))
295+
for i := 0; i < len(we); i++ {
296+
errs[i] = we[i]
294297
}
295-
fmt.Fprint(&buf, "]")
296-
return buf.String()
298+
// WriteErrors isn't returned from batch operations, but we can still use the same formatter.
299+
return "write errors: " + joinBatchErrors(errs)
297300
}
298301

299302
func writeErrorsFromDriverWriteErrors(errs driver.WriteErrors) WriteErrors {
300303
wes := make(WriteErrors, 0, len(errs))
301304
for _, err := range errs {
302-
wes = append(wes, WriteError{Index: int(err.Index), Code: int(err.Code), Message: err.Message})
305+
wes = append(wes, WriteError{
306+
Index: int(err.Index),
307+
Code: int(err.Code),
308+
Message: err.Message,
309+
Details: bson.Raw(err.Details),
310+
})
303311
}
304312
return wes
305313
}
@@ -336,11 +344,21 @@ type WriteException struct {
336344

337345
// Error implements the error interface.
338346
func (mwe WriteException) Error() string {
339-
var buf bytes.Buffer
340-
fmt.Fprint(&buf, "multiple write errors: [")
341-
fmt.Fprintf(&buf, "{%s}, ", mwe.WriteErrors)
342-
fmt.Fprintf(&buf, "{%s}]", mwe.WriteConcernError)
343-
return buf.String()
347+
causes := make([]string, 0, 2)
348+
if mwe.WriteConcernError != nil {
349+
causes = append(causes, "write concern error: "+mwe.WriteConcernError.Error())
350+
}
351+
if len(mwe.WriteErrors) > 0 {
352+
// The WriteErrors error message already starts with "write errors:", so don't add it to the
353+
// error message again.
354+
causes = append(causes, mwe.WriteErrors.Error())
355+
}
356+
357+
message := "write exception: "
358+
if len(causes) == 0 {
359+
return message + "no causes"
360+
}
361+
return message + strings.Join(causes, ", ")
344362
}
345363

346364
// HasErrorCode returns true if the error has the specified code.
@@ -420,9 +438,7 @@ type BulkWriteError struct {
420438

421439
// Error implements the error interface.
422440
func (bwe BulkWriteError) Error() string {
423-
var buf bytes.Buffer
424-
fmt.Fprintf(&buf, "{%s}", bwe.WriteError)
425-
return buf.String()
441+
return bwe.WriteError.Error()
426442
}
427443

428444
// BulkWriteException is the error type returned by BulkWrite and InsertMany operations.
@@ -439,11 +455,23 @@ type BulkWriteException struct {
439455

440456
// Error implements the error interface.
441457
func (bwe BulkWriteException) Error() string {
442-
var buf bytes.Buffer
443-
fmt.Fprint(&buf, "bulk write error: [")
444-
fmt.Fprintf(&buf, "{%s}, ", bwe.WriteErrors)
445-
fmt.Fprintf(&buf, "{%s}]", bwe.WriteConcernError)
446-
return buf.String()
458+
causes := make([]string, 0, 2)
459+
if bwe.WriteConcernError != nil {
460+
causes = append(causes, "write concern error: "+bwe.WriteConcernError.Error())
461+
}
462+
if len(bwe.WriteErrors) > 0 {
463+
errs := make([]error, len(bwe.WriteErrors))
464+
for i := 0; i < len(bwe.WriteErrors); i++ {
465+
errs[i] = &bwe.WriteErrors[i]
466+
}
467+
causes = append(causes, "write errors: "+joinBatchErrors(errs))
468+
}
469+
470+
message := "bulk write exception: "
471+
if len(causes) == 0 {
472+
return message + "no causes"
473+
}
474+
return "bulk write exception: " + strings.Join(causes, ", ")
447475
}
448476

449477
// HasErrorCode returns true if any of the errors have the specified code.
@@ -539,3 +567,34 @@ func processWriteError(err error) (returnResult, error) {
539567
return rrAll, nil
540568
}
541569
}
570+
571+
// batchErrorsTargetLength is the target length of error messages returned by batch operation
572+
// error types. Try to limit batch error messages to 2kb to prevent problems when printing error
573+
// messages from large batch operations.
574+
const batchErrorsTargetLength = 2000
575+
576+
// joinBatchErrors appends messages from the given errors to a comma-separated string. If the
577+
// string exceeds 2kb, it stops appending error messages and appends the message "+N more errors..."
578+
// to the end.
579+
//
580+
// Example format:
581+
// "[message 1, message 2, +8 more errors...]"
582+
func joinBatchErrors(errs []error) string {
583+
var buf bytes.Buffer
584+
fmt.Fprint(&buf, "[")
585+
for idx, err := range errs {
586+
if idx != 0 {
587+
fmt.Fprint(&buf, ", ")
588+
}
589+
// If the error message has exceeded the target error message length, stop appending errors
590+
// to the message and append the number of remaining errors instead.
591+
if buf.Len() > batchErrorsTargetLength {
592+
fmt.Fprintf(&buf, "+%d more errors...", len(errs)-idx)
593+
break
594+
}
595+
fmt.Fprint(&buf, err.Error())
596+
}
597+
fmt.Fprint(&buf, "]")
598+
599+
return buf.String()
600+
}

mongo/errors_test.go

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package mongo
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/assert"
7+
"github.com/stretchr/testify/require"
8+
"go.mongodb.org/mongo-driver/bson"
9+
)
10+
11+
func TestErrorMessages(t *testing.T) {
12+
details, err := bson.Marshal(bson.D{{"details", bson.D{{"operatorName", "$jsonSchema"}}}})
13+
require.Nil(t, err, "unexpected error marshaling BSON")
14+
15+
cases := []struct {
16+
desc string
17+
err error
18+
expected string
19+
}{
20+
{
21+
desc: "WriteException error message should contain the WriteError Message and Details",
22+
err: WriteException{
23+
WriteErrors: WriteErrors{
24+
{
25+
Message: "test message 1",
26+
Details: details,
27+
},
28+
{
29+
Message: "test message 2",
30+
Details: details,
31+
},
32+
},
33+
},
34+
expected: `write exception: write errors: [test message 1: {"details": {"operatorName": "$jsonSchema"}}, test message 2: {"details": {"operatorName": "$jsonSchema"}}]`,
35+
},
36+
{
37+
desc: "BulkWriteException error message should contain the WriteError Message and Details",
38+
err: BulkWriteException{
39+
WriteErrors: []BulkWriteError{
40+
{
41+
WriteError: WriteError{
42+
Message: "test message 1",
43+
Details: details,
44+
},
45+
},
46+
{
47+
WriteError: WriteError{
48+
Message: "test message 2",
49+
Details: details,
50+
},
51+
},
52+
},
53+
},
54+
expected: `bulk write exception: write errors: [test message 1: {"details": {"operatorName": "$jsonSchema"}}, test message 2: {"details": {"operatorName": "$jsonSchema"}}]`,
55+
},
56+
}
57+
58+
for _, tc := range cases {
59+
tc := tc
60+
t.Run(tc.desc, func(t *testing.T) {
61+
t.Parallel()
62+
63+
assert.Equal(t, tc.expected, tc.err.Error())
64+
})
65+
}
66+
}

0 commit comments

Comments
 (0)