diff --git a/cmd/layotto/main.go b/cmd/layotto/main.go index a6ed67961c..26a4732baa 100644 --- a/cmd/layotto/main.go +++ b/cmd/layotto/main.go @@ -97,6 +97,7 @@ import ( "mosn.io/layotto/components/lock" lock_consul "mosn.io/layotto/components/lock/consul" lock_etcd "mosn.io/layotto/components/lock/etcd" + lock_mongo "mosn.io/layotto/components/lock/mongo" lock_redis "mosn.io/layotto/components/lock/redis" lock_zookeeper "mosn.io/layotto/components/lock/zookeeper" runtime_lock "mosn.io/layotto/pkg/runtime/lock" @@ -299,6 +300,9 @@ func NewRuntimeGrpcServer(data json.RawMessage, opts ...grpc.ServerOption) (mgrp runtime_lock.NewFactory("consul", func() lock.LockStore { return lock_consul.NewConsulLock(log.DefaultLogger) }), + runtime_lock.NewFactory("mongo", func() lock.LockStore { + return lock_mongo.NewMongoLock(log.DefaultLogger) + }), ), // bindings diff --git a/components/go.mod b/components/go.mod index 5108a131b1..9925ecacbf 100644 --- a/components/go.mod +++ b/components/go.mod @@ -27,6 +27,7 @@ require ( go.etcd.io/etcd/api/v3 v3.5.0 go.etcd.io/etcd/client/v3 v3.5.0 go.etcd.io/etcd/server/v3 v3.5.0 + go.mongodb.org/mongo-driver v1.8.0 golang.org/x/net v0.0.0-20210614182718-04defd469f4e // indirect golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5 // indirect google.golang.org/grpc v1.38.0 diff --git a/components/go.sum b/components/go.sum index f850adb759..01209528fa 100644 --- a/components/go.sum +++ b/components/go.sum @@ -245,6 +245,7 @@ github.com/go-resty/resty/v2 v2.6.0/go.mod h1:PwvJS6hvaPkjtjNg9ph+VrSD92bi5Zq73w github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:56xuuqnHyryaerycW3BfssRdxQstACi0Epw/yC5E2xM= github.com/go-xorm/xorm v0.7.9/go.mod h1:XiVxrMMIhFkwSkh96BW7PACl7UhLtx2iJIHMdmjh5sQ= @@ -302,6 +303,7 @@ github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= @@ -367,22 +369,28 @@ github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBt github.com/hashicorp/consul/api v1.3.0 h1:HXNYlRkkM/t+Y/Yhxtwcy02dlYwIaoxzvxPnS+cqy78= github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/consul/sdk v0.3.0 h1:UOxjlb4xVNF93jak1mzzoBatyFju9nrkxpVwIp/QqxQ= github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI= github.com/hashicorp/go-immutable-radix v1.0.0 h1:AKDB1HM5PWEA7i4nhcpwOrO2byshxBjXVn/J/3+z5/0= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3 h1:zKjpN5BK/P5lMYrLmBHdBULWbJ0XpYR+7NGzqkZzoD4= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-plugin v1.0.1/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn3cQptSMzBuY= github.com/hashicorp/go-rootcerts v1.0.0 h1:Rqb66Oo1X/eSV1x66xbDccZjhJigjg0+e82kpwzSwCI= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-sockaddr v1.0.0 h1:GeH6tui99pF4NJgfnhp+L6+FfobzVW3Ah46sLo0ICXs= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= github.com/hashicorp/go-syslog v1.0.0 h1:KaodqZuhUoZereWVIYmpUgZysurB1kBLX2j0MwMrUAE= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= @@ -393,6 +401,7 @@ github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/memberlist v0.1.3 h1:EmmoJme1matNzb+hMpDuR/0sbJSUisxyqBGG676r31M= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/serf v0.8.2 h1:YZ7UKsJv+hKjqGVUUbtE3HNj79Eln2oQ75tniF6iPt0= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= @@ -463,6 +472,8 @@ github.com/klauspost/compress v1.10.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYs github.com/klauspost/compress v1.12.2/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= github.com/klauspost/compress v1.13.5 h1:9O69jUPDcsT9fEm74W92rZL9FQY7rCdaXVneq+yyzl4= github.com/klauspost/compress v1.13.5/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc= +github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/cpuid v1.2.3/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid v1.3.1 h1:5JNjFYYQrZeKRJ0734q51WCEEn2huer72Dc7K+R/b6s= github.com/klauspost/cpuid v1.3.1/go.mod h1:bYW4mA6ZgKPob1/Dlai2LviZJO7KGI3uoWLd42rAQw4= @@ -523,6 +534,7 @@ github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrk github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= @@ -537,6 +549,7 @@ github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lN github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= @@ -584,6 +597,7 @@ github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJ github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c h1:Lgl0gzECD8GnQ5QCWA8o6BtfL6mDH5rQgM4/fX3avOs= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= @@ -654,6 +668,7 @@ github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/shirou/gopsutil v3.20.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/shirou/gopsutil/v3 v3.20.11/go.mod h1:igHnfak0qnw1biGeI2qKQvu0ZkwvEkUcCLlYhZzdr/4= @@ -713,6 +728,8 @@ github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69 github.com/tebeka/strftime v0.1.3/go.mod h1:7wJm3dZlpr4l/oVK0t1HYIc4rMzQ2XJlOMIUJUJH6XQ= github.com/tevid/gohamcrest v1.1.1 h1:ou+xSqlIw1xfGTg1uq1nif/htZ2S3EzRqLm2BP+tYU0= github.com/tevid/gohamcrest v1.1.1/go.mod h1:3UvtWlqm8j5JbwYZh80D/PVBt0mJ1eJiYgZMibh0H/k= +github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= +github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20200427203606-3cfed13b9966/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= @@ -737,8 +754,16 @@ github.com/valyala/fasttemplate v1.1.0/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPU github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= github.com/wasmerio/wasmer-go v1.0.3/go.mod h1:0gzVdSfg6pysA6QVp6iVRPTagC6Wq9pOE8J86WKb2Fk= +github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= +github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= +github.com/xdg-go/scram v1.0.2 h1:akYIkZ28e6A96dkWNJQu3nmCzH3YfwMPQExUYDaRv7w= +github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs= +github.com/xdg-go/stringprep v1.0.2 h1:6iq84/ryjjeRmMJwxutI51F2GIPlP5BfTvXHeYjyhBc= +github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA= +github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -777,6 +802,8 @@ go.etcd.io/etcd/raft/v3 v3.5.0/go.mod h1:UFOHSIvO/nKwd4lhkwabrTD3cqW5yVyYYf/KlD0 go.etcd.io/etcd/server/v3 v3.5.0-alpha.0/go.mod h1:tsKetYpt980ZTpzl/gb+UOJj9RkIyCb1u4wjzMg90BQ= go.etcd.io/etcd/server/v3 v3.5.0 h1:jk8D/lwGEDlQU9kZXUFMSANkE22Sg5+mW27ip8xcF9E= go.etcd.io/etcd/server/v3 v3.5.0/go.mod h1:3Ah5ruV+M+7RZr0+Y/5mNLwC+eQlni+mQmOVdCRJoS4= +go.mongodb.org/mongo-driver v1.8.0 h1:R/P/JJzu8LJvJ1lDfph9GLNIKQxEtIHFfnUUUve35zY= +go.mongodb.org/mongo-driver v1.8.0/go.mod h1:0sQWfOeY63QTntERDJJ/0SuKK0T1uVSgKCuAROlKEPY= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= @@ -1056,6 +1083,7 @@ golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3 golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= diff --git a/components/lock/mongo/mongo_lock.go b/components/lock/mongo/mongo_lock.go new file mode 100644 index 0000000000..4baa7f4208 --- /dev/null +++ b/components/lock/mongo/mongo_lock.go @@ -0,0 +1,256 @@ +// +// Copyright 2021 Layotto Authors +// 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 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package mongo + +import ( + "context" + "fmt" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/options" + "go.mongodb.org/mongo-driver/mongo/readconcern" + "go.mongodb.org/mongo-driver/mongo/writeconcern" + "go.mongodb.org/mongo-driver/x/bsonx" + "mosn.io/layotto/components/lock" + "mosn.io/layotto/components/pkg/utils" + "mosn.io/pkg/log" + "time" +) + +const ( + TRY_LOCK_SUCCESS = 1 + TRY_LOCK_FAIL = 2 + UNLOCK_SUCCESS = 3 + UNLOCK_UNEXIST = 4 + UNLOCK_BELONG_TO_OTHERS = 5 + UNLOCK_FAIL = 6 +) + +// mongo lock store +type MongoLock struct { + factory utils.MongoFactory + + client utils.MongoClient + session utils.MongoSession + collection utils.MongoCollection + metadata utils.MongoMetadata + + features []lock.Feature + logger log.ErrorLogger + + ctx context.Context + cancel context.CancelFunc +} + +// NewMongoLock returns a new mongo lock +func NewMongoLock(logger log.ErrorLogger) *MongoLock { + s := &MongoLock{ + features: make([]lock.Feature, 0), + logger: logger, + } + return s +} + +func (e *MongoLock) Init(metadata lock.Metadata) error { + var client utils.MongoClient + // 1.parse config + m, err := utils.ParseMongoMetadata(metadata.Properties) + if err != nil { + return err + } + e.metadata = m + + e.factory = &utils.MongoFactoryImpl{} + + // 2. construct client + if client, err = e.factory.NewMongoClient(m); err != nil { + return err + } + + e.ctx, e.cancel = context.WithCancel(context.Background()) + + if err := client.Ping(e.ctx, nil); err != nil { + return err + } + + wc, err := utils.GetWriteConcernObject(e.metadata.WriteConcern) + if err != nil { + return err + } + + rc, err := utils.GetReadConcrenObject(e.metadata.ReadConcern) + if err != nil { + return err + } + + // set mongo options of collection + opts := options.Collection().SetWriteConcern(wc).SetReadConcern(rc) + + // create database + database := client.Database(e.metadata.DatabaseName) + + // create collection + e.collection = e.factory.NewMongoCollection(database, e.metadata.CollectionName, opts) + + // create exprie time index + indexModel := mongo.IndexModel{ + Keys: bsonx.Doc{{"Expire", bsonx.Int64(1)}}, + Options: options.Index().SetExpireAfterSeconds(0), + } + e.collection.Indexes().CreateOne(e.ctx, indexModel) + + e.client = client + + return err +} + +// Features is to get MongoLock's features +func (e *MongoLock) Features() []lock.Feature { + return e.features +} + +func (e *MongoLock) TryLock(req *lock.TryLockRequest) (*lock.TryLockResponse, error) { + var err error + // create mongo session + e.session, err = e.client.StartSession() + txnOpts := options.Transaction().SetReadConcern(readconcern.Snapshot()). + SetWriteConcern(writeconcern.New(writeconcern.WMajority())) + + // check session + if err != nil { + return &lock.TryLockResponse{ + Success: false, + }, fmt.Errorf("[mongoLock]: Create session return error: %s ResourceId: %s", err, req.ResourceId) + } + + // close mongo session + defer e.session.EndSession(e.ctx) + + // start transaction + status, err := e.session.WithTransaction(e.ctx, func(sessionContext mongo.SessionContext) (interface{}, error) { + var err error + var insertOneResult *mongo.InsertOneResult + + // set exprie date + expireTime := time.Now().Add(time.Duration(req.Expire) * time.Second) + + // insert mongo lock + insertOneResult, err = e.collection.InsertOne(e.ctx, bson.M{"_id": req.ResourceId, "LockOwner": req.LockOwner, "Expire": expireTime}) + + if err != nil { + _ = sessionContext.AbortTransaction(sessionContext) + return TRY_LOCK_FAIL, err + } + + // commit and set status + if insertOneResult != nil && insertOneResult.InsertedID == req.ResourceId { + if err = sessionContext.CommitTransaction(sessionContext); err == nil { + return TRY_LOCK_SUCCESS, err + } + } + + return TRY_LOCK_FAIL, err + }, txnOpts) + + // check lock + if err != nil { + return &lock.TryLockResponse{}, fmt.Errorf("[mongoLock]: Create new lock return error: %s ResourceId: %s", err, req.ResourceId) + } + + if status == TRY_LOCK_SUCCESS { + return &lock.TryLockResponse{ + Success: true, + }, nil + } else { + return &lock.TryLockResponse{ + Success: false, + }, nil + } +} + +func (e *MongoLock) Unlock(req *lock.UnlockRequest) (*lock.UnlockResponse, error) { + var err error + // create mongo session + e.session, err = e.client.StartSession() + txnOpts := options.Transaction().SetReadConcern(readconcern.Snapshot()). + SetWriteConcern(writeconcern.New(writeconcern.WMajority())) + + // check session + if err != nil { + return newInternalErrorUnlockResponse(), fmt.Errorf("[mongoLock]: Create Session return error: %s ResourceId: %s", err, req.ResourceId) + } + + // close mongo session + defer e.session.EndSession(e.ctx) + + // start transaction + status, err := e.session.WithTransaction(e.ctx, func(sessionContext mongo.SessionContext) (interface{}, error) { + var status int + + // delete lock + result, err := e.collection.DeleteOne(e.ctx, bson.M{"_id": req.ResourceId, "LockOwner": req.LockOwner}) + + // check delete result + if result.DeletedCount == 1 && err == nil { + status = UNLOCK_SUCCESS + } else if result.DeletedCount == 0 && err == nil { + if cursor, err := e.collection.Find(e.ctx, bson.M{"_id": req.ResourceId}); cursor != nil && cursor.RemainingBatchLength() != 0 && err == nil { + status = UNLOCK_BELONG_TO_OTHERS + } else if cursor != nil && cursor.RemainingBatchLength() == 0 && err == nil { + status = UNLOCK_UNEXIST + } + } + + if err != nil { + _ = sessionContext.AbortTransaction(sessionContext) + return UNLOCK_FAIL, err + } + + // commit and set status + if err = sessionContext.CommitTransaction(sessionContext); err == nil { + return status, err + } + + return UNLOCK_FAIL, err + }, txnOpts) + + resp := lock.INTERNAL_ERROR + + if err != nil { + return newInternalErrorUnlockResponse(), fmt.Errorf("[mongoLock]: Unlock returned error: %s ResourceId: %s", err, req.ResourceId) + } + + if status == UNLOCK_SUCCESS { + resp = lock.SUCCESS + } else if status == UNLOCK_UNEXIST { + resp = lock.LOCK_UNEXIST + } else if status == UNLOCK_BELONG_TO_OTHERS { + resp = lock.LOCK_BELONG_TO_OTHERS + } + return &lock.UnlockResponse{ + Status: resp, + }, nil +} + +func newInternalErrorUnlockResponse() *lock.UnlockResponse { + return &lock.UnlockResponse{ + Status: lock.INTERNAL_ERROR, + } +} + +func (e *MongoLock) Close() error { + e.cancel() + + return e.client.Disconnect(e.ctx) +} diff --git a/components/lock/mongo/mongo_lock_test.go b/components/lock/mongo/mongo_lock_test.go new file mode 100644 index 0000000000..ca806a8ab5 --- /dev/null +++ b/components/lock/mongo/mongo_lock_test.go @@ -0,0 +1,188 @@ +// +// Copyright 2021 Layotto Authors +// 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 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package mongo + +import ( + "github.com/google/uuid" + "github.com/stretchr/testify/assert" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/mongo" + "mosn.io/layotto/components/lock" + "mosn.io/layotto/components/pkg/utils" + "mosn.io/pkg/log" + "sync" + "testing" +) + +const ( + resourceId = "resource_xxx" + resourceId2 = "resource_xxx2" + resourceId3 = "resource_xxx3" + resourceId4 = "resource_xxx4" +) + +func TestMongoLock_Init(t *testing.T) { + var err error + var mongoUrl = "localhost:27017" + comp := NewMongoLock(log.DefaultLogger) + + cfg := lock.Metadata{ + Properties: make(map[string]string), + } + + err = comp.Init(cfg) + assert.Error(t, err) + + cfg.Properties["mongoHost"] = mongoUrl + cfg.Properties["operationTimeout"] = "a" + err = comp.Init(cfg) + assert.Error(t, err) + + cfg.Properties["operationTimeout"] = "2" + err = comp.Init(cfg) + assert.Error(t, err) +} + +func TestMongoLock_TryLock(t *testing.T) { + var err error + var resp *lock.TryLockResponse + var mongoUrl = "localhost:xxxx" + comp := NewMongoLock(log.DefaultLogger) + + cfg := lock.Metadata{ + Properties: make(map[string]string), + } + cfg.Properties["mongoHost"] = mongoUrl + _ = comp.Init(cfg) + + // mock + insertManyResult := &mongo.InsertManyResult{} + insertOneResult := &mongo.InsertOneResult{} + singleResult := &mongo.SingleResult{} + result := make(map[string]bson.M) + mockMongoClient := utils.MockMongoClient{} + mockMongoSession := utils.NewMockMongoSession() + mockMongoCollection := utils.MockMongoCollection{ + InsertManyResult: insertManyResult, + InsertOneResult: insertOneResult, + SingleResult: singleResult, + Result: result, + } + + comp.session = mockMongoSession + comp.collection = &mockMongoCollection + comp.client = &mockMongoClient + + ownerId1 := uuid.New().String() + resp, err = comp.TryLock(&lock.TryLockRequest{ + ResourceId: resourceId, + LockOwner: ownerId1, + Expire: 10, + }) + assert.NoError(t, err) + assert.Equal(t, true, resp.Success) + + resp, err = comp.TryLock(&lock.TryLockRequest{ + ResourceId: resourceId, + LockOwner: ownerId1, + Expire: 10, + }) + assert.NoError(t, err) + assert.Equal(t, false, resp.Success) + + var wg sync.WaitGroup + wg.Add(1) + + go func() { + ownerId2 := uuid.New().String() + resp, err = comp.TryLock(&lock.TryLockRequest{ + ResourceId: resourceId, + LockOwner: ownerId2, + Expire: 10, + }) + assert.NoError(t, err) + assert.Equal(t, false, resp.Success) + wg.Done() + }() + + wg.Wait() + + //another resource + resp, err = comp.TryLock(&lock.TryLockRequest{ + ResourceId: resourceId2, + LockOwner: ownerId1, + Expire: 10, + }) + assert.NoError(t, err) + assert.Equal(t, true, resp.Success) +} + +func TestMongoLock_Unlock(t *testing.T) { + var err error + var resp *lock.UnlockResponse + var lockresp *lock.TryLockResponse + var mongoUrl = "localhost:xxxx" + + comp := NewMongoLock(log.DefaultLogger) + + cfg := lock.Metadata{ + Properties: make(map[string]string), + } + + cfg.Properties["mongoHost"] = mongoUrl + err = comp.Init(cfg) + + // mock + insertManyResult := &mongo.InsertManyResult{} + insertOneResult := &mongo.InsertOneResult{} + singleResult := &mongo.SingleResult{} + result := make(map[string]bson.M) + mockMongoClient := utils.MockMongoClient{} + mockMongoSession := utils.NewMockMongoSession() + mockMongoCollection := utils.MockMongoCollection{ + InsertManyResult: insertManyResult, + InsertOneResult: insertOneResult, + SingleResult: singleResult, + Result: result, + } + + comp.session = mockMongoSession + comp.collection = &mockMongoCollection + comp.client = &mockMongoClient + + ownerId1 := uuid.New().String() + lockresp, err = comp.TryLock(&lock.TryLockRequest{ + ResourceId: resourceId3, + LockOwner: ownerId1, + Expire: 10, + }) + assert.NoError(t, err) + assert.Equal(t, true, lockresp.Success) + + //error resourceid + resp, err = comp.Unlock(&lock.UnlockRequest{ + ResourceId: resourceId4, + LockOwner: ownerId1, + }) + assert.NoError(t, err) + assert.Equal(t, lock.LOCK_UNEXIST, resp.Status) + + //success + resp, err = comp.Unlock(&lock.UnlockRequest{ + ResourceId: resourceId3, + LockOwner: ownerId1, + }) + assert.NoError(t, err) + assert.Equal(t, lock.SUCCESS, resp.Status) +} diff --git a/components/pkg/utils/mongo.go b/components/pkg/utils/mongo.go new file mode 100644 index 0000000000..d51cfd0ffd --- /dev/null +++ b/components/pkg/utils/mongo.go @@ -0,0 +1,232 @@ +// +// Copyright 2021 Layotto Authors +// 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 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package utils + +import ( + "context" + "errors" + "fmt" + "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/options" + "go.mongodb.org/mongo-driver/mongo/readconcern" + "go.mongodb.org/mongo-driver/mongo/readpref" + "go.mongodb.org/mongo-driver/mongo/writeconcern" + "strconv" + "time" +) + +const ( + mongoHost = "mongoHost" + mongoPassword = "mongoPassword" + username = "username" + server = "server" + databaseName = "databaseName" + collecttionName = "collectionName" + writeConcern = "writeConcern" + readConcern = "readConcern" + operationTimeout = "operationTimeout" + params = "params" + + defaultDatabase = "layottoStore" + defaultCollectionName = "layottoCollection" + defaultTimeout = 5 * time.Second + + // mongodb://:/ + connectionURIFormatWithAuthentication = "mongodb://%s:%s@%s/%s%s" + + // mongodb:/// + connectionURIFormat = "mongodb://%s/%s%s" + + // mongodb+srv:/// + connectionURIFormatWithSrv = "mongodb+srv://%s/%s" +) + +type MongoMetadata struct { + Host string + Username string + Password string + DatabaseName string + CollectionName string + Server string + Params string + WriteConcern string + ReadConcern string + OperationTimeout time.Duration +} + +// Item is Mongodb document wrapper. +type Item struct { + Key string `bson:"_id"` + Value interface{} `bson:"value"` + Etag string `bson:"_etag"` +} + +type MongoFactory interface { + NewMongoClient(m MongoMetadata) (MongoClient, error) + NewMongoCollection(m *mongo.Database, collectionName string, opts *options.CollectionOptions) MongoCollection +} + +type MongoClient interface { + StartSession(opts ...*options.SessionOptions) (mongo.Session, error) + Ping(ctx context.Context, rp *readpref.ReadPref) error + Database(name string, opts ...*options.DatabaseOptions) *mongo.Database + Disconnect(ctx context.Context) error +} + +type MongoSession interface { + AbortTransaction(context.Context) error + CommitTransaction(context.Context) error + WithTransaction(ctx context.Context, fn func(sessCtx mongo.SessionContext) (interface{}, error), + opts ...*options.TransactionOptions) (interface{}, error) + EndSession(context.Context) +} + +type MongoCollection interface { + FindOne(ctx context.Context, filter interface{}, opts ...*options.FindOneOptions) *mongo.SingleResult + InsertOne(ctx context.Context, document interface{}, opts ...*options.InsertOneOptions) (*mongo.InsertOneResult, error) + DeleteOne(ctx context.Context, filter interface{}, opts ...*options.DeleteOptions) (*mongo.DeleteResult, error) + Find(ctx context.Context, filter interface{}, opts ...*options.FindOptions) (*mongo.Cursor, error) + Indexes() mongo.IndexView +} + +type MongoFactoryImpl struct{} + +func (c *MongoFactoryImpl) NewMongoCollection(m *mongo.Database, collectionName string, opts *options.CollectionOptions) MongoCollection { + collection := m.Collection(collectionName, opts) + return collection +} + +func (c *MongoFactoryImpl) NewMongoClient(m MongoMetadata) (MongoClient, error) { + uri := getMongoURI(m) + + // Set client options + clientOptions := options.Client().ApplyURI(uri) + + // Connect to MongoDB + ctx, cancel := context.WithTimeout(context.Background(), m.OperationTimeout) + defer cancel() + + client, err := mongo.Connect(ctx, clientOptions) + if err != nil { + return nil, err + } + + return client, err +} + +func ParseMongoMetadata(properties map[string]string) (MongoMetadata, error) { + m := MongoMetadata{} + + if val, ok := properties[mongoHost]; ok && val != "" { + m.Host = val + } + + if val, ok := properties[server]; ok && val != "" { + m.Server = val + } + + if len(m.Host) == 0 && len(m.Server) == 0 { + return m, errors.New("must set 'host' or 'server' fields") + } + + if len(m.Host) != 0 && len(m.Server) != 0 { + return m, errors.New("'host' or 'server' fields are mutually exclusive") + } + + if val, ok := properties[username]; ok && val != "" { + m.Username = val + } + + if val, ok := properties[mongoPassword]; ok && val != "" { + m.Password = val + } + + m.DatabaseName = defaultDatabase + if val, ok := properties[databaseName]; ok && val != "" { + m.DatabaseName = val + } + + m.CollectionName = defaultCollectionName + if val, ok := properties[collecttionName]; ok && val != "" { + m.CollectionName = val + } + + if val, ok := properties[writeConcern]; ok && val != "" { + m.WriteConcern = val + } + + if val, ok := properties[readConcern]; ok && val != "" { + m.ReadConcern = val + } + + if val, ok := properties[params]; ok && val != "" { + m.Params = val + } + + var err error + m.OperationTimeout = defaultTimeout + if val, ok := properties[operationTimeout]; ok && val != "" { + m.OperationTimeout, err = time.ParseDuration(val) + if err != nil { + return m, errors.New("incorrect operationTimeout field") + } + } + return m, nil +} + +func getMongoURI(m MongoMetadata) string { + if len(m.Server) != 0 { + return fmt.Sprintf(connectionURIFormatWithSrv, m.Server, m.Params) + } + + if m.Username != "" && m.Password != "" { + return fmt.Sprintf(connectionURIFormatWithAuthentication, m.Username, m.Password, m.Host, m.DatabaseName, m.Params) + } + + return fmt.Sprintf(connectionURIFormat, m.Host, m.DatabaseName, m.Params) +} + +func GetWriteConcernObject(cn string) (*writeconcern.WriteConcern, error) { + var wc *writeconcern.WriteConcern + if cn != "" { + if cn == "majority" { + wc = writeconcern.New(writeconcern.WMajority(), writeconcern.J(true), writeconcern.WTimeout(defaultTimeout)) + } else { + w, err := strconv.Atoi(cn) + wc = writeconcern.New(writeconcern.W(w), writeconcern.J(true), writeconcern.WTimeout(defaultTimeout)) + return wc, err + } + } else { + wc = writeconcern.New(writeconcern.W(1), writeconcern.J(true), writeconcern.WTimeout(defaultTimeout)) + } + return wc, nil +} + +func GetReadConcrenObject(cn string) (*readconcern.ReadConcern, error) { + switch cn { + case "local": + return readconcern.Local(), nil + case "majority": + return readconcern.Majority(), nil + case "available": + return readconcern.Available(), nil + case "linearizable": + return readconcern.Linearizable(), nil + case "snapshot": + return readconcern.Snapshot(), nil + case "": + return readconcern.Local(), nil + } + return nil, nil +} diff --git a/components/pkg/utils/mongo_lock_mock.go b/components/pkg/utils/mongo_lock_mock.go new file mode 100644 index 0000000000..6e8ad842c2 --- /dev/null +++ b/components/pkg/utils/mongo_lock_mock.go @@ -0,0 +1,140 @@ +// +// Copyright 2021 Layotto Authors +// 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 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package utils + +import ( + "context" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/options" + "go.mongodb.org/mongo-driver/mongo/readpref" +) + +type MockMongoFactory struct{} + +// MockMongoClient is a mock of MongoClient interface +type MockMongoClient struct{} + +// MockMongoSession is a mock of MongoSession interface +type MockMongoSession struct { + mongo.SessionContext +} + +// MockMongoCollection is a mock of MongoCollection interface +type MockMongoCollection struct { + // '_id' document + Result map[string]bson.M + InsertManyResult *mongo.InsertManyResult + InsertOneResult *mongo.InsertOneResult + SingleResult *mongo.SingleResult + DeleteResult *mongo.DeleteResult +} + +func NewMockMongoFactory() *MockMongoFactory { + return &MockMongoFactory{} +} + +func NewMockMongoClient() *MockMongoClient { + return &MockMongoClient{} +} + +func NewMockMongoCollection() *MockMongoCollection { + return &MockMongoCollection{} +} + +func NewMockMongoSession() *MockMongoSession { + return &MockMongoSession{} +} + +func (f *MockMongoFactory) NewMongoClient(m MongoMetadata) (MongoClient, error) { + return &MockMongoClient{}, nil +} + +func (f *MockMongoFactory) NewMongoCollection(m *mongo.Database, collectionName string, opts *options.CollectionOptions) MongoCollection { + return &MockMongoCollection{} +} + +func (mc *MockMongoCollection) FindOne(ctx context.Context, filter interface{}, opts ...*options.FindOneOptions) *mongo.SingleResult { + result := mongo.SingleResult{} + return &result +} + +func (mc *MockMongoCollection) InsertOne(ctx context.Context, document interface{}, opts ...*options.InsertOneOptions) (*mongo.InsertOneResult, error) { + doc := document.(bson.M) + value := doc["_id"].(string) + if _, ok := mc.Result[value]; ok { + return nil, nil + } else { + // insert cache + mc.Result[value] = doc + mc.InsertOneResult.InsertedID = value + return mc.InsertOneResult, nil + } +} + +func (mc *MockMongoCollection) DeleteOne(ctx context.Context, filter interface{}, opts ...*options.DeleteOptions) (*mongo.DeleteResult, error) { + res := &mongo.DeleteResult{} + doc := filter.(bson.M) + value := doc["_id"].(string) + if v, ok := mc.Result[value]; ok { + if v["LockOwner"] == doc["LockOwner"] { + delete(mc.Result, value) + res.DeletedCount = 1 + } + } + return res, nil +} + +func (mc *MockMongoCollection) Find(ctx context.Context, filter interface{}, opts ...*options.FindOptions) (*mongo.Cursor, error) { + cursor := &mongo.Cursor{} + return cursor, nil +} + +func (mc *MockMongoCollection) Indexes() mongo.IndexView { + return mongo.IndexView{} +} + +func (c *MockMongoClient) StartSession(opts ...*options.SessionOptions) (mongo.Session, error) { + return &MockMongoSession{}, nil +} + +func (c *MockMongoClient) Ping(ctx context.Context, rp *readpref.ReadPref) error { + return nil +} + +func (c *MockMongoClient) Database(name string, opts ...*options.DatabaseOptions) *mongo.Database { + return nil +} + +func (c *MockMongoClient) Disconnect(ctx context.Context) error { + return nil +} + +func (s *MockMongoSession) AbortTransaction(context.Context) error { + return nil +} + +func (s *MockMongoSession) CommitTransaction(context.Context) error { + return nil +} + +func (s *MockMongoSession) WithTransaction(ctx context.Context, fn func(sessCtx mongo.SessionContext) (interface{}, error), + opts ...*options.TransactionOptions) (interface{}, error) { + res, err := fn(s) + return res, err +} + +func (s *MockMongoSession) EndSession(context.Context) { + +} diff --git a/configs/config_lock_mongo.json b/configs/config_lock_mongo.json new file mode 100644 index 0000000000..6fbb329b46 --- /dev/null +++ b/configs/config_lock_mongo.json @@ -0,0 +1,77 @@ +{ + "servers": [ + { + "default_log_path": "stdout", + "default_log_level": "DEBUG", + "routers": [ + { + "router_config_name": "actuator_dont_need_router" + } + ], + "listeners": [ + { + "name": "grpc", + "address": "127.0.0.1:34904", + "bind_port": true, + "filter_chains": [ + { + "filters": [ + { + "type": "grpc", + "config": { + "server_name": "runtime", + "grpc_config": { + "hellos": { + "helloworld": { + "hello": "greeting" + } + }, + "lock": { + "mongo": { + "metadata": { + "host": "localhost:27017", + "username": "", + "password": "", + "params": "" + } + } + }, + "app": { + "app_id": "app1", + "grpc_callback_port": 9999 + } + } + } + } + ] + } + ] + }, + { + "name": "actuator", + "address": "127.0.0.1:34999", + "bind_port": true, + "filter_chains": [ + { + "filters": [ + { + "type": "proxy", + "config": { + "downstream_protocol": "Http1", + "upstream_protocol": "Http1", + "router_config_name": "actuator_dont_need_router" + } + } + ] + } + ], + "stream_filters": [ + { + "type": "actuator_filter" + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/demo/lock/mongo/client.go b/demo/lock/mongo/client.go new file mode 100644 index 0000000000..564048ed33 --- /dev/null +++ b/demo/lock/mongo/client.go @@ -0,0 +1,117 @@ +package main + +import ( + "context" + "fmt" + "github.com/google/uuid" + client "mosn.io/layotto/sdk/go-sdk/client" + runtimev1pb "mosn.io/layotto/spec/proto/runtime/v1" + "sync" +) + +const ( + resourceId = "resource_a" + storeName = "mongo" +) + +func main() { + cli, err := client.NewClient() + if err != nil { + panic(err) + } + defer cli.Close() + ctx := context.Background() + // 1. Client trylock + owner1 := uuid.New().String() + fmt.Println("client1 prepare to tryLock...") + resp, err := cli.TryLock(ctx, &runtimev1pb.TryLockRequest{ + StoreName: storeName, + ResourceId: resourceId, + LockOwner: owner1, + Expire: 1000, + }) + if err != nil { + panic(err) + } + if !resp.Success { + panic("TryLock failed") + } + fmt.Printf("client1 got lock!ResourceId is %s\n", resourceId) + var wg sync.WaitGroup + wg.Add(1) + // 2. Client2 tryLock fail + go func() { + fmt.Println("client2 prepare to tryLock...") + owner2 := uuid.New().String() + resp, err := cli.TryLock(ctx, &runtimev1pb.TryLockRequest{ + StoreName: storeName, + ResourceId: resourceId, + LockOwner: owner2, + Expire: 10, + }) + if err != nil { + panic(err) + } + if resp.Success { + panic("client2 got lock?!") + } + fmt.Printf("client2 failed to get lock.ResourceId is %s\n", resourceId) + wg.Done() + }() + wg.Wait() + // 3. client 1 unlock + fmt.Println("client1 prepare to unlock...") + unlockResp, err := cli.Unlock(ctx, &runtimev1pb.UnlockRequest{ + StoreName: storeName, + ResourceId: resourceId, + LockOwner: owner1, + //XXX_NoUnkeyedLiteral: struct{}{}, + //XXX_unrecognized: nil, + //XXX_sizecache: 0, + }) + if err != nil { + panic(err) + } + if unlockResp.Status != 0 { + panic("client1 failed to unlock!") + } + fmt.Println("client1 succeeded in unlocking") + // 4. client 2 get lock + wg.Add(1) + go func() { + fmt.Println("client2 prepare to tryLock...") + owner2 := uuid.New().String() + resp, err := cli.TryLock(ctx, &runtimev1pb.TryLockRequest{ + StoreName: storeName, + ResourceId: resourceId, + LockOwner: owner2, + Expire: 10, + }) + if err != nil { + panic(err) + } + if !resp.Success { + panic("client2 failed to get lock?!") + } + fmt.Printf("client2 got lock.ResourceId is %s\n", resourceId) + // 5. client2 unlock + unlockResp, err := cli.Unlock(ctx, &runtimev1pb.UnlockRequest{ + StoreName: storeName, + ResourceId: resourceId, + LockOwner: owner2, + //XXX_NoUnkeyedLiteral: struct{}{}, + //XXX_unrecognized: nil, + //XXX_sizecache: 0, + }) + if err != nil { + panic(err) + } + if unlockResp.Status != 0 { + panic("client2 failed to unlock!") + } + fmt.Println("client2 succeeded in unlocking") + wg.Done() + }() + wg.Wait() + fmt.Println("Demo success!") +} diff --git a/docs/_sidebar.md b/docs/_sidebar.md index 07f27fad69..c6055310e7 100644 --- a/docs/_sidebar.md +++ b/docs/_sidebar.md @@ -50,6 +50,7 @@ - [Etcd](en/component_specs/lock/etcd.md) - [Zookeeper](en/component_specs/lock/zookeeper.md) - [Consul](en/component_specs/lock/consul.md) + - [MongoDB](en/component_specs/lock/mongo.md) - Configuration - [Etcd](en/component_specs/configuration/etcd.md) - [Apollo](en/component_specs/configuration/apollo.md) diff --git a/docs/en/component_specs/lock/mongo.md b/docs/en/component_specs/lock/mongo.md new file mode 100644 index 0000000000..ce4f246822 --- /dev/null +++ b/docs/en/component_specs/lock/mongo.md @@ -0,0 +1,21 @@ +# MongoDB + +## metadata fields + +Example:configs/config_lock_mongo.json + +| Field | Required | Description | +| --- | --- | --- | +| host | Y | MongoDB server address, such as localhost:27017 | +| username | N |specify username username | +| password | N | specify password | +| params | N | custom params | + + +## How to start MongoDB + +If you want to run the mongoDB demo, you need to start a mongoDB server with Docker first. + +```shell +docker run --name mongoDB -d -p 27017:27017 mongo +``` \ No newline at end of file diff --git a/docs/zh/component_specs/lock/mongo.md b/docs/zh/component_specs/lock/mongo.md new file mode 100644 index 0000000000..ec051ef4e2 --- /dev/null +++ b/docs/zh/component_specs/lock/mongo.md @@ -0,0 +1,22 @@ +# MongoDB + +## 配置项说明 + +示例:configs/config_lock_mongo.json + +| 字段 | 必填 | 说明 | +| --- | --- | --- | +| host | Y | MongoDB的服务地址,例如localhost:27017 | +| username | N | MongoDB用户名 | +| password | N | MongoDB密码 | +| database | N | MongoDB数据库 | +| params | N | 自定义参数 | + + +## 怎么启动 MongoDB + +如果想启动MongoDB的demo,需要先用Docker启动一个MongoDB 命令: + +```shell +docker run --name mongoDB -d -p 27017:27017 mongo +``` diff --git a/go.sum b/go.sum index 858a3babf1..a330a0a532 100644 --- a/go.sum +++ b/go.sum @@ -869,6 +869,8 @@ github.com/klauspost/compress v1.11.12/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdY github.com/klauspost/compress v1.12.2/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= github.com/klauspost/compress v1.13.5 h1:9O69jUPDcsT9fEm74W92rZL9FQY7rCdaXVneq+yyzl4= github.com/klauspost/compress v1.13.5/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc= +github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/cpuid v1.2.3/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid v1.3.1 h1:5JNjFYYQrZeKRJ0734q51WCEEn2huer72Dc7K+R/b6s= github.com/klauspost/cpuid v1.3.1/go.mod h1:bYW4mA6ZgKPob1/Dlai2LviZJO7KGI3uoWLd42rAQw4= @@ -1389,6 +1391,8 @@ go.etcd.io/etcd/server/v3 v3.5.0 h1:jk8D/lwGEDlQU9kZXUFMSANkE22Sg5+mW27ip8xcF9E= go.etcd.io/etcd/server/v3 v3.5.0/go.mod h1:3Ah5ruV+M+7RZr0+Y/5mNLwC+eQlni+mQmOVdCRJoS4= go.mongodb.org/mongo-driver v1.5.1 h1:9nOVLGDfOaZ9R0tBumx/BcuqkbFpyTCU2r/Po7A2azI= go.mongodb.org/mongo-driver v1.5.1/go.mod h1:gRXCHX4Jo7J0IJ1oDQyUxF7jfy19UfxniMS4xxMmUqw= +go.mongodb.org/mongo-driver v1.8.0 h1:R/P/JJzu8LJvJ1lDfph9GLNIKQxEtIHFfnUUUve35zY= +go.mongodb.org/mongo-driver v1.8.0/go.mod h1:0sQWfOeY63QTntERDJJ/0SuKK0T1uVSgKCuAROlKEPY= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= diff --git a/sdk/go-sdk/go.mod b/sdk/go-sdk/go.mod index f31ccaedf4..35224da897 100644 --- a/sdk/go-sdk/go.mod +++ b/sdk/go-sdk/go.mod @@ -3,6 +3,7 @@ module mosn.io/layotto/sdk/go-sdk go 1.14 require ( + github.com/golang/protobuf v1.5.0 github.com/pkg/errors v0.9.1 github.com/stretchr/testify v1.7.0 google.golang.org/grpc v1.37.0 diff --git a/sdk/go-sdk/go.sum b/sdk/go-sdk/go.sum index 514558823b..aa5bc4e378 100644 --- a/sdk/go-sdk/go.sum +++ b/sdk/go-sdk/go.sum @@ -94,5 +94,3 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -mosn.io/layotto/spec v0.0.0-20210707123820-584778d048d3 h1:8GbrNw6AOi4+DrQGG5TbxMNPPRQJ0uXjkSCP2qeKc08= -mosn.io/layotto/spec v0.0.0-20210707123820-584778d048d3/go.mod h1:ex31WL9Vx1PadpZmN4CZpw/57xzBeKIwqMI5fYXi7RY=