228 lines
5.9 KiB
Go
228 lines
5.9 KiB
Go
|
// Copyright (C) MongoDB, Inc. 2017-present.
|
||
|
//
|
||
|
// Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||
|
// not use this file except in compliance with the License. You may obtain
|
||
|
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||
|
|
||
|
// Package writeconcern defines write concerns for MongoDB operations.
|
||
|
package writeconcern // import "go.mongodb.org/mongo-driver/mongo/writeconcern"
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
"time"
|
||
|
|
||
|
"go.mongodb.org/mongo-driver/bson"
|
||
|
"go.mongodb.org/mongo-driver/bson/bsontype"
|
||
|
"go.mongodb.org/mongo-driver/x/bsonx/bsoncore"
|
||
|
)
|
||
|
|
||
|
// ErrInconsistent indicates that an inconsistent write concern was specified.
|
||
|
var ErrInconsistent = errors.New("a write concern cannot have both w=0 and j=true")
|
||
|
|
||
|
// ErrEmptyWriteConcern indicates that a write concern has no fields set.
|
||
|
var ErrEmptyWriteConcern = errors.New("a write concern must have at least one field set")
|
||
|
|
||
|
// ErrNegativeW indicates that a negative integer `w` field was specified.
|
||
|
var ErrNegativeW = errors.New("write concern `w` field cannot be a negative number")
|
||
|
|
||
|
// ErrNegativeWTimeout indicates that a negative WTimeout was specified.
|
||
|
var ErrNegativeWTimeout = errors.New("write concern `wtimeout` field cannot be negative")
|
||
|
|
||
|
// WriteConcern describes the level of acknowledgement requested from MongoDB for write operations
|
||
|
// to a standalone mongod or to replica sets or to sharded clusters.
|
||
|
type WriteConcern struct {
|
||
|
w interface{}
|
||
|
j bool
|
||
|
|
||
|
// NOTE(benjirewis): wTimeout will be deprecated in a future release. The more general Timeout
|
||
|
// option may be used in its place to control the amount of time that a single operation can run
|
||
|
// before returning an error. Using wTimeout and setting Timeout on the client will result in
|
||
|
// undefined behavior.
|
||
|
wTimeout time.Duration
|
||
|
}
|
||
|
|
||
|
// Option is an option to provide when creating a WriteConcern.
|
||
|
type Option func(concern *WriteConcern)
|
||
|
|
||
|
// New constructs a new WriteConcern.
|
||
|
func New(options ...Option) *WriteConcern {
|
||
|
concern := &WriteConcern{}
|
||
|
|
||
|
for _, option := range options {
|
||
|
option(concern)
|
||
|
}
|
||
|
|
||
|
return concern
|
||
|
}
|
||
|
|
||
|
// W requests acknowledgement that write operations propagate to the specified number of mongod
|
||
|
// instances.
|
||
|
func W(w int) Option {
|
||
|
return func(concern *WriteConcern) {
|
||
|
concern.w = w
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// WMajority requests acknowledgement that write operations propagate to the majority of mongod
|
||
|
// instances.
|
||
|
func WMajority() Option {
|
||
|
return func(concern *WriteConcern) {
|
||
|
concern.w = "majority"
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// WTagSet requests acknowledgement that write operations propagate to the specified mongod
|
||
|
// instance.
|
||
|
func WTagSet(tag string) Option {
|
||
|
return func(concern *WriteConcern) {
|
||
|
concern.w = tag
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// J requests acknowledgement from MongoDB that write operations are written to
|
||
|
// the journal.
|
||
|
func J(j bool) Option {
|
||
|
return func(concern *WriteConcern) {
|
||
|
concern.j = j
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// WTimeout specifies specifies a time limit for the write concern.
|
||
|
//
|
||
|
// NOTE(benjirewis): wTimeout will be deprecated in a future release. The more general Timeout
|
||
|
// option may be used in its place to control the amount of time that a single operation can run
|
||
|
// before returning an error. Using wTimeout and setting Timeout on the client will result in
|
||
|
// undefined behavior.
|
||
|
func WTimeout(d time.Duration) Option {
|
||
|
return func(concern *WriteConcern) {
|
||
|
concern.wTimeout = d
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// MarshalBSONValue implements the bson.ValueMarshaler interface.
|
||
|
func (wc *WriteConcern) MarshalBSONValue() (bsontype.Type, []byte, error) {
|
||
|
if !wc.IsValid() {
|
||
|
return bsontype.Type(0), nil, ErrInconsistent
|
||
|
}
|
||
|
|
||
|
var elems []byte
|
||
|
|
||
|
if wc.w != nil {
|
||
|
switch t := wc.w.(type) {
|
||
|
case int:
|
||
|
if t < 0 {
|
||
|
return bsontype.Type(0), nil, ErrNegativeW
|
||
|
}
|
||
|
|
||
|
elems = bsoncore.AppendInt32Element(elems, "w", int32(t))
|
||
|
case string:
|
||
|
elems = bsoncore.AppendStringElement(elems, "w", t)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if wc.j {
|
||
|
elems = bsoncore.AppendBooleanElement(elems, "j", wc.j)
|
||
|
}
|
||
|
|
||
|
if wc.wTimeout < 0 {
|
||
|
return bsontype.Type(0), nil, ErrNegativeWTimeout
|
||
|
}
|
||
|
|
||
|
if wc.wTimeout != 0 {
|
||
|
elems = bsoncore.AppendInt64Element(elems, "wtimeout", int64(wc.wTimeout/time.Millisecond))
|
||
|
}
|
||
|
|
||
|
if len(elems) == 0 {
|
||
|
return bsontype.Type(0), nil, ErrEmptyWriteConcern
|
||
|
}
|
||
|
return bsontype.EmbeddedDocument, bsoncore.BuildDocument(nil, elems), nil
|
||
|
}
|
||
|
|
||
|
// AcknowledgedValue returns true if a BSON RawValue for a write concern represents an acknowledged write concern.
|
||
|
// The element's value must be a document representing a write concern.
|
||
|
func AcknowledgedValue(rawv bson.RawValue) bool {
|
||
|
doc, ok := bsoncore.Value{Type: rawv.Type, Data: rawv.Value}.DocumentOK()
|
||
|
if !ok {
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
val, err := doc.LookupErr("w")
|
||
|
if err != nil {
|
||
|
// key w not found --> acknowledged
|
||
|
return true
|
||
|
}
|
||
|
|
||
|
i32, ok := val.Int32OK()
|
||
|
if !ok {
|
||
|
return false
|
||
|
}
|
||
|
return i32 != 0
|
||
|
}
|
||
|
|
||
|
// Acknowledged indicates whether or not a write with the given write concern will be acknowledged.
|
||
|
func (wc *WriteConcern) Acknowledged() bool {
|
||
|
if wc == nil || wc.j {
|
||
|
return true
|
||
|
}
|
||
|
|
||
|
switch v := wc.w.(type) {
|
||
|
case int:
|
||
|
if v == 0 {
|
||
|
return false
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return true
|
||
|
}
|
||
|
|
||
|
// IsValid checks whether the write concern is invalid.
|
||
|
func (wc *WriteConcern) IsValid() bool {
|
||
|
if !wc.j {
|
||
|
return true
|
||
|
}
|
||
|
|
||
|
switch v := wc.w.(type) {
|
||
|
case int:
|
||
|
if v == 0 {
|
||
|
return false
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return true
|
||
|
}
|
||
|
|
||
|
// GetW returns the write concern w level.
|
||
|
func (wc *WriteConcern) GetW() interface{} {
|
||
|
return wc.w
|
||
|
}
|
||
|
|
||
|
// GetJ returns the write concern journaling level.
|
||
|
func (wc *WriteConcern) GetJ() bool {
|
||
|
return wc.j
|
||
|
}
|
||
|
|
||
|
// GetWTimeout returns the write concern timeout.
|
||
|
func (wc *WriteConcern) GetWTimeout() time.Duration {
|
||
|
return wc.wTimeout
|
||
|
}
|
||
|
|
||
|
// WithOptions returns a copy of this WriteConcern with the options set.
|
||
|
func (wc *WriteConcern) WithOptions(options ...Option) *WriteConcern {
|
||
|
if wc == nil {
|
||
|
return New(options...)
|
||
|
}
|
||
|
newWC := &WriteConcern{}
|
||
|
*newWC = *wc
|
||
|
|
||
|
for _, option := range options {
|
||
|
option(newWC)
|
||
|
}
|
||
|
|
||
|
return newWC
|
||
|
}
|
||
|
|
||
|
// AckWrite returns true if a write concern represents an acknowledged write
|
||
|
func AckWrite(wc *WriteConcern) bool {
|
||
|
return wc == nil || wc.Acknowledged()
|
||
|
}
|