blob: 234ba19e5e5c563ce37ae24066a4959e865fc18a [file] [log] [blame]
Don Newton379ae252019-04-01 12:17:06 -04001// Copyright (C) MongoDB, Inc. 2017-present.
2//
3// Licensed under the Apache License, Version 2.0 (the "License"); you may
4// not use this file except in compliance with the License. You may obtain
5// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
6
7package writeconcern
8
9import (
10 "errors"
11 "time"
12
13 "github.com/mongodb/mongo-go-driver/bson"
14 "github.com/mongodb/mongo-go-driver/bson/bsontype"
15 "github.com/mongodb/mongo-go-driver/x/bsonx/bsoncore"
16)
17
18// ErrInconsistent indicates that an inconsistent write concern was specified.
19var ErrInconsistent = errors.New("a write concern cannot have both w=0 and j=true")
20
21// ErrEmptyWriteConcern indicates that a write concern has no fields set.
22var ErrEmptyWriteConcern = errors.New("a write concern must have at least one field set")
23
24// ErrNegativeW indicates that a negative integer `w` field was specified.
25var ErrNegativeW = errors.New("write concern `w` field cannot be a negative number")
26
27// ErrNegativeWTimeout indicates that a negative WTimeout was specified.
28var ErrNegativeWTimeout = errors.New("write concern `wtimeout` field cannot be negative")
29
30// WriteConcern describes the level of acknowledgement requested from MongoDB for write operations
31// to a standalone mongod or to replica sets or to sharded clusters.
32type WriteConcern struct {
33 w interface{}
34 j bool
35 wTimeout time.Duration
36}
37
38// Option is an option to provide when creating a ReadConcern.
39type Option func(concern *WriteConcern)
40
41// New constructs a new WriteConcern.
42func New(options ...Option) *WriteConcern {
43 concern := &WriteConcern{}
44
45 for _, option := range options {
46 option(concern)
47 }
48
49 return concern
50}
51
52// W requests acknowledgement that write operations propagate to the specified number of mongod
53// instances.
54func W(w int) Option {
55 return func(concern *WriteConcern) {
56 concern.w = w
57 }
58}
59
60// WMajority requests acknowledgement that write operations propagate to the majority of mongod
61// instances.
62func WMajority() Option {
63 return func(concern *WriteConcern) {
64 concern.w = "majority"
65 }
66}
67
68// WTagSet requests acknowledgement that write operations propagate to the specified mongod
69// instance.
70func WTagSet(tag string) Option {
71 return func(concern *WriteConcern) {
72 concern.w = tag
73 }
74}
75
76// J requests acknowledgement from MongoDB that write operations are written to
77// the journal.
78func J(j bool) Option {
79 return func(concern *WriteConcern) {
80 concern.j = j
81 }
82}
83
84// WTimeout specifies specifies a time limit for the write concern.
85func WTimeout(d time.Duration) Option {
86 return func(concern *WriteConcern) {
87 concern.wTimeout = d
88 }
89}
90
91// MarshalBSONValue implements the bson.ValueMarshaler interface.
92func (wc *WriteConcern) MarshalBSONValue() (bsontype.Type, []byte, error) {
93 if !wc.IsValid() {
94 return bsontype.Type(0), nil, ErrInconsistent
95 }
96
97 var elems []byte
98
99 if wc.w != nil {
100 switch t := wc.w.(type) {
101 case int:
102 if t < 0 {
103 return bsontype.Type(0), nil, ErrNegativeW
104 }
105
106 elems = bsoncore.AppendInt32Element(elems, "w", int32(t))
107 case string:
108 elems = bsoncore.AppendStringElement(elems, "w", string(t))
109 }
110 }
111
112 if wc.j {
113 elems = bsoncore.AppendBooleanElement(elems, "j", wc.j)
114 }
115
116 if wc.wTimeout < 0 {
117 return bsontype.Type(0), nil, ErrNegativeWTimeout
118 }
119
120 if wc.wTimeout != 0 {
121 elems = bsoncore.AppendInt64Element(elems, "wtimeout", int64(wc.wTimeout/time.Millisecond))
122 }
123
124 if len(elems) == 0 {
125 return bsontype.Type(0), nil, ErrEmptyWriteConcern
126 }
127 return bsontype.EmbeddedDocument, bsoncore.BuildDocument(nil, elems), nil
128}
129
130// AcknowledgedValue returns true if a BSON RawValue for a write concern represents an acknowledged write concern.
131// The element's value must be a document representing a write concern.
132func AcknowledgedValue(rawv bson.RawValue) bool {
133 doc, ok := bsoncore.Value{Type: rawv.Type, Data: rawv.Value}.DocumentOK()
134 if !ok {
135 return false
136 }
137
138 val, err := doc.LookupErr("w")
139 if err != nil {
140 // key w not found --> acknowledged
141 return true
142 }
143
144 i32, ok := val.Int32OK()
145 if !ok {
146 return false
147 }
148 return i32 != 0
149}
150
151// Acknowledged indicates whether or not a write with the given write concern will be acknowledged.
152func (wc *WriteConcern) Acknowledged() bool {
153 if wc == nil || wc.j {
154 return true
155 }
156
157 switch v := wc.w.(type) {
158 case int:
159 if v == 0 {
160 return false
161 }
162 }
163
164 return true
165}
166
167// IsValid checks whether the write concern is invalid.
168func (wc *WriteConcern) IsValid() bool {
169 if !wc.j {
170 return true
171 }
172
173 switch v := wc.w.(type) {
174 case int:
175 if v == 0 {
176 return false
177 }
178 }
179
180 return true
181}
182
183// AckWrite returns true if a write concern represents an acknowledged write
184func AckWrite(wc *WriteConcern) bool {
185 return wc == nil || wc.Acknowledged()
186}