blob: 234ba19e5e5c563ce37ae24066a4959e865fc18a [file] [log] [blame]
// 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
import (
"errors"
"time"
"github.com/mongodb/mongo-go-driver/bson"
"github.com/mongodb/mongo-go-driver/bson/bsontype"
"github.com/mongodb/mongo-go-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
wTimeout time.Duration
}
// Option is an option to provide when creating a ReadConcern.
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.
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", string(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
}
// AckWrite returns true if a write concern represents an acknowledged write
func AckWrite(wc *WriteConcern) bool {
return wc == nil || wc.Acknowledged()
}