Uploaded image for project: 'Core Server'
  1. Core Server
  2. SERVER-34378

Is $setOnInsert atomic across single collection?

    • Type: Icon: Question Question
    • Resolution: Done
    • Priority: Icon: Major - P3 Major - P3
    • None
    • Affects Version/s: 3.6.3
    • Component/s: Querying
    • Labels:
      None
    • Server Triage

      I'm working on a "set once" config module for mongodb, here's my go code:

      Unable to find source-code formatter for language: golang. Available languages are: actionscript, ada, applescript, bash, c, c#, c++, cpp, css, erlang, go, groovy, haskell, html, java, javascript, js, json, lua, none, nyan, objc, perl, php, python, r, rainbow, ruby, scala, sh, sql, swift, visualbasic, xml, yaml
      package dao
      
      import "github.com/globalsign/mgo/bson"
      
      func InsertOnce(selector interface{}, update interface{}) (ret bson.M, err error) {
      	s, c := Mg("config")
      	defer s.Close()
      
      	_, err = c.Upsert(selector, &bson.M{
      		"$setOnInsert": update,
      	})
      	if err != nil {
      		return
      	}
      	ret = make(bson.M)
      	err = c.Find(selector).One(&ret)
      	return
      }
      

      When I test it:

      Unable to find source-code formatter for language: golang. Available languages are: actionscript, ada, applescript, bash, c, c#, c++, cpp, css, erlang, go, groovy, haskell, html, java, javascript, js, json, lua, none, nyan, objc, perl, php, python, r, rainbow, ruby, scala, sh, sql, swift, visualbasic, xml, yaml
      package dao
      
      import (
      	"sync"
      	"sync/atomic"
      	"testing"
      
      	"github.com/globalsign/mgo/bson"
      )
      
      func TestInsertOnce(t *testing.T) {
      	s, c := Mg("config")
      	defer s.Close()
      	defer c.DropCollection()
      	for n := 0; n < 10; n++ {
      		prev := int32(-1)
      		var changed int32
      		var wg sync.WaitGroup
      		for i := 0; i < 10; i++ {
      			wg.Add(1)
      			go func(i int) {
      				defer wg.Done()
      				result, err := InsertOnce(&bson.M{"entry": "secret"}, &bson.M{"value": i})
      				if err != nil {
      					t.Fatal("InsertOnce", err)
      				}
      				value := result["value"].(int)
      				t.Log(value, prev)
      				if atomic.SwapInt32(&prev, int32(value)) != int32(value) {
      					atomic.AddInt32(&changed, 1)
      				}
      			}(i)
      		}
      		wg.Wait()
      		if changed != 1 {
      			t.Fatal("value maybe changed by later func calls", changed)
      		}
      		c.DropCollection()
      	}
      }
      

      It sometime fails with more than once changed, is it related to my implementation or test code, or MongoDB will not guarantee the atomicity of $setOnInsert across single collection?

            Assignee:
            backlog-server-triage [HELP ONLY] Backlog - Triage Team
            Reporter:
            kassiansun Kassian Sun
            Votes:
            0 Vote for this issue
            Watchers:
            4 Start watching this issue

              Created:
              Updated:
              Resolved: