Cannot create namespace centrum.policy in multi-document transaction.

Cannot create namespace centrum.policy in multi-document transaction.

Cannot create namespace xxx.xxx in multi-document transaction.

问题

看报错信息,就是说不能在 mongo 事务中创建表。

解决否

已解决

方案

这是说: 不能在事务中创建 collection 。但你一看 sql,没有创建表语句啊。

mongo 中,如果你操作的 collection 不存在是会自动创建的。但有个例外就是事务。

在事务中是无法创建表结构的。

所以,解决这个问题,就是在执行 mongo sql 前,先创建好要操作的 collection

参考

  • https://stackoverflow.com/questions/52585715/cannot-create-namespace-in-multi-document-transactionmongodb-4-0-spring-data-2

Mongo 副本集 选择副本后 连不上 server selection error

failed: server selection error: context deadline exceeded, current topology: { Type: ReplicaSetNoPrimary, Servers: [{ Addr: 127.0.0.1:27017, Type: Unknown, Last error: connection() error occurred during connection handshake: dial tcp 127.0.0.1:27017: connect: connection refused

Mongo 副本集 选主错误 server selection error

问题

failed: server selection error: context deadline exceeded, current topology: { Type: ReplicaSetNoPrimary, Servers: [{ Addr: 127.0.0.1:27017, Type: Unknown, Last error: connection() error occurred during connection handshake: dial tcp 127.0.0.1:27017: connect: connection refused

解决否

已解决

方案

登陆 mongo server 输入命令

rs.status()

rs.status

这里看图片里,name是: mongolab:27021, 那么应用也会用这个地址去连 mongodb,如果 dns 里没有 mongolab,那么应用是连不上这个 mongo 的,最终报错就是链接超时。

查看一下Mongo集群之间的通讯 IP,或者地址是怎么配置,是否配置的IP的应用端能访问的IP段。

例如,三个 mongo 集群配置在同一网段内—— 192.168.1.0 ,如果你也按照这个 IP 来配置副本集,那么 mongo 之间通信是没问题的,但如果应用端可能读取 replica 信息后,会那副本集配置的 IP 信息去访问其他节点,这个时候应用会拿着 192.168.1.0 这个网断的 IP 去访问 mongo,如果 mongo 和应用不在一个网段,就会导致如上问题。所以,mongoreplica 配置的IP也需是应用可访问的 IP

这也是,往往运维部署,发现,mongo 互通 OK,运维溜了。等应用来连,却连不上问题。

参考

  • https://www.mongodb.com/docs/manual/replication/
  • https://www.mongodb.com/docs/manual/reference/method/rs.initiate/

Golang copier入门到入坑

一个小小copier.Copy函数你确定是用对了么?快来看看还有哪些隐含参数,哪种类型变量会有隐含坑呢?还有个copier.CopyWithOption你用过么?

github: https://github.com/jinzhu/copier

由于 golang 没有对复杂结构体的 clone 方法,所以,就需要有 copier 这样的工具库。

它看起来很简单,但实际使用中,有些“坑”还是要注意!

本文:

入门为辅,探“坑”为主,

看完再划走,CS我没有。

安装

go get github.com/jinzhu/copier

快速入门

好的,来一段代码快速了解 copier

package main

import (
    "fmt"
    "github.com/jinzhu/copier"
)

type SC struct {
    C uint8
}


type M1 struct {
    A int
    W string
    S *SC
}

func main() {
    var src = M1{12, "Hello", &SC{32}}
    var dst = M1{}
    fmt.Printf("before copy src %+v\tdst %+v\n", src, dst)
    copier.Copy(&dst, src)
    fmt.Printf("after  copy src %+v\tdst %+v\n", src, dst)
}

输出:

before copy src {A:12 W:Hello S:0xc00017f550}   dst {A:0 W: S:<nil>}
after  copy src {A:12 W:Hello S:0xc00017f550}   dst {A:12 W:Hello S:0xc00017f618}

好的,看到这,你就已掌握了 copier 80%的功能了。先别着急划走,接下来还是踩坑记录。

本文代码运行输出内容是基于 github.com/jinzhu/copier@v0.3.5 和 go1.16.1 darwin/amd64 环境演示的结果。

http://itjsz.com

入坑

package main

import (
    "fmt"
    "github.com/davecgh/go-spew/spew"
    "github.com/jinzhu/copier"
)

type SC struct {
    C uint8
}

type Map1 struct {
    M map[string]int32
    A []int32
    C *SC
}

func main() {
    var src = Map1{map[string]int32{"C:": 3, "d": 4}, []int32{9, 8}, &SC{32}}
    var dst1 = Map1{}
    spew.Printf("before src %+v\t\tdst %+v\n", src, dst1)
    copier.Copy(&dst1, src)
    dst1.M["F"] = 5
    dst1.M["g"] = 6
    dst1.A[0] = 7
    dst1.C.C = 27
    spew.Printf("after  src %+v\tdst %+v\n", src, dst1)
}

以上代码运行后会输出:

before src {M:map[C::3 d:4] A:[9 8] C:<*>(0xc00012a1e8){C:32}}          dst {M:<nil> A:<nil> C:<nil>}

befre 那一行代码如上⬆️ , after 那一行会输出什么呢?

1. after  src {M:map[C::3 d:4] A:[9 8] C:<*>(0xc00012a1e8){C:27}}  dst {M:map[C::3 d:4 F:5 g:6] A:[7 8] C:<*>(0xc00012a348){C:27}}
2. after  src {M:map[C::3 d:4] A:[9 8] C:<*>(0xc00012a1e8){C:32}}  dst {M:map[C::3 d:4 F:5 g:6] A:[7 8] C:<*>(0xc00012a348){C:27}}
3. after  src {M:map[C::3 d:4] A:[7 8] C:<*>(0xc00012a1e8){C:32}}  dst {M:map[C::3 d:4 F:5 g:6] A:[7 8] C:<*>(0xc00012a348){C:27}}
4. after  src {M:map[C::3 d:4 F:5 g:6] A:[7 8] C:<*>(0xc00012a1e8){C:32}}  dst {M:map[C::3 d:4 F:5 g:6] A:[7 8] C:<*>(0xc00012a348){C:27}}
5. after  src {M:map[C::3 d:4 F:5 g:6] A:[7 8] C:<*>(0xc00012a1e8){C:27}}  dst {M:map[C::3 d:4 F:5 g:6] A:[7 8] C:<*>(0xc00012a348){C:27}}

答案是: var a = int(759 / 6 / 31.5)

为了避免不小心看了答案,请计算 759 / 6 / 31.5 得出的值四舍五入便是。

再探坑

出坑

我看其他同学使用 copier 也是像上面那样——copier.Copy($dst, src), 当然啦,也不排除我!它仿佛就是一把小巧精悍的小刀。一个简单的函数调用,就完成了它的使命。

然而,它其实是把多功能刀,我都还没有打开它的—— option

上面的问题就是,我 Copy 后,对值的改动,影响了另一个值的 map,那么这个时候,就需要进行深 copy。接下来引入 copieroption

package main

import (
    "fmt"
    "github.com/davecgh/go-spew/spew"
    "github.com/jinzhu/copier"
)

type SC struct {
    C uint8
}

type Map1 struct {
    M map[string]int32
    A []int32
    C *SC
}

func main() {
    var src = Map1{map[string]int32{"C:": 3, "d": 4}, []int32{9, 8}, &SC{32}}
    var dst1 = Map1{}
    spew.Printf("before src %+v\t\tdst %+v\n", src, dst1)

    copier.CopyWithOption(&dst1, src, copier.Option{DeepCopy: true})   // 这里!

    dst1.M["F"] = 5
    dst1.M["g"] = 6
    dst1.A[0] = 7
    dst1.C.C = 27
    spew.Printf("after  src %+v\tdst %+v\n", src, dst1)
}

好的,这样copy之后,对新变量的改动,不会传递会原变量改动了。

再盘一盘坑

package main

import (
    "fmt"

    "github.com/davecgh/go-spew/spew"
    "github.com/jinzhu/copier"
)

type ArrTC struct {
    Name [2]string
    C    *ArrTC
}

type ArrT struct {
    A  [3]int32
    S  []int32
    E  []int32
    C  string
    V  string
    M map[string]int32
    AC ArrTC
    s bool
}

func main() {
    var src = ArrT{
        [3]int32{9, 10, 0},
        []int32{12, 0},
        []int32{},
        "",
        "val",
        map[string]int32{"A:": 1, "b": 0},
        ArrTC{},
        true,
    }
    var dst = ArrT{
        [3]int32{1, 2, 3},
        []int32{4, 5, 6, 7},
        []int32{9, 10},
        "char",
        "ha",
        map[string]int32{"C:": 3, "b": 4, ".": 0},
        ArrTC{[2]string{"Y", "Z"}, nil},
        false,
    }
    spew.Printf("before src %+v\tdst %+v\n", src, dst)
    copier.CopyWithOption(&dst, src, copier.Option{IgnoreEmpty: true, DeepCopy: true})
    spew.Printf("after  src %+v\tdst %+v\n", src, dst)
    src.M["b"] = 99
    src.S[1] = 1
    dst.S[0] = 2
    spew.Printf("last  src %+v\tdst %+v\n\n", src, dst)
}

输出:

before src {A:[9 10 0] S:[12 0] E:[] C: V:val M:map[A::1 b:0] AC:{Name:[ ] C:<nil>} s:true} dst {A:[1 2 3] S:[4 5 6 7] E:[9 10] C:char V:ha M:map[C::3 b:4 .:0] AC:{Name:[Y Z] C:<nil>} s:false}
after  src {A:[9 10 0] S:[12 0] E:[] C: V:val M:map[A::1 b:0] AC:{Name:[ ] C:<nil>} s:true} dst {A:[9 10 0] S:[12 0 6 7] E:[9 10] C:char V:val M:map[A::1 C::3 b:0 .:0] AC:{Name:[Y Z] C:<nil>} s:true}
last  src {A:[9 10 0] S:[12 1] E:[] C: V:val M:map[A::1 b:99] AC:{Name:[ ] C:<nil>} s:true} dst {A:[9 10 0] S:[2 0 6 7] E:[9 10] C:char V:val M:map[C::3 b:0 .:0 A::1] AC:{Name:[Y Z] C:<nil>} s:true}

这次的代码我加上了 IgnoreEmpty: true, 也就是复制时忽略空的值。 也就说可以当作值 merge 用。

然后,又测试了一下变量独立性。复制之后,src, dst 两个变量再无瓜葛,对其中一个值的任意改动都不会同步到另一个值。

但是,这个 merge 的表现,可能不是你想的那样,

src.S = []int32{12, 0}
dst.S = []int32{4, 5, 6, 7}
## 调用 copy 后, 你预期的结果是什么?[6/7]
6. dst.S = []int32{12, 0}
7. dst.S = []int32{12, 0, 6, 7}
  • 选项6: 嗯,原来是 {12, 0} 复制给 dst 就是 {12, 0}
  • 选项7: 这个是切片,你只给我 0,1 位的值,copier把 0,1 位置的值 copy 了,dst后面2,3位的值,src没给出,那就不管。所以就是 {12, 0, 6, 7}

这块的表现,我觉得是有争议的,大佬们在评论区留下你预期选项,看看大家是不是都这样想的。

实际运行结果,见上面的代码输出就能找到答案。

结语

copier 本来是一个短小精悍的工具库,也没想要水一篇,最近使用时,突然踩坑,就特开一篇,和大家分享一下踩坑经验。

在使用外部库的时候,还是建议去 github 上看看详细说明, 或者上 pkg.go.dev 看看它暴露出来出的接口以及说明。更或者进行完整的测试,充分了解它之后,再使用。

PS: 最近项目中使用这个库,发现问题后,改疯了~

qrcode

Ubuntu 安装 MongoDB

Ubuntu 下如何用 apt-get 安装最新的 MongoDB

Ubuntu 安装 MongoDB

官网:https://www.mongodb.com/

社区版下载地址: https://www.mongodb.com/try/download/community

安装命令

## 安装依赖
curl -fsSL https://www.mongodb.org/static/pgp/server-4.4.asc | sudo apt-key add -

## 查看添加成功没
apt-key list

echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu focal/mongodb-org/4.4 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-4.4.list

## 更新 apt
sudo apt update

## 安装 mongo
sudo apt install mongodb-org

## 启动服务
sudo systemctl start mongod.service

## 查看服务状态
sudo systemctl status mongod

## 启用状态
sudo systemctl enable mongod

## 停用状态
sudo systemctl stop mongod

初始化配置

vim /etc/mongod.conf
# network interfaces
net:
  port: 27017
#  bindIp: 127.0.0.1
  bindIp: 0.0.0.0

编辑了用户记得重启

sudo systemctl restart mongod

登陆 mongo

在安装了mongod的服务本机,mongo是无密码的,可输入如下命令直接登陆

$ mongo

远端通过如下命令登陆,未设置密码情况下,绑定了外网地址情况下,也能登陆

$ mongo "mongodb://192.168.0.x:27017"

创建用户

  • 产看所用用户
> use admin
switched to db admin
> db.system.users.find().pretty()

注意:用户相关的操作都需要 在admin db下操作!

创建 root 用户

> db.createUser(
    {
        user:"root",
        pwd:"pwd",
        roles:["root"]
    }
)

注意: 密码最好不要使用 & ? # : $ ; / . @ 等符号,因为 mongo DSN 格式是个 url,url 相关的关键词都不要用哦!

创建 admin 用户

> db.createUser(  
  { user: "admin",  
    customData:{description:"superuser"},
    pwd: "admin",  
    roles: [ { role: "userAdminAnyDatabase", db: "admin" } ]  
  }  
)  

创建业务用户

> db.createUser({
    user:"user001",
    pwd:"123456",
    customData:{
        name:'jim',
        email:'jim@qq.com',
        age:18,
    },
    roles:[
        {role:"readWrite",db:"db001"},
        {role:"readWrite",db:"db002"},
        'read'// 对其他数据库有只读权限,对db001、db002是读写权限
    ]
})

用户管理

  • 改密码
use admin
db.changeUserPassword("username", "xxx")
  • 为用户追加权限
db.grantRolesToUser( "<username>", [ <roles> ], { <writeConcern> } )
  • 删除用户
use admin
db.dropUser('user001')

安全设置

访问mongo认证配置

在无密码的mongo上配置好用户后,就可以启用安全认证了。

vim /etc/mongod.conf
#security:

修改为:

security:
  authorization: enabled

如果是 mongo 低于 2.6 那么在配置文件最后一行加上:

auth = true

参考

  • https://www.digitalocean.com/community/tutorials/how-to-install-mongodb-on-ubuntu-20-04
  • https://segmentfault.com/a/1190000015603831
  • https://stackoverflow.com/questions/25325142/how-to-set-authorization-in-mongodb-config-file

float64 can only be truncated to an integer type when truncation is enabled

使用 mongo-go-dirver 反序列化到 float32 报错

error decoding key weight: float64 can only be truncated to an integer type when truncation is enabled

问题描述

通过 mongo-go-dirver findOne 查数据并反序列化到 结构体时,有个字段:

type User struct {
    Name      string    `bson:"name"`
    Age       uint32    `bson:"age"`
    Weight    float32   `bson:"weight"`
    Studying  bool      `bson:"studying"`
    Tag       []string  `bson:"tag"`
    CreatedAt time.Time `bson:"created_at"`
}

weight 字段类型为 float32,就提示:

float64 类型的值只能在启用截断时截断为整数类型

问题如何产生的

存入数据时,手写 68.1,在 Golang 中手写的 float 值默认为 float64, 所以就当 float64 存入mongo了,待你想反序列化到 float32的值时,就报错了。

在 mgo 中,程序会自动帮你截断转换了,但是在 mongo-go-driver 中需要手动处理,或者显示得配置解决方案,不然会导致报错,不输出数据!

解决否

已解决

方案

问题原因时 浮点数 decode时,超出了精度,mongo-go-driver就把这个问题抛了个error出来。那么针对这个报错,我们可以从两方面去解决。要么提高精度,要么高速golang,多的截断,咋不要了。

  1. 把float32改为 float64
  2. 在bson tag 追加 ,truncate
    Weight    float32   `bson:"weight,truncate"`

参考

  • https://segmentfault.com/a/1190000038451313