---
# System prepended metadata

title: mongo update 鎖測試

---

# mongo update 鎖測試

https://hub.docker.com/r/bitnami/mongodb-sharded/

```
func UseSessionNoSleep(client *mongo.Client, userID int) {
	ctx := context.Background()
	collection := client.Database("yolo").Collection("user")
	session, err := client.StartSession()
	if err != nil {
		log.Fatal(err)
	}
	result, err := session.WithTransaction(ctx, func(sCtx mongo.SessionContext) (interface{}, error) {
		objID, _ := primitive.ObjectIDFromHex("61ef6bd8a6c0946e9484b3a9")
		result, err := collection.UpdateOne(sCtx, bson.D{{"_id", objID}, {"age", bson.D{{"$gt", 0}}}}, bson.D{{"$inc", bson.D{{"age", -1}}}})
		if err != nil {
			return nil, err
		}
		return result, nil
	})
	if err != nil {
		fmt.Printf("userID:%d err:%s\n", userID, err)
	}
	fmt.Printf("userID:%d result:%v\n", userID, result)
}

func UseSessionHasSleep(client *mongo.Client, userID int) {
	ctx := context.Background()
	collection := client.Database("yolo").Collection("user")
	session, err := client.StartSession()
	if err != nil {
		log.Fatal(err)
	}
	result, err := session.WithTransaction(ctx, func(sCtx mongo.SessionContext) (interface{}, error) {
		objID, _ := primitive.ObjectIDFromHex("61ef6bd8a6c0946e9484b3a9")
		result, err := collection.UpdateOne(sCtx, bson.D{{"_id", objID}, {"age", bson.D{{"$gt", 0}}}}, bson.D{{"$inc", bson.D{{"age", -1}}}})
		if err != nil {
			rand.Seed(time.Now().UnixNano())
			d := time.Second * time.Duration(rand.Intn(20))
			time.Sleep(d)
			return nil, err
		}
		return result, nil
	})
	if err != nil {
		fmt.Printf("userID:%d err:%s\n", userID, err)
	}
	fmt.Printf("userID:%d result:%v\n", userID, result)
}

func UpdateWithoutSession(client *mongo.Client, userID int) {
	ctx := context.Background()
	collection := client.Database("yolo").Collection("user")
	objID, _ := primitive.ObjectIDFromHex("61ef6bd8a6c0946e9484b3a9")
	result, err := collection.UpdateOne(ctx, bson.D{{"_id", objID}, {"age", bson.D{{"$gt", 0}}}}, bson.D{{"$inc", bson.D{{"age", -1}}}})
	if err != nil {
		fmt.Printf("userID:%d err:%s\n", userID, err)
	}
	fmt.Printf("userID:%d result:%v\n", userID, result)
}

func main() {
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()
	client, err := mongo.Connect(
		ctx,
		options.Client().ApplyURI("mongodb://localhost:27017"),
		options.Client().SetAuth(options.Credential{
			Username: "root",
			Password: "password123",
		}),
	)
	if err != nil {
		log.Fatal(err)
	}
	defer func() { _ = client.Disconnect(ctx) }()
	var wg sync.WaitGroup
	maxNum := 1000
	wg.Add(maxNum)
	now := time.Now()
	// 初始庫存為 100, 1000 人同時搶
	for i := 0; i < maxNum; i++ {
		go func(userID int) {
			defer wg.Done()
			// 沒有辦法執行完
			//UseSessionNoSleep(client, userID)
			// 約 25 秒
			//UseSessionHasSleep(client, userID)
			// 約 2 秒
			UpdateWithoutSession(client, userID)
		}(i)
	}
	wg.Wait()
	fmt.Printf("operation finished cost %s\n", time.Since(now).String())
	fmt.Println()
}
```