diff --git a/.gitignore b/.gitignore index 66fd13c903cac02eb9657cd53fb227823484401d..398baf21b2dc579eaffb17134409de9b729b0050 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,5 @@ # Dependency directories (remove the comment below to include it) # vendor/ + +.idea diff --git a/README.en.md b/README.en.md deleted file mode 100644 index 3ce8792497719c7dff9c2bb9d2e80d5ddf52fc81..0000000000000000000000000000000000000000 --- a/README.en.md +++ /dev/null @@ -1,36 +0,0 @@ -# simpleactor-go - -#### Description -protoactor-go简化版 - -#### Software Architecture -Software architecture description - -#### Installation - -1. xxxx -2. xxxx -3. xxxx - -#### Instructions - -1. xxxx -2. xxxx -3. xxxx - -#### Contribution - -1. Fork the repository -2. Create Feat_xxx branch -3. Commit your code -4. Create Pull Request - - -#### Gitee Feature - -1. You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md -2. Gitee blog [blog.gitee.com](https://blog.gitee.com) -3. Explore open source project [https://gitee.com/explore](https://gitee.com/explore) -4. The most valuable open source project [GVP](https://gitee.com/gvp) -5. The manual of Gitee [https://gitee.com/help](https://gitee.com/help) -6. The most popular members [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) diff --git a/README.md b/README.md index 762ca804192349e57bfcaf4414829927a02ca3c6..afb0cd6fa098080fb64920cd831c9d7f624439b9 100644 --- a/README.md +++ b/README.md @@ -1,37 +1,4 @@ # simpleactor-go -#### 介绍 -protoactor-go简化版 - -#### 软件架构 -软件架构说明 - - -#### 安装教程 - -1. xxxx -2. xxxx -3. xxxx - -#### 使用说明 - -1. xxxx -2. xxxx -3. xxxx - -#### 参与贡献 - -1. Fork 本仓库 -2. 新建 Feat_xxx 分支 -3. 提交代码 -4. 新建 Pull Request - - -#### 特技 - -1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md -2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com) -3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目 -4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目 -5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help) -6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) +## 介绍 +protoactor-go简化版 \ No newline at end of file diff --git a/_examples/Makefile b/_examples/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..d3526fb9666386577cac41d2b9a736be1ab93323 --- /dev/null +++ b/_examples/Makefile @@ -0,0 +1,11 @@ +examples := $(wildcard $(CURDIR)/*/) + + +.PHONY: go.mod +go.mod: + @echo "go mod tidy" + @for d in $(examples); do \ + echo $$d ; \ + cd $$d && go mod tidy ; \ + done + diff --git a/_examples/actor-autorespond/go.mod b/_examples/actor-autorespond/go.mod new file mode 100644 index 0000000000000000000000000000000000000000..678f1e754826141fa258a955cd6814a86d60e353 --- /dev/null +++ b/_examples/actor-autorespond/go.mod @@ -0,0 +1,36 @@ +module autorespond + +go 1.21 + +toolchain go1.21.2 + +replace gitee.com/simplexyz/simpleactor-go => ../.. + +require gitee.com/simplexyz/simpleactor-go v0.0.0-00010101000000-000000000000 + +require ( + github.com/Workiva/go-datastructures v1.1.1 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/emirpasic/gods v1.18.1 // indirect + github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/lithammer/shortuuid/v4 v4.0.0 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/orcaman/concurrent-map v1.0.0 // indirect + github.com/prometheus/client_golang v1.16.0 // indirect + github.com/prometheus/client_model v0.4.0 // indirect + github.com/prometheus/common v0.42.0 // indirect + github.com/prometheus/procfs v0.10.1 // indirect + github.com/twmb/murmur3 v1.1.6 // indirect + go.opentelemetry.io/otel v1.16.0 // indirect + go.opentelemetry.io/otel/exporters/prometheus v0.39.0 // indirect + go.opentelemetry.io/otel/metric v1.16.0 // indirect + go.opentelemetry.io/otel/sdk v1.16.0 // indirect + go.opentelemetry.io/otel/sdk/metric v0.39.0 // indirect + go.opentelemetry.io/otel/trace v1.16.0 // indirect + golang.org/x/sys v0.13.0 // indirect + google.golang.org/protobuf v1.31.0 // indirect +) diff --git a/_examples/actor-autorespond/go.sum b/_examples/actor-autorespond/go.sum new file mode 100644 index 0000000000000000000000000000000000000000..616a6b1575c165294ea88ea440da8596a33cce3f --- /dev/null +++ b/_examples/actor-autorespond/go.sum @@ -0,0 +1,98 @@ +github.com/Workiva/go-datastructures v1.1.1 h1:9G5u1UqKt6ABseAffHGNfbNQd7omRlWE5QaxNruzhE0= +github.com/Workiva/go-datastructures v1.1.1/go.mod h1:1yZL+zfsztete+ePzZz/Zb1/t5BnDuE2Ya2MMGhzP6A= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/lithammer/shortuuid/v4 v4.0.0 h1:QRbbVkfgNippHOS8PXDkti4NaWeyYfcBTHtw7k08o4c= +github.com/lithammer/shortuuid/v4 v4.0.0/go.mod h1:Zs8puNcrvf2rV9rTH51ZLLcj7ZXqQI3lv67aw4KiB1Y= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/orcaman/concurrent-map v1.0.0 h1:I/2A2XPCb4IuQWcQhBhSwGfiuybl/J0ev9HDbW65HOY= +github.com/orcaman/concurrent-map v1.0.0/go.mod h1:Lu3tH6HLW3feq74c2GC+jIMS/K2CFcDWnWD9XkenwhI= +github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= +github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= +github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= +github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= +github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= +github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= +github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/tinylib/msgp v1.1.5/go.mod h1:eQsjooMTnV42mHu917E26IogZ2930nFyBQdofk10Udg= +github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q= +github.com/twmb/murmur3 v1.1.6 h1:mqrRot1BRxm+Yct+vavLMou2/iJt0tNVTTC0QoIjaZg= +github.com/twmb/murmur3 v1.1.6/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s= +go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4= +go.opentelemetry.io/otel/exporters/prometheus v0.39.0 h1:whAaiHxOatgtKd+w0dOi//1KUxj3KoPINZdtDaDj3IA= +go.opentelemetry.io/otel/exporters/prometheus v0.39.0/go.mod h1:4jo5Q4CROlCpSPsXLhymi+LYrDXd2ObU5wbKayfZs7Y= +go.opentelemetry.io/otel/metric v1.16.0 h1:RbrpwVG1Hfv85LgnZ7+txXioPDoh6EdbZHo26Q3hqOo= +go.opentelemetry.io/otel/metric v1.16.0/go.mod h1:QE47cpOmkwipPiefDwo2wDzwJrlfxxNYodqc4xnGCo4= +go.opentelemetry.io/otel/sdk v1.16.0 h1:Z1Ok1YsijYL0CSJpHt4cS3wDDh7p572grzNrBMiMWgE= +go.opentelemetry.io/otel/sdk v1.16.0/go.mod h1:tMsIuKXuuIWPBAOrH+eHtvhTL+SntFtXF9QD68aP6p4= +go.opentelemetry.io/otel/sdk/metric v0.39.0 h1:Kun8i1eYf48kHH83RucG93ffz0zGV1sh46FAScOTuDI= +go.opentelemetry.io/otel/sdk/metric v0.39.0/go.mod h1:piDIRgjcK7u0HCL5pCA4e74qpK/jk3NiUoAHATVAmiI= +go.opentelemetry.io/otel/trace v1.16.0 h1:8JRpaObFoW0pxuVPapkgH8UhHQj+bJW8jJsCZEu5MQs= +go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/_examples/actor-autorespond/main.go b/_examples/actor-autorespond/main.go new file mode 100644 index 0000000000000000000000000000000000000000..b07604c6e21d551f46330fbb3989a66fd72fa1ea --- /dev/null +++ b/_examples/actor-autorespond/main.go @@ -0,0 +1,43 @@ +package main + +import ( + "fmt" + "time" + + "gitee.com/simplexyz/simpleactor-go/actor" +) + +// Auto Response in Proto.Actor is a special kind of message that can create its own response message +// it is received just like any other message by the actor +// but the actor context sees the AutoResponse interface and calls GetAutoReplyMessage() to get the response message +// this is useful if you want to guarantee some form of Ack from an actor. without forcing the developer of the actor to +// use context.Respond manually + +// e.g. ClusterPubSub feature uses this to Ack back to the Topic actor to let it know the message has been received + +type myAutoResponder struct { + name string +} + +func (m myAutoResponder) GetAutoResponse(context actor.Context) interface{} { + // return some response-message + // you have full access to the actor context + + return &myAutoResponse{ + name: m.name + " " + context.Self().Id, + } +} + +type myAutoResponse struct { + name string +} + +func main() { + system := actor.NewActorSystem() + props := actor.PropsFromFunc(func(ctx actor.Context) {}) + pid := system.Root.Spawn(props) + + res, _ := system.Root.RequestFuture(pid, &myAutoResponder{name: "hello"}, 10*time.Second).Result() + + fmt.Printf("%v", res) +} diff --git a/_examples/actor-backpressure/go.mod b/_examples/actor-backpressure/go.mod new file mode 100644 index 0000000000000000000000000000000000000000..376161c1f8fde0991ef60e3133abf37db2b15eb8 --- /dev/null +++ b/_examples/actor-backpressure/go.mod @@ -0,0 +1,39 @@ +module backpressure + +go 1.21 + +toolchain go1.21.2 + +replace gitee.com/simplexyz/simpleactor-go => ../.. + +require ( + gitee.com/simplexyz/simpleactor-go v0.0.0-00010101000000-000000000000 + github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716 +) + +require ( + github.com/Workiva/go-datastructures v1.1.1 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/emirpasic/gods v1.18.1 // indirect + github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/lithammer/shortuuid/v4 v4.0.0 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/orcaman/concurrent-map v1.0.0 // indirect + github.com/prometheus/client_golang v1.16.0 // indirect + github.com/prometheus/client_model v0.4.0 // indirect + github.com/prometheus/common v0.42.0 // indirect + github.com/prometheus/procfs v0.10.1 // indirect + github.com/twmb/murmur3 v1.1.6 // indirect + go.opentelemetry.io/otel v1.16.0 // indirect + go.opentelemetry.io/otel/exporters/prometheus v0.39.0 // indirect + go.opentelemetry.io/otel/metric v1.16.0 // indirect + go.opentelemetry.io/otel/sdk v1.16.0 // indirect + go.opentelemetry.io/otel/sdk/metric v0.39.0 // indirect + go.opentelemetry.io/otel/trace v1.16.0 // indirect + golang.org/x/sys v0.13.0 // indirect + google.golang.org/protobuf v1.31.0 // indirect +) diff --git a/_examples/actor-backpressure/go.sum b/_examples/actor-backpressure/go.sum new file mode 100644 index 0000000000000000000000000000000000000000..72e1596d2e6bb1dc5da2329b876b42c528bf5f7a --- /dev/null +++ b/_examples/actor-backpressure/go.sum @@ -0,0 +1,100 @@ +github.com/Workiva/go-datastructures v1.1.1 h1:9G5u1UqKt6ABseAffHGNfbNQd7omRlWE5QaxNruzhE0= +github.com/Workiva/go-datastructures v1.1.1/go.mod h1:1yZL+zfsztete+ePzZz/Zb1/t5BnDuE2Ya2MMGhzP6A= +github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716 h1:SgyG4sXkrlalMoCfp20LiNPNhfJS7ez3opNdtihIxPc= +github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716/go.mod h1:/zSlF0T2ArAsTG6SVu8d8qlK+19jjudjA3wWsxnGFHg= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/lithammer/shortuuid/v4 v4.0.0 h1:QRbbVkfgNippHOS8PXDkti4NaWeyYfcBTHtw7k08o4c= +github.com/lithammer/shortuuid/v4 v4.0.0/go.mod h1:Zs8puNcrvf2rV9rTH51ZLLcj7ZXqQI3lv67aw4KiB1Y= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/orcaman/concurrent-map v1.0.0 h1:I/2A2XPCb4IuQWcQhBhSwGfiuybl/J0ev9HDbW65HOY= +github.com/orcaman/concurrent-map v1.0.0/go.mod h1:Lu3tH6HLW3feq74c2GC+jIMS/K2CFcDWnWD9XkenwhI= +github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= +github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= +github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= +github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= +github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= +github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= +github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/tinylib/msgp v1.1.5/go.mod h1:eQsjooMTnV42mHu917E26IogZ2930nFyBQdofk10Udg= +github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q= +github.com/twmb/murmur3 v1.1.6 h1:mqrRot1BRxm+Yct+vavLMou2/iJt0tNVTTC0QoIjaZg= +github.com/twmb/murmur3 v1.1.6/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s= +go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4= +go.opentelemetry.io/otel/exporters/prometheus v0.39.0 h1:whAaiHxOatgtKd+w0dOi//1KUxj3KoPINZdtDaDj3IA= +go.opentelemetry.io/otel/exporters/prometheus v0.39.0/go.mod h1:4jo5Q4CROlCpSPsXLhymi+LYrDXd2ObU5wbKayfZs7Y= +go.opentelemetry.io/otel/metric v1.16.0 h1:RbrpwVG1Hfv85LgnZ7+txXioPDoh6EdbZHo26Q3hqOo= +go.opentelemetry.io/otel/metric v1.16.0/go.mod h1:QE47cpOmkwipPiefDwo2wDzwJrlfxxNYodqc4xnGCo4= +go.opentelemetry.io/otel/sdk v1.16.0 h1:Z1Ok1YsijYL0CSJpHt4cS3wDDh7p572grzNrBMiMWgE= +go.opentelemetry.io/otel/sdk v1.16.0/go.mod h1:tMsIuKXuuIWPBAOrH+eHtvhTL+SntFtXF9QD68aP6p4= +go.opentelemetry.io/otel/sdk/metric v0.39.0 h1:Kun8i1eYf48kHH83RucG93ffz0zGV1sh46FAScOTuDI= +go.opentelemetry.io/otel/sdk/metric v0.39.0/go.mod h1:piDIRgjcK7u0HCL5pCA4e74qpK/jk3NiUoAHATVAmiI= +go.opentelemetry.io/otel/trace v1.16.0 h1:8JRpaObFoW0pxuVPapkgH8UhHQj+bJW8jJsCZEu5MQs= +go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/_examples/actor-backpressure/main.go b/_examples/actor-backpressure/main.go new file mode 100644 index 0000000000000000000000000000000000000000..4988e3810067e9c6c85ed016eaa2a71e5e9d58c7 --- /dev/null +++ b/_examples/actor-backpressure/main.go @@ -0,0 +1,104 @@ +package main + +import ( + "log" + "sync/atomic" + "time" + + "gitee.com/simplexyz/simpleactor-go/actor" + console "github.com/asynkron/goconsole" +) + +// sent to producer to request more work +type requestMoreWork struct { + items int +} +type requestWorkBehavior struct { + tokens int64 + producer *actor.PID +} + +func (m *requestWorkBehavior) MailboxStarted() { + m.requestMore() +} + +func (m *requestWorkBehavior) MessagePosted(msg interface{}) { +} + +func (m *requestWorkBehavior) MessageReceived(msg interface{}) { + atomic.AddInt64(&m.tokens, -1) + if m.tokens == 0 { + m.requestMore() + } +} + +func (m *requestWorkBehavior) MailboxEmpty() { +} + +func (m *requestWorkBehavior) requestMore() { + log.Println("Requesting more tokens") + m.tokens = 50 + system.Root.Send(m.producer, &requestMoreWork{items: 50}) +} + +type producer struct { + requestedWork int + producedWork int + worker *actor.PID +} + +func (p *producer) Receive(ctx actor.Context) { + switch msg := ctx.Message().(type) { + case *actor.Started: + // spawn our worker + mb := actor.Unbounded(&requestWorkBehavior{ + producer: ctx.Self(), + }) + workerProps := actor.PropsFromProducer(func() actor.Actor { + return &worker{} + }, actor.WithMailbox(mb)) + + p.worker = ctx.Spawn(workerProps) + case *requestMoreWork: + p.requestedWork += msg.items + log.Println("Producer got a new work request") + ctx.Send(ctx.Self(), &produce{}) + case *produce: + // produce more work + log.Println("Producer is producing work") + p.producedWork++ + ctx.Send(p.worker, &work{p.producedWork}) + + // decrease our workload and tell ourselves to produce more work + if p.requestedWork > 0 { + p.requestedWork-- + ctx.Send(ctx.Self(), &produce{}) + } + } +} + +type ( + produce struct{} + worker struct{} +) + +func (w *worker) Receive(ctx actor.Context) { + switch msg := ctx.Message().(type) { + case *work: + log.Printf("Worker is working %+v", msg) + time.Sleep(100 * time.Millisecond) + } +} + +type work struct { + id int +} + +var system = actor.NewActorSystem() + +func main() { + producerProps := actor.PropsFromProducer(func() actor.Actor { return &producer{} }) + system.Root.Spawn(producerProps) + + _, _ = console.ReadLine() +} diff --git a/_examples/actor-deadletter/Makefile b/_examples/actor-deadletter/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..fd04ca92aa13629d9c4febbd307fe913c3edc5f7 --- /dev/null +++ b/_examples/actor-deadletter/Makefile @@ -0,0 +1,2 @@ +start: + go run main.go -duration 20s -rate 2000000 -throttle 3 diff --git a/_examples/actor-deadletter/go.mod b/_examples/actor-deadletter/go.mod new file mode 100644 index 0000000000000000000000000000000000000000..c4da551ce88c445c3b27b26c72deed80c6bbfaae --- /dev/null +++ b/_examples/actor-deadletter/go.mod @@ -0,0 +1,40 @@ +module helloworld + +go 1.21 + +toolchain go1.21.2 + +replace gitee.com/simplexyz/simpleactor-go => ../.. + +require ( + gitee.com/simplexyz/simpleactor-go v0.0.0-00010101000000-000000000000 + github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716 + golang.org/x/time v0.1.0 +) + +require ( + github.com/Workiva/go-datastructures v1.1.1 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/emirpasic/gods v1.18.1 // indirect + github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/lithammer/shortuuid/v4 v4.0.0 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/orcaman/concurrent-map v1.0.0 // indirect + github.com/prometheus/client_golang v1.16.0 // indirect + github.com/prometheus/client_model v0.4.0 // indirect + github.com/prometheus/common v0.42.0 // indirect + github.com/prometheus/procfs v0.10.1 // indirect + github.com/twmb/murmur3 v1.1.6 // indirect + go.opentelemetry.io/otel v1.16.0 // indirect + go.opentelemetry.io/otel/exporters/prometheus v0.39.0 // indirect + go.opentelemetry.io/otel/metric v1.16.0 // indirect + go.opentelemetry.io/otel/sdk v1.16.0 // indirect + go.opentelemetry.io/otel/sdk/metric v0.39.0 // indirect + go.opentelemetry.io/otel/trace v1.16.0 // indirect + golang.org/x/sys v0.13.0 // indirect + google.golang.org/protobuf v1.31.0 // indirect +) diff --git a/_examples/actor-deadletter/go.sum b/_examples/actor-deadletter/go.sum new file mode 100644 index 0000000000000000000000000000000000000000..5be0f8bcb6b906a1d3816693457eb37f5fd8a179 --- /dev/null +++ b/_examples/actor-deadletter/go.sum @@ -0,0 +1,102 @@ +github.com/Workiva/go-datastructures v1.1.1 h1:9G5u1UqKt6ABseAffHGNfbNQd7omRlWE5QaxNruzhE0= +github.com/Workiva/go-datastructures v1.1.1/go.mod h1:1yZL+zfsztete+ePzZz/Zb1/t5BnDuE2Ya2MMGhzP6A= +github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716 h1:SgyG4sXkrlalMoCfp20LiNPNhfJS7ez3opNdtihIxPc= +github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716/go.mod h1:/zSlF0T2ArAsTG6SVu8d8qlK+19jjudjA3wWsxnGFHg= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/lithammer/shortuuid/v4 v4.0.0 h1:QRbbVkfgNippHOS8PXDkti4NaWeyYfcBTHtw7k08o4c= +github.com/lithammer/shortuuid/v4 v4.0.0/go.mod h1:Zs8puNcrvf2rV9rTH51ZLLcj7ZXqQI3lv67aw4KiB1Y= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/orcaman/concurrent-map v1.0.0 h1:I/2A2XPCb4IuQWcQhBhSwGfiuybl/J0ev9HDbW65HOY= +github.com/orcaman/concurrent-map v1.0.0/go.mod h1:Lu3tH6HLW3feq74c2GC+jIMS/K2CFcDWnWD9XkenwhI= +github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= +github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= +github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= +github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= +github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= +github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= +github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/tinylib/msgp v1.1.5/go.mod h1:eQsjooMTnV42mHu917E26IogZ2930nFyBQdofk10Udg= +github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q= +github.com/twmb/murmur3 v1.1.6 h1:mqrRot1BRxm+Yct+vavLMou2/iJt0tNVTTC0QoIjaZg= +github.com/twmb/murmur3 v1.1.6/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s= +go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4= +go.opentelemetry.io/otel/exporters/prometheus v0.39.0 h1:whAaiHxOatgtKd+w0dOi//1KUxj3KoPINZdtDaDj3IA= +go.opentelemetry.io/otel/exporters/prometheus v0.39.0/go.mod h1:4jo5Q4CROlCpSPsXLhymi+LYrDXd2ObU5wbKayfZs7Y= +go.opentelemetry.io/otel/metric v1.16.0 h1:RbrpwVG1Hfv85LgnZ7+txXioPDoh6EdbZHo26Q3hqOo= +go.opentelemetry.io/otel/metric v1.16.0/go.mod h1:QE47cpOmkwipPiefDwo2wDzwJrlfxxNYodqc4xnGCo4= +go.opentelemetry.io/otel/sdk v1.16.0 h1:Z1Ok1YsijYL0CSJpHt4cS3wDDh7p572grzNrBMiMWgE= +go.opentelemetry.io/otel/sdk v1.16.0/go.mod h1:tMsIuKXuuIWPBAOrH+eHtvhTL+SntFtXF9QD68aP6p4= +go.opentelemetry.io/otel/sdk/metric v0.39.0 h1:Kun8i1eYf48kHH83RucG93ffz0zGV1sh46FAScOTuDI= +go.opentelemetry.io/otel/sdk/metric v0.39.0/go.mod h1:piDIRgjcK7u0HCL5pCA4e74qpK/jk3NiUoAHATVAmiI= +go.opentelemetry.io/otel/trace v1.16.0 h1:8JRpaObFoW0pxuVPapkgH8UhHQj+bJW8jJsCZEu5MQs= +go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/time v0.1.0 h1:xYY+Bajn2a7VBmTM5GikTmnK8ZuX8YgnQCqZpbBNtmA= +golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/_examples/actor-deadletter/main.go b/_examples/actor-deadletter/main.go new file mode 100644 index 0000000000000000000000000000000000000000..433254702ee86f1a7e76a371125a87839f381eaa --- /dev/null +++ b/_examples/actor-deadletter/main.go @@ -0,0 +1,47 @@ +package main + +import ( + "context" + "flag" + "log" + "sync/atomic" + "time" + + "gitee.com/simplexyz/simpleactor-go/actor" + console "github.com/asynkron/goconsole" + "golang.org/x/time/rate" +) + +type hello struct { + Who string +} + +func main() { + irate := flag.Int("rate", 1000000, "How many messages per second") + throttle := flag.Int("throttle", 5, "Throttle of deadletter logs") + d := flag.Duration("duration", 10*time.Second, "How long you want to keep sending") + flag.Parse() + + // init + cfg := actor.Configure(actor.WithDeadLetterThrottleCount(int32(*throttle))) + system := actor.NewActorSystemWithConfig(cfg) + + btn := int32(1) + go func() { + time.Sleep(*d) + atomic.StoreInt32(&btn, 0) + }() + + ctx := context.TODO() + invalidPid := system.NewLocalPID("unknown") + limiter := rate.NewLimiter(rate.Limit(*irate), *irate) + + log.Printf("started") + for atomic.LoadInt32(&btn) == 1 { + system.Root.Send(invalidPid, &hello{Who: "deadleater"}) + // time.Sleep(sleepDrt) + limiter.Wait(ctx) + } + log.Printf("done") + console.ReadLine() +} diff --git a/_examples/actor-helloworld/go.mod b/_examples/actor-helloworld/go.mod new file mode 100644 index 0000000000000000000000000000000000000000..c95a881d5464fb1b93c0e7ffa40a3d8a69d5d9bc --- /dev/null +++ b/_examples/actor-helloworld/go.mod @@ -0,0 +1,39 @@ +module helloworld + +go 1.21 + +toolchain go1.21.2 + +replace gitee.com/simplexyz/simpleactor-go => ../.. + +require ( + gitee.com/simplexyz/simpleactor-go v0.0.0-00010101000000-000000000000 + github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716 +) + +require ( + github.com/Workiva/go-datastructures v1.1.1 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/emirpasic/gods v1.18.1 // indirect + github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/lithammer/shortuuid/v4 v4.0.0 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/orcaman/concurrent-map v1.0.0 // indirect + github.com/prometheus/client_golang v1.16.0 // indirect + github.com/prometheus/client_model v0.4.0 // indirect + github.com/prometheus/common v0.42.0 // indirect + github.com/prometheus/procfs v0.10.1 // indirect + github.com/twmb/murmur3 v1.1.6 // indirect + go.opentelemetry.io/otel v1.16.0 // indirect + go.opentelemetry.io/otel/exporters/prometheus v0.39.0 // indirect + go.opentelemetry.io/otel/metric v1.16.0 // indirect + go.opentelemetry.io/otel/sdk v1.16.0 // indirect + go.opentelemetry.io/otel/sdk/metric v0.39.0 // indirect + go.opentelemetry.io/otel/trace v1.16.0 // indirect + golang.org/x/sys v0.13.0 // indirect + google.golang.org/protobuf v1.31.0 // indirect +) diff --git a/_examples/actor-helloworld/go.sum b/_examples/actor-helloworld/go.sum new file mode 100644 index 0000000000000000000000000000000000000000..72e1596d2e6bb1dc5da2329b876b42c528bf5f7a --- /dev/null +++ b/_examples/actor-helloworld/go.sum @@ -0,0 +1,100 @@ +github.com/Workiva/go-datastructures v1.1.1 h1:9G5u1UqKt6ABseAffHGNfbNQd7omRlWE5QaxNruzhE0= +github.com/Workiva/go-datastructures v1.1.1/go.mod h1:1yZL+zfsztete+ePzZz/Zb1/t5BnDuE2Ya2MMGhzP6A= +github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716 h1:SgyG4sXkrlalMoCfp20LiNPNhfJS7ez3opNdtihIxPc= +github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716/go.mod h1:/zSlF0T2ArAsTG6SVu8d8qlK+19jjudjA3wWsxnGFHg= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/lithammer/shortuuid/v4 v4.0.0 h1:QRbbVkfgNippHOS8PXDkti4NaWeyYfcBTHtw7k08o4c= +github.com/lithammer/shortuuid/v4 v4.0.0/go.mod h1:Zs8puNcrvf2rV9rTH51ZLLcj7ZXqQI3lv67aw4KiB1Y= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/orcaman/concurrent-map v1.0.0 h1:I/2A2XPCb4IuQWcQhBhSwGfiuybl/J0ev9HDbW65HOY= +github.com/orcaman/concurrent-map v1.0.0/go.mod h1:Lu3tH6HLW3feq74c2GC+jIMS/K2CFcDWnWD9XkenwhI= +github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= +github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= +github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= +github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= +github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= +github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= +github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/tinylib/msgp v1.1.5/go.mod h1:eQsjooMTnV42mHu917E26IogZ2930nFyBQdofk10Udg= +github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q= +github.com/twmb/murmur3 v1.1.6 h1:mqrRot1BRxm+Yct+vavLMou2/iJt0tNVTTC0QoIjaZg= +github.com/twmb/murmur3 v1.1.6/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s= +go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4= +go.opentelemetry.io/otel/exporters/prometheus v0.39.0 h1:whAaiHxOatgtKd+w0dOi//1KUxj3KoPINZdtDaDj3IA= +go.opentelemetry.io/otel/exporters/prometheus v0.39.0/go.mod h1:4jo5Q4CROlCpSPsXLhymi+LYrDXd2ObU5wbKayfZs7Y= +go.opentelemetry.io/otel/metric v1.16.0 h1:RbrpwVG1Hfv85LgnZ7+txXioPDoh6EdbZHo26Q3hqOo= +go.opentelemetry.io/otel/metric v1.16.0/go.mod h1:QE47cpOmkwipPiefDwo2wDzwJrlfxxNYodqc4xnGCo4= +go.opentelemetry.io/otel/sdk v1.16.0 h1:Z1Ok1YsijYL0CSJpHt4cS3wDDh7p572grzNrBMiMWgE= +go.opentelemetry.io/otel/sdk v1.16.0/go.mod h1:tMsIuKXuuIWPBAOrH+eHtvhTL+SntFtXF9QD68aP6p4= +go.opentelemetry.io/otel/sdk/metric v0.39.0 h1:Kun8i1eYf48kHH83RucG93ffz0zGV1sh46FAScOTuDI= +go.opentelemetry.io/otel/sdk/metric v0.39.0/go.mod h1:piDIRgjcK7u0HCL5pCA4e74qpK/jk3NiUoAHATVAmiI= +go.opentelemetry.io/otel/trace v1.16.0 h1:8JRpaObFoW0pxuVPapkgH8UhHQj+bJW8jJsCZEu5MQs= +go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/_examples/actor-helloworld/main.go b/_examples/actor-helloworld/main.go new file mode 100644 index 0000000000000000000000000000000000000000..6c0dd692cf8e824f2e534730b6c23024d1a630e5 --- /dev/null +++ b/_examples/actor-helloworld/main.go @@ -0,0 +1,29 @@ +package main + +import ( + "fmt" + + "gitee.com/simplexyz/simpleactor-go/actor" + console "github.com/asynkron/goconsole" +) + +type ( + hello struct{ Who string } + helloActor struct{} +) + +func (state *helloActor) Receive(context actor.Context) { + switch msg := context.Message().(type) { + case *hello: + fmt.Printf("Hello %v\n", msg.Who) + } +} + +func main() { + system := actor.NewActorSystem() + props := actor.PropsFromProducer(func() actor.Actor { return &helloActor{} }) + + pid := system.Root.Spawn(props) + system.Root.Send(pid, &hello{Who: "Roger"}) + _, _ = console.ReadLine() +} diff --git a/_examples/actor-inprocess-benchmark/go.mod b/_examples/actor-inprocess-benchmark/go.mod new file mode 100644 index 0000000000000000000000000000000000000000..3ab68abe767cc859750ee3ffa88278ef49514bda --- /dev/null +++ b/_examples/actor-inprocess-benchmark/go.mod @@ -0,0 +1,36 @@ +module inprocessbenchmark + +go 1.21 + +toolchain go1.21.2 + +replace gitee.com/simplexyz/simpleactor-go => ../.. + +require gitee.com/simplexyz/simpleactor-go v0.0.0-00010101000000-000000000000 + +require ( + github.com/Workiva/go-datastructures v1.1.1 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/emirpasic/gods v1.18.1 // indirect + github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/lithammer/shortuuid/v4 v4.0.0 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/orcaman/concurrent-map v1.0.0 // indirect + github.com/prometheus/client_golang v1.16.0 // indirect + github.com/prometheus/client_model v0.4.0 // indirect + github.com/prometheus/common v0.42.0 // indirect + github.com/prometheus/procfs v0.10.1 // indirect + github.com/twmb/murmur3 v1.1.6 // indirect + go.opentelemetry.io/otel v1.16.0 // indirect + go.opentelemetry.io/otel/exporters/prometheus v0.39.0 // indirect + go.opentelemetry.io/otel/metric v1.16.0 // indirect + go.opentelemetry.io/otel/sdk v1.16.0 // indirect + go.opentelemetry.io/otel/sdk/metric v0.39.0 // indirect + go.opentelemetry.io/otel/trace v1.16.0 // indirect + golang.org/x/sys v0.13.0 // indirect + google.golang.org/protobuf v1.31.0 // indirect +) diff --git a/_examples/actor-inprocess-benchmark/go.sum b/_examples/actor-inprocess-benchmark/go.sum new file mode 100644 index 0000000000000000000000000000000000000000..616a6b1575c165294ea88ea440da8596a33cce3f --- /dev/null +++ b/_examples/actor-inprocess-benchmark/go.sum @@ -0,0 +1,98 @@ +github.com/Workiva/go-datastructures v1.1.1 h1:9G5u1UqKt6ABseAffHGNfbNQd7omRlWE5QaxNruzhE0= +github.com/Workiva/go-datastructures v1.1.1/go.mod h1:1yZL+zfsztete+ePzZz/Zb1/t5BnDuE2Ya2MMGhzP6A= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/lithammer/shortuuid/v4 v4.0.0 h1:QRbbVkfgNippHOS8PXDkti4NaWeyYfcBTHtw7k08o4c= +github.com/lithammer/shortuuid/v4 v4.0.0/go.mod h1:Zs8puNcrvf2rV9rTH51ZLLcj7ZXqQI3lv67aw4KiB1Y= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/orcaman/concurrent-map v1.0.0 h1:I/2A2XPCb4IuQWcQhBhSwGfiuybl/J0ev9HDbW65HOY= +github.com/orcaman/concurrent-map v1.0.0/go.mod h1:Lu3tH6HLW3feq74c2GC+jIMS/K2CFcDWnWD9XkenwhI= +github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= +github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= +github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= +github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= +github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= +github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= +github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/tinylib/msgp v1.1.5/go.mod h1:eQsjooMTnV42mHu917E26IogZ2930nFyBQdofk10Udg= +github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q= +github.com/twmb/murmur3 v1.1.6 h1:mqrRot1BRxm+Yct+vavLMou2/iJt0tNVTTC0QoIjaZg= +github.com/twmb/murmur3 v1.1.6/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s= +go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4= +go.opentelemetry.io/otel/exporters/prometheus v0.39.0 h1:whAaiHxOatgtKd+w0dOi//1KUxj3KoPINZdtDaDj3IA= +go.opentelemetry.io/otel/exporters/prometheus v0.39.0/go.mod h1:4jo5Q4CROlCpSPsXLhymi+LYrDXd2ObU5wbKayfZs7Y= +go.opentelemetry.io/otel/metric v1.16.0 h1:RbrpwVG1Hfv85LgnZ7+txXioPDoh6EdbZHo26Q3hqOo= +go.opentelemetry.io/otel/metric v1.16.0/go.mod h1:QE47cpOmkwipPiefDwo2wDzwJrlfxxNYodqc4xnGCo4= +go.opentelemetry.io/otel/sdk v1.16.0 h1:Z1Ok1YsijYL0CSJpHt4cS3wDDh7p572grzNrBMiMWgE= +go.opentelemetry.io/otel/sdk v1.16.0/go.mod h1:tMsIuKXuuIWPBAOrH+eHtvhTL+SntFtXF9QD68aP6p4= +go.opentelemetry.io/otel/sdk/metric v0.39.0 h1:Kun8i1eYf48kHH83RucG93ffz0zGV1sh46FAScOTuDI= +go.opentelemetry.io/otel/sdk/metric v0.39.0/go.mod h1:piDIRgjcK7u0HCL5pCA4e74qpK/jk3NiUoAHATVAmiI= +go.opentelemetry.io/otel/trace v1.16.0 h1:8JRpaObFoW0pxuVPapkgH8UhHQj+bJW8jJsCZEu5MQs= +go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/_examples/actor-inprocess-benchmark/inprocessbenchmark b/_examples/actor-inprocess-benchmark/inprocessbenchmark new file mode 100644 index 0000000000000000000000000000000000000000..6df0bfa1f2265dad3b03d74c3a553185abaa8f44 Binary files /dev/null and b/_examples/actor-inprocess-benchmark/inprocessbenchmark differ diff --git a/_examples/actor-inprocess-benchmark/main.go b/_examples/actor-inprocess-benchmark/main.go new file mode 100644 index 0000000000000000000000000000000000000000..4513fb7fd9daa8ea693a8610c7ceda3836d4b075 --- /dev/null +++ b/_examples/actor-inprocess-benchmark/main.go @@ -0,0 +1,177 @@ +package main + +import ( + "flag" + "log" + "os" + "runtime" + "runtime/pprof" + "sync" + "time" + + "gitee.com/simplexyz/simpleactor-go/actor" +) + +type Msg struct { + Sender *actor.PID +} +type Start struct { + Sender *actor.PID +} + +type pingActor struct { + count int + wgStop *sync.WaitGroup + messageCount int + batch int + batchSize int +} + +func pongActor(context actor.Context) { + switch msg := context.Message().(type) { + case *Msg: + context.Send(msg.Sender, &Msg{Sender: context.Self()}) + } +} + +func (state *pingActor) sendBatch(context actor.Context, sender *actor.PID) bool { + if state.messageCount == 0 { + return false + } + + var m interface{} = &Msg{ + Sender: context.Self(), + } + + for i := 0; i < state.batchSize; i++ { + context.Send(sender, m) + } + + state.messageCount -= state.batchSize + state.batch = state.batchSize + return true +} + +func (state *pingActor) Receive(context actor.Context) { + switch msg := context.Message().(type) { + case *Start: + state.sendBatch(context, msg.Sender) + + case *Msg: + state.batch-- + if state.batch > 0 { + return + } + + if !state.sendBatch(context, msg.Sender) { + state.wgStop.Done() + } + } +} + +func newPingActor(stop *sync.WaitGroup, messageCount int, batchSize int) actor.Producer { + return func() actor.Actor { + return &pingActor{ + wgStop: stop, + messageCount: messageCount, + batchSize: batchSize, + } + } +} + +var ( + cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file") + blockProfile = flag.String("blockprof", "", "execute contention profiling and save results here") +) + +func main() { + flag.Parse() + if *cpuprofile != "" { + f, err := os.Create(*cpuprofile) + if err != nil { + log.Fatal(err) + } + pprof.StartCPUProfile(f) + defer pprof.StopCPUProfile() + } + + // Check for lock contention profiling + if *blockProfile != "" { + prof, err := os.Create(*blockProfile) + if err != nil { + log.Fatal(err) + } + runtime.SetBlockProfileRate(1) + defer func() { + pprof.Lookup("block").WriteTo(prof, 0) + }() + } + + // runtime.GOMAXPROCS(runtime.NumCPU()) + // runtime.GC() + + system := actor.NewActorSystem() + + var wg sync.WaitGroup + + messageCount := 1000000 + batchSize := 100 + tps := []int{300, 400, 500, 600, 700, 800, 900} + log.Println("Dispatcher Throughput Elapsed Time Messages per sec") + for _, tp := range tps { + + d := actor.NewDefaultDispatcher(tp) + + clientProps := actor. + PropsFromProducer(newPingActor(&wg, messageCount, batchSize), + actor.WithMailbox(actor.Bounded(batchSize+10)), + actor.WithDispatcher(d)) + rootContext := system.Root + + echoProps := actor. + PropsFromFunc(pongActor, + actor.WithMailbox(actor.Bounded(batchSize+10)), + actor.WithDispatcher(d)) + + clients := make([]*actor.PID, 0) + echos := make([]*actor.PID, 0) + clientCount := runtime.NumCPU() * 2 + for i := 0; i < clientCount; i++ { + client := rootContext.Spawn(clientProps) + echo := rootContext.Spawn(echoProps) + clients = append(clients, client) + echos = append(echos, echo) + wg.Add(1) + } + start := time.Now() + + for i := 0; i < clientCount; i++ { + client := clients[i] + echo := echos[i] + + rootContext.Send(client, &Start{ + Sender: echo, + }) + } + + wg.Wait() + elapsed := time.Since(start) + x := int(float32(messageCount*2*clientCount) / (float32(elapsed) / float32(time.Second))) + log.Printf(" %v %s %v", tp, elapsed, x) + for i := 0; i < clientCount; i++ { + client := clients[i] + rootContext.StopFuture(client).Wait() + echo := echos[i] + rootContext.StopFuture(echo).Wait() + } + runtime.GC() + time.Sleep(2 * time.Second) + } + + // f, err := os.Create("memprofile") + // if err != nil { + // log.Fatal(err) + // } + // pprof.WriteHeapProfile(f) + // f.Close() +} diff --git a/_examples/actor-jaegertracing/README.md b/_examples/actor-jaegertracing/README.md new file mode 100644 index 0000000000000000000000000000000000000000..606d229460260f31806ba0597d4ddd5cd3597c27 --- /dev/null +++ b/_examples/actor-jaegertracing/README.md @@ -0,0 +1,17 @@ +# Jeager Tracing / OpenTracing example + +To run the example an instance of Jaeger server is required running locally. The easiest way to run a jaeger server +instance is starting it using the included docker-compose file like this + +```bash +docker-compose -f ./examples/jaegertracing/docker-compose.yaml up -d +``` + +And the just run the example: + +```bash +go run ./examples/jaegertracing/main.go +``` + +After the test has run (and also during), traces can found using the Jaeger UI started at http://localhost:16686. + diff --git a/_examples/actor-jaegertracing/docker-compose.yaml b/_examples/actor-jaegertracing/docker-compose.yaml new file mode 100644 index 0000000000000000000000000000000000000000..15129cf00e2d70abbe917cea6c42547de0201dea --- /dev/null +++ b/_examples/actor-jaegertracing/docker-compose.yaml @@ -0,0 +1,14 @@ +version: '3' +services: + jaeger: + image: jaegertracing/all-in-one:1.7 + environment: + COLLECTOR_ZIPKIN_HTTP_PORT: 9411 + ports: + - '5775:5775/udp' + - '6831:6831/udp' + - '6832:6832/udp' + - '5778:5778' + - '16686:16686' + - '14268:14268' + - '9411:9411' \ No newline at end of file diff --git a/_examples/actor-jaegertracing/go.mod b/_examples/actor-jaegertracing/go.mod new file mode 100644 index 0000000000000000000000000000000000000000..c7eb4d6dbba88ab8a8337ed3edcab8d8b3e6bb9d --- /dev/null +++ b/_examples/actor-jaegertracing/go.mod @@ -0,0 +1,46 @@ +module jaegertracing + +go 1.21 + +toolchain go1.21.2 + +replace gitee.com/simplexyz/simpleactor-go => ../.. + +require ( + gitee.com/simplexyz/simpleactor-go v0.0.0-00010101000000-000000000000 + github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716 + github.com/uber/jaeger-client-go v2.25.0+incompatible + github.com/uber/jaeger-lib v2.4.0+incompatible +) + +require ( + github.com/HdrHistogram/hdrhistogram-go v1.1.0 // indirect + github.com/Workiva/go-datastructures v1.1.1 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/emirpasic/gods v1.18.1 // indirect + github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/lithammer/shortuuid/v4 v4.0.0 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/opentracing/opentracing-go v1.2.0 // indirect + github.com/orcaman/concurrent-map v1.0.0 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/prometheus/client_golang v1.16.0 // indirect + github.com/prometheus/client_model v0.4.0 // indirect + github.com/prometheus/common v0.42.0 // indirect + github.com/prometheus/procfs v0.10.1 // indirect + github.com/serialx/hashring v0.0.0-20200727003509-22c0c7ab6b1b // indirect + github.com/twmb/murmur3 v1.1.6 // indirect + go.opentelemetry.io/otel v1.16.0 // indirect + go.opentelemetry.io/otel/exporters/prometheus v0.39.0 // indirect + go.opentelemetry.io/otel/metric v1.16.0 // indirect + go.opentelemetry.io/otel/sdk v1.16.0 // indirect + go.opentelemetry.io/otel/sdk/metric v0.39.0 // indirect + go.opentelemetry.io/otel/trace v1.16.0 // indirect + go.uber.org/atomic v1.9.0 // indirect + golang.org/x/sys v0.13.0 // indirect + google.golang.org/protobuf v1.31.0 // indirect +) diff --git a/_examples/actor-jaegertracing/go.sum b/_examples/actor-jaegertracing/go.sum new file mode 100644 index 0000000000000000000000000000000000000000..24501d8bdfdc10dfa527bc83e3ad8caf924c0119 --- /dev/null +++ b/_examples/actor-jaegertracing/go.sum @@ -0,0 +1,149 @@ +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/HdrHistogram/hdrhistogram-go v1.1.0 h1:6dpdDPTRoo78HxAJ6T1HfMiKSnqhgRRqzCuPshRkQ7I= +github.com/HdrHistogram/hdrhistogram-go v1.1.0/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= +github.com/Workiva/go-datastructures v1.1.1 h1:9G5u1UqKt6ABseAffHGNfbNQd7omRlWE5QaxNruzhE0= +github.com/Workiva/go-datastructures v1.1.1/go.mod h1:1yZL+zfsztete+ePzZz/Zb1/t5BnDuE2Ya2MMGhzP6A= +github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= +github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716 h1:SgyG4sXkrlalMoCfp20LiNPNhfJS7ez3opNdtihIxPc= +github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716/go.mod h1:/zSlF0T2ArAsTG6SVu8d8qlK+19jjudjA3wWsxnGFHg= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= +github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/lithammer/shortuuid/v4 v4.0.0 h1:QRbbVkfgNippHOS8PXDkti4NaWeyYfcBTHtw7k08o4c= +github.com/lithammer/shortuuid/v4 v4.0.0/go.mod h1:Zs8puNcrvf2rV9rTH51ZLLcj7ZXqQI3lv67aw4KiB1Y= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= +github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= +github.com/orcaman/concurrent-map v1.0.0 h1:I/2A2XPCb4IuQWcQhBhSwGfiuybl/J0ev9HDbW65HOY= +github.com/orcaman/concurrent-map v1.0.0/go.mod h1:Lu3tH6HLW3feq74c2GC+jIMS/K2CFcDWnWD9XkenwhI= +github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= +github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= +github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= +github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= +github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= +github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= +github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= +github.com/serialx/hashring v0.0.0-20200727003509-22c0c7ab6b1b h1:h+3JX2VoWTFuyQEo87pStk/a99dzIO1mM9KxIyLPGTU= +github.com/serialx/hashring v0.0.0-20200727003509-22c0c7ab6b1b/go.mod h1:/yeG0My1xr/u+HZrFQ1tOQQQQrOawfyMUH13ai5brBc= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/tinylib/msgp v1.1.5/go.mod h1:eQsjooMTnV42mHu917E26IogZ2930nFyBQdofk10Udg= +github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q= +github.com/twmb/murmur3 v1.1.6 h1:mqrRot1BRxm+Yct+vavLMou2/iJt0tNVTTC0QoIjaZg= +github.com/twmb/murmur3 v1.1.6/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= +github.com/uber/jaeger-client-go v2.25.0+incompatible h1:IxcNZ7WRY1Y3G4poYlx24szfsn/3LvK9QHCq9oQw8+U= +github.com/uber/jaeger-client-go v2.25.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= +github.com/uber/jaeger-lib v2.4.0+incompatible h1:fY7QsGQWiCt8pajv4r7JEvmATdCVaWxXbjwyYwsNaLQ= +github.com/uber/jaeger-lib v2.4.0+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s= +go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4= +go.opentelemetry.io/otel/exporters/prometheus v0.39.0 h1:whAaiHxOatgtKd+w0dOi//1KUxj3KoPINZdtDaDj3IA= +go.opentelemetry.io/otel/exporters/prometheus v0.39.0/go.mod h1:4jo5Q4CROlCpSPsXLhymi+LYrDXd2ObU5wbKayfZs7Y= +go.opentelemetry.io/otel/metric v1.16.0 h1:RbrpwVG1Hfv85LgnZ7+txXioPDoh6EdbZHo26Q3hqOo= +go.opentelemetry.io/otel/metric v1.16.0/go.mod h1:QE47cpOmkwipPiefDwo2wDzwJrlfxxNYodqc4xnGCo4= +go.opentelemetry.io/otel/sdk v1.16.0 h1:Z1Ok1YsijYL0CSJpHt4cS3wDDh7p572grzNrBMiMWgE= +go.opentelemetry.io/otel/sdk v1.16.0/go.mod h1:tMsIuKXuuIWPBAOrH+eHtvhTL+SntFtXF9QD68aP6p4= +go.opentelemetry.io/otel/sdk/metric v0.39.0 h1:Kun8i1eYf48kHH83RucG93ffz0zGV1sh46FAScOTuDI= +go.opentelemetry.io/otel/sdk/metric v0.39.0/go.mod h1:piDIRgjcK7u0HCL5pCA4e74qpK/jk3NiUoAHATVAmiI= +go.opentelemetry.io/otel/trace v1.16.0 h1:8JRpaObFoW0pxuVPapkgH8UhHQj+bJW8jJsCZEu5MQs= +go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0= +go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= +go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= +gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= +gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= +gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/_examples/actor-jaegertracing/main.go b/_examples/actor-jaegertracing/main.go new file mode 100644 index 0000000000000000000000000000000000000000..5967ffae38535cd670e1455d89c28fca7e868968 --- /dev/null +++ b/_examples/actor-jaegertracing/main.go @@ -0,0 +1,99 @@ +package main + +import ( + "fmt" + "io" + "math/rand" + "time" + + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/actor/middleware/opentracing" + console "github.com/asynkron/goconsole" + jaegercfg "github.com/uber/jaeger-client-go/config" + jaegerlog "github.com/uber/jaeger-client-go/log" + "github.com/uber/jaeger-lib/metrics" +) + +func main() { + jaegerCloser := initJaeger() + defer jaegerCloser.Close() + + system := actor.NewActorSystem() + rootContext := actor. + NewRootContext(system, nil). + WithSpawnMiddleware(opentracing.TracingMiddleware()) + + pid := rootContext.SpawnPrefix(createProps(5), "root") + for i := 0; i < 3; i++ { + rootContext.RequestFuture(pid, &request{i}, 10*time.Second).Wait() + } + _, _ = console.ReadLine() +} + +func initJaeger() io.Closer { + // Sample configuration for testing. Use constant sampling to sample every trace + // and enable LogSpan to log every span via configured Logger. + cfg := jaegercfg.Configuration{ + Sampler: &jaegercfg.SamplerConfig{ + Type: jaeger.SamplerTypeConst, + Param: 1, + }, + Reporter: &jaegercfg.ReporterConfig{ + LogSpans: true, + }, + } + + // Example logger and metrics factory. Use github.com/uber/jaeger-client-go/log + // and github.com/uber/jaeger-lib/metrics respectively to bind to real logging and metrics + // frameworks. + jLogger := jaegerlog.StdLogger + jMetricsFactory := metrics.NullFactory + + // Initialize tracer with a logger and a metrics factory + closer, err := cfg.InitGlobalTracer( + "jaeger-test", + jaegercfg.Logger(jLogger), + jaegercfg.Metrics(jMetricsFactory), + ) + if err != nil { + // log.Printf("Could not initialize jaeger tracer: %s", err.Error()) + panic(fmt.Sprintf("Could not initialize jaeger tracer: %s", err.Error())) + } + return closer +} + +func createProps(levels int) *actor.Props { + if levels <= 1 { + sleep := time.Duration(rand.Intn(5000)) + + return actor.PropsFromFunc(func(c actor.Context) { + switch msg := c.Message().(type) { + case *request: + time.Sleep(sleep * time.Millisecond) + if c.Sender() != nil { + c.Respond(&response{i: msg.i}) + } + } + }) + } + + var childs []*actor.PID + return actor.PropsFromFunc(func(c actor.Context) { + switch c.Message().(type) { + case *actor.Started: + for i := 0; i < 3; i++ { + childs = append(childs, c.Spawn(createProps(levels-1))) + } + case *request: + c.Forward(childs[rand.Intn(len(childs))]) + } + }) +} + +type request struct { + i int +} + +type response struct { + i int +} diff --git a/_examples/actor-jaegertracing/router/main.go b/_examples/actor-jaegertracing/router/main.go new file mode 100644 index 0000000000000000000000000000000000000000..6efca4e03650435f00813f52a8f7fc0a95205085 --- /dev/null +++ b/_examples/actor-jaegertracing/router/main.go @@ -0,0 +1,95 @@ +package main + +import ( + "fmt" + "io" + "math/rand" + "time" + + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/actor/middleware/opentracing" + "gitee.com/simplexyz/simpleactor-go/router" + jaegercfg "github.com/uber/jaeger-client-go/config" + jaegerlog "github.com/uber/jaeger-client-go/log" + "github.com/uber/jaeger-lib/metrics" +) + +func main() { + actorSystem := actor.NewActorSystem() + jaegerCloser := initJaeger() + defer jaegerCloser.Close() + + rootContext := actor. + NewRootContext(actorSystem, nil). + WithSpawnMiddleware(opentracing.TracingMiddleware()) + + pid := rootContext.SpawnPrefix(createProps(router.NewRoundRobinPool, 3), "root") + for i := 0; i < 3; i++ { + _ = rootContext.RequestFuture(pid, &request{i}, 10*time.Second).Wait() + } + _, _ = console.ReadLine() +} + +func initJaeger() io.Closer { + // Sample configuration for testing. Use constant sampling to sample every trace + // and enable LogSpan to log every span via configured Logger. + cfg := jaegercfg.Configuration{ + Sampler: &jaegercfg.SamplerConfig{ + Type: jaeger.SamplerTypeConst, + Param: 1, + }, + Reporter: &jaegercfg.ReporterConfig{ + LogSpans: true, + }, + } + + // Example logger and metrics factory. Use github.com/uber/jaeger-client-go/log + // and github.com/uber/jaeger-lib/metrics respectively to bind to real logging and metrics + // frameworks. + jLogger := jaegerlog.StdLogger + jMetricsFactory := metrics.NullFactory + + // Initialize tracer with a logger and a metrics factory + closer, err := cfg.InitGlobalTracer( + "jaeger-test", + jaegercfg.Logger(jLogger), + jaegercfg.Metrics(jMetricsFactory), + ) + if err != nil { + // log.Printf("Could not initialize jaeger tracer: %s", err.Error()) + panic(fmt.Sprintf("Could not initialize jaeger tracer: %s", err.Error())) + } + return closer +} + +func createProps(routerFunc func(size int) *actor.Props, levels int) *actor.Props { + if levels == 1 { + sleep := time.Duration(rand.Intn(5000)) + return routerFunc(3).WithFunc(func(c actor.Context) { + switch msg := c.Message().(type) { + case *request: + time.Sleep(sleep * time.Millisecond) + if c.Sender() != nil { + c.Respond(&response{i: msg.i}) + } + } + }) + } + var childPID *actor.PID + return routerFunc(5).WithFunc(func(c actor.Context) { + switch c.Message().(type) { + case *actor.Started: + childPID = c.Spawn(createProps(routerFunc, levels-1)) + case *request: + c.Forward(childPID) + } + }) +} + +type request struct { + i int +} + +type response struct { + i int +} diff --git a/_examples/actor-lifecycleevents/go.mod b/_examples/actor-lifecycleevents/go.mod new file mode 100644 index 0000000000000000000000000000000000000000..fa3f8e37d9a9f3b477f331d651205b5d4e34de63 --- /dev/null +++ b/_examples/actor-lifecycleevents/go.mod @@ -0,0 +1,39 @@ +module lifecycleevents + +go 1.21 + +toolchain go1.21.2 + +replace gitee.com/simplexyz/simpleactor-go => ../.. + +require ( + gitee.com/simplexyz/simpleactor-go v0.0.0-00010101000000-000000000000 + github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716 +) + +require ( + github.com/Workiva/go-datastructures v1.1.1 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/emirpasic/gods v1.18.1 // indirect + github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/lithammer/shortuuid/v4 v4.0.0 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/orcaman/concurrent-map v1.0.0 // indirect + github.com/prometheus/client_golang v1.16.0 // indirect + github.com/prometheus/client_model v0.4.0 // indirect + github.com/prometheus/common v0.42.0 // indirect + github.com/prometheus/procfs v0.10.1 // indirect + github.com/twmb/murmur3 v1.1.6 // indirect + go.opentelemetry.io/otel v1.16.0 // indirect + go.opentelemetry.io/otel/exporters/prometheus v0.39.0 // indirect + go.opentelemetry.io/otel/metric v1.16.0 // indirect + go.opentelemetry.io/otel/sdk v1.16.0 // indirect + go.opentelemetry.io/otel/sdk/metric v0.39.0 // indirect + go.opentelemetry.io/otel/trace v1.16.0 // indirect + golang.org/x/sys v0.13.0 // indirect + google.golang.org/protobuf v1.31.0 // indirect +) diff --git a/_examples/actor-lifecycleevents/go.sum b/_examples/actor-lifecycleevents/go.sum new file mode 100644 index 0000000000000000000000000000000000000000..72e1596d2e6bb1dc5da2329b876b42c528bf5f7a --- /dev/null +++ b/_examples/actor-lifecycleevents/go.sum @@ -0,0 +1,100 @@ +github.com/Workiva/go-datastructures v1.1.1 h1:9G5u1UqKt6ABseAffHGNfbNQd7omRlWE5QaxNruzhE0= +github.com/Workiva/go-datastructures v1.1.1/go.mod h1:1yZL+zfsztete+ePzZz/Zb1/t5BnDuE2Ya2MMGhzP6A= +github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716 h1:SgyG4sXkrlalMoCfp20LiNPNhfJS7ez3opNdtihIxPc= +github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716/go.mod h1:/zSlF0T2ArAsTG6SVu8d8qlK+19jjudjA3wWsxnGFHg= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/lithammer/shortuuid/v4 v4.0.0 h1:QRbbVkfgNippHOS8PXDkti4NaWeyYfcBTHtw7k08o4c= +github.com/lithammer/shortuuid/v4 v4.0.0/go.mod h1:Zs8puNcrvf2rV9rTH51ZLLcj7ZXqQI3lv67aw4KiB1Y= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/orcaman/concurrent-map v1.0.0 h1:I/2A2XPCb4IuQWcQhBhSwGfiuybl/J0ev9HDbW65HOY= +github.com/orcaman/concurrent-map v1.0.0/go.mod h1:Lu3tH6HLW3feq74c2GC+jIMS/K2CFcDWnWD9XkenwhI= +github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= +github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= +github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= +github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= +github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= +github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= +github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/tinylib/msgp v1.1.5/go.mod h1:eQsjooMTnV42mHu917E26IogZ2930nFyBQdofk10Udg= +github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q= +github.com/twmb/murmur3 v1.1.6 h1:mqrRot1BRxm+Yct+vavLMou2/iJt0tNVTTC0QoIjaZg= +github.com/twmb/murmur3 v1.1.6/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s= +go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4= +go.opentelemetry.io/otel/exporters/prometheus v0.39.0 h1:whAaiHxOatgtKd+w0dOi//1KUxj3KoPINZdtDaDj3IA= +go.opentelemetry.io/otel/exporters/prometheus v0.39.0/go.mod h1:4jo5Q4CROlCpSPsXLhymi+LYrDXd2ObU5wbKayfZs7Y= +go.opentelemetry.io/otel/metric v1.16.0 h1:RbrpwVG1Hfv85LgnZ7+txXioPDoh6EdbZHo26Q3hqOo= +go.opentelemetry.io/otel/metric v1.16.0/go.mod h1:QE47cpOmkwipPiefDwo2wDzwJrlfxxNYodqc4xnGCo4= +go.opentelemetry.io/otel/sdk v1.16.0 h1:Z1Ok1YsijYL0CSJpHt4cS3wDDh7p572grzNrBMiMWgE= +go.opentelemetry.io/otel/sdk v1.16.0/go.mod h1:tMsIuKXuuIWPBAOrH+eHtvhTL+SntFtXF9QD68aP6p4= +go.opentelemetry.io/otel/sdk/metric v0.39.0 h1:Kun8i1eYf48kHH83RucG93ffz0zGV1sh46FAScOTuDI= +go.opentelemetry.io/otel/sdk/metric v0.39.0/go.mod h1:piDIRgjcK7u0HCL5pCA4e74qpK/jk3NiUoAHATVAmiI= +go.opentelemetry.io/otel/trace v1.16.0 h1:8JRpaObFoW0pxuVPapkgH8UhHQj+bJW8jJsCZEu5MQs= +go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/_examples/actor-lifecycleevents/main.go b/_examples/actor-lifecycleevents/main.go new file mode 100644 index 0000000000000000000000000000000000000000..3630fc34b010d8c51ea8a2b3e9e0f93eeb54ad0a --- /dev/null +++ b/_examples/actor-lifecycleevents/main.go @@ -0,0 +1,46 @@ +package main + +import ( + "fmt" + "time" + + "gitee.com/simplexyz/simpleactor-go/actor" + console "github.com/asynkron/goconsole" +) + +type ( + hello struct{ Who string } + helloActor struct{} +) + +func (state *helloActor) Receive(context actor.Context) { + switch msg := context.Message().(type) { + case *actor.Started: + fmt.Println("Started, initialize actor here") + case *actor.Stopping: + fmt.Println("Stopping, actor is about shut down") + case *actor.Stopped: + fmt.Println("Stopped, actor and its children are stopped") + case *actor.Restarting: + fmt.Println("Restarting, actor is about restart") + case *hello: + fmt.Printf("Hello %v\n", msg.Who) + panic("test") + } +} + +func main() { + system := actor.NewActorSystem() + props := actor.PropsFromProducer(func() actor.Actor { return &helloActor{} }) + pid := system.Root.Spawn(props) + system.Root.Send(pid, &hello{Who: "Roger"}) + + // why wait? + // Stop is a system message and is not processed through the user message mailbox + // thus, it will be handled _before_ any user message + // we only do this to show the correct order of events in the console + time.Sleep(1 * time.Second) + system.Root.Stop(pid) + + _, _ = console.ReadLine() +} diff --git a/_examples/actor-mailbox-middleware/go.mod b/_examples/actor-mailbox-middleware/go.mod new file mode 100644 index 0000000000000000000000000000000000000000..1b4ccbe5f402bd6e7ba69b673eaa4784cc098962 --- /dev/null +++ b/_examples/actor-mailbox-middleware/go.mod @@ -0,0 +1,39 @@ +module mailboxstats + +go 1.21 + +toolchain go1.21.2 + +replace gitee.com/simplexyz/simpleactor-go => ../.. + +require ( + gitee.com/simplexyz/simpleactor-go v0.0.0-00010101000000-000000000000 + github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716 +) + +require ( + github.com/Workiva/go-datastructures v1.1.1 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/emirpasic/gods v1.18.1 // indirect + github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/lithammer/shortuuid/v4 v4.0.0 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/orcaman/concurrent-map v1.0.0 // indirect + github.com/prometheus/client_golang v1.16.0 // indirect + github.com/prometheus/client_model v0.4.0 // indirect + github.com/prometheus/common v0.42.0 // indirect + github.com/prometheus/procfs v0.10.1 // indirect + github.com/twmb/murmur3 v1.1.6 // indirect + go.opentelemetry.io/otel v1.16.0 // indirect + go.opentelemetry.io/otel/exporters/prometheus v0.39.0 // indirect + go.opentelemetry.io/otel/metric v1.16.0 // indirect + go.opentelemetry.io/otel/sdk v1.16.0 // indirect + go.opentelemetry.io/otel/sdk/metric v0.39.0 // indirect + go.opentelemetry.io/otel/trace v1.16.0 // indirect + golang.org/x/sys v0.13.0 // indirect + google.golang.org/protobuf v1.31.0 // indirect +) diff --git a/_examples/actor-mailbox-middleware/go.sum b/_examples/actor-mailbox-middleware/go.sum new file mode 100644 index 0000000000000000000000000000000000000000..72e1596d2e6bb1dc5da2329b876b42c528bf5f7a --- /dev/null +++ b/_examples/actor-mailbox-middleware/go.sum @@ -0,0 +1,100 @@ +github.com/Workiva/go-datastructures v1.1.1 h1:9G5u1UqKt6ABseAffHGNfbNQd7omRlWE5QaxNruzhE0= +github.com/Workiva/go-datastructures v1.1.1/go.mod h1:1yZL+zfsztete+ePzZz/Zb1/t5BnDuE2Ya2MMGhzP6A= +github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716 h1:SgyG4sXkrlalMoCfp20LiNPNhfJS7ez3opNdtihIxPc= +github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716/go.mod h1:/zSlF0T2ArAsTG6SVu8d8qlK+19jjudjA3wWsxnGFHg= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/lithammer/shortuuid/v4 v4.0.0 h1:QRbbVkfgNippHOS8PXDkti4NaWeyYfcBTHtw7k08o4c= +github.com/lithammer/shortuuid/v4 v4.0.0/go.mod h1:Zs8puNcrvf2rV9rTH51ZLLcj7ZXqQI3lv67aw4KiB1Y= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/orcaman/concurrent-map v1.0.0 h1:I/2A2XPCb4IuQWcQhBhSwGfiuybl/J0ev9HDbW65HOY= +github.com/orcaman/concurrent-map v1.0.0/go.mod h1:Lu3tH6HLW3feq74c2GC+jIMS/K2CFcDWnWD9XkenwhI= +github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= +github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= +github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= +github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= +github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= +github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= +github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/tinylib/msgp v1.1.5/go.mod h1:eQsjooMTnV42mHu917E26IogZ2930nFyBQdofk10Udg= +github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q= +github.com/twmb/murmur3 v1.1.6 h1:mqrRot1BRxm+Yct+vavLMou2/iJt0tNVTTC0QoIjaZg= +github.com/twmb/murmur3 v1.1.6/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s= +go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4= +go.opentelemetry.io/otel/exporters/prometheus v0.39.0 h1:whAaiHxOatgtKd+w0dOi//1KUxj3KoPINZdtDaDj3IA= +go.opentelemetry.io/otel/exporters/prometheus v0.39.0/go.mod h1:4jo5Q4CROlCpSPsXLhymi+LYrDXd2ObU5wbKayfZs7Y= +go.opentelemetry.io/otel/metric v1.16.0 h1:RbrpwVG1Hfv85LgnZ7+txXioPDoh6EdbZHo26Q3hqOo= +go.opentelemetry.io/otel/metric v1.16.0/go.mod h1:QE47cpOmkwipPiefDwo2wDzwJrlfxxNYodqc4xnGCo4= +go.opentelemetry.io/otel/sdk v1.16.0 h1:Z1Ok1YsijYL0CSJpHt4cS3wDDh7p572grzNrBMiMWgE= +go.opentelemetry.io/otel/sdk v1.16.0/go.mod h1:tMsIuKXuuIWPBAOrH+eHtvhTL+SntFtXF9QD68aP6p4= +go.opentelemetry.io/otel/sdk/metric v0.39.0 h1:Kun8i1eYf48kHH83RucG93ffz0zGV1sh46FAScOTuDI= +go.opentelemetry.io/otel/sdk/metric v0.39.0/go.mod h1:piDIRgjcK7u0HCL5pCA4e74qpK/jk3NiUoAHATVAmiI= +go.opentelemetry.io/otel/trace v1.16.0 h1:8JRpaObFoW0pxuVPapkgH8UhHQj+bJW8jJsCZEu5MQs= +go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/_examples/actor-mailbox-middleware/main.go b/_examples/actor-mailbox-middleware/main.go new file mode 100644 index 0000000000000000000000000000000000000000..d7163e4d14d5018fe7b97293ad368b4873145fb4 --- /dev/null +++ b/_examples/actor-mailbox-middleware/main.go @@ -0,0 +1,36 @@ +package main + +import ( + "log" + + "gitee.com/simplexyz/simpleactor-go/actor" + console "github.com/asynkron/goconsole" +) + +type mailboxLogger struct{} + +func (m *mailboxLogger) MailboxStarted() { + log.Print("Mailbox started") +} + +func (m *mailboxLogger) MessagePosted(msg interface{}) { + log.Printf("Message posted %v", msg) +} + +func (m *mailboxLogger) MessageReceived(msg interface{}) { + log.Printf("Message received %v", msg) +} + +func (m *mailboxLogger) MailboxEmpty() { + log.Print("No more messages") +} + +func main() { + system := actor.NewActorSystem() + rootContext := system.Root + props := actor.PropsFromFunc(func(ctx actor.Context) { + }, actor.WithMailbox(actor.Unbounded(&mailboxLogger{}))) + pid := rootContext.Spawn(props) + rootContext.Send(pid, "Hello") + _, _ = console.ReadLine() +} diff --git a/_examples/actor-messagebatch/go.mod b/_examples/actor-messagebatch/go.mod new file mode 100644 index 0000000000000000000000000000000000000000..b21ebed16e55c716d88b2e5ffa64a25f755704d2 --- /dev/null +++ b/_examples/actor-messagebatch/go.mod @@ -0,0 +1,39 @@ +module messagebatch + +go 1.21 + +toolchain go1.21.2 + +replace gitee.com/simplexyz/simpleactor-go => ../.. + +require ( + gitee.com/simplexyz/simpleactor-go v0.0.0-00010101000000-000000000000 + github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716 +) + +require ( + github.com/Workiva/go-datastructures v1.1.1 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/emirpasic/gods v1.18.1 // indirect + github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/lithammer/shortuuid/v4 v4.0.0 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/orcaman/concurrent-map v1.0.0 // indirect + github.com/prometheus/client_golang v1.16.0 // indirect + github.com/prometheus/client_model v0.4.0 // indirect + github.com/prometheus/common v0.42.0 // indirect + github.com/prometheus/procfs v0.10.1 // indirect + github.com/twmb/murmur3 v1.1.6 // indirect + go.opentelemetry.io/otel v1.16.0 // indirect + go.opentelemetry.io/otel/exporters/prometheus v0.39.0 // indirect + go.opentelemetry.io/otel/metric v1.16.0 // indirect + go.opentelemetry.io/otel/sdk v1.16.0 // indirect + go.opentelemetry.io/otel/sdk/metric v0.39.0 // indirect + go.opentelemetry.io/otel/trace v1.16.0 // indirect + golang.org/x/sys v0.13.0 // indirect + google.golang.org/protobuf v1.31.0 // indirect +) diff --git a/_examples/actor-messagebatch/go.sum b/_examples/actor-messagebatch/go.sum new file mode 100644 index 0000000000000000000000000000000000000000..72e1596d2e6bb1dc5da2329b876b42c528bf5f7a --- /dev/null +++ b/_examples/actor-messagebatch/go.sum @@ -0,0 +1,100 @@ +github.com/Workiva/go-datastructures v1.1.1 h1:9G5u1UqKt6ABseAffHGNfbNQd7omRlWE5QaxNruzhE0= +github.com/Workiva/go-datastructures v1.1.1/go.mod h1:1yZL+zfsztete+ePzZz/Zb1/t5BnDuE2Ya2MMGhzP6A= +github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716 h1:SgyG4sXkrlalMoCfp20LiNPNhfJS7ez3opNdtihIxPc= +github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716/go.mod h1:/zSlF0T2ArAsTG6SVu8d8qlK+19jjudjA3wWsxnGFHg= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/lithammer/shortuuid/v4 v4.0.0 h1:QRbbVkfgNippHOS8PXDkti4NaWeyYfcBTHtw7k08o4c= +github.com/lithammer/shortuuid/v4 v4.0.0/go.mod h1:Zs8puNcrvf2rV9rTH51ZLLcj7ZXqQI3lv67aw4KiB1Y= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/orcaman/concurrent-map v1.0.0 h1:I/2A2XPCb4IuQWcQhBhSwGfiuybl/J0ev9HDbW65HOY= +github.com/orcaman/concurrent-map v1.0.0/go.mod h1:Lu3tH6HLW3feq74c2GC+jIMS/K2CFcDWnWD9XkenwhI= +github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= +github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= +github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= +github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= +github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= +github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= +github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/tinylib/msgp v1.1.5/go.mod h1:eQsjooMTnV42mHu917E26IogZ2930nFyBQdofk10Udg= +github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q= +github.com/twmb/murmur3 v1.1.6 h1:mqrRot1BRxm+Yct+vavLMou2/iJt0tNVTTC0QoIjaZg= +github.com/twmb/murmur3 v1.1.6/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s= +go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4= +go.opentelemetry.io/otel/exporters/prometheus v0.39.0 h1:whAaiHxOatgtKd+w0dOi//1KUxj3KoPINZdtDaDj3IA= +go.opentelemetry.io/otel/exporters/prometheus v0.39.0/go.mod h1:4jo5Q4CROlCpSPsXLhymi+LYrDXd2ObU5wbKayfZs7Y= +go.opentelemetry.io/otel/metric v1.16.0 h1:RbrpwVG1Hfv85LgnZ7+txXioPDoh6EdbZHo26Q3hqOo= +go.opentelemetry.io/otel/metric v1.16.0/go.mod h1:QE47cpOmkwipPiefDwo2wDzwJrlfxxNYodqc4xnGCo4= +go.opentelemetry.io/otel/sdk v1.16.0 h1:Z1Ok1YsijYL0CSJpHt4cS3wDDh7p572grzNrBMiMWgE= +go.opentelemetry.io/otel/sdk v1.16.0/go.mod h1:tMsIuKXuuIWPBAOrH+eHtvhTL+SntFtXF9QD68aP6p4= +go.opentelemetry.io/otel/sdk/metric v0.39.0 h1:Kun8i1eYf48kHH83RucG93ffz0zGV1sh46FAScOTuDI= +go.opentelemetry.io/otel/sdk/metric v0.39.0/go.mod h1:piDIRgjcK7u0HCL5pCA4e74qpK/jk3NiUoAHATVAmiI= +go.opentelemetry.io/otel/trace v1.16.0 h1:8JRpaObFoW0pxuVPapkgH8UhHQj+bJW8jJsCZEu5MQs= +go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/_examples/actor-messagebatch/main.go b/_examples/actor-messagebatch/main.go new file mode 100644 index 0000000000000000000000000000000000000000..edd162e6ab61c0c9a76bfeb78f7312b955e4d439 --- /dev/null +++ b/_examples/actor-messagebatch/main.go @@ -0,0 +1,46 @@ +package main + +import ( + "fmt" + "strconv" + + "gitee.com/simplexyz/simpleactor-go/actor" + console "github.com/asynkron/goconsole" +) + +// MessageBatch is a message that is sent to the actor and unpacks its payload in the mailbox +// This allows you to group messages together and send them as a single message +// while processing them as individual messages +// this is used by the Cluster PubSub feature to send a batch of messages and then Ack to the entire batch +// In that specific case, both MessageBatch and AutoRespond are required + +type myMessageBatch struct { + messages []interface{} +} + +func (m myMessageBatch) GetMessages() []interface{} { + return m.messages +} + +func main() { + system := actor.NewActorSystem() + props := actor.PropsFromFunc(func(ctx actor.Context) { + if m, ok := ctx.Message().(string); ok { + fmt.Println(m) + } + }) + pid := system.Root.Spawn(props) + + messages := make([]interface{}, 0) + + for i := 0; i < 100; i++ { + messages = append(messages, "Hello"+strconv.Itoa(i)) + } + + batch := &myMessageBatch{ + messages: messages, + } + system.Root.Send(pid, batch) + + console.ReadLine() +} diff --git a/_examples/actor-mixins/go.mod b/_examples/actor-mixins/go.mod new file mode 100644 index 0000000000000000000000000000000000000000..5a7e1ca4b9f87c2b109f05ffb647d96762103974 --- /dev/null +++ b/_examples/actor-mixins/go.mod @@ -0,0 +1,39 @@ +module mixins + +go 1.21 + +toolchain go1.21.2 + +replace gitee.com/simplexyz/simpleactor-go => ../.. + +require ( + gitee.com/simplexyz/simpleactor-go v0.0.0-00010101000000-000000000000 + github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716 +) + +require ( + github.com/Workiva/go-datastructures v1.1.1 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/emirpasic/gods v1.18.1 // indirect + github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/lithammer/shortuuid/v4 v4.0.0 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/orcaman/concurrent-map v1.0.0 // indirect + github.com/prometheus/client_golang v1.16.0 // indirect + github.com/prometheus/client_model v0.4.0 // indirect + github.com/prometheus/common v0.42.0 // indirect + github.com/prometheus/procfs v0.10.1 // indirect + github.com/twmb/murmur3 v1.1.6 // indirect + go.opentelemetry.io/otel v1.16.0 // indirect + go.opentelemetry.io/otel/exporters/prometheus v0.39.0 // indirect + go.opentelemetry.io/otel/metric v1.16.0 // indirect + go.opentelemetry.io/otel/sdk v1.16.0 // indirect + go.opentelemetry.io/otel/sdk/metric v0.39.0 // indirect + go.opentelemetry.io/otel/trace v1.16.0 // indirect + golang.org/x/sys v0.13.0 // indirect + google.golang.org/protobuf v1.31.0 // indirect +) diff --git a/_examples/actor-mixins/go.sum b/_examples/actor-mixins/go.sum new file mode 100644 index 0000000000000000000000000000000000000000..72e1596d2e6bb1dc5da2329b876b42c528bf5f7a --- /dev/null +++ b/_examples/actor-mixins/go.sum @@ -0,0 +1,100 @@ +github.com/Workiva/go-datastructures v1.1.1 h1:9G5u1UqKt6ABseAffHGNfbNQd7omRlWE5QaxNruzhE0= +github.com/Workiva/go-datastructures v1.1.1/go.mod h1:1yZL+zfsztete+ePzZz/Zb1/t5BnDuE2Ya2MMGhzP6A= +github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716 h1:SgyG4sXkrlalMoCfp20LiNPNhfJS7ez3opNdtihIxPc= +github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716/go.mod h1:/zSlF0T2ArAsTG6SVu8d8qlK+19jjudjA3wWsxnGFHg= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/lithammer/shortuuid/v4 v4.0.0 h1:QRbbVkfgNippHOS8PXDkti4NaWeyYfcBTHtw7k08o4c= +github.com/lithammer/shortuuid/v4 v4.0.0/go.mod h1:Zs8puNcrvf2rV9rTH51ZLLcj7ZXqQI3lv67aw4KiB1Y= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/orcaman/concurrent-map v1.0.0 h1:I/2A2XPCb4IuQWcQhBhSwGfiuybl/J0ev9HDbW65HOY= +github.com/orcaman/concurrent-map v1.0.0/go.mod h1:Lu3tH6HLW3feq74c2GC+jIMS/K2CFcDWnWD9XkenwhI= +github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= +github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= +github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= +github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= +github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= +github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= +github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/tinylib/msgp v1.1.5/go.mod h1:eQsjooMTnV42mHu917E26IogZ2930nFyBQdofk10Udg= +github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q= +github.com/twmb/murmur3 v1.1.6 h1:mqrRot1BRxm+Yct+vavLMou2/iJt0tNVTTC0QoIjaZg= +github.com/twmb/murmur3 v1.1.6/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s= +go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4= +go.opentelemetry.io/otel/exporters/prometheus v0.39.0 h1:whAaiHxOatgtKd+w0dOi//1KUxj3KoPINZdtDaDj3IA= +go.opentelemetry.io/otel/exporters/prometheus v0.39.0/go.mod h1:4jo5Q4CROlCpSPsXLhymi+LYrDXd2ObU5wbKayfZs7Y= +go.opentelemetry.io/otel/metric v1.16.0 h1:RbrpwVG1Hfv85LgnZ7+txXioPDoh6EdbZHo26Q3hqOo= +go.opentelemetry.io/otel/metric v1.16.0/go.mod h1:QE47cpOmkwipPiefDwo2wDzwJrlfxxNYodqc4xnGCo4= +go.opentelemetry.io/otel/sdk v1.16.0 h1:Z1Ok1YsijYL0CSJpHt4cS3wDDh7p572grzNrBMiMWgE= +go.opentelemetry.io/otel/sdk v1.16.0/go.mod h1:tMsIuKXuuIWPBAOrH+eHtvhTL+SntFtXF9QD68aP6p4= +go.opentelemetry.io/otel/sdk/metric v0.39.0 h1:Kun8i1eYf48kHH83RucG93ffz0zGV1sh46FAScOTuDI= +go.opentelemetry.io/otel/sdk/metric v0.39.0/go.mod h1:piDIRgjcK7u0HCL5pCA4e74qpK/jk3NiUoAHATVAmiI= +go.opentelemetry.io/otel/trace v1.16.0 h1:8JRpaObFoW0pxuVPapkgH8UhHQj+bJW8jJsCZEu5MQs= +go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/_examples/actor-mixins/main.go b/_examples/actor-mixins/main.go new file mode 100644 index 0000000000000000000000000000000000000000..9994bb91ca7a4bbeff62b21df4745c5421173fc3 --- /dev/null +++ b/_examples/actor-mixins/main.go @@ -0,0 +1,58 @@ +package main + +import ( + "fmt" + + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/actor/middleware" + "gitee.com/simplexyz/simpleactor-go/plugin" + console "github.com/asynkron/goconsole" +) + +type myActor struct { + NameAwareHolder +} + +func (state *myActor) Receive(context actor.Context) { + switch context.Message().(type) { + case *actor.Started: + // this actor have been initialized by the receive pipeline + fmt.Printf("My name is %v\n", state.name) + } +} + +type NameAware interface { + SetName(name string) +} + +type NameAwareHolder struct { + name string +} + +func (state *NameAwareHolder) SetName(name string) { + state.name = name +} + +type NamerPlugin struct{} + +func (p *NamerPlugin) OnStart(ctx actor.ReceiverContext) { + if p, ok := ctx.Actor().(NameAware); ok { + p.SetName("Proto.Actor") + } +} +func (p *NamerPlugin) OnOtherMessage(ctx actor.ReceiverContext, env *actor.MessageEnvelope) {} + +func main() { + system := actor.NewActorSystem() + rootContext := system.Root + props := actor. + PropsFromProducer(func() actor.Actor { return &myActor{} }, + actor.WithReceiverMiddleware( + plugin.Use(&NamerPlugin{}), + middleware.Logger, + )) + + pid := rootContext.Spawn(props) + rootContext.Send(pid, "bar") + _, _ = console.ReadLine() +} diff --git a/_examples/actor-receive-middleware/go.mod b/_examples/actor-receive-middleware/go.mod new file mode 100644 index 0000000000000000000000000000000000000000..5213e04aa83117cf600c983a1784c5b41500c52d --- /dev/null +++ b/_examples/actor-receive-middleware/go.mod @@ -0,0 +1,39 @@ +module receivepipeline + +go 1.21 + +toolchain go1.21.2 + +replace gitee.com/simplexyz/simpleactor-go => ../.. + +require ( + gitee.com/simplexyz/simpleactor-go v0.0.0-00010101000000-000000000000 + github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716 +) + +require ( + github.com/Workiva/go-datastructures v1.1.1 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/emirpasic/gods v1.18.1 // indirect + github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/lithammer/shortuuid/v4 v4.0.0 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/orcaman/concurrent-map v1.0.0 // indirect + github.com/prometheus/client_golang v1.16.0 // indirect + github.com/prometheus/client_model v0.4.0 // indirect + github.com/prometheus/common v0.42.0 // indirect + github.com/prometheus/procfs v0.10.1 // indirect + github.com/twmb/murmur3 v1.1.6 // indirect + go.opentelemetry.io/otel v1.16.0 // indirect + go.opentelemetry.io/otel/exporters/prometheus v0.39.0 // indirect + go.opentelemetry.io/otel/metric v1.16.0 // indirect + go.opentelemetry.io/otel/sdk v1.16.0 // indirect + go.opentelemetry.io/otel/sdk/metric v0.39.0 // indirect + go.opentelemetry.io/otel/trace v1.16.0 // indirect + golang.org/x/sys v0.13.0 // indirect + google.golang.org/protobuf v1.31.0 // indirect +) diff --git a/_examples/actor-receive-middleware/go.sum b/_examples/actor-receive-middleware/go.sum new file mode 100644 index 0000000000000000000000000000000000000000..72e1596d2e6bb1dc5da2329b876b42c528bf5f7a --- /dev/null +++ b/_examples/actor-receive-middleware/go.sum @@ -0,0 +1,100 @@ +github.com/Workiva/go-datastructures v1.1.1 h1:9G5u1UqKt6ABseAffHGNfbNQd7omRlWE5QaxNruzhE0= +github.com/Workiva/go-datastructures v1.1.1/go.mod h1:1yZL+zfsztete+ePzZz/Zb1/t5BnDuE2Ya2MMGhzP6A= +github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716 h1:SgyG4sXkrlalMoCfp20LiNPNhfJS7ez3opNdtihIxPc= +github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716/go.mod h1:/zSlF0T2ArAsTG6SVu8d8qlK+19jjudjA3wWsxnGFHg= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/lithammer/shortuuid/v4 v4.0.0 h1:QRbbVkfgNippHOS8PXDkti4NaWeyYfcBTHtw7k08o4c= +github.com/lithammer/shortuuid/v4 v4.0.0/go.mod h1:Zs8puNcrvf2rV9rTH51ZLLcj7ZXqQI3lv67aw4KiB1Y= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/orcaman/concurrent-map v1.0.0 h1:I/2A2XPCb4IuQWcQhBhSwGfiuybl/J0ev9HDbW65HOY= +github.com/orcaman/concurrent-map v1.0.0/go.mod h1:Lu3tH6HLW3feq74c2GC+jIMS/K2CFcDWnWD9XkenwhI= +github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= +github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= +github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= +github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= +github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= +github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= +github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/tinylib/msgp v1.1.5/go.mod h1:eQsjooMTnV42mHu917E26IogZ2930nFyBQdofk10Udg= +github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q= +github.com/twmb/murmur3 v1.1.6 h1:mqrRot1BRxm+Yct+vavLMou2/iJt0tNVTTC0QoIjaZg= +github.com/twmb/murmur3 v1.1.6/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s= +go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4= +go.opentelemetry.io/otel/exporters/prometheus v0.39.0 h1:whAaiHxOatgtKd+w0dOi//1KUxj3KoPINZdtDaDj3IA= +go.opentelemetry.io/otel/exporters/prometheus v0.39.0/go.mod h1:4jo5Q4CROlCpSPsXLhymi+LYrDXd2ObU5wbKayfZs7Y= +go.opentelemetry.io/otel/metric v1.16.0 h1:RbrpwVG1Hfv85LgnZ7+txXioPDoh6EdbZHo26Q3hqOo= +go.opentelemetry.io/otel/metric v1.16.0/go.mod h1:QE47cpOmkwipPiefDwo2wDzwJrlfxxNYodqc4xnGCo4= +go.opentelemetry.io/otel/sdk v1.16.0 h1:Z1Ok1YsijYL0CSJpHt4cS3wDDh7p572grzNrBMiMWgE= +go.opentelemetry.io/otel/sdk v1.16.0/go.mod h1:tMsIuKXuuIWPBAOrH+eHtvhTL+SntFtXF9QD68aP6p4= +go.opentelemetry.io/otel/sdk/metric v0.39.0 h1:Kun8i1eYf48kHH83RucG93ffz0zGV1sh46FAScOTuDI= +go.opentelemetry.io/otel/sdk/metric v0.39.0/go.mod h1:piDIRgjcK7u0HCL5pCA4e74qpK/jk3NiUoAHATVAmiI= +go.opentelemetry.io/otel/trace v1.16.0 h1:8JRpaObFoW0pxuVPapkgH8UhHQj+bJW8jJsCZEu5MQs= +go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/_examples/actor-receive-middleware/main.go b/_examples/actor-receive-middleware/main.go new file mode 100644 index 0000000000000000000000000000000000000000..56a5cdada71f6a3cc8d6efe6c291c1762816d137 --- /dev/null +++ b/_examples/actor-receive-middleware/main.go @@ -0,0 +1,27 @@ +package main + +import ( + "fmt" + + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/actor/middleware" + console "github.com/asynkron/goconsole" +) + +type hello struct{ Who string } + +func receive(context actor.Context) { + switch msg := context.Message().(type) { + case *hello: + fmt.Printf("Hello %v\n", msg.Who) + } +} + +func main() { + system := actor.NewActorSystem() + rootContext := system.Root + props := actor.PropsFromFunc(receive, actor.WithReceiverMiddleware(middleware.Logger)) + pid := rootContext.Spawn(props) + rootContext.Send(pid, &hello{Who: "Roger"}) + _, _ = console.ReadLine() +} diff --git a/_examples/actor-receive-timeout/go.mod b/_examples/actor-receive-timeout/go.mod new file mode 100644 index 0000000000000000000000000000000000000000..624f92d6baa81e24dadd5939477766823bcaa9ab --- /dev/null +++ b/_examples/actor-receive-timeout/go.mod @@ -0,0 +1,39 @@ +module receivetimeout + +go 1.21 + +toolchain go1.21.2 + +replace gitee.com/simplexyz/simpleactor-go => ../.. + +require ( + gitee.com/simplexyz/simpleactor-go v0.0.0-00010101000000-000000000000 + github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716 +) + +require ( + github.com/Workiva/go-datastructures v1.1.1 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/emirpasic/gods v1.18.1 // indirect + github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/lithammer/shortuuid/v4 v4.0.0 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/orcaman/concurrent-map v1.0.0 // indirect + github.com/prometheus/client_golang v1.16.0 // indirect + github.com/prometheus/client_model v0.4.0 // indirect + github.com/prometheus/common v0.42.0 // indirect + github.com/prometheus/procfs v0.10.1 // indirect + github.com/twmb/murmur3 v1.1.6 // indirect + go.opentelemetry.io/otel v1.16.0 // indirect + go.opentelemetry.io/otel/exporters/prometheus v0.39.0 // indirect + go.opentelemetry.io/otel/metric v1.16.0 // indirect + go.opentelemetry.io/otel/sdk v1.16.0 // indirect + go.opentelemetry.io/otel/sdk/metric v0.39.0 // indirect + go.opentelemetry.io/otel/trace v1.16.0 // indirect + golang.org/x/sys v0.13.0 // indirect + google.golang.org/protobuf v1.31.0 // indirect +) diff --git a/_examples/actor-receive-timeout/go.sum b/_examples/actor-receive-timeout/go.sum new file mode 100644 index 0000000000000000000000000000000000000000..72e1596d2e6bb1dc5da2329b876b42c528bf5f7a --- /dev/null +++ b/_examples/actor-receive-timeout/go.sum @@ -0,0 +1,100 @@ +github.com/Workiva/go-datastructures v1.1.1 h1:9G5u1UqKt6ABseAffHGNfbNQd7omRlWE5QaxNruzhE0= +github.com/Workiva/go-datastructures v1.1.1/go.mod h1:1yZL+zfsztete+ePzZz/Zb1/t5BnDuE2Ya2MMGhzP6A= +github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716 h1:SgyG4sXkrlalMoCfp20LiNPNhfJS7ez3opNdtihIxPc= +github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716/go.mod h1:/zSlF0T2ArAsTG6SVu8d8qlK+19jjudjA3wWsxnGFHg= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/lithammer/shortuuid/v4 v4.0.0 h1:QRbbVkfgNippHOS8PXDkti4NaWeyYfcBTHtw7k08o4c= +github.com/lithammer/shortuuid/v4 v4.0.0/go.mod h1:Zs8puNcrvf2rV9rTH51ZLLcj7ZXqQI3lv67aw4KiB1Y= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/orcaman/concurrent-map v1.0.0 h1:I/2A2XPCb4IuQWcQhBhSwGfiuybl/J0ev9HDbW65HOY= +github.com/orcaman/concurrent-map v1.0.0/go.mod h1:Lu3tH6HLW3feq74c2GC+jIMS/K2CFcDWnWD9XkenwhI= +github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= +github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= +github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= +github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= +github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= +github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= +github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/tinylib/msgp v1.1.5/go.mod h1:eQsjooMTnV42mHu917E26IogZ2930nFyBQdofk10Udg= +github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q= +github.com/twmb/murmur3 v1.1.6 h1:mqrRot1BRxm+Yct+vavLMou2/iJt0tNVTTC0QoIjaZg= +github.com/twmb/murmur3 v1.1.6/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s= +go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4= +go.opentelemetry.io/otel/exporters/prometheus v0.39.0 h1:whAaiHxOatgtKd+w0dOi//1KUxj3KoPINZdtDaDj3IA= +go.opentelemetry.io/otel/exporters/prometheus v0.39.0/go.mod h1:4jo5Q4CROlCpSPsXLhymi+LYrDXd2ObU5wbKayfZs7Y= +go.opentelemetry.io/otel/metric v1.16.0 h1:RbrpwVG1Hfv85LgnZ7+txXioPDoh6EdbZHo26Q3hqOo= +go.opentelemetry.io/otel/metric v1.16.0/go.mod h1:QE47cpOmkwipPiefDwo2wDzwJrlfxxNYodqc4xnGCo4= +go.opentelemetry.io/otel/sdk v1.16.0 h1:Z1Ok1YsijYL0CSJpHt4cS3wDDh7p572grzNrBMiMWgE= +go.opentelemetry.io/otel/sdk v1.16.0/go.mod h1:tMsIuKXuuIWPBAOrH+eHtvhTL+SntFtXF9QD68aP6p4= +go.opentelemetry.io/otel/sdk/metric v0.39.0 h1:Kun8i1eYf48kHH83RucG93ffz0zGV1sh46FAScOTuDI= +go.opentelemetry.io/otel/sdk/metric v0.39.0/go.mod h1:piDIRgjcK7u0HCL5pCA4e74qpK/jk3NiUoAHATVAmiI= +go.opentelemetry.io/otel/trace v1.16.0 h1:8JRpaObFoW0pxuVPapkgH8UhHQj+bJW8jJsCZEu5MQs= +go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/_examples/actor-receive-timeout/main.go b/_examples/actor-receive-timeout/main.go new file mode 100644 index 0000000000000000000000000000000000000000..188ca45473edb6d39e859565d9b66ee471d7cd40 --- /dev/null +++ b/_examples/actor-receive-timeout/main.go @@ -0,0 +1,67 @@ +package main + +import ( + "fmt" + "log" + "time" + + "gitee.com/simplexyz/simpleactor-go/actor" + console "github.com/asynkron/goconsole" +) + +type NoInfluence string + +func (NoInfluence) NotInfluenceReceiveTimeout() {} + +func main() { + log.Println("Receive timeout test") + + system := actor.NewActorSystem() + c := 0 + + rootContext := system.Root + props := actor.PropsFromFunc(func(context actor.Context) { + switch msg := context.Message().(type) { + case *actor.Started: + context.SetReceiveTimeout(1 * time.Second) + + case *actor.ReceiveTimeout: + c++ + log.Printf("ReceiveTimeout: %d", c) + + case string: + log.Printf("received '%s'", msg) + if msg == "cancel" { + fmt.Println("Cancelling") + context.CancelReceiveTimeout() + } + + case NoInfluence: + log.Println("received a no-influence message") + + } + }) + + pid := rootContext.Spawn(props) + for i := 0; i < 6; i++ { + rootContext.Send(pid, "hello") + time.Sleep(500 * time.Millisecond) + } + + log.Println("hit [return] to send no-influence messages") + _, _ = console.ReadLine() + + for i := 0; i < 6; i++ { + rootContext.Send(pid, NoInfluence("hello")) + time.Sleep(500 * time.Millisecond) + } + + log.Println("hit [return] to send a message to cancel the timeout") + _, _ = console.ReadLine() + rootContext.Send(pid, "cancel") + + log.Println("hit [return] to finish") + _, _ = console.ReadLine() + + rootContext.Stop(pid) +} diff --git a/_examples/actor-request-response/go.mod b/_examples/actor-request-response/go.mod new file mode 100644 index 0000000000000000000000000000000000000000..37e95d65dcc814dd8757756a92c59d3c3547b51a --- /dev/null +++ b/_examples/actor-request-response/go.mod @@ -0,0 +1,39 @@ +module requestresponse + +go 1.21 + +toolchain go1.21.2 + +replace gitee.com/simplexyz/simpleactor-go => ../.. + +require ( + gitee.com/simplexyz/simpleactor-go v0.0.0-00010101000000-000000000000 + github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716 +) + +require ( + github.com/Workiva/go-datastructures v1.1.1 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/emirpasic/gods v1.18.1 // indirect + github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/lithammer/shortuuid/v4 v4.0.0 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/orcaman/concurrent-map v1.0.0 // indirect + github.com/prometheus/client_golang v1.16.0 // indirect + github.com/prometheus/client_model v0.4.0 // indirect + github.com/prometheus/common v0.42.0 // indirect + github.com/prometheus/procfs v0.10.1 // indirect + github.com/twmb/murmur3 v1.1.6 // indirect + go.opentelemetry.io/otel v1.16.0 // indirect + go.opentelemetry.io/otel/exporters/prometheus v0.39.0 // indirect + go.opentelemetry.io/otel/metric v1.16.0 // indirect + go.opentelemetry.io/otel/sdk v1.16.0 // indirect + go.opentelemetry.io/otel/sdk/metric v0.39.0 // indirect + go.opentelemetry.io/otel/trace v1.16.0 // indirect + golang.org/x/sys v0.13.0 // indirect + google.golang.org/protobuf v1.31.0 // indirect +) diff --git a/_examples/actor-request-response/go.sum b/_examples/actor-request-response/go.sum new file mode 100644 index 0000000000000000000000000000000000000000..72e1596d2e6bb1dc5da2329b876b42c528bf5f7a --- /dev/null +++ b/_examples/actor-request-response/go.sum @@ -0,0 +1,100 @@ +github.com/Workiva/go-datastructures v1.1.1 h1:9G5u1UqKt6ABseAffHGNfbNQd7omRlWE5QaxNruzhE0= +github.com/Workiva/go-datastructures v1.1.1/go.mod h1:1yZL+zfsztete+ePzZz/Zb1/t5BnDuE2Ya2MMGhzP6A= +github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716 h1:SgyG4sXkrlalMoCfp20LiNPNhfJS7ez3opNdtihIxPc= +github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716/go.mod h1:/zSlF0T2ArAsTG6SVu8d8qlK+19jjudjA3wWsxnGFHg= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/lithammer/shortuuid/v4 v4.0.0 h1:QRbbVkfgNippHOS8PXDkti4NaWeyYfcBTHtw7k08o4c= +github.com/lithammer/shortuuid/v4 v4.0.0/go.mod h1:Zs8puNcrvf2rV9rTH51ZLLcj7ZXqQI3lv67aw4KiB1Y= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/orcaman/concurrent-map v1.0.0 h1:I/2A2XPCb4IuQWcQhBhSwGfiuybl/J0ev9HDbW65HOY= +github.com/orcaman/concurrent-map v1.0.0/go.mod h1:Lu3tH6HLW3feq74c2GC+jIMS/K2CFcDWnWD9XkenwhI= +github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= +github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= +github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= +github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= +github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= +github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= +github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/tinylib/msgp v1.1.5/go.mod h1:eQsjooMTnV42mHu917E26IogZ2930nFyBQdofk10Udg= +github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q= +github.com/twmb/murmur3 v1.1.6 h1:mqrRot1BRxm+Yct+vavLMou2/iJt0tNVTTC0QoIjaZg= +github.com/twmb/murmur3 v1.1.6/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s= +go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4= +go.opentelemetry.io/otel/exporters/prometheus v0.39.0 h1:whAaiHxOatgtKd+w0dOi//1KUxj3KoPINZdtDaDj3IA= +go.opentelemetry.io/otel/exporters/prometheus v0.39.0/go.mod h1:4jo5Q4CROlCpSPsXLhymi+LYrDXd2ObU5wbKayfZs7Y= +go.opentelemetry.io/otel/metric v1.16.0 h1:RbrpwVG1Hfv85LgnZ7+txXioPDoh6EdbZHo26Q3hqOo= +go.opentelemetry.io/otel/metric v1.16.0/go.mod h1:QE47cpOmkwipPiefDwo2wDzwJrlfxxNYodqc4xnGCo4= +go.opentelemetry.io/otel/sdk v1.16.0 h1:Z1Ok1YsijYL0CSJpHt4cS3wDDh7p572grzNrBMiMWgE= +go.opentelemetry.io/otel/sdk v1.16.0/go.mod h1:tMsIuKXuuIWPBAOrH+eHtvhTL+SntFtXF9QD68aP6p4= +go.opentelemetry.io/otel/sdk/metric v0.39.0 h1:Kun8i1eYf48kHH83RucG93ffz0zGV1sh46FAScOTuDI= +go.opentelemetry.io/otel/sdk/metric v0.39.0/go.mod h1:piDIRgjcK7u0HCL5pCA4e74qpK/jk3NiUoAHATVAmiI= +go.opentelemetry.io/otel/trace v1.16.0 h1:8JRpaObFoW0pxuVPapkgH8UhHQj+bJW8jJsCZEu5MQs= +go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/_examples/actor-request-response/main.go b/_examples/actor-request-response/main.go new file mode 100644 index 0000000000000000000000000000000000000000..d1925728dfe297a93aa055baaabac8e501038154 --- /dev/null +++ b/_examples/actor-request-response/main.go @@ -0,0 +1,29 @@ +package main + +import ( + "fmt" + "time" + + "gitee.com/simplexyz/simpleactor-go/actor" + console "github.com/asynkron/goconsole" +) + +type Hello struct{ Who string } + +func Receive(context actor.Context) { + switch msg := context.Message().(type) { + case Hello: + context.Respond("Hello " + msg.Who) + } +} + +func main() { + system := actor.NewActorSystem() + rootContext := system.Root + props := actor.PropsFromFunc(Receive) + pid := rootContext.Spawn(props) + result, _ := rootContext.RequestFuture(pid, Hello{Who: "Roger"}, 30*time.Second).Result() // await result + + fmt.Println(result) + _, _ = console.ReadLine() +} diff --git a/_examples/actor-setbehavior/README.MD b/_examples/actor-setbehavior/README.MD new file mode 100644 index 0000000000000000000000000000000000000000..c1124f73a3b210f7a93b0bacf45b315b9f0605e6 --- /dev/null +++ b/_examples/actor-setbehavior/README.MD @@ -0,0 +1,68 @@ +# Switchable Behaviors + +Actors have the power to switch their behaviors at any point in time. This is usually referred as *becoming something*, +as in *the actor becomes busy* or *the actor becomes connected*. + +This is accomplished by replacing the method that handles messages inside the actor using `SetBehavior` +or `PushBehavior`. +These methods accept a delegate that will handle the next messages until you decide to replace it again. + +This is a powerful concept that is behind other features like Finite State Machines. + +> **Note:**
When you change the actor behavior, the new behaviour will take effect for all subsequent messages +> until the behaviour is changed again. The current message will continue processing with the existing behaviour. +> You can use Stashing to reprocess the current message with the new behavior. + +## API + +The API to change behaviors is available to the actor instance is very simple: + +* `Become` - Replaces the message handler with the specified delegate; +* `BecomeStacked` - Adds the specified message handler to the top of the behavior stack, while maintaining the previous + ones; +* `UnbecomeStacked` - Reverts to the previous message handler from the stack (only works with PushBehavior); + +```go +... + +type Hello struct{ Who string } +type SetBehaviorActor struct { + behavior actor.Behavior +} + +func (state *SetBehaviorActor) Receive(context actor.Context) { + state.behavior.Receive(context) +} + +func (state *SetBehaviorActor) One(context actor.Context) { + switch msg := context.Message().(type) { + case Hello: + fmt.Printf("Hello %v\n", msg.Who) + state.behavior.Become(state.Other) + } +} + +func (state *SetBehaviorActor) Other(context actor.Context) { + switch msg := context.Message().(type) { + case Hello: + fmt.Printf("%v, ey we are now handling messages in another behavior", msg.Who) + } +} + +func NewSetBehaviorActor() actor.Actor { + act := &SetBehaviorActor{ + behavior: actor.NewBehavior(), + } + act.behavior.Become(act.One) + return act +} + +func main() { + rootContext := actor.EmptyRootContext + props := actor.PropsFromProducer(NewSetBehaviorActor) + pid, _ := rootContext.Spawn(props) + rootContext.Send(pid, Hello{Who: "Roger"}) + rootContext.Send(pid, Hello{Who: "Roger"}) + console.ReadLine() +} +``` \ No newline at end of file diff --git a/_examples/actor-setbehavior/go.mod b/_examples/actor-setbehavior/go.mod new file mode 100644 index 0000000000000000000000000000000000000000..e6516063935085696db10ed5cc19832da42c505c --- /dev/null +++ b/_examples/actor-setbehavior/go.mod @@ -0,0 +1,39 @@ +module setbehavior + +go 1.21 + +toolchain go1.21.2 + +replace gitee.com/simplexyz/simpleactor-go => ../.. + +require ( + gitee.com/simplexyz/simpleactor-go v0.0.0-00010101000000-000000000000 + github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716 +) + +require ( + github.com/Workiva/go-datastructures v1.1.1 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/emirpasic/gods v1.18.1 // indirect + github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/lithammer/shortuuid/v4 v4.0.0 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/orcaman/concurrent-map v1.0.0 // indirect + github.com/prometheus/client_golang v1.16.0 // indirect + github.com/prometheus/client_model v0.4.0 // indirect + github.com/prometheus/common v0.42.0 // indirect + github.com/prometheus/procfs v0.10.1 // indirect + github.com/twmb/murmur3 v1.1.6 // indirect + go.opentelemetry.io/otel v1.16.0 // indirect + go.opentelemetry.io/otel/exporters/prometheus v0.39.0 // indirect + go.opentelemetry.io/otel/metric v1.16.0 // indirect + go.opentelemetry.io/otel/sdk v1.16.0 // indirect + go.opentelemetry.io/otel/sdk/metric v0.39.0 // indirect + go.opentelemetry.io/otel/trace v1.16.0 // indirect + golang.org/x/sys v0.13.0 // indirect + google.golang.org/protobuf v1.31.0 // indirect +) diff --git a/_examples/actor-setbehavior/go.sum b/_examples/actor-setbehavior/go.sum new file mode 100644 index 0000000000000000000000000000000000000000..72e1596d2e6bb1dc5da2329b876b42c528bf5f7a --- /dev/null +++ b/_examples/actor-setbehavior/go.sum @@ -0,0 +1,100 @@ +github.com/Workiva/go-datastructures v1.1.1 h1:9G5u1UqKt6ABseAffHGNfbNQd7omRlWE5QaxNruzhE0= +github.com/Workiva/go-datastructures v1.1.1/go.mod h1:1yZL+zfsztete+ePzZz/Zb1/t5BnDuE2Ya2MMGhzP6A= +github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716 h1:SgyG4sXkrlalMoCfp20LiNPNhfJS7ez3opNdtihIxPc= +github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716/go.mod h1:/zSlF0T2ArAsTG6SVu8d8qlK+19jjudjA3wWsxnGFHg= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/lithammer/shortuuid/v4 v4.0.0 h1:QRbbVkfgNippHOS8PXDkti4NaWeyYfcBTHtw7k08o4c= +github.com/lithammer/shortuuid/v4 v4.0.0/go.mod h1:Zs8puNcrvf2rV9rTH51ZLLcj7ZXqQI3lv67aw4KiB1Y= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/orcaman/concurrent-map v1.0.0 h1:I/2A2XPCb4IuQWcQhBhSwGfiuybl/J0ev9HDbW65HOY= +github.com/orcaman/concurrent-map v1.0.0/go.mod h1:Lu3tH6HLW3feq74c2GC+jIMS/K2CFcDWnWD9XkenwhI= +github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= +github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= +github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= +github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= +github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= +github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= +github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/tinylib/msgp v1.1.5/go.mod h1:eQsjooMTnV42mHu917E26IogZ2930nFyBQdofk10Udg= +github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q= +github.com/twmb/murmur3 v1.1.6 h1:mqrRot1BRxm+Yct+vavLMou2/iJt0tNVTTC0QoIjaZg= +github.com/twmb/murmur3 v1.1.6/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s= +go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4= +go.opentelemetry.io/otel/exporters/prometheus v0.39.0 h1:whAaiHxOatgtKd+w0dOi//1KUxj3KoPINZdtDaDj3IA= +go.opentelemetry.io/otel/exporters/prometheus v0.39.0/go.mod h1:4jo5Q4CROlCpSPsXLhymi+LYrDXd2ObU5wbKayfZs7Y= +go.opentelemetry.io/otel/metric v1.16.0 h1:RbrpwVG1Hfv85LgnZ7+txXioPDoh6EdbZHo26Q3hqOo= +go.opentelemetry.io/otel/metric v1.16.0/go.mod h1:QE47cpOmkwipPiefDwo2wDzwJrlfxxNYodqc4xnGCo4= +go.opentelemetry.io/otel/sdk v1.16.0 h1:Z1Ok1YsijYL0CSJpHt4cS3wDDh7p572grzNrBMiMWgE= +go.opentelemetry.io/otel/sdk v1.16.0/go.mod h1:tMsIuKXuuIWPBAOrH+eHtvhTL+SntFtXF9QD68aP6p4= +go.opentelemetry.io/otel/sdk/metric v0.39.0 h1:Kun8i1eYf48kHH83RucG93ffz0zGV1sh46FAScOTuDI= +go.opentelemetry.io/otel/sdk/metric v0.39.0/go.mod h1:piDIRgjcK7u0HCL5pCA4e74qpK/jk3NiUoAHATVAmiI= +go.opentelemetry.io/otel/trace v1.16.0 h1:8JRpaObFoW0pxuVPapkgH8UhHQj+bJW8jJsCZEu5MQs= +go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/_examples/actor-setbehavior/main.go b/_examples/actor-setbehavior/main.go new file mode 100644 index 0000000000000000000000000000000000000000..009cd065cef4700b0b440bf178c8451ce2f8ba34 --- /dev/null +++ b/_examples/actor-setbehavior/main.go @@ -0,0 +1,53 @@ +package main + +import ( + "fmt" + + console "github.com/asynkron/goconsole" + + "gitee.com/simplexyz/simpleactor-go/actor" +) + +type ( + Hello struct{ Who string } + SetBehaviorActor struct { + behavior actor.Behavior + } +) + +func (state *SetBehaviorActor) Receive(context actor.Context) { + state.behavior.Receive(context) +} + +func (state *SetBehaviorActor) One(context actor.Context) { + switch msg := context.Message().(type) { + case Hello: + fmt.Printf("Hello %v\n", msg.Who) + state.behavior.Become(state.Other) + } +} + +func (state *SetBehaviorActor) Other(context actor.Context) { + switch msg := context.Message().(type) { + case Hello: + fmt.Printf("%v, ey we are now handling messages in another behavior", msg.Who) + } +} + +func NewSetBehaviorActor() actor.Actor { + act := &SetBehaviorActor{ + behavior: actor.NewBehavior(), + } + act.behavior.Become(act.One) + return act +} + +func main() { + system := actor.NewActorSystem() + rootContext := system.Root + props := actor.PropsFromProducer(NewSetBehaviorActor) + pid := rootContext.Spawn(props) + rootContext.Send(pid, Hello{Who: "Roger"}) + rootContext.Send(pid, Hello{Who: "Roger"}) + _, _ = console.ReadLine() +} diff --git a/_examples/actor-spawn-benchmark/go.mod b/_examples/actor-spawn-benchmark/go.mod new file mode 100644 index 0000000000000000000000000000000000000000..c415defeb33a062204b21323f9b4b0286a372128 --- /dev/null +++ b/_examples/actor-spawn-benchmark/go.mod @@ -0,0 +1,36 @@ +module spawnbenchmark + +go 1.21 + +toolchain go1.21.2 + +replace gitee.com/simplexyz/simpleactor-go => ../.. + +require gitee.com/simplexyz/simpleactor-go v0.0.0-00010101000000-000000000000 + +require ( + github.com/Workiva/go-datastructures v1.1.1 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/emirpasic/gods v1.18.1 // indirect + github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/lithammer/shortuuid/v4 v4.0.0 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/orcaman/concurrent-map v1.0.0 // indirect + github.com/prometheus/client_golang v1.16.0 // indirect + github.com/prometheus/client_model v0.4.0 // indirect + github.com/prometheus/common v0.42.0 // indirect + github.com/prometheus/procfs v0.10.1 // indirect + github.com/twmb/murmur3 v1.1.6 // indirect + go.opentelemetry.io/otel v1.16.0 // indirect + go.opentelemetry.io/otel/exporters/prometheus v0.39.0 // indirect + go.opentelemetry.io/otel/metric v1.16.0 // indirect + go.opentelemetry.io/otel/sdk v1.16.0 // indirect + go.opentelemetry.io/otel/sdk/metric v0.39.0 // indirect + go.opentelemetry.io/otel/trace v1.16.0 // indirect + golang.org/x/sys v0.13.0 // indirect + google.golang.org/protobuf v1.31.0 // indirect +) diff --git a/_examples/actor-spawn-benchmark/go.sum b/_examples/actor-spawn-benchmark/go.sum new file mode 100644 index 0000000000000000000000000000000000000000..616a6b1575c165294ea88ea440da8596a33cce3f --- /dev/null +++ b/_examples/actor-spawn-benchmark/go.sum @@ -0,0 +1,98 @@ +github.com/Workiva/go-datastructures v1.1.1 h1:9G5u1UqKt6ABseAffHGNfbNQd7omRlWE5QaxNruzhE0= +github.com/Workiva/go-datastructures v1.1.1/go.mod h1:1yZL+zfsztete+ePzZz/Zb1/t5BnDuE2Ya2MMGhzP6A= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/lithammer/shortuuid/v4 v4.0.0 h1:QRbbVkfgNippHOS8PXDkti4NaWeyYfcBTHtw7k08o4c= +github.com/lithammer/shortuuid/v4 v4.0.0/go.mod h1:Zs8puNcrvf2rV9rTH51ZLLcj7ZXqQI3lv67aw4KiB1Y= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/orcaman/concurrent-map v1.0.0 h1:I/2A2XPCb4IuQWcQhBhSwGfiuybl/J0ev9HDbW65HOY= +github.com/orcaman/concurrent-map v1.0.0/go.mod h1:Lu3tH6HLW3feq74c2GC+jIMS/K2CFcDWnWD9XkenwhI= +github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= +github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= +github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= +github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= +github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= +github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= +github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/tinylib/msgp v1.1.5/go.mod h1:eQsjooMTnV42mHu917E26IogZ2930nFyBQdofk10Udg= +github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q= +github.com/twmb/murmur3 v1.1.6 h1:mqrRot1BRxm+Yct+vavLMou2/iJt0tNVTTC0QoIjaZg= +github.com/twmb/murmur3 v1.1.6/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s= +go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4= +go.opentelemetry.io/otel/exporters/prometheus v0.39.0 h1:whAaiHxOatgtKd+w0dOi//1KUxj3KoPINZdtDaDj3IA= +go.opentelemetry.io/otel/exporters/prometheus v0.39.0/go.mod h1:4jo5Q4CROlCpSPsXLhymi+LYrDXd2ObU5wbKayfZs7Y= +go.opentelemetry.io/otel/metric v1.16.0 h1:RbrpwVG1Hfv85LgnZ7+txXioPDoh6EdbZHo26Q3hqOo= +go.opentelemetry.io/otel/metric v1.16.0/go.mod h1:QE47cpOmkwipPiefDwo2wDzwJrlfxxNYodqc4xnGCo4= +go.opentelemetry.io/otel/sdk v1.16.0 h1:Z1Ok1YsijYL0CSJpHt4cS3wDDh7p572grzNrBMiMWgE= +go.opentelemetry.io/otel/sdk v1.16.0/go.mod h1:tMsIuKXuuIWPBAOrH+eHtvhTL+SntFtXF9QD68aP6p4= +go.opentelemetry.io/otel/sdk/metric v0.39.0 h1:Kun8i1eYf48kHH83RucG93ffz0zGV1sh46FAScOTuDI= +go.opentelemetry.io/otel/sdk/metric v0.39.0/go.mod h1:piDIRgjcK7u0HCL5pCA4e74qpK/jk3NiUoAHATVAmiI= +go.opentelemetry.io/otel/trace v1.16.0 h1:8JRpaObFoW0pxuVPapkgH8UhHQj+bJW8jJsCZEu5MQs= +go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/_examples/actor-spawn-benchmark/main.go b/_examples/actor-spawn-benchmark/main.go new file mode 100644 index 0000000000000000000000000000000000000000..f4514d157e5fe4a092f6d234b1fe4069bca82a74 --- /dev/null +++ b/_examples/actor-spawn-benchmark/main.go @@ -0,0 +1,103 @@ +package main + +import ( + "flag" + "fmt" + "log" + "os" + "runtime/pprof" + "time" + + "gitee.com/simplexyz/simpleactor-go/actor" +) + +type request struct { + num int + size int + div int +} + +var props = actor.PropsFromProducer(newState, actor.WithMailbox(actor.Unbounded())) + +type state struct { + sum int + replies int + replyTo *actor.PID +} + +func newState() actor.Actor { + return &state{} +} + +func (s *state) Receive(ctx actor.Context) { + switch msg := ctx.Message().(type) { + case *request: + if msg.size == 1 { + ctx.Respond(msg.num) + return + } + + s.replies = msg.div + s.replyTo = ctx.Sender() + for i := 0; i < msg.div; i++ { + child := ctx.ActorSystem().Root.Spawn(props) + ctx.Request(child, &request{ + num: msg.num + i*(msg.size/msg.div), + size: msg.size / msg.div, + div: msg.div, + }) + } + case int: + s.sum += msg + s.replies-- + if s.replies == 0 { + ctx.Send(s.replyTo, s.sum) + } + } +} + +var ( + cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file") + memprofile = flag.String("memprofile", "", "write mem profile to file") +) + +func main() { + flag.Parse() + if *cpuprofile != "" { + f, err := os.Create(*cpuprofile) + if err != nil { + log.Fatal(err) + } + _ = pprof.StartCPUProfile(f) + defer pprof.StopCPUProfile() + } + // runtime.GOMAXPROCS(runtime.NumCPU()) + // runtime.GC() + + for i := 0; i < 10; i++ { + system := actor.NewActorSystem() + rootContext := system.Root + + start := time.Now() + pid := rootContext.Spawn(props) + res, _ := rootContext.RequestFuture(pid, &request{ + num: 0, + size: 1000000, + div: 10, + }, 10*time.Second).Result() + result := res.(int) + + took := time.Since(start) + fmt.Printf("Result: %d in %d ms.\n", result, took.Nanoseconds()/1e6) + } + + if *memprofile != "" { + f, err := os.Create(*memprofile) + if err != nil { + log.Fatal(err) + } + _ = pprof.WriteHeapProfile(f) + _ = f.Close() + return + } +} diff --git a/_examples/actor-supervision/go.mod b/_examples/actor-supervision/go.mod new file mode 100644 index 0000000000000000000000000000000000000000..8518fe209683f12649ddc9bacb3876438a6114ff --- /dev/null +++ b/_examples/actor-supervision/go.mod @@ -0,0 +1,39 @@ +module supervision + +go 1.21 + +toolchain go1.21.2 + +replace gitee.com/simplexyz/simpleactor-go => ../.. + +require ( + gitee.com/simplexyz/simpleactor-go v0.0.0-00010101000000-000000000000 + github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716 +) + +require ( + github.com/Workiva/go-datastructures v1.1.1 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/emirpasic/gods v1.18.1 // indirect + github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/lithammer/shortuuid/v4 v4.0.0 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/orcaman/concurrent-map v1.0.0 // indirect + github.com/prometheus/client_golang v1.16.0 // indirect + github.com/prometheus/client_model v0.4.0 // indirect + github.com/prometheus/common v0.42.0 // indirect + github.com/prometheus/procfs v0.10.1 // indirect + github.com/twmb/murmur3 v1.1.6 // indirect + go.opentelemetry.io/otel v1.16.0 // indirect + go.opentelemetry.io/otel/exporters/prometheus v0.39.0 // indirect + go.opentelemetry.io/otel/metric v1.16.0 // indirect + go.opentelemetry.io/otel/sdk v1.16.0 // indirect + go.opentelemetry.io/otel/sdk/metric v0.39.0 // indirect + go.opentelemetry.io/otel/trace v1.16.0 // indirect + golang.org/x/sys v0.13.0 // indirect + google.golang.org/protobuf v1.31.0 // indirect +) diff --git a/_examples/actor-supervision/go.sum b/_examples/actor-supervision/go.sum new file mode 100644 index 0000000000000000000000000000000000000000..72e1596d2e6bb1dc5da2329b876b42c528bf5f7a --- /dev/null +++ b/_examples/actor-supervision/go.sum @@ -0,0 +1,100 @@ +github.com/Workiva/go-datastructures v1.1.1 h1:9G5u1UqKt6ABseAffHGNfbNQd7omRlWE5QaxNruzhE0= +github.com/Workiva/go-datastructures v1.1.1/go.mod h1:1yZL+zfsztete+ePzZz/Zb1/t5BnDuE2Ya2MMGhzP6A= +github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716 h1:SgyG4sXkrlalMoCfp20LiNPNhfJS7ez3opNdtihIxPc= +github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716/go.mod h1:/zSlF0T2ArAsTG6SVu8d8qlK+19jjudjA3wWsxnGFHg= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/lithammer/shortuuid/v4 v4.0.0 h1:QRbbVkfgNippHOS8PXDkti4NaWeyYfcBTHtw7k08o4c= +github.com/lithammer/shortuuid/v4 v4.0.0/go.mod h1:Zs8puNcrvf2rV9rTH51ZLLcj7ZXqQI3lv67aw4KiB1Y= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/orcaman/concurrent-map v1.0.0 h1:I/2A2XPCb4IuQWcQhBhSwGfiuybl/J0ev9HDbW65HOY= +github.com/orcaman/concurrent-map v1.0.0/go.mod h1:Lu3tH6HLW3feq74c2GC+jIMS/K2CFcDWnWD9XkenwhI= +github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= +github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= +github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= +github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= +github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= +github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= +github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/tinylib/msgp v1.1.5/go.mod h1:eQsjooMTnV42mHu917E26IogZ2930nFyBQdofk10Udg= +github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q= +github.com/twmb/murmur3 v1.1.6 h1:mqrRot1BRxm+Yct+vavLMou2/iJt0tNVTTC0QoIjaZg= +github.com/twmb/murmur3 v1.1.6/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s= +go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4= +go.opentelemetry.io/otel/exporters/prometheus v0.39.0 h1:whAaiHxOatgtKd+w0dOi//1KUxj3KoPINZdtDaDj3IA= +go.opentelemetry.io/otel/exporters/prometheus v0.39.0/go.mod h1:4jo5Q4CROlCpSPsXLhymi+LYrDXd2ObU5wbKayfZs7Y= +go.opentelemetry.io/otel/metric v1.16.0 h1:RbrpwVG1Hfv85LgnZ7+txXioPDoh6EdbZHo26Q3hqOo= +go.opentelemetry.io/otel/metric v1.16.0/go.mod h1:QE47cpOmkwipPiefDwo2wDzwJrlfxxNYodqc4xnGCo4= +go.opentelemetry.io/otel/sdk v1.16.0 h1:Z1Ok1YsijYL0CSJpHt4cS3wDDh7p572grzNrBMiMWgE= +go.opentelemetry.io/otel/sdk v1.16.0/go.mod h1:tMsIuKXuuIWPBAOrH+eHtvhTL+SntFtXF9QD68aP6p4= +go.opentelemetry.io/otel/sdk/metric v0.39.0 h1:Kun8i1eYf48kHH83RucG93ffz0zGV1sh46FAScOTuDI= +go.opentelemetry.io/otel/sdk/metric v0.39.0/go.mod h1:piDIRgjcK7u0HCL5pCA4e74qpK/jk3NiUoAHATVAmiI= +go.opentelemetry.io/otel/trace v1.16.0 h1:8JRpaObFoW0pxuVPapkgH8UhHQj+bJW8jJsCZEu5MQs= +go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/_examples/actor-supervision/main.go b/_examples/actor-supervision/main.go new file mode 100644 index 0000000000000000000000000000000000000000..56a11572d5b9cb87a791f3b5fd0ad86babbb1ffb --- /dev/null +++ b/_examples/actor-supervision/main.go @@ -0,0 +1,66 @@ +package main + +import ( + "fmt" + + "gitee.com/simplexyz/simpleactor-go/actor" + console "github.com/asynkron/goconsole" +) + +type ( + hello struct{ Who string } + parentActor struct{} +) + +func (state *parentActor) Receive(context actor.Context) { + switch msg := context.Message().(type) { + case *hello: + props := actor.PropsFromProducer(newChildActor) + child := context.Spawn(props) + context.Send(child, msg) + } +} + +func newParentActor() actor.Actor { + return &parentActor{} +} + +type childActor struct{} + +func (state *childActor) Receive(context actor.Context) { + switch msg := context.Message().(type) { + case *actor.Started: + fmt.Println("Starting, initialize actor here") + case *actor.Stopping: + fmt.Println("Stopping, actor is about to shut down") + case *actor.Stopped: + fmt.Println("Stopped, actor and its children are stopped") + case *actor.Restarting: + fmt.Println("Restarting, actor is about to restart") + case *hello: + fmt.Printf("Hello %v\n", msg.Who) + panic("Ouch") + } +} + +func newChildActor() actor.Actor { + return &childActor{} +} + +func main() { + system := actor.NewActorSystem() + decider := func(reason interface{}) actor.Directive { + fmt.Println("handling failure for child") + return actor.StopDirective + } + supervisor := actor.NewOneForOneStrategy(10, 1000, decider) + rootContext := system.Root + props := actor. + PropsFromProducer(newParentActor, + actor.WithSupervisor(supervisor)) + + pid := rootContext.Spawn(props) + rootContext.Send(pid, &hello{Who: "Roger"}) + + _, _ = console.ReadLine() +} diff --git a/_examples/cluster-basic/Makefile b/_examples/cluster-basic/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..0bb231412a50360c6f984c3a358922c50ce05e49 --- /dev/null +++ b/_examples/cluster-basic/Makefile @@ -0,0 +1,11 @@ +start: + tmux new-session -d -s eg + tmux split-window -t "eg:0" -v + tmux send-keys -t "eg:0.0" "go run node1/main.go" Enter + tmux send-keys -t "eg:0.1" "go run node2/main.go" Enter + tmux attach -t eg + tmux kill-session -t eg + + +stop: + tmux kill-session -t eg diff --git a/_examples/cluster-basic/docker-compose.yml b/_examples/cluster-basic/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..3cfdc0477bbf722994271019dd9d88438116a101 --- /dev/null +++ b/_examples/cluster-basic/docker-compose.yml @@ -0,0 +1,48 @@ +version: '3.7' +services: + + consul-agent-1: &consul-agent + image: hashicorp/consul:latest + networks: + - consul + command: "agent -retry-join consul-server-bootstrap -client 0.0.0.0" + + consul-agent-2: + <<: *consul-agent + + consul-agent-3: + <<: *consul-agent + + consul-server-1: &consul-server + <<: *consul-agent + command: "agent -server -retry-join consul-server-bootstrap -client 0.0.0.0" + + consul-server-2: + <<: *consul-server + + consul-server-bootstrap: + <<: *consul-agent + ports: + - "8400:8400" + - "8500:8500" + - "8600:8600" + - "8600:8600/udp" + command: "agent -server -bootstrap-expect 3 -ui -client 0.0.0.0" + + mongodb: + image: mongo:latest + ports: + - 127.0.0.1:27017:27017 + volumes: + - mongodb_data:/data/db + + redis: + image: redis:latest + ports: + - 127.0.0.1:6379:6379 + +networks: + consul: + +volumes: + mongodb_data: \ No newline at end of file diff --git a/_examples/cluster-basic/go.mod b/_examples/cluster-basic/go.mod new file mode 100644 index 0000000000000000000000000000000000000000..f113ae9900b7c9ddd5e43613102d518d68574f0e --- /dev/null +++ b/_examples/cluster-basic/go.mod @@ -0,0 +1,60 @@ +module cluster-broadcast + +go 1.21 + +toolchain go1.21.2 + +replace gitee.com/simplexyz/simpleactor-go => ../.. + +require ( + gitee.com/simplexyz/simpleactor-go v0.0.0-00010101000000-000000000000 + github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716 + google.golang.org/protobuf v1.31.0 +) + +require ( + github.com/Workiva/go-datastructures v1.1.1 // indirect + github.com/armon/go-metrics v0.4.0 // indirect + github.com/asynkron/gofun v0.0.0-20220329210725-34fed760f4c2 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/emirpasic/gods v1.18.1 // indirect + github.com/fatih/color v1.13.0 // indirect + github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/google/btree v1.0.1 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/hashicorp/consul/api v1.18.0 // indirect + github.com/hashicorp/go-cleanhttp v0.5.2 // indirect + github.com/hashicorp/go-hclog v1.2.0 // indirect + github.com/hashicorp/go-immutable-radix v1.3.1 // indirect + github.com/hashicorp/go-rootcerts v1.0.2 // indirect + github.com/hashicorp/golang-lru v0.5.4 // indirect + github.com/hashicorp/serf v0.10.1 // indirect + github.com/lithammer/shortuuid/v4 v4.0.0 // indirect + github.com/mattn/go-colorable v0.1.12 // indirect + github.com/mattn/go-isatty v0.0.16 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/orcaman/concurrent-map v1.0.0 // indirect + github.com/prometheus/client_golang v1.16.0 // indirect + github.com/prometheus/client_model v0.4.0 // indirect + github.com/prometheus/common v0.42.0 // indirect + github.com/prometheus/procfs v0.10.1 // indirect + github.com/twmb/murmur3 v1.1.6 // indirect + go.opentelemetry.io/otel v1.16.0 // indirect + go.opentelemetry.io/otel/exporters/prometheus v0.39.0 // indirect + go.opentelemetry.io/otel/metric v1.16.0 // indirect + go.opentelemetry.io/otel/sdk v1.16.0 // indirect + go.opentelemetry.io/otel/sdk/metric v0.39.0 // indirect + go.opentelemetry.io/otel/trace v1.16.0 // indirect + golang.org/x/exp v0.0.0-20220827204233-334a2380cb91 // indirect + golang.org/x/net v0.17.0 // indirect + golang.org/x/sys v0.13.0 // indirect + golang.org/x/text v0.13.0 // indirect + google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97 // indirect + google.golang.org/grpc v1.58.3 // indirect +) diff --git a/_examples/cluster-basic/go.sum b/_examples/cluster-basic/go.sum new file mode 100644 index 0000000000000000000000000000000000000000..198f0c3fd3de7cb23fa4607c1a34c58ec417b305 --- /dev/null +++ b/_examples/cluster-basic/go.sum @@ -0,0 +1,322 @@ +github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= +github.com/Workiva/go-datastructures v1.1.1 h1:9G5u1UqKt6ABseAffHGNfbNQd7omRlWE5QaxNruzhE0= +github.com/Workiva/go-datastructures v1.1.1/go.mod h1:1yZL+zfsztete+ePzZz/Zb1/t5BnDuE2Ya2MMGhzP6A= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-metrics v0.4.0 h1:yCQqn7dwca4ITXb+CbubHmedzaQYHhNhrEXLYUeEe8Q= +github.com/armon/go-metrics v0.4.0/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716 h1:SgyG4sXkrlalMoCfp20LiNPNhfJS7ez3opNdtihIxPc= +github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716/go.mod h1:/zSlF0T2ArAsTG6SVu8d8qlK+19jjudjA3wWsxnGFHg= +github.com/asynkron/gofun v0.0.0-20220329210725-34fed760f4c2 h1:jEsFZ9d/ieJGVrx3fSPi8oe/qv21fRmyUL5cS3ZEn5A= +github.com/asynkron/gofun v0.0.0-20220329210725-34fed760f4c2/go.mod h1:5GMOSqaYxNWwuVRWyampTPJEntwz7Mj9J8v1a7gSU2E= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= +github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= +github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= +github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/hashicorp/consul/api v1.18.0 h1:R7PPNzTCeN6VuQNDwwhZWJvzCtGSrNpJqfb22h3yH9g= +github.com/hashicorp/consul/api v1.18.0/go.mod h1:owRRGJ9M5xReDC5nfT8FTJrNAPbT4NM6p/k+d03q2v4= +github.com/hashicorp/consul/sdk v0.13.0 h1:lce3nFlpv8humJL8rNrrGHYSKc3q+Kxfeg3Ii1m6ZWU= +github.com/hashicorp/consul/sdk v0.13.0/go.mod h1:0hs/l5fOVhJy/VdcoaNqUSi2AUs95eF5WKtv+EYIQqE= +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.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= +github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-hclog v1.2.0 h1:La19f8d7WIlm4ogzNHB0JGqs5AUDAZ2UfCY4sJXcJdM= +github.com/hashicorp/go-hclog v1.2.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= +github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-msgpack v0.5.5 h1:i9R9JSrqIz0QVLz3sz+i3YJdT7TTSLcfLLzJi9aZTuI= +github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-multierror v1.1.0 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g4TbElacI= +github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= +github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= +github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= +github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc= +github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= +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/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE= +github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.2.1 h1:zEfKbn2+PDgroKdiOzqiE8rsmLqU2uwi5PB5pBJ3TkI= +github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= +github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= +github.com/hashicorp/memberlist v0.5.0 h1:EtYPN8DpAURiapus508I4n9CzHs2W+8NZGbmmR/prTM= +github.com/hashicorp/memberlist v0.5.0/go.mod h1:yvyXLpo0QaGE59Y7hDTsTzDD25JYBZ4mHgHUZ8lrOI0= +github.com/hashicorp/serf v0.10.1 h1:Z1H2J60yRKvfDYAOZLd2MU0ND4AH/WDz7xYHDWQsIPY= +github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/lithammer/shortuuid/v4 v4.0.0 h1:QRbbVkfgNippHOS8PXDkti4NaWeyYfcBTHtw7k08o4c= +github.com/lithammer/shortuuid/v4 v4.0.0/go.mod h1:Zs8puNcrvf2rV9rTH51ZLLcj7ZXqQI3lv67aw4KiB1Y= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= +github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= +github.com/miekg/dns v1.1.41 h1:WMszZWJG0XmzbK9FEmzH2TVcqYzFesusSIB41b8KHxY= +github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= +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-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/orcaman/concurrent-map v1.0.0 h1:I/2A2XPCb4IuQWcQhBhSwGfiuybl/J0ev9HDbW65HOY= +github.com/orcaman/concurrent-map v1.0.0/go.mod h1:Lu3tH6HLW3feq74c2GC+jIMS/K2CFcDWnWD9XkenwhI= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= +github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= +github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= +github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= +github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= +github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= +github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= +github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +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/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/tinylib/msgp v1.1.5/go.mod h1:eQsjooMTnV42mHu917E26IogZ2930nFyBQdofk10Udg= +github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q= +github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= +github.com/twmb/murmur3 v1.1.6 h1:mqrRot1BRxm+Yct+vavLMou2/iJt0tNVTTC0QoIjaZg= +github.com/twmb/murmur3 v1.1.6/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s= +go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4= +go.opentelemetry.io/otel/exporters/prometheus v0.39.0 h1:whAaiHxOatgtKd+w0dOi//1KUxj3KoPINZdtDaDj3IA= +go.opentelemetry.io/otel/exporters/prometheus v0.39.0/go.mod h1:4jo5Q4CROlCpSPsXLhymi+LYrDXd2ObU5wbKayfZs7Y= +go.opentelemetry.io/otel/metric v1.16.0 h1:RbrpwVG1Hfv85LgnZ7+txXioPDoh6EdbZHo26Q3hqOo= +go.opentelemetry.io/otel/metric v1.16.0/go.mod h1:QE47cpOmkwipPiefDwo2wDzwJrlfxxNYodqc4xnGCo4= +go.opentelemetry.io/otel/sdk v1.16.0 h1:Z1Ok1YsijYL0CSJpHt4cS3wDDh7p572grzNrBMiMWgE= +go.opentelemetry.io/otel/sdk v1.16.0/go.mod h1:tMsIuKXuuIWPBAOrH+eHtvhTL+SntFtXF9QD68aP6p4= +go.opentelemetry.io/otel/sdk/metric v0.39.0 h1:Kun8i1eYf48kHH83RucG93ffz0zGV1sh46FAScOTuDI= +go.opentelemetry.io/otel/sdk/metric v0.39.0/go.mod h1:piDIRgjcK7u0HCL5pCA4e74qpK/jk3NiUoAHATVAmiI= +go.opentelemetry.io/otel/trace v1.16.0 h1:8JRpaObFoW0pxuVPapkgH8UhHQj+bJW8jJsCZEu5MQs= +go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20220827204233-334a2380cb91 h1:tnebWN09GYg9OLPss1KXj8txwZc6X6uMr6VFdcGNbHw= +golang.org/x/exp v0.0.0-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= +golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 h1:DdoeryqhaXp1LtT/emMP1BRJPHHKFi5akj/nbx/zNTA= +google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= +google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97/go.mod h1:t1VqOqqvce95G3hIDCT5FeO3YUc6Q4Oe24L/+rNMxRk= +google.golang.org/grpc v1.55.0 h1:3Oj82/tFSCeUrRTg/5E/7d/W5A1tj6Ky1ABAuZuv5ag= +google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8= +google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/_examples/cluster-basic/node1/main.go b/_examples/cluster-basic/node1/main.go new file mode 100644 index 0000000000000000000000000000000000000000..b8b80b56349fd67e5dddb695d555501d1cf4eef7 --- /dev/null +++ b/_examples/cluster-basic/node1/main.go @@ -0,0 +1,37 @@ +package main + +import ( + "fmt" + + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/cluster" + "gitee.com/simplexyz/simpleactor-go/cluster/clusterproviders/consul" + "gitee.com/simplexyz/simpleactor-go/cluster/identitylookup/disthash" + "gitee.com/simplexyz/simpleactor-go/remote" + console "github.com/asynkron/goconsole" +) + +func main() { + c := startNode() + + fmt.Print("\nBoot other nodes and press Enter\n") + console.ReadLine() + pid := c.Get("abc", "hello") + fmt.Printf("Got pid %v", pid) + fmt.Println() + console.ReadLine() + c.Shutdown(true) +} + +func startNode() *cluster.Cluster { + system := actor.NewActorSystem() + + provider, _ := consul.New() + lookup := disthash.New() + config := remote.Configure("localhost", 0) + clusterConfig := cluster.Configure("my-cluster", provider, lookup, config) + c := cluster.New(system, clusterConfig) + c.StartMember() + + return c +} diff --git a/_examples/cluster-basic/node2/main.go b/_examples/cluster-basic/node2/main.go new file mode 100644 index 0000000000000000000000000000000000000000..f774027c6a546b40a56e42733db209b89531bc3d --- /dev/null +++ b/_examples/cluster-basic/node2/main.go @@ -0,0 +1,46 @@ +package main + +import ( + "fmt" + + "cluster-broadcast/shared" + + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/cluster" + "gitee.com/simplexyz/simpleactor-go/cluster/clusterproviders/consul" + "gitee.com/simplexyz/simpleactor-go/cluster/identitylookup/disthash" + "gitee.com/simplexyz/simpleactor-go/remote" + console "github.com/asynkron/goconsole" +) + +func main() { + cluster := startNode() + + fmt.Print("\nBoot other nodes and press Enter\n") + console.ReadLine() + + cluster.Shutdown(true) +} + +func startNode() *cluster.Cluster { + system := actor.NewActorSystem() + + provider, _ := consul.New() + lookup := disthash.New() + config := remote.Configure("localhost", 0) + + props := actor.PropsFromFunc(func(ctx actor.Context) { + switch msg := ctx.Message().(type) { + case *actor.Started: + fmt.Printf("Started %v", msg) + case *shared.Hello: + fmt.Printf("Hello %v\n", msg.Name) + } + }) + helloKind := cluster.NewKind("hello", props) + clusterConfig := cluster.Configure("my-cluster", provider, lookup, config, cluster.WithKinds(helloKind)) + c := cluster.New(system, clusterConfig) + + c.StartMember() + return c +} diff --git a/_examples/cluster-basic/shared/build.sh b/_examples/cluster-basic/shared/build.sh new file mode 100644 index 0000000000000000000000000000000000000000..28f9be836fdee9993e2446e07361301c1eac81a5 --- /dev/null +++ b/_examples/cluster-basic/shared/build.sh @@ -0,0 +1,2 @@ +protoc --go_out=. --go_opt=paths=source_relative --proto_path=. protos.proto +protoc -I=. -I=$GOPATH/src --gograinv2_out=. protos.proto diff --git a/_examples/cluster-basic/shared/protos.pb.go b/_examples/cluster-basic/shared/protos.pb.go new file mode 100644 index 0000000000000000000000000000000000000000..82cc0c5ef0e6ca16ff0c6e2c5674d7ea01193ed6 --- /dev/null +++ b/_examples/cluster-basic/shared/protos.pb.go @@ -0,0 +1,144 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc v3.19.1 +// source: protos.proto + +package shared + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type Hello struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` +} + +func (x *Hello) Reset() { + *x = Hello{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Hello) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Hello) ProtoMessage() {} + +func (x *Hello) ProtoReflect() protoreflect.Message { + mi := &file_protos_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Hello.ProtoReflect.Descriptor instead. +func (*Hello) Descriptor() ([]byte, []int) { + return file_protos_proto_rawDescGZIP(), []int{0} +} + +func (x *Hello) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +var File_protos_proto protoreflect.FileDescriptor + +var file_protos_proto_rawDesc = []byte{ + 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06, + 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x22, 0x1b, 0x0a, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x12, + 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x42, 0x42, 0x5a, 0x40, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x61, 0x73, 0x79, 0x6e, 0x6b, 0x72, 0x6f, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x61, 0x63, 0x74, 0x6f, 0x72, 0x2d, 0x67, 0x6f, 0x2f, 0x5f, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, + 0x65, 0x73, 0x2f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x2d, 0x62, 0x61, 0x73, 0x69, 0x63, + 0x2f, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_protos_proto_rawDescOnce sync.Once + file_protos_proto_rawDescData = file_protos_proto_rawDesc +) + +func file_protos_proto_rawDescGZIP() []byte { + file_protos_proto_rawDescOnce.Do(func() { + file_protos_proto_rawDescData = protoimpl.X.CompressGZIP(file_protos_proto_rawDescData) + }) + return file_protos_proto_rawDescData +} + +var file_protos_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_protos_proto_goTypes = []interface{}{ + (*Hello)(nil), // 0: shared.Hello +} +var file_protos_proto_depIdxs = []int32{ + 0, // [0:0] is the sub-list for method output_type + 0, // [0:0] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_protos_proto_init() } +func file_protos_proto_init() { + if File_protos_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_protos_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Hello); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_protos_proto_rawDesc, + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_protos_proto_goTypes, + DependencyIndexes: file_protos_proto_depIdxs, + MessageInfos: file_protos_proto_msgTypes, + }.Build() + File_protos_proto = out.File + file_protos_proto_rawDesc = nil + file_protos_proto_goTypes = nil + file_protos_proto_depIdxs = nil +} diff --git a/_examples/cluster-basic/shared/protos.proto b/_examples/cluster-basic/shared/protos.proto new file mode 100644 index 0000000000000000000000000000000000000000..b71bd07a72a107d74ad12787498771718b65a1de --- /dev/null +++ b/_examples/cluster-basic/shared/protos.proto @@ -0,0 +1,7 @@ +syntax = "proto3"; +package shared; +option go_package = "gitee.com/simplexyz/simpleactor-go/_examples/cluster-basic/shared"; + +message Hello { + string name = 1; +} diff --git a/_examples/cluster-broadcast/Makefile b/_examples/cluster-broadcast/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..0bb231412a50360c6f984c3a358922c50ce05e49 --- /dev/null +++ b/_examples/cluster-broadcast/Makefile @@ -0,0 +1,11 @@ +start: + tmux new-session -d -s eg + tmux split-window -t "eg:0" -v + tmux send-keys -t "eg:0.0" "go run node1/main.go" Enter + tmux send-keys -t "eg:0.1" "go run node2/main.go" Enter + tmux attach -t eg + tmux kill-session -t eg + + +stop: + tmux kill-session -t eg diff --git a/_examples/cluster-broadcast/go.mod b/_examples/cluster-broadcast/go.mod new file mode 100644 index 0000000000000000000000000000000000000000..a3db93305c033928b9ccf76198b4f2edfc45265f --- /dev/null +++ b/_examples/cluster-broadcast/go.mod @@ -0,0 +1,54 @@ +module cluster-broadcast + +go 1.21 + +toolchain go1.21.2 + +replace gitee.com/simplexyz/simpleactor-go => ../.. + +require ( + gitee.com/simplexyz/simpleactor-go v0.0.0-00010101000000-000000000000 + github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716 + google.golang.org/protobuf v1.31.0 +) + +require ( + github.com/Workiva/go-datastructures v1.1.1 // indirect + github.com/asynkron/gofun v0.0.0-20220329210725-34fed760f4c2 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/emirpasic/gods v1.18.1 // indirect + github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/labstack/echo v3.3.10+incompatible // indirect + github.com/labstack/gommon v0.3.1 // indirect + github.com/lithammer/shortuuid/v4 v4.0.0 // indirect + github.com/mattn/go-colorable v0.1.12 // indirect + github.com/mattn/go-isatty v0.0.16 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/orcaman/concurrent-map v1.0.0 // indirect + github.com/prometheus/client_golang v1.16.0 // indirect + github.com/prometheus/client_model v0.4.0 // indirect + github.com/prometheus/common v0.42.0 // indirect + github.com/prometheus/procfs v0.10.1 // indirect + github.com/twmb/murmur3 v1.1.6 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/valyala/fasttemplate v1.2.1 // indirect + go.opentelemetry.io/otel v1.16.0 // indirect + go.opentelemetry.io/otel/exporters/prometheus v0.39.0 // indirect + go.opentelemetry.io/otel/metric v1.16.0 // indirect + go.opentelemetry.io/otel/sdk v1.16.0 // indirect + go.opentelemetry.io/otel/sdk/metric v0.39.0 // indirect + go.opentelemetry.io/otel/trace v1.16.0 // indirect + golang.org/x/crypto v0.14.0 // indirect + golang.org/x/exp v0.0.0-20220827204233-334a2380cb91 // indirect + golang.org/x/net v0.17.0 // indirect + golang.org/x/sync v0.3.0 // indirect + golang.org/x/sys v0.13.0 // indirect + golang.org/x/text v0.13.0 // indirect + google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97 // indirect + google.golang.org/grpc v1.58.3 // indirect +) diff --git a/_examples/cluster-broadcast/go.sum b/_examples/cluster-broadcast/go.sum new file mode 100644 index 0000000000000000000000000000000000000000..f5151bdf77fa2b926bd72affe6b715e74a4f7b0f --- /dev/null +++ b/_examples/cluster-broadcast/go.sum @@ -0,0 +1,150 @@ +github.com/Workiva/go-datastructures v1.1.1 h1:9G5u1UqKt6ABseAffHGNfbNQd7omRlWE5QaxNruzhE0= +github.com/Workiva/go-datastructures v1.1.1/go.mod h1:1yZL+zfsztete+ePzZz/Zb1/t5BnDuE2Ya2MMGhzP6A= +github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716 h1:SgyG4sXkrlalMoCfp20LiNPNhfJS7ez3opNdtihIxPc= +github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716/go.mod h1:/zSlF0T2ArAsTG6SVu8d8qlK+19jjudjA3wWsxnGFHg= +github.com/asynkron/gofun v0.0.0-20220329210725-34fed760f4c2 h1:jEsFZ9d/ieJGVrx3fSPi8oe/qv21fRmyUL5cS3ZEn5A= +github.com/asynkron/gofun v0.0.0-20220329210725-34fed760f4c2/go.mod h1:5GMOSqaYxNWwuVRWyampTPJEntwz7Mj9J8v1a7gSU2E= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/labstack/echo v3.3.10+incompatible h1:pGRcYk231ExFAyoAjAfD85kQzRJCRI8bbnE7CX5OEgg= +github.com/labstack/echo v3.3.10+incompatible/go.mod h1:0INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s= +github.com/labstack/gommon v0.3.1 h1:OomWaJXm7xR6L1HmEtGyQf26TEn7V6X88mktX9kee9o= +github.com/labstack/gommon v0.3.1/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM= +github.com/lithammer/shortuuid/v4 v4.0.0 h1:QRbbVkfgNippHOS8PXDkti4NaWeyYfcBTHtw7k08o4c= +github.com/lithammer/shortuuid/v4 v4.0.0/go.mod h1:Zs8puNcrvf2rV9rTH51ZLLcj7ZXqQI3lv67aw4KiB1Y= +github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/orcaman/concurrent-map v1.0.0 h1:I/2A2XPCb4IuQWcQhBhSwGfiuybl/J0ev9HDbW65HOY= +github.com/orcaman/concurrent-map v1.0.0/go.mod h1:Lu3tH6HLW3feq74c2GC+jIMS/K2CFcDWnWD9XkenwhI= +github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= +github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= +github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= +github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= +github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= +github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= +github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/tinylib/msgp v1.1.5/go.mod h1:eQsjooMTnV42mHu917E26IogZ2930nFyBQdofk10Udg= +github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q= +github.com/twmb/murmur3 v1.1.6 h1:mqrRot1BRxm+Yct+vavLMou2/iJt0tNVTTC0QoIjaZg= +github.com/twmb/murmur3 v1.1.6/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasttemplate v1.2.1 h1:TVEnxayobAdVkhQfrfes2IzOB6o+z4roRkPF52WA1u4= +github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s= +go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4= +go.opentelemetry.io/otel/exporters/prometheus v0.39.0 h1:whAaiHxOatgtKd+w0dOi//1KUxj3KoPINZdtDaDj3IA= +go.opentelemetry.io/otel/exporters/prometheus v0.39.0/go.mod h1:4jo5Q4CROlCpSPsXLhymi+LYrDXd2ObU5wbKayfZs7Y= +go.opentelemetry.io/otel/metric v1.16.0 h1:RbrpwVG1Hfv85LgnZ7+txXioPDoh6EdbZHo26Q3hqOo= +go.opentelemetry.io/otel/metric v1.16.0/go.mod h1:QE47cpOmkwipPiefDwo2wDzwJrlfxxNYodqc4xnGCo4= +go.opentelemetry.io/otel/sdk v1.16.0 h1:Z1Ok1YsijYL0CSJpHt4cS3wDDh7p572grzNrBMiMWgE= +go.opentelemetry.io/otel/sdk v1.16.0/go.mod h1:tMsIuKXuuIWPBAOrH+eHtvhTL+SntFtXF9QD68aP6p4= +go.opentelemetry.io/otel/sdk/metric v0.39.0 h1:Kun8i1eYf48kHH83RucG93ffz0zGV1sh46FAScOTuDI= +go.opentelemetry.io/otel/sdk/metric v0.39.0/go.mod h1:piDIRgjcK7u0HCL5pCA4e74qpK/jk3NiUoAHATVAmiI= +go.opentelemetry.io/otel/trace v1.16.0 h1:8JRpaObFoW0pxuVPapkgH8UhHQj+bJW8jJsCZEu5MQs= +go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/exp v0.0.0-20220827204233-334a2380cb91 h1:tnebWN09GYg9OLPss1KXj8txwZc6X6uMr6VFdcGNbHw= +golang.org/x/exp v0.0.0-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= +golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 h1:DdoeryqhaXp1LtT/emMP1BRJPHHKFi5akj/nbx/zNTA= +google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= +google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97/go.mod h1:t1VqOqqvce95G3hIDCT5FeO3YUc6Q4Oe24L/+rNMxRk= +google.golang.org/grpc v1.55.0 h1:3Oj82/tFSCeUrRTg/5E/7d/W5A1tj6Ky1ABAuZuv5ag= +google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8= +google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/_examples/cluster-broadcast/node1/main.go b/_examples/cluster-broadcast/node1/main.go new file mode 100644 index 0000000000000000000000000000000000000000..6b89826cb17a27c5329405251af3b8336303813c --- /dev/null +++ b/_examples/cluster-broadcast/node1/main.go @@ -0,0 +1,105 @@ +package main + +import ( + "fmt" + "time" + + "cluster-broadcast/shared" + + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/cluster" + "gitee.com/simplexyz/simpleactor-go/cluster/clusterproviders/automanaged" + "gitee.com/simplexyz/simpleactor-go/cluster/identitylookup/disthash" + "gitee.com/simplexyz/simpleactor-go/remote" + console "github.com/asynkron/goconsole" +) + +func main() { + shared.TrackerFactory(func() shared.Tracker { + return &shared.TrackGrain{} + }) + shared.CalculatorFactory(func() shared.Calculator { + return &shared.CalcGrain{} + }) + + c := startNode(8080) + + fmt.Print("\nBoot other nodes and press Enter\n") + console.ReadLine() + + fmt.Print("\nAdding 1 Egg - Enter\n") + console.ReadLine() + calcAdd(c, "Eggs", 1) + + fmt.Print("\nAdding 10 Egg - Enter\n") + console.ReadLine() + calcAdd(c, "Eggs", 10) + + fmt.Print("\nAdding 100 Bananas - Enter\n") + console.ReadLine() + calcAdd(c, "Bananas", 100) + + fmt.Print("\nAdding 2 Meat - Enter\n") + console.ReadLine() + calcAdd(c, "Meat", 3) + calcAdd(c, "Meat", 9000) + + getAll(c) + + console.ReadLine() + + c.Shutdown(true) +} + +func startNode(port int64) *cluster.Cluster { + // how long before the grain poisons itself + system := actor.NewActorSystem() + + provider := automanaged.NewWithConfig(2*time.Second, 6331, "localhost:6330", "localhost:6331") + lookup := disthash.New() + config := remote.Configure("localhost", 0) + + calculatorKind := shared.NewCalculatorKind(func() shared.Calculator { + return &shared.CalcGrain{} + }, 0) + + trackerKind := shared.NewTrackerKind(func() shared.Tracker { + return &shared.TrackGrain{} + }, 0) + + clusterConfig := cluster.Configure("my-cluster", provider, lookup, config, + cluster.WithKinds(calculatorKind, trackerKind)) + + cluster := cluster.New(system, clusterConfig) + + shared.TrackerFactory(func() shared.Tracker { + return &shared.TrackGrain{} + }) + + cluster.StartMember() + + return cluster +} + +func calcAdd(cluster *cluster.Cluster, grainId string, addNumber int64) { + calcGrain := shared.GetCalculatorGrainClient(cluster, grainId) + total1, err := calcGrain.Add(&shared.NumberRequest{Number: addNumber}) + if err != nil { + panic(err) + } + + fmt.Printf("Grain: %v - Total: %v \n", calcGrain.Identity, total1.Number) +} + +func getAll(cluster *cluster.Cluster) { + trackerGrain := shared.GetTrackerGrainClient(cluster, "singleTrackerGrain") + totals, err := trackerGrain.BroadcastGetCounts(&shared.Noop{}) + if err != nil { + panic(err) + } + + fmt.Println("--- Totals ---") + for grainId, count := range totals.Totals { + fmt.Printf("%v : %v\n", grainId, count) + } +} diff --git a/_examples/cluster-broadcast/node2/main.go b/_examples/cluster-broadcast/node2/main.go new file mode 100644 index 0000000000000000000000000000000000000000..2196f532b09c88ab02e397725e666709553cd0a7 --- /dev/null +++ b/_examples/cluster-broadcast/node2/main.go @@ -0,0 +1,93 @@ +package main + +import ( + "fmt" + "time" + + "cluster-broadcast/shared" + + "gitee.com/simplexyz/simpleactor-go/cluster/clusterproviders/automanaged" + "gitee.com/simplexyz/simpleactor-go/cluster/identitylookup/disthash" + + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/cluster" + "gitee.com/simplexyz/simpleactor-go/remote" + console "github.com/asynkron/goconsole" +) + +func main() { + c := startNode(8081) + + fmt.Print("\nBoot other nodes and press Enter\n") + console.ReadLine() + + fmt.Print("\nAdding 1 Egg - Enter\n") + console.ReadLine() + calcAdd(c, "Eggs", 1) + + fmt.Print("\nAdding 10 Egg - Enter\n") + console.ReadLine() + calcAdd(c, "Eggs", 10) + + fmt.Print("\nAdding 100 Bananas - Enter\n") + console.ReadLine() + calcAdd(c, "Bananas", 100) + + fmt.Print("\nAdding 2 Meat - Enter\n") + console.ReadLine() + calcAdd(c, "Meat", 3) + calcAdd(c, "Meat", 9000) + + getAll(c) + + console.ReadLine() + + c.Shutdown(true) +} + +func startNode(port int64) *cluster.Cluster { + system := actor.NewActorSystem() + + provider := automanaged.NewWithConfig(2*time.Second, 6330, "localhost:6330", "localhost:6331") + lookup := disthash.New() + config := remote.Configure("localhost", 0) + + calculatorKind := shared.NewCalculatorKind(func() shared.Calculator { + return &shared.CalcGrain{} + }, 0) + + trackerKind := shared.NewTrackerKind(func() shared.Tracker { + return &shared.TrackGrain{} + }, 0) + + clusterConfig := cluster.Configure("my-cluster", provider, lookup, config, + cluster.WithKinds(calculatorKind, trackerKind)) + + cluster := cluster.New(system, clusterConfig) + + cluster.StartMember() + return cluster +} + +func calcAdd(cluster *cluster.Cluster, grainId string, addNumber int64) { + calcGrain := shared.GetCalculatorGrainClient(cluster, grainId) + total1, err := calcGrain.Add(&shared.NumberRequest{Number: addNumber}) + if err != nil { + panic(err) + } + + fmt.Printf("Grain: %v - Total: %v \n", calcGrain.Identity, total1.Number) +} + +func getAll(cluster *cluster.Cluster) { + trackerGrain := shared.GetTrackerGrainClient(cluster, "singleTrackerGrain") + totals, err := trackerGrain.BroadcastGetCounts(&shared.Noop{}) + if err != nil { + panic(err) + } + + fmt.Println("--- Totals ---") + for grainId, count := range totals.Totals { + fmt.Printf("%v : %v\n", grainId, count) + } +} diff --git a/_examples/cluster-broadcast/shared/build.sh b/_examples/cluster-broadcast/shared/build.sh new file mode 100644 index 0000000000000000000000000000000000000000..a0b752d4944adc1d2e473eb03c2e6bf766dad636 --- /dev/null +++ b/_examples/cluster-broadcast/shared/build.sh @@ -0,0 +1,2 @@ +protoc --go_out=. --go_opt=paths=source_relative --proto_path=. protos.proto +protoc -I=. --gograinv2_out=. protos.proto \ No newline at end of file diff --git a/_examples/cluster-broadcast/shared/calculator-grain.go b/_examples/cluster-broadcast/shared/calculator-grain.go new file mode 100644 index 0000000000000000000000000000000000000000..0e2860e5bca44fef4211836fd51cb63397941992 --- /dev/null +++ b/_examples/cluster-broadcast/shared/calculator-grain.go @@ -0,0 +1,40 @@ +package shared + +import ( + "gitee.com/simplexyz/simpleactor-go/cluster" +) + +type CalcGrain struct { + total int64 +} + +func (c *CalcGrain) ReceiveDefault(ctx cluster.GrainContext) { +} + +func (c *CalcGrain) Init(ctx cluster.GrainContext) { + c.total = 0 + + // register with the tracker + trackerGrain := GetTrackerGrainClient(ctx.Cluster(), "singleTrackerGrain") + trackerGrain.RegisterGrain(&RegisterMessage{GrainId: ctx.Identity()}) +} + +func (c *CalcGrain) Terminate(ctx cluster.GrainContext) { + // deregister with the tracker + trackerGrain := GetTrackerGrainClient(ctx.Cluster(), "singleTrackerGrain") + trackerGrain.DeregisterGrain(&RegisterMessage{GrainId: ctx.Identity()}) +} + +func (c *CalcGrain) Add(n *NumberRequest, ctx cluster.GrainContext) (*CountResponse, error) { + c.total = c.total + n.Number + return &CountResponse{Number: c.total}, nil +} + +func (c *CalcGrain) Subtract(n *NumberRequest, ctx cluster.GrainContext) (*CountResponse, error) { + c.total = c.total - n.Number + return &CountResponse{Number: c.total}, nil +} + +func (c *CalcGrain) GetCurrent(n *Noop, ctx cluster.GrainContext) (*CountResponse, error) { + return &CountResponse{Number: c.total}, nil +} diff --git a/_examples/cluster-broadcast/shared/protos.pb.go b/_examples/cluster-broadcast/shared/protos.pb.go new file mode 100644 index 0000000000000000000000000000000000000000..b4df29b391222878a08f5f56fab1dbd64eeefb38 --- /dev/null +++ b/_examples/cluster-broadcast/shared/protos.pb.go @@ -0,0 +1,428 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc v3.19.1 +// source: protos.proto + +package shared + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type Noop struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *Noop) Reset() { + *x = Noop{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Noop) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Noop) ProtoMessage() {} + +func (x *Noop) ProtoReflect() protoreflect.Message { + mi := &file_protos_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Noop.ProtoReflect.Descriptor instead. +func (*Noop) Descriptor() ([]byte, []int) { + return file_protos_proto_rawDescGZIP(), []int{0} +} + +type NumberRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Number int64 `protobuf:"varint,1,opt,name=number,proto3" json:"number,omitempty"` +} + +func (x *NumberRequest) Reset() { + *x = NumberRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *NumberRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NumberRequest) ProtoMessage() {} + +func (x *NumberRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use NumberRequest.ProtoReflect.Descriptor instead. +func (*NumberRequest) Descriptor() ([]byte, []int) { + return file_protos_proto_rawDescGZIP(), []int{1} +} + +func (x *NumberRequest) GetNumber() int64 { + if x != nil { + return x.Number + } + return 0 +} + +type CountResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Number int64 `protobuf:"varint,1,opt,name=number,proto3" json:"number,omitempty"` +} + +func (x *CountResponse) Reset() { + *x = CountResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CountResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CountResponse) ProtoMessage() {} + +func (x *CountResponse) ProtoReflect() protoreflect.Message { + mi := &file_protos_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CountResponse.ProtoReflect.Descriptor instead. +func (*CountResponse) Descriptor() ([]byte, []int) { + return file_protos_proto_rawDescGZIP(), []int{2} +} + +func (x *CountResponse) GetNumber() int64 { + if x != nil { + return x.Number + } + return 0 +} + +type RegisterMessage struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + GrainId string `protobuf:"bytes,1,opt,name=grain_id,json=grainId,proto3" json:"grain_id,omitempty"` +} + +func (x *RegisterMessage) Reset() { + *x = RegisterMessage{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RegisterMessage) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RegisterMessage) ProtoMessage() {} + +func (x *RegisterMessage) ProtoReflect() protoreflect.Message { + mi := &file_protos_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RegisterMessage.ProtoReflect.Descriptor instead. +func (*RegisterMessage) Descriptor() ([]byte, []int) { + return file_protos_proto_rawDescGZIP(), []int{3} +} + +func (x *RegisterMessage) GetGrainId() string { + if x != nil { + return x.GrainId + } + return "" +} + +type TotalsResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Totals map[string]int64 `protobuf:"bytes,1,rep,name=totals,proto3" json:"totals,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"` +} + +func (x *TotalsResponse) Reset() { + *x = TotalsResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *TotalsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TotalsResponse) ProtoMessage() {} + +func (x *TotalsResponse) ProtoReflect() protoreflect.Message { + mi := &file_protos_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TotalsResponse.ProtoReflect.Descriptor instead. +func (*TotalsResponse) Descriptor() ([]byte, []int) { + return file_protos_proto_rawDescGZIP(), []int{4} +} + +func (x *TotalsResponse) GetTotals() map[string]int64 { + if x != nil { + return x.Totals + } + return nil +} + +var File_protos_proto protoreflect.FileDescriptor + +var file_protos_proto_rawDesc = []byte{ + 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06, + 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x22, 0x06, 0x0a, 0x04, 0x4e, 0x6f, 0x6f, 0x70, 0x22, 0x27, + 0x0a, 0x0d, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x16, 0x0a, 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x22, 0x27, 0x0a, 0x0d, 0x43, 0x6f, 0x75, 0x6e, 0x74, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6e, 0x75, 0x6d, 0x62, + 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, + 0x22, 0x2c, 0x0a, 0x0f, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x67, 0x72, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x67, 0x72, 0x61, 0x69, 0x6e, 0x49, 0x64, 0x22, 0x87, + 0x01, 0x0a, 0x0e, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x3a, 0x0a, 0x06, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x22, 0x2e, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x2e, 0x54, 0x6f, 0x74, 0x61, 0x6c, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x73, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x73, 0x1a, 0x39, 0x0a, + 0x0b, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, + 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, + 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x32, 0xb4, 0x01, 0x0a, 0x0a, 0x43, 0x61, 0x6c, + 0x63, 0x75, 0x6c, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x35, 0x0a, 0x03, 0x41, 0x64, 0x64, 0x12, 0x15, + 0x2e, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x2e, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x2e, 0x43, + 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3a, + 0x0a, 0x08, 0x53, 0x75, 0x62, 0x74, 0x72, 0x61, 0x63, 0x74, 0x12, 0x15, 0x2e, 0x73, 0x68, 0x61, + 0x72, 0x65, 0x64, 0x2e, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x15, 0x2e, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x2e, 0x43, 0x6f, 0x75, 0x6e, 0x74, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x33, 0x0a, 0x0a, 0x47, 0x65, + 0x74, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x12, 0x0c, 0x2e, 0x73, 0x68, 0x61, 0x72, 0x65, + 0x64, 0x2e, 0x4e, 0x6f, 0x6f, 0x70, 0x1a, 0x15, 0x2e, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x2e, + 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x32, + 0xbd, 0x01, 0x0a, 0x07, 0x54, 0x72, 0x61, 0x63, 0x6b, 0x65, 0x72, 0x12, 0x38, 0x0a, 0x0d, 0x52, + 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x47, 0x72, 0x61, 0x69, 0x6e, 0x12, 0x17, 0x2e, 0x73, + 0x68, 0x61, 0x72, 0x65, 0x64, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x0c, 0x2e, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x2e, 0x4e, + 0x6f, 0x6f, 0x70, 0x22, 0x00, 0x12, 0x3a, 0x0a, 0x0f, 0x44, 0x65, 0x72, 0x65, 0x67, 0x69, 0x73, + 0x74, 0x65, 0x72, 0x47, 0x72, 0x61, 0x69, 0x6e, 0x12, 0x17, 0x2e, 0x73, 0x68, 0x61, 0x72, 0x65, + 0x64, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x1a, 0x0c, 0x2e, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x2e, 0x4e, 0x6f, 0x6f, 0x70, 0x22, + 0x00, 0x12, 0x3c, 0x0a, 0x12, 0x42, 0x72, 0x6f, 0x61, 0x64, 0x63, 0x61, 0x73, 0x74, 0x47, 0x65, + 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x12, 0x0c, 0x2e, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, + 0x2e, 0x4e, 0x6f, 0x6f, 0x70, 0x1a, 0x16, 0x2e, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x2e, 0x54, + 0x6f, 0x74, 0x61, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, + 0x46, 0x5a, 0x44, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x73, + 0x79, 0x6e, 0x6b, 0x72, 0x6f, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x61, 0x63, 0x74, 0x6f, + 0x72, 0x2d, 0x67, 0x6f, 0x2f, 0x5f, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x2f, 0x63, + 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x2d, 0x62, 0x72, 0x6f, 0x61, 0x64, 0x63, 0x61, 0x73, 0x74, + 0x2f, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_protos_proto_rawDescOnce sync.Once + file_protos_proto_rawDescData = file_protos_proto_rawDesc +) + +func file_protos_proto_rawDescGZIP() []byte { + file_protos_proto_rawDescOnce.Do(func() { + file_protos_proto_rawDescData = protoimpl.X.CompressGZIP(file_protos_proto_rawDescData) + }) + return file_protos_proto_rawDescData +} + +var file_protos_proto_msgTypes = make([]protoimpl.MessageInfo, 6) +var file_protos_proto_goTypes = []interface{}{ + (*Noop)(nil), // 0: shared.Noop + (*NumberRequest)(nil), // 1: shared.NumberRequest + (*CountResponse)(nil), // 2: shared.CountResponse + (*RegisterMessage)(nil), // 3: shared.RegisterMessage + (*TotalsResponse)(nil), // 4: shared.TotalsResponse + nil, // 5: shared.TotalsResponse.TotalsEntry +} +var file_protos_proto_depIdxs = []int32{ + 5, // 0: shared.TotalsResponse.totals:type_name -> shared.TotalsResponse.TotalsEntry + 1, // 1: shared.Calculator.Add:input_type -> shared.NumberRequest + 1, // 2: shared.Calculator.Subtract:input_type -> shared.NumberRequest + 0, // 3: shared.Calculator.GetCurrent:input_type -> shared.Noop + 3, // 4: shared.Tracker.RegisterGrain:input_type -> shared.RegisterMessage + 3, // 5: shared.Tracker.DeregisterGrain:input_type -> shared.RegisterMessage + 0, // 6: shared.Tracker.BroadcastGetCounts:input_type -> shared.Noop + 2, // 7: shared.Calculator.Add:output_type -> shared.CountResponse + 2, // 8: shared.Calculator.Subtract:output_type -> shared.CountResponse + 2, // 9: shared.Calculator.GetCurrent:output_type -> shared.CountResponse + 0, // 10: shared.Tracker.RegisterGrain:output_type -> shared.Noop + 0, // 11: shared.Tracker.DeregisterGrain:output_type -> shared.Noop + 4, // 12: shared.Tracker.BroadcastGetCounts:output_type -> shared.TotalsResponse + 7, // [7:13] is the sub-list for method output_type + 1, // [1:7] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name +} + +func init() { file_protos_proto_init() } +func file_protos_proto_init() { + if File_protos_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_protos_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Noop); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NumberRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CountResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RegisterMessage); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TotalsResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_protos_proto_rawDesc, + NumEnums: 0, + NumMessages: 6, + NumExtensions: 0, + NumServices: 2, + }, + GoTypes: file_protos_proto_goTypes, + DependencyIndexes: file_protos_proto_depIdxs, + MessageInfos: file_protos_proto_msgTypes, + }.Build() + File_protos_proto = out.File + file_protos_proto_rawDesc = nil + file_protos_proto_goTypes = nil + file_protos_proto_depIdxs = nil +} diff --git a/_examples/cluster-broadcast/shared/protos.proto b/_examples/cluster-broadcast/shared/protos.proto new file mode 100644 index 0000000000000000000000000000000000000000..5099070ebb3387116d52fb4615caa332c2efa8a6 --- /dev/null +++ b/_examples/cluster-broadcast/shared/protos.proto @@ -0,0 +1,33 @@ +syntax = "proto3"; +package shared; +option go_package = "gitee.com/simplexyz/simpleactor-go/_examples/cluster-broadcast/shared"; + +message Noop {} + +message NumberRequest { + int64 number = 1; +} + +message CountResponse { + int64 number = 1; +} + +service Calculator { + rpc Add(NumberRequest) returns (CountResponse) {} + rpc Subtract(NumberRequest) returns (CountResponse) {} + rpc GetCurrent(Noop) returns (CountResponse) {} +} + +message RegisterMessage { + string grain_id = 1; +} + +message TotalsResponse { + map totals = 1; +} + +service Tracker { + rpc RegisterGrain(RegisterMessage) returns (Noop) {} + rpc DeregisterGrain(RegisterMessage) returns (Noop) {} + rpc BroadcastGetCounts(Noop) returns (TotalsResponse) {} +} diff --git a/_examples/cluster-broadcast/shared/protos_protoactor.go b/_examples/cluster-broadcast/shared/protos_protoactor.go new file mode 100644 index 0000000000000000000000000000000000000000..109cb792c80656f5c68e33b5b7f8fec03cc882ee --- /dev/null +++ b/_examples/cluster-broadcast/shared/protos_protoactor.go @@ -0,0 +1,510 @@ +// Package shared is generated by protoactor-go/protoc-gen-gograin@0.1.0 +package shared + +import ( + "errors" + "fmt" + "math" + "time" + + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/cluster" + logmod "gitee.com/simplexyz/simpleactor-go/log" + "google.golang.org/protobuf/proto" +) + +var ( + plog = logmod.New(logmod.InfoLevel, "[GRAIN][shared]") + _ = proto.Marshal + _ = fmt.Errorf + _ = math.Inf +) + +// SetLogLevel sets the log level. +func SetLogLevel(level logmod.Level) { + plog.SetLevel(level) +} + +var xCalculatorFactory func() Calculator + +// CalculatorFactory produces a Calculator +func CalculatorFactory(factory func() Calculator) { + xCalculatorFactory = factory +} + +// GetCalculatorGrainClient instantiates a new CalculatorGrainClient with given Identity +func GetCalculatorGrainClient(c *cluster.Cluster, id string) *CalculatorGrainClient { + if c == nil { + panic(fmt.Errorf("nil cluster instance")) + } + if id == "" { + panic(fmt.Errorf("empty id")) + } + return &CalculatorGrainClient{Identity: id, cluster: c} +} + +// GetCalculatorKind instantiates a new cluster.Kind for Calculator +func GetCalculatorKind(opts ...actor.PropsOption) *cluster.Kind { + props := actor.PropsFromProducer(func() actor.Actor { + return &CalculatorActor{ + Timeout: 60 * time.Second, + } + }, opts...) + kind := cluster.NewKind("Calculator", props) + return kind +} + +// GetCalculatorKind instantiates a new cluster.Kind for Calculator +func NewCalculatorKind(factory func() Calculator, timeout time.Duration, opts ...actor.PropsOption) *cluster.Kind { + xCalculatorFactory = factory + props := actor.PropsFromProducer(func() actor.Actor { + return &CalculatorActor{ + Timeout: timeout, + } + }, opts...) + kind := cluster.NewKind("Calculator", props) + return kind +} + +// Calculator interfaces the services available to the Calculator +type Calculator interface { + Init(ctx cluster.GrainContext) + Terminate(ctx cluster.GrainContext) + ReceiveDefault(ctx cluster.GrainContext) + Add(*NumberRequest, cluster.GrainContext) (*CountResponse, error) + Subtract(*NumberRequest, cluster.GrainContext) (*CountResponse, error) + GetCurrent(*Noop, cluster.GrainContext) (*CountResponse, error) +} + +// CalculatorGrainClient holds the base data for the CalculatorGrain +type CalculatorGrainClient struct { + Identity string + cluster *cluster.Cluster +} + +// Add requests the execution on to the cluster with CallOptions +func (g *CalculatorGrainClient) Add(r *NumberRequest, opts ...cluster.GrainCallOption) (*CountResponse, error) { + bytes, err := proto.Marshal(r) + if err != nil { + return nil, err + } + reqMsg := &cluster.GrainRequest{MethodIndex: 0, MessageData: bytes} + resp, err := g.cluster.Call(g.Identity, "Calculator", reqMsg, opts...) + if err != nil { + return nil, err + } + switch msg := resp.(type) { + case *cluster.GrainResponse: + result := &CountResponse{} + err = proto.Unmarshal(msg.MessageData, result) + if err != nil { + return nil, err + } + return result, nil + case *cluster.GrainErrorResponse: + return nil, errors.New(msg.Err) + default: + return nil, errors.New("unknown response") + } +} + +// Subtract requests the execution on to the cluster with CallOptions +func (g *CalculatorGrainClient) Subtract(r *NumberRequest, opts ...cluster.GrainCallOption) (*CountResponse, error) { + bytes, err := proto.Marshal(r) + if err != nil { + return nil, err + } + reqMsg := &cluster.GrainRequest{MethodIndex: 1, MessageData: bytes} + resp, err := g.cluster.Call(g.Identity, "Calculator", reqMsg, opts...) + if err != nil { + return nil, err + } + switch msg := resp.(type) { + case *cluster.GrainResponse: + result := &CountResponse{} + err = proto.Unmarshal(msg.MessageData, result) + if err != nil { + return nil, err + } + return result, nil + case *cluster.GrainErrorResponse: + return nil, errors.New(msg.Err) + default: + return nil, errors.New("unknown response") + } +} + +// GetCurrent requests the execution on to the cluster with CallOptions +func (g *CalculatorGrainClient) GetCurrent(r *Noop, opts ...cluster.GrainCallOption) (*CountResponse, error) { + bytes, err := proto.Marshal(r) + if err != nil { + return nil, err + } + reqMsg := &cluster.GrainRequest{MethodIndex: 2, MessageData: bytes} + resp, err := g.cluster.Call(g.Identity, "Calculator", reqMsg, opts...) + if err != nil { + return nil, err + } + switch msg := resp.(type) { + case *cluster.GrainResponse: + result := &CountResponse{} + err = proto.Unmarshal(msg.MessageData, result) + if err != nil { + return nil, err + } + return result, nil + case *cluster.GrainErrorResponse: + return nil, errors.New(msg.Err) + default: + return nil, errors.New("unknown response") + } +} + +// CalculatorActor represents the actor structure +type CalculatorActor struct { + ctx cluster.GrainContext + inner Calculator + Timeout time.Duration +} + +// Receive ensures the lifecycle of the actor for the received message +func (a *CalculatorActor) Receive(ctx actor.Context) { + switch msg := ctx.Message().(type) { + case *actor.Started: // pass + case *cluster.ClusterInit: + a.ctx = cluster.NewGrainContext(ctx, msg.Identity, msg.Cluster) + a.inner = xCalculatorFactory() + a.inner.Init(a.ctx) + + if a.Timeout > 0 { + ctx.SetReceiveTimeout(a.Timeout) + } + case *actor.ReceiveTimeout: + ctx.Poison(ctx.Self()) + case *actor.Stopped: + a.inner.Terminate(a.ctx) + case actor.AutoReceiveMessage: // pass + case actor.SystemMessage: // pass + + case *cluster.GrainRequest: + switch msg.MethodIndex { + case 0: + req := &NumberRequest{} + err := proto.Unmarshal(msg.MessageData, req) + if err != nil { + plog.Error("Add(NumberRequest) proto.Unmarshal failed.", logmod.Error(err)) + resp := &cluster.GrainErrorResponse{Err: err.Error()} + ctx.Respond(resp) + return + } + r0, err := a.inner.Add(req, a.ctx) + if err != nil { + resp := &cluster.GrainErrorResponse{Err: err.Error()} + ctx.Respond(resp) + return + } + bytes, err := proto.Marshal(r0) + if err != nil { + plog.Error("Add(NumberRequest) proto.Marshal failed", logmod.Error(err)) + resp := &cluster.GrainErrorResponse{Err: err.Error()} + ctx.Respond(resp) + return + } + resp := &cluster.GrainResponse{MessageData: bytes} + ctx.Respond(resp) + case 1: + req := &NumberRequest{} + err := proto.Unmarshal(msg.MessageData, req) + if err != nil { + plog.Error("Subtract(NumberRequest) proto.Unmarshal failed.", logmod.Error(err)) + resp := &cluster.GrainErrorResponse{Err: err.Error()} + ctx.Respond(resp) + return + } + r0, err := a.inner.Subtract(req, a.ctx) + if err != nil { + resp := &cluster.GrainErrorResponse{Err: err.Error()} + ctx.Respond(resp) + return + } + bytes, err := proto.Marshal(r0) + if err != nil { + plog.Error("Subtract(NumberRequest) proto.Marshal failed", logmod.Error(err)) + resp := &cluster.GrainErrorResponse{Err: err.Error()} + ctx.Respond(resp) + return + } + resp := &cluster.GrainResponse{MessageData: bytes} + ctx.Respond(resp) + case 2: + req := &Noop{} + err := proto.Unmarshal(msg.MessageData, req) + if err != nil { + plog.Error("GetCurrent(Noop) proto.Unmarshal failed.", logmod.Error(err)) + resp := &cluster.GrainErrorResponse{Err: err.Error()} + ctx.Respond(resp) + return + } + r0, err := a.inner.GetCurrent(req, a.ctx) + if err != nil { + resp := &cluster.GrainErrorResponse{Err: err.Error()} + ctx.Respond(resp) + return + } + bytes, err := proto.Marshal(r0) + if err != nil { + plog.Error("GetCurrent(Noop) proto.Marshal failed", logmod.Error(err)) + resp := &cluster.GrainErrorResponse{Err: err.Error()} + ctx.Respond(resp) + return + } + resp := &cluster.GrainResponse{MessageData: bytes} + ctx.Respond(resp) + + } + default: + a.inner.ReceiveDefault(a.ctx) + } +} + +var xTrackerFactory func() Tracker + +// TrackerFactory produces a Tracker +func TrackerFactory(factory func() Tracker) { + xTrackerFactory = factory +} + +// GetTrackerGrainClient instantiates a new TrackerGrainClient with given Identity +func GetTrackerGrainClient(c *cluster.Cluster, id string) *TrackerGrainClient { + if c == nil { + panic(fmt.Errorf("nil cluster instance")) + } + if id == "" { + panic(fmt.Errorf("empty id")) + } + return &TrackerGrainClient{Identity: id, cluster: c} +} + +// GetTrackerKind instantiates a new cluster.Kind for Tracker +func GetTrackerKind(opts ...actor.PropsOption) *cluster.Kind { + props := actor.PropsFromProducer(func() actor.Actor { + return &TrackerActor{ + Timeout: 60 * time.Second, + } + }, opts...) + kind := cluster.NewKind("Tracker", props) + return kind +} + +// GetTrackerKind instantiates a new cluster.Kind for Tracker +func NewTrackerKind(factory func() Tracker, timeout time.Duration, opts ...actor.PropsOption) *cluster.Kind { + xTrackerFactory = factory + props := actor.PropsFromProducer(func() actor.Actor { + return &TrackerActor{ + Timeout: timeout, + } + }, opts...) + kind := cluster.NewKind("Tracker", props) + return kind +} + +// Tracker interfaces the services available to the Tracker +type Tracker interface { + Init(ctx cluster.GrainContext) + Terminate(ctx cluster.GrainContext) + ReceiveDefault(ctx cluster.GrainContext) + RegisterGrain(*RegisterMessage, cluster.GrainContext) (*Noop, error) + DeregisterGrain(*RegisterMessage, cluster.GrainContext) (*Noop, error) + BroadcastGetCounts(*Noop, cluster.GrainContext) (*TotalsResponse, error) +} + +// TrackerGrainClient holds the base data for the TrackerGrain +type TrackerGrainClient struct { + Identity string + cluster *cluster.Cluster +} + +// RegisterGrain requests the execution on to the cluster with CallOptions +func (g *TrackerGrainClient) RegisterGrain(r *RegisterMessage, opts ...cluster.GrainCallOption) (*Noop, error) { + bytes, err := proto.Marshal(r) + if err != nil { + return nil, err + } + reqMsg := &cluster.GrainRequest{MethodIndex: 0, MessageData: bytes} + resp, err := g.cluster.Call(g.Identity, "Tracker", reqMsg, opts...) + if err != nil { + return nil, err + } + switch msg := resp.(type) { + case *cluster.GrainResponse: + result := &Noop{} + err = proto.Unmarshal(msg.MessageData, result) + if err != nil { + return nil, err + } + return result, nil + case *cluster.GrainErrorResponse: + return nil, errors.New(msg.Err) + default: + return nil, errors.New("unknown response") + } +} + +// DeregisterGrain requests the execution on to the cluster with CallOptions +func (g *TrackerGrainClient) DeregisterGrain(r *RegisterMessage, opts ...cluster.GrainCallOption) (*Noop, error) { + bytes, err := proto.Marshal(r) + if err != nil { + return nil, err + } + reqMsg := &cluster.GrainRequest{MethodIndex: 1, MessageData: bytes} + resp, err := g.cluster.Call(g.Identity, "Tracker", reqMsg, opts...) + if err != nil { + return nil, err + } + switch msg := resp.(type) { + case *cluster.GrainResponse: + result := &Noop{} + err = proto.Unmarshal(msg.MessageData, result) + if err != nil { + return nil, err + } + return result, nil + case *cluster.GrainErrorResponse: + return nil, errors.New(msg.Err) + default: + return nil, errors.New("unknown response") + } +} + +// BroadcastGetCounts requests the execution on to the cluster with CallOptions +func (g *TrackerGrainClient) BroadcastGetCounts(r *Noop, opts ...cluster.GrainCallOption) (*TotalsResponse, error) { + bytes, err := proto.Marshal(r) + if err != nil { + return nil, err + } + reqMsg := &cluster.GrainRequest{MethodIndex: 2, MessageData: bytes} + resp, err := g.cluster.Call(g.Identity, "Tracker", reqMsg, opts...) + if err != nil { + return nil, err + } + switch msg := resp.(type) { + case *cluster.GrainResponse: + result := &TotalsResponse{} + err = proto.Unmarshal(msg.MessageData, result) + if err != nil { + return nil, err + } + return result, nil + case *cluster.GrainErrorResponse: + return nil, errors.New(msg.Err) + default: + return nil, errors.New("unknown response") + } +} + +// TrackerActor represents the actor structure +type TrackerActor struct { + ctx cluster.GrainContext + inner Tracker + Timeout time.Duration +} + +// Receive ensures the lifecycle of the actor for the received message +func (a *TrackerActor) Receive(ctx actor.Context) { + switch msg := ctx.Message().(type) { + case *actor.Started: // pass + case *cluster.ClusterInit: + a.ctx = cluster.NewGrainContext(ctx, msg.Identity, msg.Cluster) + a.inner = xTrackerFactory() + a.inner.Init(a.ctx) + + if a.Timeout > 0 { + ctx.SetReceiveTimeout(a.Timeout) + } + case *actor.ReceiveTimeout: + ctx.Poison(ctx.Self()) + case *actor.Stopped: + a.inner.Terminate(a.ctx) + case actor.AutoReceiveMessage: // pass + case actor.SystemMessage: // pass + + case *cluster.GrainRequest: + switch msg.MethodIndex { + case 0: + req := &RegisterMessage{} + err := proto.Unmarshal(msg.MessageData, req) + if err != nil { + plog.Error("RegisterGrain(RegisterMessage) proto.Unmarshal failed.", logmod.Error(err)) + resp := &cluster.GrainErrorResponse{Err: err.Error()} + ctx.Respond(resp) + return + } + r0, err := a.inner.RegisterGrain(req, a.ctx) + if err != nil { + resp := &cluster.GrainErrorResponse{Err: err.Error()} + ctx.Respond(resp) + return + } + bytes, err := proto.Marshal(r0) + if err != nil { + plog.Error("RegisterGrain(RegisterMessage) proto.Marshal failed", logmod.Error(err)) + resp := &cluster.GrainErrorResponse{Err: err.Error()} + ctx.Respond(resp) + return + } + resp := &cluster.GrainResponse{MessageData: bytes} + ctx.Respond(resp) + case 1: + req := &RegisterMessage{} + err := proto.Unmarshal(msg.MessageData, req) + if err != nil { + plog.Error("DeregisterGrain(RegisterMessage) proto.Unmarshal failed.", logmod.Error(err)) + resp := &cluster.GrainErrorResponse{Err: err.Error()} + ctx.Respond(resp) + return + } + r0, err := a.inner.DeregisterGrain(req, a.ctx) + if err != nil { + resp := &cluster.GrainErrorResponse{Err: err.Error()} + ctx.Respond(resp) + return + } + bytes, err := proto.Marshal(r0) + if err != nil { + plog.Error("DeregisterGrain(RegisterMessage) proto.Marshal failed", logmod.Error(err)) + resp := &cluster.GrainErrorResponse{Err: err.Error()} + ctx.Respond(resp) + return + } + resp := &cluster.GrainResponse{MessageData: bytes} + ctx.Respond(resp) + case 2: + req := &Noop{} + err := proto.Unmarshal(msg.MessageData, req) + if err != nil { + plog.Error("BroadcastGetCounts(Noop) proto.Unmarshal failed.", logmod.Error(err)) + resp := &cluster.GrainErrorResponse{Err: err.Error()} + ctx.Respond(resp) + return + } + r0, err := a.inner.BroadcastGetCounts(req, a.ctx) + if err != nil { + resp := &cluster.GrainErrorResponse{Err: err.Error()} + ctx.Respond(resp) + return + } + bytes, err := proto.Marshal(r0) + if err != nil { + plog.Error("BroadcastGetCounts(Noop) proto.Marshal failed", logmod.Error(err)) + resp := &cluster.GrainErrorResponse{Err: err.Error()} + ctx.Respond(resp) + return + } + resp := &cluster.GrainResponse{MessageData: bytes} + ctx.Respond(resp) + + } + default: + a.inner.ReceiveDefault(a.ctx) + } +} diff --git a/_examples/cluster-broadcast/shared/tracker-grain.go b/_examples/cluster-broadcast/shared/tracker-grain.go new file mode 100644 index 0000000000000000000000000000000000000000..866042eea6baee1527687b75af00636b09d6e349 --- /dev/null +++ b/_examples/cluster-broadcast/shared/tracker-grain.go @@ -0,0 +1,49 @@ +package shared + +import ( + "fmt" + "strings" + + "gitee.com/simplexyz/simpleactor-go/cluster" +) + +type TrackGrain struct { + grainsMap map[string]bool +} + +func (t *TrackGrain) ReceiveDefault(ctx cluster.GrainContext) { +} + +func (t *TrackGrain) Init(ctx cluster.GrainContext) { + t.grainsMap = map[string]bool{} +} + +func (t *TrackGrain) Terminate(ctx cluster.GrainContext) { +} + +func (t *TrackGrain) RegisterGrain(n *RegisterMessage, ctx cluster.GrainContext) (*Noop, error) { + parts := strings.Split(n.GrainId, "/") + grainID := parts[len(parts)-1] + t.grainsMap[grainID] = true + return &Noop{}, nil +} + +func (t *TrackGrain) DeregisterGrain(n *RegisterMessage, ctx cluster.GrainContext) (*Noop, error) { + delete(t.grainsMap, n.GrainId) + return &Noop{}, nil +} + +func (t *TrackGrain) BroadcastGetCounts(n *Noop, ctx cluster.GrainContext) (*TotalsResponse, error) { + totals := map[string]int64{} + for grainAddress := range t.grainsMap { + calcGrain := GetCalculatorGrainClient(ctx.Cluster(), grainAddress) + grainTotal, err := calcGrain.GetCurrent(&Noop{}) + if err != nil { + fmt.Sprintf("Grain %s issued an error : %s", grainAddress, err) + } + fmt.Sprintf("Grain %s - %v", grainAddress, grainTotal.Number) + totals[grainAddress] = grainTotal.Number + } + + return &TotalsResponse{Totals: totals}, nil +} diff --git a/_examples/cluster-eventstream-broadcast/build.sh b/_examples/cluster-eventstream-broadcast/build.sh new file mode 100644 index 0000000000000000000000000000000000000000..28f9be836fdee9993e2446e07361301c1eac81a5 --- /dev/null +++ b/_examples/cluster-eventstream-broadcast/build.sh @@ -0,0 +1,2 @@ +protoc --go_out=. --go_opt=paths=source_relative --proto_path=. protos.proto +protoc -I=. -I=$GOPATH/src --gograinv2_out=. protos.proto diff --git a/_examples/cluster-eventstream-broadcast/go.mod b/_examples/cluster-eventstream-broadcast/go.mod new file mode 100644 index 0000000000000000000000000000000000000000..c09da302c5ed8e0f4aa471b29a9b5b9925370aeb --- /dev/null +++ b/_examples/cluster-eventstream-broadcast/go.mod @@ -0,0 +1,54 @@ +module cluster-evenstream-broadcast + +go 1.21 + +toolchain go1.21.2 + +replace gitee.com/simplexyz/simpleactor-go => ../.. + +require ( + gitee.com/simplexyz/simpleactor-go v0.0.0-00010101000000-000000000000 + github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716 + github.com/google/uuid v1.3.0 + google.golang.org/protobuf v1.31.0 +) + +require ( + github.com/Workiva/go-datastructures v1.1.1 // indirect + github.com/asynkron/gofun v0.0.0-20220329210725-34fed760f4c2 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/emirpasic/gods v1.18.1 // indirect + github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/labstack/echo v3.3.10+incompatible // indirect + github.com/labstack/gommon v0.3.1 // indirect + github.com/lithammer/shortuuid/v4 v4.0.0 // indirect + github.com/mattn/go-colorable v0.1.12 // indirect + github.com/mattn/go-isatty v0.0.16 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/orcaman/concurrent-map v1.0.0 // indirect + github.com/prometheus/client_golang v1.16.0 // indirect + github.com/prometheus/client_model v0.4.0 // indirect + github.com/prometheus/common v0.42.0 // indirect + github.com/prometheus/procfs v0.10.1 // indirect + github.com/twmb/murmur3 v1.1.6 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/valyala/fasttemplate v1.2.1 // indirect + go.opentelemetry.io/otel v1.16.0 // indirect + go.opentelemetry.io/otel/exporters/prometheus v0.39.0 // indirect + go.opentelemetry.io/otel/metric v1.16.0 // indirect + go.opentelemetry.io/otel/sdk v1.16.0 // indirect + go.opentelemetry.io/otel/sdk/metric v0.39.0 // indirect + go.opentelemetry.io/otel/trace v1.16.0 // indirect + golang.org/x/crypto v0.14.0 // indirect + golang.org/x/exp v0.0.0-20220827204233-334a2380cb91 // indirect + golang.org/x/net v0.17.0 // indirect + golang.org/x/sync v0.3.0 // indirect + golang.org/x/sys v0.13.0 // indirect + golang.org/x/text v0.13.0 // indirect + google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97 // indirect + google.golang.org/grpc v1.58.3 // indirect +) diff --git a/_examples/cluster-eventstream-broadcast/go.sum b/_examples/cluster-eventstream-broadcast/go.sum new file mode 100644 index 0000000000000000000000000000000000000000..f5151bdf77fa2b926bd72affe6b715e74a4f7b0f --- /dev/null +++ b/_examples/cluster-eventstream-broadcast/go.sum @@ -0,0 +1,150 @@ +github.com/Workiva/go-datastructures v1.1.1 h1:9G5u1UqKt6ABseAffHGNfbNQd7omRlWE5QaxNruzhE0= +github.com/Workiva/go-datastructures v1.1.1/go.mod h1:1yZL+zfsztete+ePzZz/Zb1/t5BnDuE2Ya2MMGhzP6A= +github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716 h1:SgyG4sXkrlalMoCfp20LiNPNhfJS7ez3opNdtihIxPc= +github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716/go.mod h1:/zSlF0T2ArAsTG6SVu8d8qlK+19jjudjA3wWsxnGFHg= +github.com/asynkron/gofun v0.0.0-20220329210725-34fed760f4c2 h1:jEsFZ9d/ieJGVrx3fSPi8oe/qv21fRmyUL5cS3ZEn5A= +github.com/asynkron/gofun v0.0.0-20220329210725-34fed760f4c2/go.mod h1:5GMOSqaYxNWwuVRWyampTPJEntwz7Mj9J8v1a7gSU2E= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/labstack/echo v3.3.10+incompatible h1:pGRcYk231ExFAyoAjAfD85kQzRJCRI8bbnE7CX5OEgg= +github.com/labstack/echo v3.3.10+incompatible/go.mod h1:0INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s= +github.com/labstack/gommon v0.3.1 h1:OomWaJXm7xR6L1HmEtGyQf26TEn7V6X88mktX9kee9o= +github.com/labstack/gommon v0.3.1/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM= +github.com/lithammer/shortuuid/v4 v4.0.0 h1:QRbbVkfgNippHOS8PXDkti4NaWeyYfcBTHtw7k08o4c= +github.com/lithammer/shortuuid/v4 v4.0.0/go.mod h1:Zs8puNcrvf2rV9rTH51ZLLcj7ZXqQI3lv67aw4KiB1Y= +github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/orcaman/concurrent-map v1.0.0 h1:I/2A2XPCb4IuQWcQhBhSwGfiuybl/J0ev9HDbW65HOY= +github.com/orcaman/concurrent-map v1.0.0/go.mod h1:Lu3tH6HLW3feq74c2GC+jIMS/K2CFcDWnWD9XkenwhI= +github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= +github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= +github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= +github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= +github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= +github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= +github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/tinylib/msgp v1.1.5/go.mod h1:eQsjooMTnV42mHu917E26IogZ2930nFyBQdofk10Udg= +github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q= +github.com/twmb/murmur3 v1.1.6 h1:mqrRot1BRxm+Yct+vavLMou2/iJt0tNVTTC0QoIjaZg= +github.com/twmb/murmur3 v1.1.6/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasttemplate v1.2.1 h1:TVEnxayobAdVkhQfrfes2IzOB6o+z4roRkPF52WA1u4= +github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s= +go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4= +go.opentelemetry.io/otel/exporters/prometheus v0.39.0 h1:whAaiHxOatgtKd+w0dOi//1KUxj3KoPINZdtDaDj3IA= +go.opentelemetry.io/otel/exporters/prometheus v0.39.0/go.mod h1:4jo5Q4CROlCpSPsXLhymi+LYrDXd2ObU5wbKayfZs7Y= +go.opentelemetry.io/otel/metric v1.16.0 h1:RbrpwVG1Hfv85LgnZ7+txXioPDoh6EdbZHo26Q3hqOo= +go.opentelemetry.io/otel/metric v1.16.0/go.mod h1:QE47cpOmkwipPiefDwo2wDzwJrlfxxNYodqc4xnGCo4= +go.opentelemetry.io/otel/sdk v1.16.0 h1:Z1Ok1YsijYL0CSJpHt4cS3wDDh7p572grzNrBMiMWgE= +go.opentelemetry.io/otel/sdk v1.16.0/go.mod h1:tMsIuKXuuIWPBAOrH+eHtvhTL+SntFtXF9QD68aP6p4= +go.opentelemetry.io/otel/sdk/metric v0.39.0 h1:Kun8i1eYf48kHH83RucG93ffz0zGV1sh46FAScOTuDI= +go.opentelemetry.io/otel/sdk/metric v0.39.0/go.mod h1:piDIRgjcK7u0HCL5pCA4e74qpK/jk3NiUoAHATVAmiI= +go.opentelemetry.io/otel/trace v1.16.0 h1:8JRpaObFoW0pxuVPapkgH8UhHQj+bJW8jJsCZEu5MQs= +go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/exp v0.0.0-20220827204233-334a2380cb91 h1:tnebWN09GYg9OLPss1KXj8txwZc6X6uMr6VFdcGNbHw= +golang.org/x/exp v0.0.0-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= +golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 h1:DdoeryqhaXp1LtT/emMP1BRJPHHKFi5akj/nbx/zNTA= +google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= +google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97/go.mod h1:t1VqOqqvce95G3hIDCT5FeO3YUc6Q4Oe24L/+rNMxRk= +google.golang.org/grpc v1.55.0 h1:3Oj82/tFSCeUrRTg/5E/7d/W5A1tj6Ky1ABAuZuv5ag= +google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8= +google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/_examples/cluster-eventstream-broadcast/main.go b/_examples/cluster-eventstream-broadcast/main.go new file mode 100644 index 0000000000000000000000000000000000000000..abff006e0a8b45ab40a51fcff22b0a1965d3da99 --- /dev/null +++ b/_examples/cluster-eventstream-broadcast/main.go @@ -0,0 +1,101 @@ +package main + +import ( + "flag" + "fmt" + "strings" + "time" + + "gitee.com/simplexyz/simpleactor-go/cluster/clusterproviders/automanaged" + "gitee.com/simplexyz/simpleactor-go/cluster/identitylookup/disthash" + + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/cluster" + "gitee.com/simplexyz/simpleactor-go/remote" + console "github.com/asynkron/goconsole" + "github.com/google/uuid" +) + +func main() { + remotingPort, clusteringPort, clusterMembers := getArgs() + + cluster := startNode(remotingPort, clusteringPort, clusterMembers) + + cancelPublisher := publish(cluster) + cancelSubscriber := subscribe(cluster) + + console.ReadLine() + + cancelPublisher() + cancelSubscriber() + + cluster.Shutdown(true) +} + +func startNode(remotingPort int, clusteringPort int, clusterMembers []string) *cluster.Cluster { + system := actor.NewActorSystem() + + provider := automanaged.NewWithConfig(2*time.Second, clusteringPort, clusterMembers...) + lookup := disthash.New() + config := remote.Configure("localhost", remotingPort) + + clusterConfig := cluster.Configure("my-cluster", provider, lookup, config) + cluster := cluster.New(system, clusterConfig) + + cluster.StartMember() + + return cluster +} + +func publish(cluster *cluster.Cluster) (cancel func()) { + id := uuid.New().String()[:8] + done := make(chan struct{}) + + ticker := time.NewTicker(time.Second) + + go func() { + for { + select { + case <-done: + return + case <-ticker.C: + fmt.Printf("==>> Publisher %s sending message\n", id) + event := &MyEvent{ + Description: fmt.Sprintf("Hello from %s at %s", id, time.Now().Format(time.RFC3339)), + } + cluster.MemberList.BroadcastEvent(event, true) + } + } + }() + + return func() { + ticker.Stop() + done <- struct{}{} + } +} + +func subscribe(cluster *cluster.Cluster) (cancel func()) { + subscription := cluster.ActorSystem.EventStream.Subscribe(func(evt interface{}) { + if event, ok := evt.(*MyEvent); ok { + fmt.Printf("<<== Subscriber received event: %s\n", event.Description) + } + }) + + return func() { + cluster.ActorSystem.EventStream.Unsubscribe(subscription) + } +} + +func getArgs() (remotingPort int, clusteringPort int, clusterMembers []string) { + flag.IntVar(&remotingPort, "remoting-port", 18080, "port for actor remote communication") + flag.IntVar(&clusteringPort, "clustering-port", 28080, "port for cluster provider communication") + + var membersString string + flag.StringVar(&membersString, "members", "localhost:28080", "cluster members e.g. `--members=localhost:28080,localhost:28081") + + flag.Parse() + + clusterMembers = strings.Split(membersString, ",") + + return +} diff --git a/_examples/cluster-eventstream-broadcast/protos.pb.go b/_examples/cluster-eventstream-broadcast/protos.pb.go new file mode 100644 index 0000000000000000000000000000000000000000..e75b544e387ae3f98d714a9513829daa80516164 --- /dev/null +++ b/_examples/cluster-eventstream-broadcast/protos.pb.go @@ -0,0 +1,146 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc v3.19.1 +// source: protos.proto + +package main + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type MyEvent struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Description string `protobuf:"bytes,1,opt,name=description,proto3" json:"description,omitempty"` +} + +func (x *MyEvent) Reset() { + *x = MyEvent{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *MyEvent) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MyEvent) ProtoMessage() {} + +func (x *MyEvent) ProtoReflect() protoreflect.Message { + mi := &file_protos_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use MyEvent.ProtoReflect.Descriptor instead. +func (*MyEvent) Descriptor() ([]byte, []int) { + return file_protos_proto_rawDescGZIP(), []int{0} +} + +func (x *MyEvent) GetDescription() string { + if x != nil { + return x.Description + } + return "" +} + +var File_protos_proto protoreflect.FileDescriptor + +var file_protos_proto_rawDesc = []byte{ + 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x04, + 0x6d, 0x61, 0x69, 0x6e, 0x22, 0x2b, 0x0a, 0x07, 0x4d, 0x79, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, + 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, + 0x6e, 0x42, 0x50, 0x5a, 0x4e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x61, 0x73, 0x79, 0x6e, 0x6b, 0x72, 0x6f, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x61, 0x63, + 0x74, 0x6f, 0x72, 0x2d, 0x67, 0x6f, 0x2f, 0x5f, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, + 0x2f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x2d, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x74, + 0x72, 0x65, 0x61, 0x6d, 0x2d, 0x62, 0x72, 0x6f, 0x61, 0x64, 0x63, 0x61, 0x73, 0x74, 0x2f, 0x6d, + 0x61, 0x69, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_protos_proto_rawDescOnce sync.Once + file_protos_proto_rawDescData = file_protos_proto_rawDesc +) + +func file_protos_proto_rawDescGZIP() []byte { + file_protos_proto_rawDescOnce.Do(func() { + file_protos_proto_rawDescData = protoimpl.X.CompressGZIP(file_protos_proto_rawDescData) + }) + return file_protos_proto_rawDescData +} + +var file_protos_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_protos_proto_goTypes = []interface{}{ + (*MyEvent)(nil), // 0: main.MyEvent +} +var file_protos_proto_depIdxs = []int32{ + 0, // [0:0] is the sub-list for method output_type + 0, // [0:0] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_protos_proto_init() } +func file_protos_proto_init() { + if File_protos_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_protos_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*MyEvent); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_protos_proto_rawDesc, + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_protos_proto_goTypes, + DependencyIndexes: file_protos_proto_depIdxs, + MessageInfos: file_protos_proto_msgTypes, + }.Build() + File_protos_proto = out.File + file_protos_proto_rawDesc = nil + file_protos_proto_goTypes = nil + file_protos_proto_depIdxs = nil +} diff --git a/_examples/cluster-eventstream-broadcast/protos.proto b/_examples/cluster-eventstream-broadcast/protos.proto new file mode 100644 index 0000000000000000000000000000000000000000..ebf7a53480814cbefe65b5d9d11f621ce7a0192d --- /dev/null +++ b/_examples/cluster-eventstream-broadcast/protos.proto @@ -0,0 +1,7 @@ +syntax = "proto3"; +option go_package = "gitee.com/simplexyz/simpleactor-go/_examples/cluster-eventstream-broadcast/main"; +package main; + +message MyEvent { + string description = 1; +} \ No newline at end of file diff --git a/_examples/cluster-eventstream-broadcast/run.sh b/_examples/cluster-eventstream-broadcast/run.sh new file mode 100644 index 0000000000000000000000000000000000000000..40808c42834b385475129f97c778ea1bfa5a75c0 --- /dev/null +++ b/_examples/cluster-eventstream-broadcast/run.sh @@ -0,0 +1,6 @@ +tmux new-session -d -s eg +tmux split-window -t "eg:0" -v +tmux send-keys -t "eg:0.0" "go run ./... --remoting-port=18080 --clustering-port=28080 --members=localhost:28080,localhost:28081" Enter +tmux send-keys -t "eg:0.1" "go run ./... --remoting-port=18081 --clustering-port=28081 --members=localhost:28080,localhost:28081" Enter +tmux attach -t eg +tmux kill-session -t eg \ No newline at end of file diff --git a/_examples/cluster-grain/Makefile b/_examples/cluster-grain/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..0bb231412a50360c6f984c3a358922c50ce05e49 --- /dev/null +++ b/_examples/cluster-grain/Makefile @@ -0,0 +1,11 @@ +start: + tmux new-session -d -s eg + tmux split-window -t "eg:0" -v + tmux send-keys -t "eg:0.0" "go run node1/main.go" Enter + tmux send-keys -t "eg:0.1" "go run node2/main.go" Enter + tmux attach -t eg + tmux kill-session -t eg + + +stop: + tmux kill-session -t eg diff --git a/_examples/cluster-grain/docker-compose.yml b/_examples/cluster-grain/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..3cfdc0477bbf722994271019dd9d88438116a101 --- /dev/null +++ b/_examples/cluster-grain/docker-compose.yml @@ -0,0 +1,48 @@ +version: '3.7' +services: + + consul-agent-1: &consul-agent + image: hashicorp/consul:latest + networks: + - consul + command: "agent -retry-join consul-server-bootstrap -client 0.0.0.0" + + consul-agent-2: + <<: *consul-agent + + consul-agent-3: + <<: *consul-agent + + consul-server-1: &consul-server + <<: *consul-agent + command: "agent -server -retry-join consul-server-bootstrap -client 0.0.0.0" + + consul-server-2: + <<: *consul-server + + consul-server-bootstrap: + <<: *consul-agent + ports: + - "8400:8400" + - "8500:8500" + - "8600:8600" + - "8600:8600/udp" + command: "agent -server -bootstrap-expect 3 -ui -client 0.0.0.0" + + mongodb: + image: mongo:latest + ports: + - 127.0.0.1:27017:27017 + volumes: + - mongodb_data:/data/db + + redis: + image: redis:latest + ports: + - 127.0.0.1:6379:6379 + +networks: + consul: + +volumes: + mongodb_data: \ No newline at end of file diff --git a/_examples/cluster-grain/go.mod b/_examples/cluster-grain/go.mod new file mode 100644 index 0000000000000000000000000000000000000000..8f747d5e2e3c94ba4f18a969979a6a78d5c866a9 --- /dev/null +++ b/_examples/cluster-grain/go.mod @@ -0,0 +1,60 @@ +module cluster-grain + +go 1.21 + +toolchain go1.21.2 + +replace gitee.com/simplexyz/simpleactor-go => ../.. + +require ( + gitee.com/simplexyz/simpleactor-go v0.0.0-00010101000000-000000000000 + github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716 + google.golang.org/protobuf v1.31.0 +) + +require ( + github.com/Workiva/go-datastructures v1.1.1 // indirect + github.com/armon/go-metrics v0.4.0 // indirect + github.com/asynkron/gofun v0.0.0-20220329210725-34fed760f4c2 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/emirpasic/gods v1.18.1 // indirect + github.com/fatih/color v1.13.0 // indirect + github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/google/btree v1.0.1 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/hashicorp/consul/api v1.18.0 // indirect + github.com/hashicorp/go-cleanhttp v0.5.2 // indirect + github.com/hashicorp/go-hclog v1.2.0 // indirect + github.com/hashicorp/go-immutable-radix v1.3.1 // indirect + github.com/hashicorp/go-rootcerts v1.0.2 // indirect + github.com/hashicorp/golang-lru v0.5.4 // indirect + github.com/hashicorp/serf v0.10.1 // indirect + github.com/lithammer/shortuuid/v4 v4.0.0 // indirect + github.com/mattn/go-colorable v0.1.12 // indirect + github.com/mattn/go-isatty v0.0.16 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/orcaman/concurrent-map v1.0.0 // indirect + github.com/prometheus/client_golang v1.16.0 // indirect + github.com/prometheus/client_model v0.4.0 // indirect + github.com/prometheus/common v0.42.0 // indirect + github.com/prometheus/procfs v0.10.1 // indirect + github.com/twmb/murmur3 v1.1.6 // indirect + go.opentelemetry.io/otel v1.16.0 // indirect + go.opentelemetry.io/otel/exporters/prometheus v0.39.0 // indirect + go.opentelemetry.io/otel/metric v1.16.0 // indirect + go.opentelemetry.io/otel/sdk v1.16.0 // indirect + go.opentelemetry.io/otel/sdk/metric v0.39.0 // indirect + go.opentelemetry.io/otel/trace v1.16.0 // indirect + golang.org/x/exp v0.0.0-20220827204233-334a2380cb91 // indirect + golang.org/x/net v0.17.0 // indirect + golang.org/x/sys v0.13.0 // indirect + golang.org/x/text v0.13.0 // indirect + google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97 // indirect + google.golang.org/grpc v1.58.3 // indirect +) diff --git a/_examples/cluster-grain/go.sum b/_examples/cluster-grain/go.sum new file mode 100644 index 0000000000000000000000000000000000000000..198f0c3fd3de7cb23fa4607c1a34c58ec417b305 --- /dev/null +++ b/_examples/cluster-grain/go.sum @@ -0,0 +1,322 @@ +github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= +github.com/Workiva/go-datastructures v1.1.1 h1:9G5u1UqKt6ABseAffHGNfbNQd7omRlWE5QaxNruzhE0= +github.com/Workiva/go-datastructures v1.1.1/go.mod h1:1yZL+zfsztete+ePzZz/Zb1/t5BnDuE2Ya2MMGhzP6A= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-metrics v0.4.0 h1:yCQqn7dwca4ITXb+CbubHmedzaQYHhNhrEXLYUeEe8Q= +github.com/armon/go-metrics v0.4.0/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716 h1:SgyG4sXkrlalMoCfp20LiNPNhfJS7ez3opNdtihIxPc= +github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716/go.mod h1:/zSlF0T2ArAsTG6SVu8d8qlK+19jjudjA3wWsxnGFHg= +github.com/asynkron/gofun v0.0.0-20220329210725-34fed760f4c2 h1:jEsFZ9d/ieJGVrx3fSPi8oe/qv21fRmyUL5cS3ZEn5A= +github.com/asynkron/gofun v0.0.0-20220329210725-34fed760f4c2/go.mod h1:5GMOSqaYxNWwuVRWyampTPJEntwz7Mj9J8v1a7gSU2E= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= +github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= +github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= +github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/hashicorp/consul/api v1.18.0 h1:R7PPNzTCeN6VuQNDwwhZWJvzCtGSrNpJqfb22h3yH9g= +github.com/hashicorp/consul/api v1.18.0/go.mod h1:owRRGJ9M5xReDC5nfT8FTJrNAPbT4NM6p/k+d03q2v4= +github.com/hashicorp/consul/sdk v0.13.0 h1:lce3nFlpv8humJL8rNrrGHYSKc3q+Kxfeg3Ii1m6ZWU= +github.com/hashicorp/consul/sdk v0.13.0/go.mod h1:0hs/l5fOVhJy/VdcoaNqUSi2AUs95eF5WKtv+EYIQqE= +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.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= +github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-hclog v1.2.0 h1:La19f8d7WIlm4ogzNHB0JGqs5AUDAZ2UfCY4sJXcJdM= +github.com/hashicorp/go-hclog v1.2.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= +github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-msgpack v0.5.5 h1:i9R9JSrqIz0QVLz3sz+i3YJdT7TTSLcfLLzJi9aZTuI= +github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-multierror v1.1.0 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g4TbElacI= +github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= +github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= +github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= +github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc= +github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= +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/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE= +github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.2.1 h1:zEfKbn2+PDgroKdiOzqiE8rsmLqU2uwi5PB5pBJ3TkI= +github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= +github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= +github.com/hashicorp/memberlist v0.5.0 h1:EtYPN8DpAURiapus508I4n9CzHs2W+8NZGbmmR/prTM= +github.com/hashicorp/memberlist v0.5.0/go.mod h1:yvyXLpo0QaGE59Y7hDTsTzDD25JYBZ4mHgHUZ8lrOI0= +github.com/hashicorp/serf v0.10.1 h1:Z1H2J60yRKvfDYAOZLd2MU0ND4AH/WDz7xYHDWQsIPY= +github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/lithammer/shortuuid/v4 v4.0.0 h1:QRbbVkfgNippHOS8PXDkti4NaWeyYfcBTHtw7k08o4c= +github.com/lithammer/shortuuid/v4 v4.0.0/go.mod h1:Zs8puNcrvf2rV9rTH51ZLLcj7ZXqQI3lv67aw4KiB1Y= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= +github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= +github.com/miekg/dns v1.1.41 h1:WMszZWJG0XmzbK9FEmzH2TVcqYzFesusSIB41b8KHxY= +github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= +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-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/orcaman/concurrent-map v1.0.0 h1:I/2A2XPCb4IuQWcQhBhSwGfiuybl/J0ev9HDbW65HOY= +github.com/orcaman/concurrent-map v1.0.0/go.mod h1:Lu3tH6HLW3feq74c2GC+jIMS/K2CFcDWnWD9XkenwhI= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= +github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= +github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= +github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= +github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= +github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= +github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= +github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +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/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/tinylib/msgp v1.1.5/go.mod h1:eQsjooMTnV42mHu917E26IogZ2930nFyBQdofk10Udg= +github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q= +github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= +github.com/twmb/murmur3 v1.1.6 h1:mqrRot1BRxm+Yct+vavLMou2/iJt0tNVTTC0QoIjaZg= +github.com/twmb/murmur3 v1.1.6/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s= +go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4= +go.opentelemetry.io/otel/exporters/prometheus v0.39.0 h1:whAaiHxOatgtKd+w0dOi//1KUxj3KoPINZdtDaDj3IA= +go.opentelemetry.io/otel/exporters/prometheus v0.39.0/go.mod h1:4jo5Q4CROlCpSPsXLhymi+LYrDXd2ObU5wbKayfZs7Y= +go.opentelemetry.io/otel/metric v1.16.0 h1:RbrpwVG1Hfv85LgnZ7+txXioPDoh6EdbZHo26Q3hqOo= +go.opentelemetry.io/otel/metric v1.16.0/go.mod h1:QE47cpOmkwipPiefDwo2wDzwJrlfxxNYodqc4xnGCo4= +go.opentelemetry.io/otel/sdk v1.16.0 h1:Z1Ok1YsijYL0CSJpHt4cS3wDDh7p572grzNrBMiMWgE= +go.opentelemetry.io/otel/sdk v1.16.0/go.mod h1:tMsIuKXuuIWPBAOrH+eHtvhTL+SntFtXF9QD68aP6p4= +go.opentelemetry.io/otel/sdk/metric v0.39.0 h1:Kun8i1eYf48kHH83RucG93ffz0zGV1sh46FAScOTuDI= +go.opentelemetry.io/otel/sdk/metric v0.39.0/go.mod h1:piDIRgjcK7u0HCL5pCA4e74qpK/jk3NiUoAHATVAmiI= +go.opentelemetry.io/otel/trace v1.16.0 h1:8JRpaObFoW0pxuVPapkgH8UhHQj+bJW8jJsCZEu5MQs= +go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20220827204233-334a2380cb91 h1:tnebWN09GYg9OLPss1KXj8txwZc6X6uMr6VFdcGNbHw= +golang.org/x/exp v0.0.0-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= +golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 h1:DdoeryqhaXp1LtT/emMP1BRJPHHKFi5akj/nbx/zNTA= +google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= +google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97/go.mod h1:t1VqOqqvce95G3hIDCT5FeO3YUc6Q4Oe24L/+rNMxRk= +google.golang.org/grpc v1.55.0 h1:3Oj82/tFSCeUrRTg/5E/7d/W5A1tj6Ky1ABAuZuv5ag= +google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8= +google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/_examples/cluster-grain/node1/main.go b/_examples/cluster-grain/node1/main.go new file mode 100644 index 0000000000000000000000000000000000000000..e52f470a7150d876872c232e2f8959f184d87260 --- /dev/null +++ b/_examples/cluster-grain/node1/main.go @@ -0,0 +1,54 @@ +package main + +import ( + "fmt" + + "cluster-grain/shared" + + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/cluster" + "gitee.com/simplexyz/simpleactor-go/cluster/clusterproviders/consul" + "gitee.com/simplexyz/simpleactor-go/cluster/identitylookup/disthash" + "gitee.com/simplexyz/simpleactor-go/remote" + console "github.com/asynkron/goconsole" +) + +func main() { + system := actor.NewActorSystem() + + provider, _ := consul.New() + lookup := disthash.New() + config := remote.Configure("localhost", 0) + clusterConfig := cluster.Configure("my-cluster", provider, lookup, config) + c := cluster.New(system, clusterConfig) + c.StartMember() + + fmt.Print("\nBoot other nodes and press Enter\n") + console.ReadLine() + client := shared.GetHelloGrainClient(c, "mygrain1") + res, err := client.SayHello(&shared.HelloRequest{ + Name: "World", + }) + if err != nil { + fmt.Printf("Error: %v\n", err) + return + } + + fmt.Printf("Response: %v\n", res) + fmt.Println() + console.ReadLine() + + res, err = client.SayHello(&shared.HelloRequest{ + Name: "World", + }) + if err != nil { + fmt.Printf("Error: %v\n", err) + return + } + + fmt.Printf("Response: %v\n", res) + fmt.Println() + + console.ReadLine() + c.Shutdown(true) +} diff --git a/_examples/cluster-grain/node2/main.go b/_examples/cluster-grain/node2/main.go new file mode 100644 index 0000000000000000000000000000000000000000..588fa5616fba10c988d3f4442864ff7b47bf2689 --- /dev/null +++ b/_examples/cluster-grain/node2/main.go @@ -0,0 +1,42 @@ +package main + +import ( + "fmt" + + "cluster-grain/shared" + + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/cluster" + "gitee.com/simplexyz/simpleactor-go/cluster/clusterproviders/consul" + "gitee.com/simplexyz/simpleactor-go/cluster/identitylookup/disthash" + "gitee.com/simplexyz/simpleactor-go/remote" + console "github.com/asynkron/goconsole" +) + +type HelloGrain struct{} + +func (h HelloGrain) Init(ctx cluster.GrainContext) {} +func (h HelloGrain) Terminate(ctx cluster.GrainContext) {} +func (h HelloGrain) ReceiveDefault(ctx cluster.GrainContext) {} + +func (h HelloGrain) SayHello(request *shared.HelloRequest, ctx cluster.GrainContext) (*shared.HelloResponse, error) { + return &shared.HelloResponse{Message: "Hello " + request.Name}, nil +} + +func main() { + system := actor.NewActorSystem() + provider, _ := consul.New() + lookup := disthash.New() + remoteConfig := remote.Configure("localhost", 0) + helloKind := shared.NewHelloKind(func() shared.Hello { + return &HelloGrain{} + }, 0) + clusterConfig := cluster.Configure("my-cluster", provider, lookup, remoteConfig, + cluster.WithKinds(helloKind)) + + c := cluster.New(system, clusterConfig) + c.StartMember() + fmt.Print("\nBoot other nodes and press Enter\n") + console.ReadLine() + c.Shutdown(true) +} diff --git a/_examples/cluster-grain/shared/build.sh b/_examples/cluster-grain/shared/build.sh new file mode 100644 index 0000000000000000000000000000000000000000..28f9be836fdee9993e2446e07361301c1eac81a5 --- /dev/null +++ b/_examples/cluster-grain/shared/build.sh @@ -0,0 +1,2 @@ +protoc --go_out=. --go_opt=paths=source_relative --proto_path=. protos.proto +protoc -I=. -I=$GOPATH/src --gograinv2_out=. protos.proto diff --git a/_examples/cluster-grain/shared/protos.pb.go b/_examples/cluster-grain/shared/protos.pb.go new file mode 100644 index 0000000000000000000000000000000000000000..9b006f45331fee34a75aa7a4e1c1500ca117f069 --- /dev/null +++ b/_examples/cluster-grain/shared/protos.pb.go @@ -0,0 +1,214 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc v3.19.1 +// source: protos.proto + +package shared + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type HelloRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` +} + +func (x *HelloRequest) Reset() { + *x = HelloRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *HelloRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*HelloRequest) ProtoMessage() {} + +func (x *HelloRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use HelloRequest.ProtoReflect.Descriptor instead. +func (*HelloRequest) Descriptor() ([]byte, []int) { + return file_protos_proto_rawDescGZIP(), []int{0} +} + +func (x *HelloRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +type HelloResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` +} + +func (x *HelloResponse) Reset() { + *x = HelloResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *HelloResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*HelloResponse) ProtoMessage() {} + +func (x *HelloResponse) ProtoReflect() protoreflect.Message { + mi := &file_protos_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use HelloResponse.ProtoReflect.Descriptor instead. +func (*HelloResponse) Descriptor() ([]byte, []int) { + return file_protos_proto_rawDescGZIP(), []int{1} +} + +func (x *HelloResponse) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + +var File_protos_proto protoreflect.FileDescriptor + +var file_protos_proto_rawDesc = []byte{ + 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06, + 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x22, 0x22, 0x0a, 0x0c, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x29, 0x0a, 0x0d, 0x48, 0x65, + 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x32, 0x42, 0x0a, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x12, 0x39, + 0x0a, 0x08, 0x53, 0x61, 0x79, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x12, 0x14, 0x2e, 0x73, 0x68, 0x61, + 0x72, 0x65, 0x64, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x15, 0x2e, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x42, 0x5a, 0x40, 0x67, 0x69, 0x74, + 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x73, 0x79, 0x6e, 0x6b, 0x72, 0x6f, 0x6e, + 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x2d, 0x67, 0x6f, 0x2f, 0x5f, + 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x2f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, + 0x2d, 0x67, 0x72, 0x61, 0x69, 0x6e, 0x2f, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_protos_proto_rawDescOnce sync.Once + file_protos_proto_rawDescData = file_protos_proto_rawDesc +) + +func file_protos_proto_rawDescGZIP() []byte { + file_protos_proto_rawDescOnce.Do(func() { + file_protos_proto_rawDescData = protoimpl.X.CompressGZIP(file_protos_proto_rawDescData) + }) + return file_protos_proto_rawDescData +} + +var file_protos_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_protos_proto_goTypes = []interface{}{ + (*HelloRequest)(nil), // 0: shared.HelloRequest + (*HelloResponse)(nil), // 1: shared.HelloResponse +} +var file_protos_proto_depIdxs = []int32{ + 0, // 0: shared.Hello.SayHello:input_type -> shared.HelloRequest + 1, // 1: shared.Hello.SayHello:output_type -> shared.HelloResponse + 1, // [1:2] is the sub-list for method output_type + 0, // [0:1] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_protos_proto_init() } +func file_protos_proto_init() { + if File_protos_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_protos_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*HelloRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*HelloResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_protos_proto_rawDesc, + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_protos_proto_goTypes, + DependencyIndexes: file_protos_proto_depIdxs, + MessageInfos: file_protos_proto_msgTypes, + }.Build() + File_protos_proto = out.File + file_protos_proto_rawDesc = nil + file_protos_proto_goTypes = nil + file_protos_proto_depIdxs = nil +} diff --git a/_examples/cluster-grain/shared/protos.proto b/_examples/cluster-grain/shared/protos.proto new file mode 100644 index 0000000000000000000000000000000000000000..85e9c6594a4d858aef520f67dc1fc81a87ea9dd8 --- /dev/null +++ b/_examples/cluster-grain/shared/protos.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; +package shared; +option go_package = "gitee.com/simplexyz/simpleactor-go/_examples/cluster-grain/shared"; + +message HelloRequest { + string name = 1; +} + +message HelloResponse { + string message = 1; +} + +service Hello { + rpc SayHello (HelloRequest) returns (HelloResponse) {} +} \ No newline at end of file diff --git a/_examples/cluster-grain/shared/protos_protoactor.go b/_examples/cluster-grain/shared/protos_protoactor.go new file mode 100644 index 0000000000000000000000000000000000000000..e067db5115455e8fd490eb4918b6801248edcd49 --- /dev/null +++ b/_examples/cluster-grain/shared/protos_protoactor.go @@ -0,0 +1,165 @@ +// Package shared is generated by protoactor-go/protoc-gen-gograin@0.1.0 +package shared + +import ( + "errors" + "fmt" + "math" + "time" + + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/cluster" + logmod "gitee.com/simplexyz/simpleactor-go/log" + "google.golang.org/protobuf/proto" +) + +var ( + plog = logmod.New(logmod.InfoLevel, "[GRAIN][shared]") + _ = proto.Marshal + _ = fmt.Errorf + _ = math.Inf +) + +// SetLogLevel sets the log level. +func SetLogLevel(level logmod.Level) { + plog.SetLevel(level) +} + +var xHelloFactory func() Hello + +// HelloFactory produces a Hello +func HelloFactory(factory func() Hello) { + xHelloFactory = factory +} + +// GetHelloGrainClient instantiates a new HelloGrainClient with given Identity +func GetHelloGrainClient(c *cluster.Cluster, id string) *HelloGrainClient { + if c == nil { + panic(fmt.Errorf("nil cluster instance")) + } + if id == "" { + panic(fmt.Errorf("empty id")) + } + return &HelloGrainClient{Identity: id, cluster: c} +} + +// GetHelloKind instantiates a new cluster.Kind for Hello +func GetHelloKind(opts ...actor.PropsOption) *cluster.Kind { + props := actor.PropsFromProducer(func() actor.Actor { + return &HelloActor{ + Timeout: 60 * time.Second, + } + }, opts...) + kind := cluster.NewKind("Hello", props) + return kind +} + +// GetHelloKind instantiates a new cluster.Kind for Hello +func NewHelloKind(factory func() Hello, timeout time.Duration, opts ...actor.PropsOption) *cluster.Kind { + xHelloFactory = factory + props := actor.PropsFromProducer(func() actor.Actor { + return &HelloActor{ + Timeout: timeout, + } + }, opts...) + kind := cluster.NewKind("Hello", props) + return kind +} + +// Hello interfaces the services available to the Hello +type Hello interface { + Init(ctx cluster.GrainContext) + Terminate(ctx cluster.GrainContext) + ReceiveDefault(ctx cluster.GrainContext) + SayHello(*HelloRequest, cluster.GrainContext) (*HelloResponse, error) +} + +// HelloGrainClient holds the base data for the HelloGrain +type HelloGrainClient struct { + Identity string + cluster *cluster.Cluster +} + +// SayHello requests the execution on to the cluster with CallOptions +func (g *HelloGrainClient) SayHello(r *HelloRequest, opts ...cluster.GrainCallOption) (*HelloResponse, error) { + bytes, err := proto.Marshal(r) + if err != nil { + return nil, err + } + reqMsg := &cluster.GrainRequest{MethodIndex: 0, MessageData: bytes} + resp, err := g.cluster.Call(g.Identity, "Hello", reqMsg, opts...) + if err != nil { + return nil, err + } + switch msg := resp.(type) { + case *cluster.GrainResponse: + result := &HelloResponse{} + err = proto.Unmarshal(msg.MessageData, result) + if err != nil { + return nil, err + } + return result, nil + case *cluster.GrainErrorResponse: + return nil, errors.New(msg.Err) + default: + return nil, errors.New("unknown response") + } +} + +// HelloActor represents the actor structure +type HelloActor struct { + ctx cluster.GrainContext + inner Hello + Timeout time.Duration +} + +// Receive ensures the lifecycle of the actor for the received message +func (a *HelloActor) Receive(ctx actor.Context) { + switch msg := ctx.Message().(type) { + case *actor.Started: // pass + case *cluster.ClusterInit: + a.ctx = cluster.NewGrainContext(ctx, msg.Identity, msg.Cluster) + a.inner = xHelloFactory() + a.inner.Init(a.ctx) + + if a.Timeout > 0 { + ctx.SetReceiveTimeout(a.Timeout) + } + case *actor.ReceiveTimeout: + ctx.Poison(ctx.Self()) + case *actor.Stopped: + a.inner.Terminate(a.ctx) + case actor.AutoReceiveMessage: // pass + case actor.SystemMessage: // pass + + case *cluster.GrainRequest: + switch msg.MethodIndex { + case 0: + req := &HelloRequest{} + err := proto.Unmarshal(msg.MessageData, req) + if err != nil { + plog.Error("SayHello(HelloRequest) proto.Unmarshal failed.", logmod.Error(err)) + resp := &cluster.GrainErrorResponse{Err: err.Error()} + ctx.Respond(resp) + return + } + r0, err := a.inner.SayHello(req, a.ctx) + if err != nil { + resp := &cluster.GrainErrorResponse{Err: err.Error()} + ctx.Respond(resp) + return + } + bytes, err := proto.Marshal(r0) + if err != nil { + plog.Error("SayHello(HelloRequest) proto.Marshal failed", logmod.Error(err)) + resp := &cluster.GrainErrorResponse{Err: err.Error()} + ctx.Respond(resp) + return + } + resp := &cluster.GrainResponse{MessageData: bytes} + ctx.Respond(resp) + } + default: + a.inner.ReceiveDefault(a.ctx) + } +} diff --git a/_examples/cluster-metrics/Makefile b/_examples/cluster-metrics/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..31c0c5ec6ed33c896fd17d2ff77980b5475d8146 --- /dev/null +++ b/_examples/cluster-metrics/Makefile @@ -0,0 +1,19 @@ +start: + tmux new-session -d -s eg + tmux split-window -t "eg:0" -v + tmux split-window -t "eg:0.0" -h + tmux select-pane -t "eg:0.2" + tmux send-keys -t "eg:0.0" "go run server/main.go -port 8080" Enter + tmux send-keys -t "eg:0.1" "go run server/main.go -port 8081" Enter + tmux send-keys -t "eg:0.2" "go run client/main.go" Enter + tmux attach -t eg + tmux kill-session -t eg + + +stop: + tmux kill-session -t eg + + +proto: + protoc -I=. --gogoslick_out=. shared/*.proto + protoc -I=. --gograinv2_out=. shared/*.proto diff --git a/_examples/cluster-metrics/client/main.go b/_examples/cluster-metrics/client/main.go new file mode 100644 index 0000000000000000000000000000000000000000..cbe84a04f9666b41e401ea9d6a8a8084507c2869 --- /dev/null +++ b/_examples/cluster-metrics/client/main.go @@ -0,0 +1,86 @@ +package main + +import ( + "fmt" + "log" + "time" + + "gitee.com/simplexyz/simpleactor-go/cluster/identitylookup/disthash" + + "cluster-metrics/shared" + + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/cluster" + "gitee.com/simplexyz/simpleactor-go/cluster/clusterproviders/consul" + "gitee.com/simplexyz/simpleactor-go/remote" + console "github.com/asynkron/goconsole" +) + +func main() { + system := actor.NewActorSystem() + config := remote.Configure("localhost", 0) + + provider, _ := consul.New() + lookup := disthash.New() + + clusterConfig := cluster.Configure("my-cluster", provider, lookup, config) + c := cluster.New(system, clusterConfig) + setupLogger(c) + c.StartMember() + + callopts := cluster.NewGrainCallOptions(c).WithTimeout(5 * time.Second).WithRetry(5) + doRequests(c, callopts) + doRequestsAsync(c, callopts) + console.ReadLine() +} + +func doRequests(c *cluster.Cluster, callopts *cluster.GrainCallConfig) { + msg := &shared.HelloRequest{Name: "Proto.Actor"} + helloGrain := shared.GetHelloGrainClient(c, "MyGrain123") + // with default callopts + resp, err := helloGrain.SayHello(msg) + if err != nil { + log.Fatalf("SayHello failed. err:%v", err) + } + + // with custom callopts + resp, err = helloGrain.SayHello(msg, callopts) + if err != nil { + log.Fatalf("SayHello failed. err:%v", err) + } + log.Printf("Message from SayHello: %v", resp.Message) + for i := 0; i < 10000; i++ { + grainId := fmt.Sprintf("hello%v", i) + x := shared.GetHelloGrainClient(c, grainId) + x.SayHello(&shared.HelloRequest{Name: grainId}) + } + log.Println("Done") +} + +func doRequestsAsync(c *cluster.Cluster, callopts *cluster.GrainCallConfig) { + // sorry, golang has not magic, just use goroutine. + go func() { + doRequests(c, callopts) + }() +} + +func setupLogger(c *cluster.Cluster) { + system := c.ActorSystem + // Subscribe + system.EventStream.Subscribe(func(event interface{}) { + switch msg := event.(type) { + case *cluster.MemberJoinedEvent: + log.Printf("Member Joined " + msg.Name()) + case *cluster.MemberLeftEvent: + log.Printf("Member Left " + msg.Name()) + case *cluster.MemberRejoinedEvent: + log.Printf("Member Rejoined " + msg.Name()) + case *cluster.MemberUnavailableEvent: + log.Printf("Member Unavailable " + msg.Name()) + case *cluster.MemberAvailableEvent: + log.Printf("Member Available " + msg.Name()) + case cluster.ClusterTopology: + log.Printf("Cluster Topology Poll") + } + }) +} diff --git a/_examples/cluster-metrics/go.mod b/_examples/cluster-metrics/go.mod new file mode 100644 index 0000000000000000000000000000000000000000..8506f93a7870de17b8b4331ecff29fd211aff5d6 --- /dev/null +++ b/_examples/cluster-metrics/go.mod @@ -0,0 +1,60 @@ +module cluster-metrics + +go 1.21 + +toolchain go1.21.2 + +replace gitee.com/simplexyz/simpleactor-go => ../.. + +require ( + gitee.com/simplexyz/simpleactor-go v0.0.0-00010101000000-000000000000 + github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716 + google.golang.org/protobuf v1.31.0 +) + +require ( + github.com/Workiva/go-datastructures v1.1.1 // indirect + github.com/armon/go-metrics v0.4.0 // indirect + github.com/asynkron/gofun v0.0.0-20220329210725-34fed760f4c2 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/emirpasic/gods v1.18.1 // indirect + github.com/fatih/color v1.13.0 // indirect + github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/google/btree v1.0.1 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/hashicorp/consul/api v1.18.0 // indirect + github.com/hashicorp/go-cleanhttp v0.5.2 // indirect + github.com/hashicorp/go-hclog v1.2.0 // indirect + github.com/hashicorp/go-immutable-radix v1.3.1 // indirect + github.com/hashicorp/go-rootcerts v1.0.2 // indirect + github.com/hashicorp/golang-lru v0.5.4 // indirect + github.com/hashicorp/serf v0.10.1 // indirect + github.com/lithammer/shortuuid/v4 v4.0.0 // indirect + github.com/mattn/go-colorable v0.1.12 // indirect + github.com/mattn/go-isatty v0.0.16 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/orcaman/concurrent-map v1.0.0 // indirect + github.com/prometheus/client_golang v1.16.0 // indirect + github.com/prometheus/client_model v0.4.0 // indirect + github.com/prometheus/common v0.42.0 // indirect + github.com/prometheus/procfs v0.10.1 // indirect + github.com/twmb/murmur3 v1.1.6 // indirect + go.opentelemetry.io/otel v1.16.0 // indirect + go.opentelemetry.io/otel/exporters/prometheus v0.39.0 // indirect + go.opentelemetry.io/otel/metric v1.16.0 // indirect + go.opentelemetry.io/otel/sdk v1.16.0 // indirect + go.opentelemetry.io/otel/sdk/metric v0.39.0 // indirect + go.opentelemetry.io/otel/trace v1.16.0 // indirect + golang.org/x/exp v0.0.0-20220827204233-334a2380cb91 // indirect + golang.org/x/net v0.17.0 // indirect + golang.org/x/sys v0.13.0 // indirect + golang.org/x/text v0.13.0 // indirect + google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97 // indirect + google.golang.org/grpc v1.58.3 // indirect +) diff --git a/_examples/cluster-metrics/go.sum b/_examples/cluster-metrics/go.sum new file mode 100644 index 0000000000000000000000000000000000000000..198f0c3fd3de7cb23fa4607c1a34c58ec417b305 --- /dev/null +++ b/_examples/cluster-metrics/go.sum @@ -0,0 +1,322 @@ +github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= +github.com/Workiva/go-datastructures v1.1.1 h1:9G5u1UqKt6ABseAffHGNfbNQd7omRlWE5QaxNruzhE0= +github.com/Workiva/go-datastructures v1.1.1/go.mod h1:1yZL+zfsztete+ePzZz/Zb1/t5BnDuE2Ya2MMGhzP6A= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-metrics v0.4.0 h1:yCQqn7dwca4ITXb+CbubHmedzaQYHhNhrEXLYUeEe8Q= +github.com/armon/go-metrics v0.4.0/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716 h1:SgyG4sXkrlalMoCfp20LiNPNhfJS7ez3opNdtihIxPc= +github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716/go.mod h1:/zSlF0T2ArAsTG6SVu8d8qlK+19jjudjA3wWsxnGFHg= +github.com/asynkron/gofun v0.0.0-20220329210725-34fed760f4c2 h1:jEsFZ9d/ieJGVrx3fSPi8oe/qv21fRmyUL5cS3ZEn5A= +github.com/asynkron/gofun v0.0.0-20220329210725-34fed760f4c2/go.mod h1:5GMOSqaYxNWwuVRWyampTPJEntwz7Mj9J8v1a7gSU2E= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= +github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= +github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= +github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/hashicorp/consul/api v1.18.0 h1:R7PPNzTCeN6VuQNDwwhZWJvzCtGSrNpJqfb22h3yH9g= +github.com/hashicorp/consul/api v1.18.0/go.mod h1:owRRGJ9M5xReDC5nfT8FTJrNAPbT4NM6p/k+d03q2v4= +github.com/hashicorp/consul/sdk v0.13.0 h1:lce3nFlpv8humJL8rNrrGHYSKc3q+Kxfeg3Ii1m6ZWU= +github.com/hashicorp/consul/sdk v0.13.0/go.mod h1:0hs/l5fOVhJy/VdcoaNqUSi2AUs95eF5WKtv+EYIQqE= +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.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= +github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-hclog v1.2.0 h1:La19f8d7WIlm4ogzNHB0JGqs5AUDAZ2UfCY4sJXcJdM= +github.com/hashicorp/go-hclog v1.2.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= +github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-msgpack v0.5.5 h1:i9R9JSrqIz0QVLz3sz+i3YJdT7TTSLcfLLzJi9aZTuI= +github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-multierror v1.1.0 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g4TbElacI= +github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= +github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= +github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= +github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc= +github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= +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/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE= +github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.2.1 h1:zEfKbn2+PDgroKdiOzqiE8rsmLqU2uwi5PB5pBJ3TkI= +github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= +github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= +github.com/hashicorp/memberlist v0.5.0 h1:EtYPN8DpAURiapus508I4n9CzHs2W+8NZGbmmR/prTM= +github.com/hashicorp/memberlist v0.5.0/go.mod h1:yvyXLpo0QaGE59Y7hDTsTzDD25JYBZ4mHgHUZ8lrOI0= +github.com/hashicorp/serf v0.10.1 h1:Z1H2J60yRKvfDYAOZLd2MU0ND4AH/WDz7xYHDWQsIPY= +github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/lithammer/shortuuid/v4 v4.0.0 h1:QRbbVkfgNippHOS8PXDkti4NaWeyYfcBTHtw7k08o4c= +github.com/lithammer/shortuuid/v4 v4.0.0/go.mod h1:Zs8puNcrvf2rV9rTH51ZLLcj7ZXqQI3lv67aw4KiB1Y= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= +github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= +github.com/miekg/dns v1.1.41 h1:WMszZWJG0XmzbK9FEmzH2TVcqYzFesusSIB41b8KHxY= +github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= +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-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/orcaman/concurrent-map v1.0.0 h1:I/2A2XPCb4IuQWcQhBhSwGfiuybl/J0ev9HDbW65HOY= +github.com/orcaman/concurrent-map v1.0.0/go.mod h1:Lu3tH6HLW3feq74c2GC+jIMS/K2CFcDWnWD9XkenwhI= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= +github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= +github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= +github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= +github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= +github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= +github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= +github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +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/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/tinylib/msgp v1.1.5/go.mod h1:eQsjooMTnV42mHu917E26IogZ2930nFyBQdofk10Udg= +github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q= +github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= +github.com/twmb/murmur3 v1.1.6 h1:mqrRot1BRxm+Yct+vavLMou2/iJt0tNVTTC0QoIjaZg= +github.com/twmb/murmur3 v1.1.6/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s= +go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4= +go.opentelemetry.io/otel/exporters/prometheus v0.39.0 h1:whAaiHxOatgtKd+w0dOi//1KUxj3KoPINZdtDaDj3IA= +go.opentelemetry.io/otel/exporters/prometheus v0.39.0/go.mod h1:4jo5Q4CROlCpSPsXLhymi+LYrDXd2ObU5wbKayfZs7Y= +go.opentelemetry.io/otel/metric v1.16.0 h1:RbrpwVG1Hfv85LgnZ7+txXioPDoh6EdbZHo26Q3hqOo= +go.opentelemetry.io/otel/metric v1.16.0/go.mod h1:QE47cpOmkwipPiefDwo2wDzwJrlfxxNYodqc4xnGCo4= +go.opentelemetry.io/otel/sdk v1.16.0 h1:Z1Ok1YsijYL0CSJpHt4cS3wDDh7p572grzNrBMiMWgE= +go.opentelemetry.io/otel/sdk v1.16.0/go.mod h1:tMsIuKXuuIWPBAOrH+eHtvhTL+SntFtXF9QD68aP6p4= +go.opentelemetry.io/otel/sdk/metric v0.39.0 h1:Kun8i1eYf48kHH83RucG93ffz0zGV1sh46FAScOTuDI= +go.opentelemetry.io/otel/sdk/metric v0.39.0/go.mod h1:piDIRgjcK7u0HCL5pCA4e74qpK/jk3NiUoAHATVAmiI= +go.opentelemetry.io/otel/trace v1.16.0 h1:8JRpaObFoW0pxuVPapkgH8UhHQj+bJW8jJsCZEu5MQs= +go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20220827204233-334a2380cb91 h1:tnebWN09GYg9OLPss1KXj8txwZc6X6uMr6VFdcGNbHw= +golang.org/x/exp v0.0.0-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= +golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 h1:DdoeryqhaXp1LtT/emMP1BRJPHHKFi5akj/nbx/zNTA= +google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= +google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97/go.mod h1:t1VqOqqvce95G3hIDCT5FeO3YUc6Q4Oe24L/+rNMxRk= +google.golang.org/grpc v1.55.0 h1:3Oj82/tFSCeUrRTg/5E/7d/W5A1tj6Ky1ABAuZuv5ag= +google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8= +google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/_examples/cluster-metrics/server/main.go b/_examples/cluster-metrics/server/main.go new file mode 100644 index 0000000000000000000000000000000000000000..a30254a5e2fa3bb78be9eb7e62956495ea88bb34 --- /dev/null +++ b/_examples/cluster-metrics/server/main.go @@ -0,0 +1,93 @@ +package main + +import ( + "flag" + "log" + "time" + + "gitee.com/simplexyz/simpleactor-go/cluster/identitylookup/disthash" + + "cluster-metrics/shared" + + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/cluster" + "gitee.com/simplexyz/simpleactor-go/cluster/clusterproviders/consul" + logmod "gitee.com/simplexyz/simpleactor-go/log" + "gitee.com/simplexyz/simpleactor-go/remote" + console "github.com/asynkron/goconsole" +) + +func Logger(next actor.ReceiverFunc) actor.ReceiverFunc { + fn := func(context actor.ReceiverContext, env *actor.MessageEnvelope) { + switch env.Message.(type) { + case *actor.Started: + log.Printf("actor started " + context.Self().String()) + case *actor.Stopped: + log.Printf("actor stopped " + context.Self().String()) + } + next(context, env) + } + + return fn +} + +func newHelloActor() actor.Actor { + return &shared.HelloActor{ + Timeout: 20 * time.Second, + } +} + +// a Go struct implementing the Hello interface +type HelloGrain struct{} + +func (h *HelloGrain) Init(ctx cluster.GrainContext) { + log.Printf("new grain id=%s", ctx.Identity) +} + +func (h *HelloGrain) Terminate(ctx cluster.GrainContext) { + log.Printf("delete grain id=%s", ctx.Identity()) +} + +func (*HelloGrain) ReceiveDefault(ctx cluster.GrainContext) { + msg := ctx.Message() + log.Printf("Unknown message %v", msg) +} + +func (h *HelloGrain) SayHello(r *shared.HelloRequest, ctx cluster.GrainContext) (*shared.HelloResponse, error) { + return &shared.HelloResponse{Message: "hello " + r.Name + " from " + ctx.Identity()}, nil +} + +func (*HelloGrain) Add(r *shared.AddRequest, ctx cluster.GrainContext) (*shared.AddResponse, error) { + return &shared.AddResponse{Result: r.A + r.B}, nil +} + +func (*HelloGrain) VoidFunc(r *shared.AddRequest, ctx cluster.GrainContext) (*shared.Unit, error) { + return &shared.Unit{}, nil +} + +func main() { + port := flag.Int("port", 0, "") + flag.Parse() + system := actor.NewActorSystem() + remoteConfig := remote.Configure("127.0.0.1", *port) + helloKind := shared.NewHelloKind(func() shared.Hello { return &HelloGrain{} }, 0, actor.WithReceiverMiddleware(Logger)) + cluster.SetLogLevel(logmod.InfoLevel) + + provider, _ := consul.New() + lookup := disthash.New() + + clusterConfig := cluster.Configure("my-cluster", provider, lookup, remoteConfig, cluster.WithKinds(helloKind)) + c := cluster.New(system, clusterConfig) + c.StartMember() + + // this node knows about Hello kind + hello := shared.GetHelloGrainClient(c, "MyGrain") + msg := &shared.HelloRequest{Name: "Roger"} + res, err := hello.SayHello(msg) + if err != nil { + log.Fatalf("failed to call SayHello, err:%v", err) + } + log.Printf("Message from grain: %v", res.Message) + _, _ = console.ReadLine() + c.Shutdown(true) +} diff --git a/_examples/cluster-metrics/shared/build.sh b/_examples/cluster-metrics/shared/build.sh new file mode 100644 index 0000000000000000000000000000000000000000..5fcdfc759c64741b07f04920edb13084d07da977 --- /dev/null +++ b/_examples/cluster-metrics/shared/build.sh @@ -0,0 +1,2 @@ +protoc --go_out=. --go_opt=paths=source_relative --proto_path=. protos.proto +protoc -I=. --gograinv2_out=. protos.proto diff --git a/_examples/cluster-metrics/shared/protos.pb.go b/_examples/cluster-metrics/shared/protos.pb.go new file mode 100644 index 0000000000000000000000000000000000000000..0becc7923af02ed1d92756f70aad9292c895ef2d --- /dev/null +++ b/_examples/cluster-metrics/shared/protos.pb.go @@ -0,0 +1,409 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc v3.19.1 +// source: protos.proto + +package shared + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type Unit struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *Unit) Reset() { + *x = Unit{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Unit) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Unit) ProtoMessage() {} + +func (x *Unit) ProtoReflect() protoreflect.Message { + mi := &file_protos_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Unit.ProtoReflect.Descriptor instead. +func (*Unit) Descriptor() ([]byte, []int) { + return file_protos_proto_rawDescGZIP(), []int{0} +} + +type HelloRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` +} + +func (x *HelloRequest) Reset() { + *x = HelloRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *HelloRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*HelloRequest) ProtoMessage() {} + +func (x *HelloRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use HelloRequest.ProtoReflect.Descriptor instead. +func (*HelloRequest) Descriptor() ([]byte, []int) { + return file_protos_proto_rawDescGZIP(), []int{1} +} + +func (x *HelloRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +type HelloResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` +} + +func (x *HelloResponse) Reset() { + *x = HelloResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *HelloResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*HelloResponse) ProtoMessage() {} + +func (x *HelloResponse) ProtoReflect() protoreflect.Message { + mi := &file_protos_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use HelloResponse.ProtoReflect.Descriptor instead. +func (*HelloResponse) Descriptor() ([]byte, []int) { + return file_protos_proto_rawDescGZIP(), []int{2} +} + +func (x *HelloResponse) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + +type AddRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + A float64 `protobuf:"fixed64,1,opt,name=a,proto3" json:"a,omitempty"` + B float64 `protobuf:"fixed64,2,opt,name=b,proto3" json:"b,omitempty"` +} + +func (x *AddRequest) Reset() { + *x = AddRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AddRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AddRequest) ProtoMessage() {} + +func (x *AddRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AddRequest.ProtoReflect.Descriptor instead. +func (*AddRequest) Descriptor() ([]byte, []int) { + return file_protos_proto_rawDescGZIP(), []int{3} +} + +func (x *AddRequest) GetA() float64 { + if x != nil { + return x.A + } + return 0 +} + +func (x *AddRequest) GetB() float64 { + if x != nil { + return x.B + } + return 0 +} + +type AddResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Result float64 `protobuf:"fixed64,1,opt,name=result,proto3" json:"result,omitempty"` +} + +func (x *AddResponse) Reset() { + *x = AddResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AddResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AddResponse) ProtoMessage() {} + +func (x *AddResponse) ProtoReflect() protoreflect.Message { + mi := &file_protos_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AddResponse.ProtoReflect.Descriptor instead. +func (*AddResponse) Descriptor() ([]byte, []int) { + return file_protos_proto_rawDescGZIP(), []int{4} +} + +func (x *AddResponse) GetResult() float64 { + if x != nil { + return x.Result + } + return 0 +} + +var File_protos_proto protoreflect.FileDescriptor + +var file_protos_proto_rawDesc = []byte{ + 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06, + 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x22, 0x06, 0x0a, 0x04, 0x55, 0x6e, 0x69, 0x74, 0x22, 0x22, + 0x0a, 0x0c, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, + 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x22, 0x29, 0x0a, 0x0d, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x28, 0x0a, + 0x0a, 0x41, 0x64, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0c, 0x0a, 0x01, 0x61, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x01, 0x52, 0x01, 0x61, 0x12, 0x0c, 0x0a, 0x01, 0x62, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x01, 0x52, 0x01, 0x62, 0x22, 0x25, 0x0a, 0x0b, 0x41, 0x64, 0x64, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x01, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x32, 0xa4, + 0x01, 0x0a, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x12, 0x39, 0x0a, 0x08, 0x53, 0x61, 0x79, 0x48, + 0x65, 0x6c, 0x6c, 0x6f, 0x12, 0x14, 0x2e, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x2e, 0x48, 0x65, + 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x73, 0x68, 0x61, + 0x72, 0x65, 0x64, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x00, 0x12, 0x30, 0x0a, 0x03, 0x41, 0x64, 0x64, 0x12, 0x12, 0x2e, 0x73, 0x68, 0x61, + 0x72, 0x65, 0x64, 0x2e, 0x41, 0x64, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, + 0x2e, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x2e, 0x41, 0x64, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x2e, 0x0a, 0x08, 0x56, 0x6f, 0x69, 0x64, 0x46, 0x75, 0x6e, + 0x63, 0x12, 0x12, 0x2e, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x2e, 0x41, 0x64, 0x64, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0c, 0x2e, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x2e, 0x55, + 0x6e, 0x69, 0x74, 0x22, 0x00, 0x42, 0x44, 0x5a, 0x42, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x73, 0x79, 0x6e, 0x6b, 0x72, 0x6f, 0x6e, 0x2f, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x2d, 0x67, 0x6f, 0x2f, 0x5f, 0x65, 0x78, 0x61, 0x6d, + 0x70, 0x6c, 0x65, 0x73, 0x2f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x2d, 0x6d, 0x65, 0x74, + 0x72, 0x69, 0x63, 0x73, 0x2f, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x62, 0x06, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x33, +} + +var ( + file_protos_proto_rawDescOnce sync.Once + file_protos_proto_rawDescData = file_protos_proto_rawDesc +) + +func file_protos_proto_rawDescGZIP() []byte { + file_protos_proto_rawDescOnce.Do(func() { + file_protos_proto_rawDescData = protoimpl.X.CompressGZIP(file_protos_proto_rawDescData) + }) + return file_protos_proto_rawDescData +} + +var file_protos_proto_msgTypes = make([]protoimpl.MessageInfo, 5) +var file_protos_proto_goTypes = []interface{}{ + (*Unit)(nil), // 0: shared.Unit + (*HelloRequest)(nil), // 1: shared.HelloRequest + (*HelloResponse)(nil), // 2: shared.HelloResponse + (*AddRequest)(nil), // 3: shared.AddRequest + (*AddResponse)(nil), // 4: shared.AddResponse +} +var file_protos_proto_depIdxs = []int32{ + 1, // 0: shared.Hello.SayHello:input_type -> shared.HelloRequest + 3, // 1: shared.Hello.Add:input_type -> shared.AddRequest + 3, // 2: shared.Hello.VoidFunc:input_type -> shared.AddRequest + 2, // 3: shared.Hello.SayHello:output_type -> shared.HelloResponse + 4, // 4: shared.Hello.Add:output_type -> shared.AddResponse + 0, // 5: shared.Hello.VoidFunc:output_type -> shared.Unit + 3, // [3:6] is the sub-list for method output_type + 0, // [0:3] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_protos_proto_init() } +func file_protos_proto_init() { + if File_protos_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_protos_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Unit); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*HelloRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*HelloResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AddRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AddResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_protos_proto_rawDesc, + NumEnums: 0, + NumMessages: 5, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_protos_proto_goTypes, + DependencyIndexes: file_protos_proto_depIdxs, + MessageInfos: file_protos_proto_msgTypes, + }.Build() + File_protos_proto = out.File + file_protos_proto_rawDesc = nil + file_protos_proto_goTypes = nil + file_protos_proto_depIdxs = nil +} diff --git a/_examples/cluster-metrics/shared/protos.proto b/_examples/cluster-metrics/shared/protos.proto new file mode 100644 index 0000000000000000000000000000000000000000..f6bb8234b2b9daf5fe2c0eb378b4b9f385e3c5dd --- /dev/null +++ b/_examples/cluster-metrics/shared/protos.proto @@ -0,0 +1,28 @@ +syntax = "proto3"; +option go_package = "gitee.com/simplexyz/simpleactor-go/_examples/cluster-metrics/shared"; +package shared; + +message Unit {} + +message HelloRequest { + string name = 1; +} + +message HelloResponse { + string message = 1; +} + +message AddRequest { + double a = 1; + double b = 2; +} + +message AddResponse { + double result = 1; +} + +service Hello { + rpc SayHello (HelloRequest) returns (HelloResponse) {} + rpc Add(AddRequest) returns (AddResponse) {} + rpc VoidFunc(AddRequest) returns (Unit) {} +} diff --git a/_examples/cluster-metrics/shared/protos_protoactor.go b/_examples/cluster-metrics/shared/protos_protoactor.go new file mode 100644 index 0000000000000000000000000000000000000000..20ff065e50259e215d4b78959125c3079f659e34 --- /dev/null +++ b/_examples/cluster-metrics/shared/protos_protoactor.go @@ -0,0 +1,268 @@ +// Package shared is generated by protoactor-go/protoc-gen-gograin@0.1.0 +package shared + +import ( + "errors" + "fmt" + "math" + "time" + + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/cluster" + logmod "gitee.com/simplexyz/simpleactor-go/log" + "google.golang.org/protobuf/proto" +) + +var ( + plog = logmod.New(logmod.InfoLevel, "[GRAIN][shared]") + _ = proto.Marshal + _ = fmt.Errorf + _ = math.Inf +) + +// SetLogLevel sets the log level. +func SetLogLevel(level logmod.Level) { + plog.SetLevel(level) +} + +var xHelloFactory func() Hello + +// HelloFactory produces a Hello +func HelloFactory(factory func() Hello) { + xHelloFactory = factory +} + +// GetHelloGrainClient instantiates a new HelloGrainClient with given Identity +func GetHelloGrainClient(c *cluster.Cluster, id string) *HelloGrainClient { + if c == nil { + panic(fmt.Errorf("nil cluster instance")) + } + if id == "" { + panic(fmt.Errorf("empty id")) + } + return &HelloGrainClient{Identity: id, cluster: c} +} + +// GetHelloKind instantiates a new cluster.Kind for Hello +func GetHelloKind(opts ...actor.PropsOption) *cluster.Kind { + props := actor.PropsFromProducer(func() actor.Actor { + return &HelloActor{ + Timeout: 60 * time.Second, + } + }, opts...) + kind := cluster.NewKind("Hello", props) + return kind +} + +// GetHelloKind instantiates a new cluster.Kind for Hello +func NewHelloKind(factory func() Hello, timeout time.Duration, opts ...actor.PropsOption) *cluster.Kind { + xHelloFactory = factory + props := actor.PropsFromProducer(func() actor.Actor { + return &HelloActor{ + Timeout: timeout, + } + }, opts...) + kind := cluster.NewKind("Hello", props) + return kind +} + +// Hello interfaces the services available to the Hello +type Hello interface { + Init(ctx cluster.GrainContext) + Terminate(ctx cluster.GrainContext) + ReceiveDefault(ctx cluster.GrainContext) + SayHello(*HelloRequest, cluster.GrainContext) (*HelloResponse, error) + Add(*AddRequest, cluster.GrainContext) (*AddResponse, error) + VoidFunc(*AddRequest, cluster.GrainContext) (*Unit, error) +} + +// HelloGrainClient holds the base data for the HelloGrain +type HelloGrainClient struct { + Identity string + cluster *cluster.Cluster +} + +// SayHello requests the execution on to the cluster with CallOptions +func (g *HelloGrainClient) SayHello(r *HelloRequest, opts ...cluster.GrainCallOption) (*HelloResponse, error) { + bytes, err := proto.Marshal(r) + if err != nil { + return nil, err + } + reqMsg := &cluster.GrainRequest{MethodIndex: 0, MessageData: bytes} + resp, err := g.cluster.Call(g.Identity, "Hello", reqMsg, opts...) + if err != nil { + return nil, err + } + switch msg := resp.(type) { + case *cluster.GrainResponse: + result := &HelloResponse{} + err = proto.Unmarshal(msg.MessageData, result) + if err != nil { + return nil, err + } + return result, nil + case *cluster.GrainErrorResponse: + return nil, errors.New(msg.Err) + default: + return nil, errors.New("unknown response") + } +} + +// Add requests the execution on to the cluster with CallOptions +func (g *HelloGrainClient) Add(r *AddRequest, opts ...cluster.GrainCallOption) (*AddResponse, error) { + bytes, err := proto.Marshal(r) + if err != nil { + return nil, err + } + reqMsg := &cluster.GrainRequest{MethodIndex: 1, MessageData: bytes} + resp, err := g.cluster.Call(g.Identity, "Hello", reqMsg, opts...) + if err != nil { + return nil, err + } + switch msg := resp.(type) { + case *cluster.GrainResponse: + result := &AddResponse{} + err = proto.Unmarshal(msg.MessageData, result) + if err != nil { + return nil, err + } + return result, nil + case *cluster.GrainErrorResponse: + return nil, errors.New(msg.Err) + default: + return nil, errors.New("unknown response") + } +} + +// VoidFunc requests the execution on to the cluster with CallOptions +func (g *HelloGrainClient) VoidFunc(r *AddRequest, opts ...cluster.GrainCallOption) (*Unit, error) { + bytes, err := proto.Marshal(r) + if err != nil { + return nil, err + } + reqMsg := &cluster.GrainRequest{MethodIndex: 2, MessageData: bytes} + resp, err := g.cluster.Call(g.Identity, "Hello", reqMsg, opts...) + if err != nil { + return nil, err + } + switch msg := resp.(type) { + case *cluster.GrainResponse: + result := &Unit{} + err = proto.Unmarshal(msg.MessageData, result) + if err != nil { + return nil, err + } + return result, nil + case *cluster.GrainErrorResponse: + return nil, errors.New(msg.Err) + default: + return nil, errors.New("unknown response") + } +} + +// HelloActor represents the actor structure +type HelloActor struct { + ctx cluster.GrainContext + inner Hello + Timeout time.Duration +} + +// Receive ensures the lifecycle of the actor for the received message +func (a *HelloActor) Receive(ctx actor.Context) { + switch msg := ctx.Message().(type) { + case *actor.Started: // pass + case *cluster.ClusterInit: + a.ctx = cluster.NewGrainContext(ctx, msg.Identity, msg.Cluster) + a.inner = xHelloFactory() + a.inner.Init(a.ctx) + + if a.Timeout > 0 { + ctx.SetReceiveTimeout(a.Timeout) + } + case *actor.ReceiveTimeout: + ctx.Poison(ctx.Self()) + case *actor.Stopped: + a.inner.Terminate(a.ctx) + case actor.AutoReceiveMessage: // pass + case actor.SystemMessage: // pass + + case *cluster.GrainRequest: + switch msg.MethodIndex { + case 0: + req := &HelloRequest{} + err := proto.Unmarshal(msg.MessageData, req) + if err != nil { + plog.Error("SayHello(HelloRequest) proto.Unmarshal failed.", logmod.Error(err)) + resp := &cluster.GrainErrorResponse{Err: err.Error()} + ctx.Respond(resp) + return + } + r0, err := a.inner.SayHello(req, a.ctx) + if err != nil { + resp := &cluster.GrainErrorResponse{Err: err.Error()} + ctx.Respond(resp) + return + } + bytes, err := proto.Marshal(r0) + if err != nil { + plog.Error("SayHello(HelloRequest) proto.Marshal failed", logmod.Error(err)) + resp := &cluster.GrainErrorResponse{Err: err.Error()} + ctx.Respond(resp) + return + } + resp := &cluster.GrainResponse{MessageData: bytes} + ctx.Respond(resp) + case 1: + req := &AddRequest{} + err := proto.Unmarshal(msg.MessageData, req) + if err != nil { + plog.Error("Add(AddRequest) proto.Unmarshal failed.", logmod.Error(err)) + resp := &cluster.GrainErrorResponse{Err: err.Error()} + ctx.Respond(resp) + return + } + r0, err := a.inner.Add(req, a.ctx) + if err != nil { + resp := &cluster.GrainErrorResponse{Err: err.Error()} + ctx.Respond(resp) + return + } + bytes, err := proto.Marshal(r0) + if err != nil { + plog.Error("Add(AddRequest) proto.Marshal failed", logmod.Error(err)) + resp := &cluster.GrainErrorResponse{Err: err.Error()} + ctx.Respond(resp) + return + } + resp := &cluster.GrainResponse{MessageData: bytes} + ctx.Respond(resp) + case 2: + req := &AddRequest{} + err := proto.Unmarshal(msg.MessageData, req) + if err != nil { + plog.Error("VoidFunc(AddRequest) proto.Unmarshal failed.", logmod.Error(err)) + resp := &cluster.GrainErrorResponse{Err: err.Error()} + ctx.Respond(resp) + return + } + r0, err := a.inner.VoidFunc(req, a.ctx) + if err != nil { + resp := &cluster.GrainErrorResponse{Err: err.Error()} + ctx.Respond(resp) + return + } + bytes, err := proto.Marshal(r0) + if err != nil { + plog.Error("VoidFunc(AddRequest) proto.Marshal failed", logmod.Error(err)) + resp := &cluster.GrainErrorResponse{Err: err.Error()} + ctx.Respond(resp) + return + } + resp := &cluster.GrainResponse{MessageData: bytes} + ctx.Respond(resp) + + } + default: + a.inner.ReceiveDefault(a.ctx) + } +} diff --git a/_examples/cluster-pubsub-batching-producer/build.sh b/_examples/cluster-pubsub-batching-producer/build.sh new file mode 100644 index 0000000000000000000000000000000000000000..d2d41b15f65732390fc14bc83e413e57c2584941 --- /dev/null +++ b/_examples/cluster-pubsub-batching-producer/build.sh @@ -0,0 +1 @@ +protoc --go_out=. --go_opt=paths=source_relative --proto_path=. protos.proto diff --git a/_examples/cluster-pubsub-batching-producer/go.mod b/_examples/cluster-pubsub-batching-producer/go.mod new file mode 100644 index 0000000000000000000000000000000000000000..fbc265e0b9bd731c21b283304b8449a5a8b4d082 --- /dev/null +++ b/_examples/cluster-pubsub-batching-producer/go.mod @@ -0,0 +1,43 @@ +module cluster-pubsub-batching-provider + +go 1.18 + +require ( + github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716 + gitee.com/simplexyz/simpleactor-go v0.0.0-20230121103438-4df724400d0f + google.golang.org/protobuf v1.28.1 +) + +require ( + github.com/Workiva/go-datastructures v1.0.53 // indirect + github.com/asynkron/gofun v0.0.0-20220329210725-34fed760f4c2 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.1.2 // indirect + github.com/emirpasic/gods v1.18.1 // indirect + github.com/go-logr/logr v1.2.3 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/lithammer/shortuuid/v4 v4.0.0 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect + github.com/orcaman/concurrent-map v1.0.0 // indirect + github.com/prometheus/client_golang v1.12.2 // indirect + github.com/prometheus/client_model v0.2.0 // indirect + github.com/prometheus/common v0.34.0 // indirect + github.com/prometheus/procfs v0.7.3 // indirect + github.com/twmb/murmur3 v1.1.6 // indirect + go.opentelemetry.io/otel v1.7.0 // indirect + go.opentelemetry.io/otel/exporters/prometheus v0.30.0 // indirect + go.opentelemetry.io/otel/metric v0.30.0 // indirect + go.opentelemetry.io/otel/sdk v1.7.0 // indirect + go.opentelemetry.io/otel/sdk/export/metric v0.28.0 // indirect + go.opentelemetry.io/otel/sdk/metric v0.30.0 // indirect + go.opentelemetry.io/otel/trace v1.7.0 // indirect + golang.org/x/exp v0.0.0-20220518171630-0b5c67f07fdf // indirect + golang.org/x/net v0.3.1-0.20221206200815-1e63c2f08a10 // indirect + golang.org/x/sys v0.3.0 // indirect + golang.org/x/text v0.5.0 // indirect + google.golang.org/genproto v0.0.0-20220526192754-51939a95c655 // indirect + google.golang.org/grpc v1.46.2 // indirect +) diff --git a/_examples/cluster-pubsub-batching-producer/go.work b/_examples/cluster-pubsub-batching-producer/go.work new file mode 100644 index 0000000000000000000000000000000000000000..6195ec621a673819451273cb29f7a7ea6b96db49 --- /dev/null +++ b/_examples/cluster-pubsub-batching-producer/go.work @@ -0,0 +1,7 @@ +go 1.19 + +use ( + ../../../protoactor-go + . +) + diff --git a/_examples/cluster-pubsub-batching-producer/main.go b/_examples/cluster-pubsub-batching-producer/main.go new file mode 100644 index 0000000000000000000000000000000000000000..ef7755640fa01a873a3ba3428a648b4236282394 --- /dev/null +++ b/_examples/cluster-pubsub-batching-producer/main.go @@ -0,0 +1,72 @@ +package main + +import ( + "context" + "fmt" + "sync/atomic" + "time" + + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/cluster" + "gitee.com/simplexyz/simpleactor-go/cluster/clusterproviders/test" + "gitee.com/simplexyz/simpleactor-go/cluster/identitylookup/disthash" + "gitee.com/simplexyz/simpleactor-go/remote" + console "github.com/asynkron/goconsole" +) + +func main() { + c := startNode() + + const topic = "my-topic" + var deliveredCount int32 = 0 + for i := 0; i < 3; i++ { + _, _ = c.SubscribeWithReceive(topic, func(context actor.Context) { + switch context.Message().(type) { + case *PingMessage: + atomic.AddInt32(&deliveredCount, 1) + } + }) + } + const count = 1_000_000 + fmt.Println("Starting producer...") + + producer := c.BatchingProducer(topic) + + start := time.Now() + + tasks := make([]*cluster.ProduceProcessInfo, count) + for i := 0; i < count; i++ { + info, err := producer.Produce(context.Background(), &PingMessage{Data: int32(i)}) + if err != nil { + panic(err) + } + tasks[i] = info + } + for _, task := range tasks { + <-task.Finished + } + elapsed := time.Since(start) + producer.Dispose() + c.Shutdown(true) + + fmt.Printf("Sent: %d, delivered: %d, msg/s %f\n", count, deliveredCount, float64(deliveredCount)/elapsed.Seconds()) + + console.ReadLine() +} + +func startNode() *cluster.Cluster { + // how long before the grain poisons itself + system := actor.NewActorSystem() + + provider := test.NewTestProvider(test.NewInMemAgent()) + lookup := disthash.New() + config := remote.Configure("localhost", 0) + + clusterConfig := cluster.Configure("my-cluster", provider, lookup, config) + + cluster := cluster.New(system, clusterConfig) + + cluster.StartMember() + + return cluster +} diff --git a/_examples/cluster-pubsub-batching-producer/protos.pb.go b/_examples/cluster-pubsub-batching-producer/protos.pb.go new file mode 100644 index 0000000000000000000000000000000000000000..e3f2f2c766d51229bf71306003b0469389d87b3d --- /dev/null +++ b/_examples/cluster-pubsub-batching-producer/protos.pb.go @@ -0,0 +1,146 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.28.1 +// protoc v3.21.9 +// source: protos.proto + +package main + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type PingMessage struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Data int32 `protobuf:"varint,1,opt,name=data,proto3" json:"data,omitempty"` +} + +func (x *PingMessage) Reset() { + *x = PingMessage{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PingMessage) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PingMessage) ProtoMessage() {} + +func (x *PingMessage) ProtoReflect() protoreflect.Message { + mi := &file_protos_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PingMessage.ProtoReflect.Descriptor instead. +func (*PingMessage) Descriptor() ([]byte, []int) { + return file_protos_proto_rawDescGZIP(), []int{0} +} + +func (x *PingMessage) GetData() int32 { + if x != nil { + return x.Data + } + return 0 +} + +var File_protos_proto protoreflect.FileDescriptor + +var file_protos_proto_rawDesc = []byte{ + 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x04, + 0x6d, 0x61, 0x69, 0x6e, 0x22, 0x21, 0x0a, 0x0b, 0x50, 0x69, 0x6e, 0x67, 0x4d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x42, 0x53, 0x5a, 0x51, 0x67, 0x69, 0x74, 0x68, 0x75, + 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x73, 0x79, 0x6e, 0x6b, 0x72, 0x6f, 0x6e, 0x2f, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x2d, 0x67, 0x6f, 0x2f, 0x5f, 0x65, 0x78, + 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x2f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x2d, 0x70, + 0x75, 0x62, 0x73, 0x75, 0x62, 0x2d, 0x62, 0x61, 0x74, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x2d, 0x70, + 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x2f, 0x6d, 0x61, 0x69, 0x6e, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_protos_proto_rawDescOnce sync.Once + file_protos_proto_rawDescData = file_protos_proto_rawDesc +) + +func file_protos_proto_rawDescGZIP() []byte { + file_protos_proto_rawDescOnce.Do(func() { + file_protos_proto_rawDescData = protoimpl.X.CompressGZIP(file_protos_proto_rawDescData) + }) + return file_protos_proto_rawDescData +} + +var file_protos_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_protos_proto_goTypes = []interface{}{ + (*PingMessage)(nil), // 0: main.PingMessage +} +var file_protos_proto_depIdxs = []int32{ + 0, // [0:0] is the sub-list for method output_type + 0, // [0:0] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_protos_proto_init() } +func file_protos_proto_init() { + if File_protos_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_protos_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PingMessage); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_protos_proto_rawDesc, + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_protos_proto_goTypes, + DependencyIndexes: file_protos_proto_depIdxs, + MessageInfos: file_protos_proto_msgTypes, + }.Build() + File_protos_proto = out.File + file_protos_proto_rawDesc = nil + file_protos_proto_goTypes = nil + file_protos_proto_depIdxs = nil +} diff --git a/_examples/cluster-pubsub-batching-producer/protos.proto b/_examples/cluster-pubsub-batching-producer/protos.proto new file mode 100644 index 0000000000000000000000000000000000000000..28505ed03b1c004c2991e78151124dac5dfcfcc5 --- /dev/null +++ b/_examples/cluster-pubsub-batching-producer/protos.proto @@ -0,0 +1,7 @@ +syntax = "proto3"; +option go_package = "gitee.com/simplexyz/simpleactor-go/_examples/cluster-pubsub-batching-producer/main"; +package main; + +message PingMessage { + int32 data = 1; +} diff --git a/_examples/cluster-pubsub/build.sh b/_examples/cluster-pubsub/build.sh new file mode 100644 index 0000000000000000000000000000000000000000..5fcdfc759c64741b07f04920edb13084d07da977 --- /dev/null +++ b/_examples/cluster-pubsub/build.sh @@ -0,0 +1,2 @@ +protoc --go_out=. --go_opt=paths=source_relative --proto_path=. protos.proto +protoc -I=. --gograinv2_out=. protos.proto diff --git a/_examples/cluster-pubsub/go.mod b/_examples/cluster-pubsub/go.mod new file mode 100644 index 0000000000000000000000000000000000000000..cdf49ae8c25d5746731a99c00fec7633e1de9122 --- /dev/null +++ b/_examples/cluster-pubsub/go.mod @@ -0,0 +1,43 @@ +module cluster-pubsub + +go 1.18 + +require ( + github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716 + gitee.com/simplexyz/simpleactor-go v0.0.0-20230121103438-4df724400d0f + google.golang.org/protobuf v1.28.1 +) + +require ( + github.com/Workiva/go-datastructures v1.0.53 // indirect + github.com/asynkron/gofun v0.0.0-20220329210725-34fed760f4c2 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.1.2 // indirect + github.com/emirpasic/gods v1.18.1 // indirect + github.com/go-logr/logr v1.2.3 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/lithammer/shortuuid/v4 v4.0.0 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect + github.com/orcaman/concurrent-map v1.0.0 // indirect + github.com/prometheus/client_golang v1.12.2 // indirect + github.com/prometheus/client_model v0.2.0 // indirect + github.com/prometheus/common v0.34.0 // indirect + github.com/prometheus/procfs v0.7.3 // indirect + github.com/twmb/murmur3 v1.1.6 // indirect + go.opentelemetry.io/otel v1.7.0 // indirect + go.opentelemetry.io/otel/exporters/prometheus v0.30.0 // indirect + go.opentelemetry.io/otel/metric v0.30.0 // indirect + go.opentelemetry.io/otel/sdk v1.7.0 // indirect + go.opentelemetry.io/otel/sdk/export/metric v0.28.0 // indirect + go.opentelemetry.io/otel/sdk/metric v0.30.0 // indirect + go.opentelemetry.io/otel/trace v1.7.0 // indirect + golang.org/x/exp v0.0.0-20220518171630-0b5c67f07fdf // indirect + golang.org/x/net v0.3.1-0.20221206200815-1e63c2f08a10 // indirect + golang.org/x/sys v0.3.0 // indirect + golang.org/x/text v0.5.0 // indirect + google.golang.org/genproto v0.0.0-20220526192754-51939a95c655 // indirect + google.golang.org/grpc v1.46.2 // indirect +) diff --git a/_examples/cluster-pubsub/go.work b/_examples/cluster-pubsub/go.work new file mode 100644 index 0000000000000000000000000000000000000000..6195ec621a673819451273cb29f7a7ea6b96db49 --- /dev/null +++ b/_examples/cluster-pubsub/go.work @@ -0,0 +1,7 @@ +go 1.19 + +use ( + ../../../protoactor-go + . +) + diff --git a/_examples/cluster-pubsub/main.go b/_examples/cluster-pubsub/main.go new file mode 100644 index 0000000000000000000000000000000000000000..a8cc2d13b5db63670cd535d59d778036a055391e --- /dev/null +++ b/_examples/cluster-pubsub/main.go @@ -0,0 +1,45 @@ +package main + +import ( + "strconv" + + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/cluster" + "gitee.com/simplexyz/simpleactor-go/cluster/clusterproviders/test" + "gitee.com/simplexyz/simpleactor-go/cluster/identitylookup/disthash" + "gitee.com/simplexyz/simpleactor-go/remote" + console "github.com/asynkron/goconsole" +) + +func main() { + c := startNode() + + for i := 0; i < 3; i++ { + GetUserActorGrainClient(c, "user"+strconv.Itoa(i)).Connect(&Empty{}) + } + + console.ReadLine() + c.Shutdown(true) +} + +func startNode() *cluster.Cluster { + // how long before the grain poisons itself + system := actor.NewActorSystem() + + provider := test.NewTestProvider(test.NewInMemAgent()) + lookup := disthash.New() + config := remote.Configure("localhost", 0) + + userKind := NewUserActorKind(func() UserActor { + return &User{} + }, 0) + + clusterConfig := cluster.Configure("my-cluster", provider, lookup, config, + cluster.WithKinds(userKind)) + + cluster := cluster.New(system, clusterConfig) + + cluster.StartMember() + + return cluster +} diff --git a/_examples/cluster-pubsub/protos.pb.go b/_examples/cluster-pubsub/protos.pb.go new file mode 100644 index 0000000000000000000000000000000000000000..a43d6a83cffaa1e8d78a551a0bbfe0efc5900592 --- /dev/null +++ b/_examples/cluster-pubsub/protos.pb.go @@ -0,0 +1,211 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.28.1 +// protoc v3.21.9 +// source: protos.proto + +package main + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type ChatMessage struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Sender string `protobuf:"bytes,1,opt,name=sender,proto3" json:"sender,omitempty"` + Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` +} + +func (x *ChatMessage) Reset() { + *x = ChatMessage{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ChatMessage) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ChatMessage) ProtoMessage() {} + +func (x *ChatMessage) ProtoReflect() protoreflect.Message { + mi := &file_protos_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ChatMessage.ProtoReflect.Descriptor instead. +func (*ChatMessage) Descriptor() ([]byte, []int) { + return file_protos_proto_rawDescGZIP(), []int{0} +} + +func (x *ChatMessage) GetSender() string { + if x != nil { + return x.Sender + } + return "" +} + +func (x *ChatMessage) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + +type Empty struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *Empty) Reset() { + *x = Empty{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Empty) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Empty) ProtoMessage() {} + +func (x *Empty) ProtoReflect() protoreflect.Message { + mi := &file_protos_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Empty.ProtoReflect.Descriptor instead. +func (*Empty) Descriptor() ([]byte, []int) { + return file_protos_proto_rawDescGZIP(), []int{1} +} + +var File_protos_proto protoreflect.FileDescriptor + +var file_protos_proto_rawDesc = []byte{ + 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x04, + 0x6d, 0x61, 0x69, 0x6e, 0x22, 0x3f, 0x0a, 0x0b, 0x43, 0x68, 0x61, 0x74, 0x4d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x6d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x07, 0x0a, 0x05, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x32, 0x30, + 0x0a, 0x09, 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x23, 0x0a, 0x07, 0x43, + 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x12, 0x0b, 0x2e, 0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x45, 0x6d, + 0x70, 0x74, 0x79, 0x1a, 0x0b, 0x2e, 0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, + 0x42, 0x41, 0x5a, 0x3f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, + 0x73, 0x79, 0x6e, 0x6b, 0x72, 0x6f, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x61, 0x63, 0x74, + 0x6f, 0x72, 0x2d, 0x67, 0x6f, 0x2f, 0x5f, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x2f, + 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x2d, 0x70, 0x75, 0x62, 0x73, 0x75, 0x62, 0x2f, 0x6d, + 0x61, 0x69, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_protos_proto_rawDescOnce sync.Once + file_protos_proto_rawDescData = file_protos_proto_rawDesc +) + +func file_protos_proto_rawDescGZIP() []byte { + file_protos_proto_rawDescOnce.Do(func() { + file_protos_proto_rawDescData = protoimpl.X.CompressGZIP(file_protos_proto_rawDescData) + }) + return file_protos_proto_rawDescData +} + +var file_protos_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_protos_proto_goTypes = []interface{}{ + (*ChatMessage)(nil), // 0: main.ChatMessage + (*Empty)(nil), // 1: main.Empty +} +var file_protos_proto_depIdxs = []int32{ + 1, // 0: main.UserActor.Connect:input_type -> main.Empty + 1, // 1: main.UserActor.Connect:output_type -> main.Empty + 1, // [1:2] is the sub-list for method output_type + 0, // [0:1] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_protos_proto_init() } +func file_protos_proto_init() { + if File_protos_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_protos_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ChatMessage); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Empty); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_protos_proto_rawDesc, + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_protos_proto_goTypes, + DependencyIndexes: file_protos_proto_depIdxs, + MessageInfos: file_protos_proto_msgTypes, + }.Build() + File_protos_proto = out.File + file_protos_proto_rawDesc = nil + file_protos_proto_goTypes = nil + file_protos_proto_depIdxs = nil +} diff --git a/_examples/cluster-pubsub/protos.proto b/_examples/cluster-pubsub/protos.proto new file mode 100644 index 0000000000000000000000000000000000000000..3f0902c8293b31543699e172c3632d988b122b2a --- /dev/null +++ b/_examples/cluster-pubsub/protos.proto @@ -0,0 +1,14 @@ +syntax = "proto3"; +option go_package = "gitee.com/simplexyz/simpleactor-go/_examples/cluster-pubsub/main"; +package main; + +message ChatMessage { + string sender = 1; + string message = 2; +} + +message Empty {} + +service UserActor { + rpc Connect(Empty) returns (Empty); +} diff --git a/_examples/cluster-pubsub/protos_protoactor.go b/_examples/cluster-pubsub/protos_protoactor.go new file mode 100644 index 0000000000000000000000000000000000000000..98a06e01b6dd45fce3ed9ec7bf4913f09605cafb --- /dev/null +++ b/_examples/cluster-pubsub/protos_protoactor.go @@ -0,0 +1,165 @@ +// Package main is generated by protoactor-go/protoc-gen-gograin@0.1.0 +package main + +import ( + "errors" + "fmt" + "math" + "time" + + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/cluster" + logmod "gitee.com/simplexyz/simpleactor-go/log" + "google.golang.org/protobuf/proto" +) + +var ( + plog = logmod.New(logmod.InfoLevel, "[GRAIN][main]") + _ = proto.Marshal + _ = fmt.Errorf + _ = math.Inf +) + +// SetLogLevel sets the log level. +func SetLogLevel(level logmod.Level) { + plog.SetLevel(level) +} + +var xUserActorFactory func() UserActor + +// UserActorFactory produces a UserActor +func UserActorFactory(factory func() UserActor) { + xUserActorFactory = factory +} + +// GetUserActorGrainClient instantiates a new UserActorGrainClient with given Identity +func GetUserActorGrainClient(c *cluster.Cluster, id string) *UserActorGrainClient { + if c == nil { + panic(fmt.Errorf("nil cluster instance")) + } + if id == "" { + panic(fmt.Errorf("empty id")) + } + return &UserActorGrainClient{Identity: id, cluster: c} +} + +// GetUserActorKind instantiates a new cluster.Kind for UserActor +func GetUserActorKind(opts ...actor.PropsOption) *cluster.Kind { + props := actor.PropsFromProducer(func() actor.Actor { + return &UserActorActor{ + Timeout: 60 * time.Second, + } + }, opts...) + kind := cluster.NewKind("UserActor", props) + return kind +} + +// GetUserActorKind instantiates a new cluster.Kind for UserActor +func NewUserActorKind(factory func() UserActor, timeout time.Duration, opts ...actor.PropsOption) *cluster.Kind { + xUserActorFactory = factory + props := actor.PropsFromProducer(func() actor.Actor { + return &UserActorActor{ + Timeout: timeout, + } + }, opts...) + kind := cluster.NewKind("UserActor", props) + return kind +} + +// UserActor interfaces the services available to the UserActor +type UserActor interface { + Init(ctx cluster.GrainContext) + Terminate(ctx cluster.GrainContext) + ReceiveDefault(ctx cluster.GrainContext) + Connect(*Empty, cluster.GrainContext) (*Empty, error) +} + +// UserActorGrainClient holds the base data for the UserActorGrain +type UserActorGrainClient struct { + Identity string + cluster *cluster.Cluster +} + +// Connect requests the execution on to the cluster with CallOptions +func (g *UserActorGrainClient) Connect(r *Empty, opts ...cluster.GrainCallOption) (*Empty, error) { + bytes, err := proto.Marshal(r) + if err != nil { + return nil, err + } + reqMsg := &cluster.GrainRequest{MethodIndex: 0, MessageData: bytes} + resp, err := g.cluster.Call(g.Identity, "UserActor", reqMsg, opts...) + if err != nil { + return nil, err + } + switch msg := resp.(type) { + case *cluster.GrainResponse: + result := &Empty{} + err = proto.Unmarshal(msg.MessageData, result) + if err != nil { + return nil, err + } + return result, nil + case *cluster.GrainErrorResponse: + return nil, errors.New(msg.Err) + default: + return nil, errors.New("unknown response") + } +} + +// UserActorActor represents the actor structure +type UserActorActor struct { + ctx cluster.GrainContext + inner UserActor + Timeout time.Duration +} + +// Receive ensures the lifecycle of the actor for the received message +func (a *UserActorActor) Receive(ctx actor.Context) { + switch msg := ctx.Message().(type) { + case *actor.Started: // pass + case *cluster.ClusterInit: + a.ctx = cluster.NewGrainContext(ctx, msg.Identity, msg.Cluster) + a.inner = xUserActorFactory() + a.inner.Init(a.ctx) + + if a.Timeout > 0 { + ctx.SetReceiveTimeout(a.Timeout) + } + case *actor.ReceiveTimeout: + ctx.Poison(ctx.Self()) + case *actor.Stopped: + a.inner.Terminate(a.ctx) + case actor.AutoReceiveMessage: // pass + case actor.SystemMessage: // pass + + case *cluster.GrainRequest: + switch msg.MethodIndex { + case 0: + req := &Empty{} + err := proto.Unmarshal(msg.MessageData, req) + if err != nil { + plog.Error("Connect(Empty) proto.Unmarshal failed.", logmod.Error(err)) + resp := &cluster.GrainErrorResponse{Err: err.Error()} + ctx.Respond(resp) + return + } + r0, err := a.inner.Connect(req, a.ctx) + if err != nil { + resp := &cluster.GrainErrorResponse{Err: err.Error()} + ctx.Respond(resp) + return + } + bytes, err := proto.Marshal(r0) + if err != nil { + plog.Error("Connect(Empty) proto.Marshal failed", logmod.Error(err)) + resp := &cluster.GrainErrorResponse{Err: err.Error()} + ctx.Respond(resp) + return + } + resp := &cluster.GrainResponse{MessageData: bytes} + ctx.Respond(resp) + } + default: + a.inner.ReceiveDefault(a.ctx) + } +} diff --git a/_examples/cluster-pubsub/user.go b/_examples/cluster-pubsub/user.go new file mode 100644 index 0000000000000000000000000000000000000000..61a1f7b0a1608512fa5106e12df2a51542793218 --- /dev/null +++ b/_examples/cluster-pubsub/user.go @@ -0,0 +1,62 @@ +package main + +import ( + "context" + "fmt" + "math/rand" + "time" + + "gitee.com/simplexyz/simpleactor-go/cluster" + "gitee.com/simplexyz/simpleactor-go/scheduler" +) + +const Topic = "chat" + +var messages = []string{ + "Good day sir!", + "Lovely weather, innit?", + "How do you do?", + "Pardon me!", +} + +func randomMessage() string { + return messages[rand.Intn(len(messages))] +} + +type Tick struct{} + +type User struct { + cancelTick scheduler.CancelFunc +} + +func (u *User) Init(ctx cluster.GrainContext) { +} + +func (u *User) Terminate(ctx cluster.GrainContext) { + if u.cancelTick != nil { + u.cancelTick() + } +} + +func (u *User) ReceiveDefault(ctx cluster.GrainContext) { + switch msg := ctx.Message().(type) { + case *Tick: + chatMsg := randomMessage() + fmt.Printf("[SEND] User %s says: %s\n", ctx.Identity(), chatMsg) + _, err := ctx.Cluster().Publisher().Publish(context.Background(), Topic, &ChatMessage{Message: chatMsg, Sender: ctx.Identity()}) + if err != nil { + fmt.Println(err) + } + case *ChatMessage: + fmt.Printf("[RECEIVED] User %s received %s from %s\n", ctx.Identity(), msg.Message, msg.Sender) + } +} + +func (u *User) Connect(_ *Empty, context cluster.GrainContext) (*Empty, error) { + s := scheduler.NewTimerScheduler(context) + interval := time.Second * time.Duration(rand.Intn(4)+2) + u.cancelTick = s.SendRepeatedly(interval, interval, context.Self(), &Tick{}) + + _, err := context.Cluster().SubscribeByPid(Topic, context.Self()) + return &Empty{}, err +} diff --git a/_examples/cluster-restartgracefully/Makefile b/_examples/cluster-restartgracefully/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..0676a8202166ba608f387e86ee56dab103a673d1 --- /dev/null +++ b/_examples/cluster-restartgracefully/Makefile @@ -0,0 +1,46 @@ +cp:=consul +ttl:=10s +loops:=10000 +clients:=10 +interval:=0ms +env=prod + +start: + tmux new-session -d -s eg + tmux setenv -t eg PROTO_ACTOR_ENV $(env) + tmux split-window -t "eg:0" -v + tmux split-window -t "eg:0.0" -h -p 66 + tmux split-window -t "eg:0.1" -h -p 50 + tmux select-pane -t "eg:0.3" + tmux send-keys -t "eg:0.0" "go run server/main.go --provider $(cp) --ttl $(ttl) --port 9991" Enter + tmux send-keys -t "eg:0.1" "go run server/main.go --provider $(cp) --ttl $(ttl) --port 9992" Enter + tmux send-keys -t "eg:0.2" "go run server/main.go --provider $(cp) --ttl $(ttl) --port 9993" Enter + tmux send-keys -t "eg:0.3" "sleep 2 && go run client/main.go --provider $(cp) --clients $(clients) --loops $(loops) --interval $(interval) " Enter + # tmux send-keys -t "eg:0.2" "go run member/main.go" Enter + tmux attach -t eg + tmux kill-session -t eg + + +start-with-etcd: + make start cp=etcd + + +debug: + PROTO_ACTOR_ENV=dev make start cp=etcd + + +mock-clients-10w: + make start cp=etcd clients=100000 loops=10 interval=100ms + + +mock-clients-20w: + make start cp=etcd clients=200000 loops=10 interval=100ms + + +stop: + tmux kill-session -t eg + + +proto: + protoc -I=. --gogoslick_out=. shared/*.proto + protoc -I=. --gograinv2_out=. shared/*.proto diff --git a/_examples/cluster-restartgracefully/cache/cache.go b/_examples/cluster-restartgracefully/cache/cache.go new file mode 100644 index 0000000000000000000000000000000000000000..abbfa35b3ea5ce0b453496ff03728e836211f620 --- /dev/null +++ b/_examples/cluster-restartgracefully/cache/cache.go @@ -0,0 +1,37 @@ +package cache + +import ( + "log" + "time" + + "github.com/go-redis/redis" +) + +var rds *redis.Client + +func init() { + // var err error + addr := "127.0.0.1:6379" + rds = redis.NewClient(&redis.Options{ + Addr: addr, + DialTimeout: 1 * time.Second, + }) + if err := rds.Ping().Err(); err != nil { + log.Printf("no redis err=%v", err) + } +} + +func GetCountor(key string) int64 { + v, err := rds.Get(key).Int64() + if err != nil && err != redis.Nil { + panic(err) + } + return v +} + +func SetCountor(key string, val int64) { + err := rds.Set(key, val, time.Hour).Err() + if err != nil { + panic(err) + } +} diff --git a/_examples/cluster-restartgracefully/client/main.go b/_examples/cluster-restartgracefully/client/main.go new file mode 100644 index 0000000000000000000000000000000000000000..09383a40052d73ff075e983c3415f4f86dea7894 --- /dev/null +++ b/_examples/cluster-restartgracefully/client/main.go @@ -0,0 +1,142 @@ +package main + +import ( + "flag" + "fmt" + "sync" + "time" + + "cluster-restartgracefully/shared" + + "gitee.com/simplexyz/simpleactor-go/cluster/identitylookup/partition" + + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/cluster" + "gitee.com/simplexyz/simpleactor-go/cluster/clusterproviders/consul" + "gitee.com/simplexyz/simpleactor-go/log" + "gitee.com/simplexyz/simpleactor-go/remote" + console "github.com/asynkron/goconsole" +) + +var ( + system = actor.NewActorSystem() + plog = log.New(log.DebugLevel, "[Example]") + _cluster *cluster.Cluster +) + +func main() { + cluster.SetLogLevel(log.InfoLevel) + loops := flag.Int("loops", 10000, "request times.") + interval := flag.Duration("interval", 0, "request interval miliseconds per client.") + clients := flag.Int("clients", 1, "clients count.") + provider := flag.String("provider", "consul", "clients count.") + flag.Parse() + + // start server + startNode(0, *provider) + for { + runClientsAll(*clients, *loops, *interval) + plog.Info("countinue? (y/n)") + cmd, err := console.ReadLine() + if err != nil { + panic(err) + } + if cmd == "n" || cmd == "quit" { + break + } + } + plog.Info("shutdown ...") + _cluster.Shutdown(true) + plog.Info("shutdown OK") +} + +func startNode(port int, provider string) { + var cp cluster.ClusterProvider + var err error + switch provider { + case "consul": + ttl := consul.WithTTL(100 * time.Millisecond) + refreshTTL := consul.WithRefreshTTL(100 * time.Millisecond) + cp, err = consul.New(ttl, refreshTTL) + // case "etcd": + // cp, err = etcd.New() + default: + panic(fmt.Errorf("invalid provider:%s", provider)) + } + + if err != nil { + panic(err) + } + + id := partition.New() + remoteCfg := remote.Configure("127.0.0.1", port) + cfg := cluster.Configure("cluster-restartgracefully", cp, id, remoteCfg) + _cluster = cluster.New(system, cfg) + _cluster.StartClient() +} + +func runClientsAll(clients int, loops int, interval time.Duration) { + var wg sync.WaitGroup + now := time.Now() + for i := 0; i < clients; i++ { + wg.Add(1) + grainId := fmt.Sprintf("client-%d", i) + go func() { + runClient(grainId, loops, interval) + wg.Done() + }() + } + wg.Wait() + cost := time.Since(now) + total := clients * loops + costSecs := int(cost / time.Second) + if costSecs <= 0 { + costSecs = 1 + } + plog.Info("end all.", + log.Int("clients", clients), + log.Int("total", total), + log.Int("req/s", total/costSecs), + log.Duration("take", cost)) +} + +func runClient(grainId string, loops int, interval time.Duration) { + now := time.Now() + calcGrain := shared.GetCalculatorGrainClient(_cluster, grainId) + resp, err := calcGrain.GetCurrent(&shared.Void{}, cluster.WithRetry(3), cluster.WithTimeout(6*time.Second)) + if err != nil { + _cluster.Shutdown(true) + panic(err) + } + baseNumber := resp.Number + plog.Info("requests", + log.String("grainId", grainId), + log.String("status", "start")) + for i := 1; i <= loops; i++ { + assert_calcAdd(grainId, 1, baseNumber+int64(i)) + time.Sleep(interval) + } + plog.Info("requests", + log.String("grainId", grainId), + log.String("status", "end"), + log.Int("loops", loops), + log.Duration("take", time.Since(now))) +} + +func calcAdd(grainId string, addNumber int64) int64 { + calcGrain := shared.GetCalculatorGrainClient(_cluster, grainId) + resp, err := calcGrain.Add(&shared.NumberRequest{Number: addNumber}, cluster.WithRetry(3), cluster.WithTimeout(6*time.Second)) + if err != nil { + plog.Error("call grain failed", log.Error(err)) + } + return resp.Number +} + +func assert_calcAdd(grainId string, inc, expectedNumber int64) { + number := calcAdd(grainId, inc) + if number != expectedNumber { + err := fmt.Errorf("grainId:%s inc:%d number:%d (expected=%d)", + grainId, inc, number, expectedNumber) + panic(err) + } +} diff --git a/_examples/cluster-restartgracefully/client/main_test.go b/_examples/cluster-restartgracefully/client/main_test.go new file mode 100644 index 0000000000000000000000000000000000000000..c0d6d309c8af24a0389495bd5322934d018b235e --- /dev/null +++ b/_examples/cluster-restartgracefully/client/main_test.go @@ -0,0 +1,12 @@ +package main + +import "testing" + +func BenchmarkCalcAdd(t *testing.B) { + startNode(0, "consul") + calcAdd("yes", 1) + t.ResetTimer() + for i := 0; i < t.N; i++ { + calcAdd("yes", 1) + } +} diff --git a/_examples/cluster-restartgracefully/docker-compose.yml b/_examples/cluster-restartgracefully/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..bcbc8627270e63a435ea9213133f3ad1f24e31f4 --- /dev/null +++ b/_examples/cluster-restartgracefully/docker-compose.yml @@ -0,0 +1,7 @@ +# @see https://docs.docker.com/compose/compose-file/ +version: '3.4' +services: + redis: + image: redis:5.0.7-alpine + ports: + - 127.0.0.1:6380:6379 diff --git a/_examples/cluster-restartgracefully/go.mod b/_examples/cluster-restartgracefully/go.mod new file mode 100644 index 0000000000000000000000000000000000000000..0b7668fe55cb03a278113455d3f9771fd74ff465 --- /dev/null +++ b/_examples/cluster-restartgracefully/go.mod @@ -0,0 +1,63 @@ +module cluster-restartgracefully + +go 1.21 + +toolchain go1.21.2 + +replace gitee.com/simplexyz/simpleactor-go => ../.. + +require ( + gitee.com/simplexyz/simpleactor-go v0.0.0-00010101000000-000000000000 + github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716 + github.com/go-redis/redis v6.15.9+incompatible + google.golang.org/protobuf v1.31.0 +) + +require ( + github.com/Workiva/go-datastructures v1.1.1 // indirect + github.com/armon/go-metrics v0.4.0 // indirect + github.com/asynkron/gofun v0.0.0-20220329210725-34fed760f4c2 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/emirpasic/gods v1.18.1 // indirect + github.com/fatih/color v1.13.0 // indirect + github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/google/btree v1.0.1 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/hashicorp/consul/api v1.18.0 // indirect + github.com/hashicorp/go-cleanhttp v0.5.2 // indirect + github.com/hashicorp/go-hclog v1.2.0 // indirect + github.com/hashicorp/go-immutable-radix v1.3.1 // indirect + github.com/hashicorp/go-rootcerts v1.0.2 // indirect + github.com/hashicorp/golang-lru v0.5.4 // indirect + github.com/hashicorp/serf v0.10.1 // indirect + github.com/lithammer/shortuuid/v4 v4.0.0 // indirect + github.com/mattn/go-colorable v0.1.12 // indirect + github.com/mattn/go-isatty v0.0.16 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/onsi/ginkgo v1.16.4 // indirect + github.com/onsi/gomega v1.23.0 // indirect + github.com/orcaman/concurrent-map v1.0.0 // indirect + github.com/prometheus/client_golang v1.16.0 // indirect + github.com/prometheus/client_model v0.4.0 // indirect + github.com/prometheus/common v0.42.0 // indirect + github.com/prometheus/procfs v0.10.1 // indirect + github.com/twmb/murmur3 v1.1.6 // indirect + go.opentelemetry.io/otel v1.16.0 // indirect + go.opentelemetry.io/otel/exporters/prometheus v0.39.0 // indirect + go.opentelemetry.io/otel/metric v1.16.0 // indirect + go.opentelemetry.io/otel/sdk v1.16.0 // indirect + go.opentelemetry.io/otel/sdk/metric v0.39.0 // indirect + go.opentelemetry.io/otel/trace v1.16.0 // indirect + golang.org/x/exp v0.0.0-20220827204233-334a2380cb91 // indirect + golang.org/x/net v0.17.0 // indirect + golang.org/x/sys v0.13.0 // indirect + golang.org/x/text v0.13.0 // indirect + google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97 // indirect + google.golang.org/grpc v1.58.3 // indirect +) diff --git a/_examples/cluster-restartgracefully/go.sum b/_examples/cluster-restartgracefully/go.sum new file mode 100644 index 0000000000000000000000000000000000000000..0cbfbd8542aae6f373aa42963390282f7a8666f9 --- /dev/null +++ b/_examples/cluster-restartgracefully/go.sum @@ -0,0 +1,368 @@ +github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= +github.com/Workiva/go-datastructures v1.1.1 h1:9G5u1UqKt6ABseAffHGNfbNQd7omRlWE5QaxNruzhE0= +github.com/Workiva/go-datastructures v1.1.1/go.mod h1:1yZL+zfsztete+ePzZz/Zb1/t5BnDuE2Ya2MMGhzP6A= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-metrics v0.4.0 h1:yCQqn7dwca4ITXb+CbubHmedzaQYHhNhrEXLYUeEe8Q= +github.com/armon/go-metrics v0.4.0/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716 h1:SgyG4sXkrlalMoCfp20LiNPNhfJS7ez3opNdtihIxPc= +github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716/go.mod h1:/zSlF0T2ArAsTG6SVu8d8qlK+19jjudjA3wWsxnGFHg= +github.com/asynkron/gofun v0.0.0-20220329210725-34fed760f4c2 h1:jEsFZ9d/ieJGVrx3fSPi8oe/qv21fRmyUL5cS3ZEn5A= +github.com/asynkron/gofun v0.0.0-20220329210725-34fed760f4c2/go.mod h1:5GMOSqaYxNWwuVRWyampTPJEntwz7Mj9J8v1a7gSU2E= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= +github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= +github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg= +github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= +github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/hashicorp/consul/api v1.18.0 h1:R7PPNzTCeN6VuQNDwwhZWJvzCtGSrNpJqfb22h3yH9g= +github.com/hashicorp/consul/api v1.18.0/go.mod h1:owRRGJ9M5xReDC5nfT8FTJrNAPbT4NM6p/k+d03q2v4= +github.com/hashicorp/consul/sdk v0.13.0 h1:lce3nFlpv8humJL8rNrrGHYSKc3q+Kxfeg3Ii1m6ZWU= +github.com/hashicorp/consul/sdk v0.13.0/go.mod h1:0hs/l5fOVhJy/VdcoaNqUSi2AUs95eF5WKtv+EYIQqE= +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.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= +github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-hclog v1.2.0 h1:La19f8d7WIlm4ogzNHB0JGqs5AUDAZ2UfCY4sJXcJdM= +github.com/hashicorp/go-hclog v1.2.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= +github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-msgpack v0.5.5 h1:i9R9JSrqIz0QVLz3sz+i3YJdT7TTSLcfLLzJi9aZTuI= +github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-multierror v1.1.0 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g4TbElacI= +github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= +github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= +github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= +github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc= +github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= +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/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE= +github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.2.1 h1:zEfKbn2+PDgroKdiOzqiE8rsmLqU2uwi5PB5pBJ3TkI= +github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= +github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= +github.com/hashicorp/memberlist v0.5.0 h1:EtYPN8DpAURiapus508I4n9CzHs2W+8NZGbmmR/prTM= +github.com/hashicorp/memberlist v0.5.0/go.mod h1:yvyXLpo0QaGE59Y7hDTsTzDD25JYBZ4mHgHUZ8lrOI0= +github.com/hashicorp/serf v0.10.1 h1:Z1H2J60yRKvfDYAOZLd2MU0ND4AH/WDz7xYHDWQsIPY= +github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/lithammer/shortuuid/v4 v4.0.0 h1:QRbbVkfgNippHOS8PXDkti4NaWeyYfcBTHtw7k08o4c= +github.com/lithammer/shortuuid/v4 v4.0.0/go.mod h1:Zs8puNcrvf2rV9rTH51ZLLcj7ZXqQI3lv67aw4KiB1Y= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= +github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= +github.com/miekg/dns v1.1.41 h1:WMszZWJG0XmzbK9FEmzH2TVcqYzFesusSIB41b8KHxY= +github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= +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-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= +github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.23.0 h1:/oxKu9c2HVap+F3PfKort2Hw5DEU+HGlW8n+tguWsys= +github.com/onsi/gomega v1.23.0/go.mod h1:Z/NWtiqwBrwUt4/2loMmHL63EDLnYHmVbuBpDr2vQAg= +github.com/orcaman/concurrent-map v1.0.0 h1:I/2A2XPCb4IuQWcQhBhSwGfiuybl/J0ev9HDbW65HOY= +github.com/orcaman/concurrent-map v1.0.0/go.mod h1:Lu3tH6HLW3feq74c2GC+jIMS/K2CFcDWnWD9XkenwhI= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= +github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= +github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= +github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= +github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= +github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= +github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= +github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +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/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/tinylib/msgp v1.1.5/go.mod h1:eQsjooMTnV42mHu917E26IogZ2930nFyBQdofk10Udg= +github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q= +github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= +github.com/twmb/murmur3 v1.1.6 h1:mqrRot1BRxm+Yct+vavLMou2/iJt0tNVTTC0QoIjaZg= +github.com/twmb/murmur3 v1.1.6/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s= +go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4= +go.opentelemetry.io/otel/exporters/prometheus v0.39.0 h1:whAaiHxOatgtKd+w0dOi//1KUxj3KoPINZdtDaDj3IA= +go.opentelemetry.io/otel/exporters/prometheus v0.39.0/go.mod h1:4jo5Q4CROlCpSPsXLhymi+LYrDXd2ObU5wbKayfZs7Y= +go.opentelemetry.io/otel/metric v1.16.0 h1:RbrpwVG1Hfv85LgnZ7+txXioPDoh6EdbZHo26Q3hqOo= +go.opentelemetry.io/otel/metric v1.16.0/go.mod h1:QE47cpOmkwipPiefDwo2wDzwJrlfxxNYodqc4xnGCo4= +go.opentelemetry.io/otel/sdk v1.16.0 h1:Z1Ok1YsijYL0CSJpHt4cS3wDDh7p572grzNrBMiMWgE= +go.opentelemetry.io/otel/sdk v1.16.0/go.mod h1:tMsIuKXuuIWPBAOrH+eHtvhTL+SntFtXF9QD68aP6p4= +go.opentelemetry.io/otel/sdk/metric v0.39.0 h1:Kun8i1eYf48kHH83RucG93ffz0zGV1sh46FAScOTuDI= +go.opentelemetry.io/otel/sdk/metric v0.39.0/go.mod h1:piDIRgjcK7u0HCL5pCA4e74qpK/jk3NiUoAHATVAmiI= +go.opentelemetry.io/otel/trace v1.16.0 h1:8JRpaObFoW0pxuVPapkgH8UhHQj+bJW8jJsCZEu5MQs= +go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20220827204233-334a2380cb91 h1:tnebWN09GYg9OLPss1KXj8txwZc6X6uMr6VFdcGNbHw= +golang.org/x/exp v0.0.0-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= +golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 h1:DdoeryqhaXp1LtT/emMP1BRJPHHKFi5akj/nbx/zNTA= +google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= +google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97/go.mod h1:t1VqOqqvce95G3hIDCT5FeO3YUc6Q4Oe24L/+rNMxRk= +google.golang.org/grpc v1.55.0 h1:3Oj82/tFSCeUrRTg/5E/7d/W5A1tj6Ky1ABAuZuv5ag= +google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8= +google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/_examples/cluster-restartgracefully/server/calculator-grain.go b/_examples/cluster-restartgracefully/server/calculator-grain.go new file mode 100644 index 0000000000000000000000000000000000000000..4aef24492813f373aac9cd4964dedb2af573a98d --- /dev/null +++ b/_examples/cluster-restartgracefully/server/calculator-grain.go @@ -0,0 +1,40 @@ +package main + +import ( + "cluster-restartgracefully/cache" + "cluster-restartgracefully/shared" + "gitee.com/simplexyz/simpleactor-go/cluster" + "gitee.com/simplexyz/simpleactor-go/log" +) + +type CalcGrain struct { + total int64 +} + +func (c *CalcGrain) Init(ctx cluster.GrainContext) { + c.total = cache.GetCountor(ctx.Identity()) + plog.Info("start", log.String("id", ctx.Identity()), log.Int64("number", c.total)) +} + +func (c *CalcGrain) Terminate(ctx cluster.GrainContext) { + id := ctx.Identity() + cache.SetCountor(id, c.total) + plog.Info("stop", log.String("id", id), log.Int64("number", c.total)) +} + +func (c *CalcGrain) ReceiveDefault(ctx cluster.GrainContext) { +} + +func (c *CalcGrain) Add(n *shared.NumberRequest, ctx cluster.GrainContext) (*shared.CountResponse, error) { + c.total = c.total + n.Number + return &shared.CountResponse{Number: c.total}, nil +} + +func (c *CalcGrain) Subtract(n *shared.NumberRequest, ctx cluster.GrainContext) (*shared.CountResponse, error) { + c.total = c.total - n.Number + return &shared.CountResponse{Number: c.total}, nil +} + +func (c *CalcGrain) GetCurrent(n *shared.Void, ctx cluster.GrainContext) (*shared.CountResponse, error) { + return &shared.CountResponse{Number: c.total}, nil +} diff --git a/_examples/cluster-restartgracefully/server/main.go b/_examples/cluster-restartgracefully/server/main.go new file mode 100644 index 0000000000000000000000000000000000000000..7e4b724c941da8961320f7efdc0967cc2c425967 --- /dev/null +++ b/_examples/cluster-restartgracefully/server/main.go @@ -0,0 +1,80 @@ +package main + +import ( + "flag" + "fmt" + "os" + "os/signal" + "syscall" + "time" + + "cluster-restartgracefully/shared" + + "gitee.com/simplexyz/simpleactor-go/cluster/identitylookup/disthash" + + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/cluster" + "gitee.com/simplexyz/simpleactor-go/cluster/clusterproviders/consul" + "gitee.com/simplexyz/simpleactor-go/log" + "gitee.com/simplexyz/simpleactor-go/remote" +) + +var ( + plog = log.New(log.DebugLevel, "[Example]") + system = actor.NewActorSystem() + _cluster *cluster.Cluster +) + +func main() { + provider := flag.String("provider", "consul", "clients count.") + actorTTL := flag.Duration("ttl", 10*time.Second, "time to live of actor.") + port := flag.Int("port", 0, "listen port.") + + flag.Parse() + startNode(*port, *provider, *actorTTL) + + // waiting CTRL-C + sigCh := make(chan os.Signal) + signal.Notify(sigCh, syscall.SIGINT) + for sig := range sigCh { + switch sig { + case syscall.SIGINT: + plog.Info("Shutdown...") + _cluster.Shutdown(true) + plog.Info("Shutdown ok") + time.Sleep(time.Second) + os.Exit(0) + default: + plog.Info("Skipping", log.Object("sig", sig)) + } + } +} + +func startNode(port int, provider string, timeout time.Duration) { + plog.Info("press 'CTRL-C' to shutdown server.") + shared.CalculatorFactory(func() shared.Calculator { + return &CalcGrain{} + }) + + var cp cluster.ClusterProvider + var err error + switch provider { + case "consul": + cp, err = consul.New() + // case "etcd": + // cp, err = etcd.New() + default: + panic(fmt.Errorf("invalid provider:%s", provider)) + } + + id := disthash.New() + + if err != nil { + panic(err) + } + + remoteCfg := remote.Configure("127.0.0.1", port) + cfg := cluster.Configure("cluster-restartgracefully", cp, id, remoteCfg, cluster.WithKinds(shared.GetCalculatorKind())) + _cluster = cluster.New(system, cfg) + _cluster.StartMember() +} diff --git a/_examples/cluster-restartgracefully/shared/build.sh b/_examples/cluster-restartgracefully/shared/build.sh new file mode 100644 index 0000000000000000000000000000000000000000..5fcdfc759c64741b07f04920edb13084d07da977 --- /dev/null +++ b/_examples/cluster-restartgracefully/shared/build.sh @@ -0,0 +1,2 @@ +protoc --go_out=. --go_opt=paths=source_relative --proto_path=. protos.proto +protoc -I=. --gograinv2_out=. protos.proto diff --git a/_examples/cluster-restartgracefully/shared/protos.pb.go b/_examples/cluster-restartgracefully/shared/protos.pb.go new file mode 100644 index 0000000000000000000000000000000000000000..46245d446a13c68f4c37f41a5720f1050abb7934 --- /dev/null +++ b/_examples/cluster-restartgracefully/shared/protos.pb.go @@ -0,0 +1,277 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc v3.19.1 +// source: protos.proto + +package shared + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type Void struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *Void) Reset() { + *x = Void{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Void) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Void) ProtoMessage() {} + +func (x *Void) ProtoReflect() protoreflect.Message { + mi := &file_protos_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Void.ProtoReflect.Descriptor instead. +func (*Void) Descriptor() ([]byte, []int) { + return file_protos_proto_rawDescGZIP(), []int{0} +} + +type NumberRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Number int64 `protobuf:"varint,1,opt,name=number,proto3" json:"number,omitempty"` +} + +func (x *NumberRequest) Reset() { + *x = NumberRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *NumberRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NumberRequest) ProtoMessage() {} + +func (x *NumberRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use NumberRequest.ProtoReflect.Descriptor instead. +func (*NumberRequest) Descriptor() ([]byte, []int) { + return file_protos_proto_rawDescGZIP(), []int{1} +} + +func (x *NumberRequest) GetNumber() int64 { + if x != nil { + return x.Number + } + return 0 +} + +type CountResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Number int64 `protobuf:"varint,1,opt,name=number,proto3" json:"number,omitempty"` +} + +func (x *CountResponse) Reset() { + *x = CountResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CountResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CountResponse) ProtoMessage() {} + +func (x *CountResponse) ProtoReflect() protoreflect.Message { + mi := &file_protos_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CountResponse.ProtoReflect.Descriptor instead. +func (*CountResponse) Descriptor() ([]byte, []int) { + return file_protos_proto_rawDescGZIP(), []int{2} +} + +func (x *CountResponse) GetNumber() int64 { + if x != nil { + return x.Number + } + return 0 +} + +var File_protos_proto protoreflect.FileDescriptor + +var file_protos_proto_rawDesc = []byte{ + 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06, + 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x22, 0x06, 0x0a, 0x04, 0x56, 0x6f, 0x69, 0x64, 0x22, 0x27, + 0x0a, 0x0d, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x16, 0x0a, 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x22, 0x27, 0x0a, 0x0d, 0x43, 0x6f, 0x75, 0x6e, 0x74, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6e, 0x75, 0x6d, 0x62, + 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, + 0x32, 0xb4, 0x01, 0x0a, 0x0a, 0x43, 0x61, 0x6c, 0x63, 0x75, 0x6c, 0x61, 0x74, 0x6f, 0x72, 0x12, + 0x35, 0x0a, 0x03, 0x41, 0x64, 0x64, 0x12, 0x15, 0x2e, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x2e, + 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, + 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x2e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3a, 0x0a, 0x08, 0x53, 0x75, 0x62, 0x74, 0x72, 0x61, + 0x63, 0x74, 0x12, 0x15, 0x2e, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x2e, 0x4e, 0x75, 0x6d, 0x62, + 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x73, 0x68, 0x61, 0x72, + 0x65, 0x64, 0x2e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x00, 0x12, 0x33, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, + 0x12, 0x0c, 0x2e, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x2e, 0x56, 0x6f, 0x69, 0x64, 0x1a, 0x15, + 0x2e, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x2e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x4e, 0x5a, 0x4c, 0x67, 0x69, 0x74, 0x68, 0x75, + 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x73, 0x79, 0x6e, 0x6b, 0x72, 0x6f, 0x6e, 0x2f, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x2d, 0x67, 0x6f, 0x2f, 0x5f, 0x65, 0x78, + 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x2f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x2d, 0x72, + 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x67, 0x72, 0x61, 0x63, 0x65, 0x66, 0x75, 0x6c, 0x6c, 0x79, + 0x2f, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_protos_proto_rawDescOnce sync.Once + file_protos_proto_rawDescData = file_protos_proto_rawDesc +) + +func file_protos_proto_rawDescGZIP() []byte { + file_protos_proto_rawDescOnce.Do(func() { + file_protos_proto_rawDescData = protoimpl.X.CompressGZIP(file_protos_proto_rawDescData) + }) + return file_protos_proto_rawDescData +} + +var file_protos_proto_msgTypes = make([]protoimpl.MessageInfo, 3) +var file_protos_proto_goTypes = []interface{}{ + (*Void)(nil), // 0: shared.Void + (*NumberRequest)(nil), // 1: shared.NumberRequest + (*CountResponse)(nil), // 2: shared.CountResponse +} +var file_protos_proto_depIdxs = []int32{ + 1, // 0: shared.Calculator.Add:input_type -> shared.NumberRequest + 1, // 1: shared.Calculator.Subtract:input_type -> shared.NumberRequest + 0, // 2: shared.Calculator.GetCurrent:input_type -> shared.Void + 2, // 3: shared.Calculator.Add:output_type -> shared.CountResponse + 2, // 4: shared.Calculator.Subtract:output_type -> shared.CountResponse + 2, // 5: shared.Calculator.GetCurrent:output_type -> shared.CountResponse + 3, // [3:6] is the sub-list for method output_type + 0, // [0:3] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_protos_proto_init() } +func file_protos_proto_init() { + if File_protos_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_protos_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Void); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NumberRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CountResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_protos_proto_rawDesc, + NumEnums: 0, + NumMessages: 3, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_protos_proto_goTypes, + DependencyIndexes: file_protos_proto_depIdxs, + MessageInfos: file_protos_proto_msgTypes, + }.Build() + File_protos_proto = out.File + file_protos_proto_rawDesc = nil + file_protos_proto_goTypes = nil + file_protos_proto_depIdxs = nil +} diff --git a/_examples/cluster-restartgracefully/shared/protos.proto b/_examples/cluster-restartgracefully/shared/protos.proto new file mode 100644 index 0000000000000000000000000000000000000000..1de7b24b3deeaa69ad97eba5682d7c142f5f5cab --- /dev/null +++ b/_examples/cluster-restartgracefully/shared/protos.proto @@ -0,0 +1,19 @@ +syntax = "proto3"; +option go_package = "gitee.com/simplexyz/simpleactor-go/_examples/cluster-restartgracefully/shared"; +package shared; + +message Void {} + +message NumberRequest { + int64 number = 1; +} + +message CountResponse { + int64 number = 1; +} + +service Calculator { + rpc Add(NumberRequest) returns (CountResponse) {} + rpc Subtract(NumberRequest) returns (CountResponse) {} + rpc GetCurrent(Void) returns (CountResponse) {} +} diff --git a/_examples/cluster-restartgracefully/shared/protos_protoactor.go b/_examples/cluster-restartgracefully/shared/protos_protoactor.go new file mode 100644 index 0000000000000000000000000000000000000000..9cef46abc580fc74ac371a1488da61754f3c0f5c --- /dev/null +++ b/_examples/cluster-restartgracefully/shared/protos_protoactor.go @@ -0,0 +1,268 @@ +// Package shared is generated by protoactor-go/protoc-gen-gograin@0.1.0 +package shared + +import ( + "errors" + "fmt" + "math" + "time" + + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/cluster" + logmod "gitee.com/simplexyz/simpleactor-go/log" + "google.golang.org/protobuf/proto" +) + +var ( + plog = logmod.New(logmod.InfoLevel, "[GRAIN][shared]") + _ = proto.Marshal + _ = fmt.Errorf + _ = math.Inf +) + +// SetLogLevel sets the log level. +func SetLogLevel(level logmod.Level) { + plog.SetLevel(level) +} + +var xCalculatorFactory func() Calculator + +// CalculatorFactory produces a Calculator +func CalculatorFactory(factory func() Calculator) { + xCalculatorFactory = factory +} + +// GetCalculatorGrainClient instantiates a new CalculatorGrainClient with given Identity +func GetCalculatorGrainClient(c *cluster.Cluster, id string) *CalculatorGrainClient { + if c == nil { + panic(fmt.Errorf("nil cluster instance")) + } + if id == "" { + panic(fmt.Errorf("empty id")) + } + return &CalculatorGrainClient{Identity: id, cluster: c} +} + +// GetCalculatorKind instantiates a new cluster.Kind for Calculator +func GetCalculatorKind(opts ...actor.PropsOption) *cluster.Kind { + props := actor.PropsFromProducer(func() actor.Actor { + return &CalculatorActor{ + Timeout: 60 * time.Second, + } + }, opts...) + kind := cluster.NewKind("Calculator", props) + return kind +} + +// GetCalculatorKind instantiates a new cluster.Kind for Calculator +func NewCalculatorKind(factory func() Calculator, timeout time.Duration, opts ...actor.PropsOption) *cluster.Kind { + xCalculatorFactory = factory + props := actor.PropsFromProducer(func() actor.Actor { + return &CalculatorActor{ + Timeout: timeout, + } + }, opts...) + kind := cluster.NewKind("Calculator", props) + return kind +} + +// Calculator interfaces the services available to the Calculator +type Calculator interface { + Init(ctx cluster.GrainContext) + Terminate(ctx cluster.GrainContext) + ReceiveDefault(ctx cluster.GrainContext) + Add(*NumberRequest, cluster.GrainContext) (*CountResponse, error) + Subtract(*NumberRequest, cluster.GrainContext) (*CountResponse, error) + GetCurrent(*Void, cluster.GrainContext) (*CountResponse, error) +} + +// CalculatorGrainClient holds the base data for the CalculatorGrain +type CalculatorGrainClient struct { + Identity string + cluster *cluster.Cluster +} + +// Add requests the execution on to the cluster with CallOptions +func (g *CalculatorGrainClient) Add(r *NumberRequest, opts ...cluster.GrainCallOption) (*CountResponse, error) { + bytes, err := proto.Marshal(r) + if err != nil { + return nil, err + } + reqMsg := &cluster.GrainRequest{MethodIndex: 0, MessageData: bytes} + resp, err := g.cluster.Call(g.Identity, "Calculator", reqMsg, opts...) + if err != nil { + return nil, err + } + switch msg := resp.(type) { + case *cluster.GrainResponse: + result := &CountResponse{} + err = proto.Unmarshal(msg.MessageData, result) + if err != nil { + return nil, err + } + return result, nil + case *cluster.GrainErrorResponse: + return nil, errors.New(msg.Err) + default: + return nil, errors.New("unknown response") + } +} + +// Subtract requests the execution on to the cluster with CallOptions +func (g *CalculatorGrainClient) Subtract(r *NumberRequest, opts ...cluster.GrainCallOption) (*CountResponse, error) { + bytes, err := proto.Marshal(r) + if err != nil { + return nil, err + } + reqMsg := &cluster.GrainRequest{MethodIndex: 1, MessageData: bytes} + resp, err := g.cluster.Call(g.Identity, "Calculator", reqMsg, opts...) + if err != nil { + return nil, err + } + switch msg := resp.(type) { + case *cluster.GrainResponse: + result := &CountResponse{} + err = proto.Unmarshal(msg.MessageData, result) + if err != nil { + return nil, err + } + return result, nil + case *cluster.GrainErrorResponse: + return nil, errors.New(msg.Err) + default: + return nil, errors.New("unknown response") + } +} + +// GetCurrent requests the execution on to the cluster with CallOptions +func (g *CalculatorGrainClient) GetCurrent(r *Void, opts ...cluster.GrainCallOption) (*CountResponse, error) { + bytes, err := proto.Marshal(r) + if err != nil { + return nil, err + } + reqMsg := &cluster.GrainRequest{MethodIndex: 2, MessageData: bytes} + resp, err := g.cluster.Call(g.Identity, "Calculator", reqMsg, opts...) + if err != nil { + return nil, err + } + switch msg := resp.(type) { + case *cluster.GrainResponse: + result := &CountResponse{} + err = proto.Unmarshal(msg.MessageData, result) + if err != nil { + return nil, err + } + return result, nil + case *cluster.GrainErrorResponse: + return nil, errors.New(msg.Err) + default: + return nil, errors.New("unknown response") + } +} + +// CalculatorActor represents the actor structure +type CalculatorActor struct { + ctx cluster.GrainContext + inner Calculator + Timeout time.Duration +} + +// Receive ensures the lifecycle of the actor for the received message +func (a *CalculatorActor) Receive(ctx actor.Context) { + switch msg := ctx.Message().(type) { + case *actor.Started: // pass + case *cluster.ClusterInit: + a.ctx = cluster.NewGrainContext(ctx, msg.Identity, msg.Cluster) + a.inner = xCalculatorFactory() + a.inner.Init(a.ctx) + + if a.Timeout > 0 { + ctx.SetReceiveTimeout(a.Timeout) + } + case *actor.ReceiveTimeout: + ctx.Poison(ctx.Self()) + case *actor.Stopped: + a.inner.Terminate(a.ctx) + case actor.AutoReceiveMessage: // pass + case actor.SystemMessage: // pass + + case *cluster.GrainRequest: + switch msg.MethodIndex { + case 0: + req := &NumberRequest{} + err := proto.Unmarshal(msg.MessageData, req) + if err != nil { + plog.Error("Add(NumberRequest) proto.Unmarshal failed.", logmod.Error(err)) + resp := &cluster.GrainErrorResponse{Err: err.Error()} + ctx.Respond(resp) + return + } + r0, err := a.inner.Add(req, a.ctx) + if err != nil { + resp := &cluster.GrainErrorResponse{Err: err.Error()} + ctx.Respond(resp) + return + } + bytes, err := proto.Marshal(r0) + if err != nil { + plog.Error("Add(NumberRequest) proto.Marshal failed", logmod.Error(err)) + resp := &cluster.GrainErrorResponse{Err: err.Error()} + ctx.Respond(resp) + return + } + resp := &cluster.GrainResponse{MessageData: bytes} + ctx.Respond(resp) + case 1: + req := &NumberRequest{} + err := proto.Unmarshal(msg.MessageData, req) + if err != nil { + plog.Error("Subtract(NumberRequest) proto.Unmarshal failed.", logmod.Error(err)) + resp := &cluster.GrainErrorResponse{Err: err.Error()} + ctx.Respond(resp) + return + } + r0, err := a.inner.Subtract(req, a.ctx) + if err != nil { + resp := &cluster.GrainErrorResponse{Err: err.Error()} + ctx.Respond(resp) + return + } + bytes, err := proto.Marshal(r0) + if err != nil { + plog.Error("Subtract(NumberRequest) proto.Marshal failed", logmod.Error(err)) + resp := &cluster.GrainErrorResponse{Err: err.Error()} + ctx.Respond(resp) + return + } + resp := &cluster.GrainResponse{MessageData: bytes} + ctx.Respond(resp) + case 2: + req := &Void{} + err := proto.Unmarshal(msg.MessageData, req) + if err != nil { + plog.Error("GetCurrent(Void) proto.Unmarshal failed.", logmod.Error(err)) + resp := &cluster.GrainErrorResponse{Err: err.Error()} + ctx.Respond(resp) + return + } + r0, err := a.inner.GetCurrent(req, a.ctx) + if err != nil { + resp := &cluster.GrainErrorResponse{Err: err.Error()} + ctx.Respond(resp) + return + } + bytes, err := proto.Marshal(r0) + if err != nil { + plog.Error("GetCurrent(Void) proto.Marshal failed", logmod.Error(err)) + resp := &cluster.GrainErrorResponse{Err: err.Error()} + ctx.Respond(resp) + return + } + resp := &cluster.GrainResponse{MessageData: bytes} + ctx.Respond(resp) + + } + default: + a.inner.ReceiveDefault(a.ctx) + } +} diff --git a/_examples/kubernetes-sample/.dockerignore b/_examples/kubernetes-sample/.dockerignore new file mode 100644 index 0000000000000000000000000000000000000000..b80d55cd3e0d61d7a6244a5ff0e04a7a71ce3c19 --- /dev/null +++ b/_examples/kubernetes-sample/.dockerignore @@ -0,0 +1,2 @@ +.git +debug \ No newline at end of file diff --git a/_examples/kubernetes-sample/Dockerfile b/_examples/kubernetes-sample/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..6390ff49a9e0cbcd8fd32825f24a433dbd7f420d --- /dev/null +++ b/_examples/kubernetes-sample/Dockerfile @@ -0,0 +1,11 @@ +FROM golang:1.19-alpine as build + +WORKDIR /src +COPY . ./ + +WORKDIR /src/_examples/kubernetes-sample +RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -tags netgo -o app + +FROM scratch +COPY --from=build /src/_examples/kubernetes-sample/app / +ENTRYPOINT ["/app"] \ No newline at end of file diff --git a/_examples/kubernetes-sample/Dockerfile-localcompile b/_examples/kubernetes-sample/Dockerfile-localcompile new file mode 100644 index 0000000000000000000000000000000000000000..92fcce82c1d2c5626644e964ba3ae131fdc51e3a --- /dev/null +++ b/_examples/kubernetes-sample/Dockerfile-localcompile @@ -0,0 +1,3 @@ +FROM scratch +COPY app / +ENTRYPOINT ["/app"] \ No newline at end of file diff --git a/_examples/kubernetes-sample/Makefile b/_examples/kubernetes-sample/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..e92db6deda42481ae165c59eaf3efb20e5b6a65c --- /dev/null +++ b/_examples/kubernetes-sample/Makefile @@ -0,0 +1,16 @@ +.PHONY: docker docker-local deploy delete redeploy + +docker: + cd ../.. && docker build -f _examples/kubernetes-sample/Dockerfile -t kubernetes-sample:latest . + +docker-localcompile: + CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -tags netgo -o app + docker build -f Dockerfile-localcompile -t kubernetes-sample:latest . + +deploy: + kubectl apply -f deploy-sample.yaml + +delete: + kubectl delete -f deploy-sample.yaml + +redeploy: | delete deploy \ No newline at end of file diff --git a/_examples/kubernetes-sample/deploy-sample.yaml b/_examples/kubernetes-sample/deploy-sample.yaml new file mode 100644 index 0000000000000000000000000000000000000000..c82c9d18a32ae3d7df0e8f8353cac76f44de6514 --- /dev/null +++ b/_examples/kubernetes-sample/deploy-sample.yaml @@ -0,0 +1,72 @@ +kind: Namespace +apiVersion: v1 +metadata: + name: kubernetes-sample + labels: + name: kubernetes-sample +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: kubernetes-sample + namespace: kubernetes-sample +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: kubernetes-sample + namespace: kubernetes-sample +rules: + - apiGroups: + - "" + resources: + - pods + verbs: + - get + - list + - watch + - patch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: kubernetes-sample + namespace: kubernetes-sample +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: kubernetes-sample +subjects: + - kind: ServiceAccount + name: kubernetes-sample +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: kubernetes-sample + namespace: kubernetes-sample + labels: + app: kubernetes-sample +spec: + replicas: 1 + selector: + matchLabels: + app: kubernetes-sample + template: + metadata: + namespace: kubernetes-sample + labels: + app: kubernetes-sample + spec: + serviceAccountName: kubernetes-sample + containers: + - name: kubernetes-sample + image: kubernetes-sample:latest + imagePullPolicy: Never # docker desktop + env: + - name: PROTOHOST + valueFrom: + fieldRef: + fieldPath: status.podIP + - name: PROTOPORT + value: "50051" diff --git a/_examples/kubernetes-sample/go.mod b/_examples/kubernetes-sample/go.mod new file mode 100644 index 0000000000000000000000000000000000000000..40e26f4f6e1861fa4c917b932b8eeadf36a6df06 --- /dev/null +++ b/_examples/kubernetes-sample/go.mod @@ -0,0 +1,75 @@ +module kubernetes-sample + +go 1.21 + +toolchain go1.21.2 + +replace gitee.com/simplexyz/simpleactor-go => ../.. + +require ( + gitee.com/simplexyz/simpleactor-go v0.0.0-00010101000000-000000000000 + google.golang.org/protobuf v1.31.0 + k8s.io/utils v0.0.0-20221107191617-1a15be271d1d +) + +require ( + github.com/Workiva/go-datastructures v1.1.1 // indirect + github.com/asynkron/gofun v0.0.0-20220329210725-34fed760f4c2 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/emicklei/go-restful/v3 v3.9.0 // indirect + github.com/emirpasic/gods v1.18.1 // indirect + github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-openapi/jsonpointer v0.19.5 // indirect + github.com/go-openapi/jsonreference v0.20.0 // indirect + github.com/go-openapi/swag v0.21.1 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/google/gnostic v0.6.9 // indirect + github.com/google/go-cmp v0.5.9 // indirect + github.com/google/gofuzz v1.2.0 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/lithammer/shortuuid/v4 v4.0.0 // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/orcaman/concurrent-map v1.0.0 // indirect + github.com/prometheus/client_golang v1.16.0 // indirect + github.com/prometheus/client_model v0.4.0 // indirect + github.com/prometheus/common v0.42.0 // indirect + github.com/prometheus/procfs v0.10.1 // indirect + github.com/twmb/murmur3 v1.1.6 // indirect + go.opentelemetry.io/otel v1.16.0 // indirect + go.opentelemetry.io/otel/exporters/prometheus v0.39.0 // indirect + go.opentelemetry.io/otel/metric v1.16.0 // indirect + go.opentelemetry.io/otel/sdk v1.16.0 // indirect + go.opentelemetry.io/otel/sdk/metric v0.39.0 // indirect + go.opentelemetry.io/otel/trace v1.16.0 // indirect + golang.org/x/exp v0.0.0-20220518171630-0b5c67f07fdf // indirect + golang.org/x/net v0.17.0 // indirect + golang.org/x/oauth2 v0.10.0 // indirect + golang.org/x/sys v0.13.0 // indirect + golang.org/x/term v0.13.0 // indirect + golang.org/x/text v0.13.0 // indirect + golang.org/x/time v0.0.0-20220411224347-583f2d630306 // indirect + google.golang.org/appengine v1.6.7 // indirect + google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97 // indirect + google.golang.org/grpc v1.58.3 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + k8s.io/api v0.26.1 // indirect + k8s.io/apimachinery v0.26.1 // indirect + k8s.io/client-go v0.26.1 // indirect + k8s.io/klog/v2 v2.80.1 // indirect + k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 // indirect + sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect + sigs.k8s.io/yaml v1.3.0 // indirect +) diff --git a/_examples/kubernetes-sample/go.sum b/_examples/kubernetes-sample/go.sum new file mode 100644 index 0000000000000000000000000000000000000000..fd10341cbf7d00490c50267f42ddac24fcfc1b15 --- /dev/null +++ b/_examples/kubernetes-sample/go.sum @@ -0,0 +1,324 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/Workiva/go-datastructures v1.1.1 h1:9G5u1UqKt6ABseAffHGNfbNQd7omRlWE5QaxNruzhE0= +github.com/Workiva/go-datastructures v1.1.1/go.mod h1:1yZL+zfsztete+ePzZz/Zb1/t5BnDuE2Ya2MMGhzP6A= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/asynkron/gofun v0.0.0-20220329210725-34fed760f4c2 h1:jEsFZ9d/ieJGVrx3fSPi8oe/qv21fRmyUL5cS3ZEn5A= +github.com/asynkron/gofun v0.0.0-20220329210725-34fed760f4c2/go.mod h1:5GMOSqaYxNWwuVRWyampTPJEntwz7Mj9J8v1a7gSU2E= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= +github.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE= +github.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/flowstack/go-jsonschema v0.1.1/go.mod h1:yL7fNggx1o8rm9RlgXv7hTBWxdBM0rVwpMwimd3F3N0= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= +github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonreference v0.20.0 h1:MYlu0sBgChmCfJxxUKZ8g1cPWFOB37YSZqewK7OKeyA= +github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.21.1 h1:wm0rhTb5z7qpJRHBdPOMuY4QjVUMbF6/kwoYeRAOrKU= +github.com/go-openapi/swag v0.21.1/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/gnostic v0.6.9 h1:ZK/5VhkoX835RikCHpSUJV9a+S3e1zLh59YnyWeBW+0= +github.com/google/gnostic v0.6.9/go.mod h1:Nm8234We1lq6iB9OmlgNv3nH91XLLVZHCDayfA3xq+E= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/lithammer/shortuuid/v4 v4.0.0 h1:QRbbVkfgNippHOS8PXDkti4NaWeyYfcBTHtw7k08o4c= +github.com/lithammer/shortuuid/v4 v4.0.0/go.mod h1:Zs8puNcrvf2rV9rTH51ZLLcj7ZXqQI3lv67aw4KiB1Y= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/onsi/ginkgo/v2 v2.4.0 h1:+Ig9nvqgS5OBSACXNk15PLdp0U9XPYROt9CFzVdFGIs= +github.com/onsi/ginkgo/v2 v2.4.0/go.mod h1:iHkDK1fKGcBoEHT5W7YBq4RFWaQulw+caOMkAt4OrFo= +github.com/onsi/gomega v1.23.0 h1:/oxKu9c2HVap+F3PfKort2Hw5DEU+HGlW8n+tguWsys= +github.com/onsi/gomega v1.23.0/go.mod h1:Z/NWtiqwBrwUt4/2loMmHL63EDLnYHmVbuBpDr2vQAg= +github.com/orcaman/concurrent-map v1.0.0 h1:I/2A2XPCb4IuQWcQhBhSwGfiuybl/J0ev9HDbW65HOY= +github.com/orcaman/concurrent-map v1.0.0/go.mod h1:Lu3tH6HLW3feq74c2GC+jIMS/K2CFcDWnWD9XkenwhI= +github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= +github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= +github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= +github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= +github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= +github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/tinylib/msgp v1.1.5/go.mod h1:eQsjooMTnV42mHu917E26IogZ2930nFyBQdofk10Udg= +github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q= +github.com/twmb/murmur3 v1.1.6 h1:mqrRot1BRxm+Yct+vavLMou2/iJt0tNVTTC0QoIjaZg= +github.com/twmb/murmur3 v1.1.6/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s= +go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4= +go.opentelemetry.io/otel/exporters/prometheus v0.39.0 h1:whAaiHxOatgtKd+w0dOi//1KUxj3KoPINZdtDaDj3IA= +go.opentelemetry.io/otel/exporters/prometheus v0.39.0/go.mod h1:4jo5Q4CROlCpSPsXLhymi+LYrDXd2ObU5wbKayfZs7Y= +go.opentelemetry.io/otel/metric v1.16.0 h1:RbrpwVG1Hfv85LgnZ7+txXioPDoh6EdbZHo26Q3hqOo= +go.opentelemetry.io/otel/metric v1.16.0/go.mod h1:QE47cpOmkwipPiefDwo2wDzwJrlfxxNYodqc4xnGCo4= +go.opentelemetry.io/otel/sdk v1.16.0 h1:Z1Ok1YsijYL0CSJpHt4cS3wDDh7p572grzNrBMiMWgE= +go.opentelemetry.io/otel/sdk v1.16.0/go.mod h1:tMsIuKXuuIWPBAOrH+eHtvhTL+SntFtXF9QD68aP6p4= +go.opentelemetry.io/otel/sdk/metric v0.39.0 h1:Kun8i1eYf48kHH83RucG93ffz0zGV1sh46FAScOTuDI= +go.opentelemetry.io/otel/sdk/metric v0.39.0/go.mod h1:piDIRgjcK7u0HCL5pCA4e74qpK/jk3NiUoAHATVAmiI= +go.opentelemetry.io/otel/trace v1.16.0 h1:8JRpaObFoW0pxuVPapkgH8UhHQj+bJW8jJsCZEu5MQs= +go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20220518171630-0b5c67f07fdf h1:oXVg4h2qJDd9htKxb5SCpFBHLipW6hXmL3qpUixS2jw= +golang.org/x/exp v0.0.0-20220518171630-0b5c67f07fdf/go.mod h1:yh0Ynu2b5ZUe3MQfp2nM0ecK7wsgouWTDN0FNeJuIys= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.6.0 h1:Lh8GPgSKBfWSwFvtuWOfeI3aAAnbXTSutYxJiOJFgIw= +golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw= +golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.12.0 h1:/ZfYdc3zq+q02Rv9vGqTeSItdzZTSNDmfTi0mBAuidU= +golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= +golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/time v0.0.0-20220411224347-583f2d630306 h1:+gHMid33q6pen7kv9xvT+JRinntgeXO2AeZVd0AWD3w= +golang.org/x/time v0.0.0-20220411224347-583f2d630306/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 h1:DdoeryqhaXp1LtT/emMP1BRJPHHKFi5akj/nbx/zNTA= +google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= +google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97/go.mod h1:t1VqOqqvce95G3hIDCT5FeO3YUc6Q4Oe24L/+rNMxRk= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.55.0 h1:3Oj82/tFSCeUrRTg/5E/7d/W5A1tj6Ky1ABAuZuv5ag= +google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8= +google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/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= +k8s.io/api v0.26.1 h1:f+SWYiPd/GsiWwVRz+NbFyCgvv75Pk9NK6dlkZgpCRQ= +k8s.io/api v0.26.1/go.mod h1:xd/GBNgR0f707+ATNyPmQ1oyKSgndzXij81FzWGsejg= +k8s.io/apimachinery v0.26.1 h1:8EZ/eGJL+hY/MYCNwhmDzVqq2lPl3N3Bo8rvweJwXUQ= +k8s.io/apimachinery v0.26.1/go.mod h1:tnPmbONNJ7ByJNz9+n9kMjNP8ON+1qoAIIC70lztu74= +k8s.io/client-go v0.26.1 h1:87CXzYJnAMGaa/IDDfRdhTzxk/wzGZ+/HUQpqgVSZXU= +k8s.io/client-go v0.26.1/go.mod h1:IWNSglg+rQ3OcvDkhY6+QLeasV4OYHDjdqeWkDQZwGE= +k8s.io/klog/v2 v2.80.1 h1:atnLQ121W371wYYFawwYx1aEY2eUfs4l3J72wtgAwV4= +k8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 h1:+70TFaan3hfJzs+7VK2o+OGxg8HsuBr/5f6tVAjDu6E= +k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280/go.mod h1:+Axhij7bCpeqhklhUTe3xmOn6bWxolyZEeyaFpjGtl4= +k8s.io/utils v0.0.0-20221107191617-1a15be271d1d h1:0Smp/HP1OH4Rvhe+4B8nWGERtlqAGSftbSbbmm45oFs= +k8s.io/utils v0.0.0-20221107191617-1a15be271d1d/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k= +sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= +sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= +sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= +sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/_examples/kubernetes-sample/main.go b/_examples/kubernetes-sample/main.go new file mode 100644 index 0000000000000000000000000000000000000000..f5b1addf40ebf7fb02ffbfb76f411e76d3a5076f --- /dev/null +++ b/_examples/kubernetes-sample/main.go @@ -0,0 +1,94 @@ +package main + +import ( + "context" + "fmt" + "log" + "os" + "os/signal" + "syscall" + "time" + + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/cluster" + "gitee.com/simplexyz/simpleactor-go/cluster/clusterproviders/k8s" + "gitee.com/simplexyz/simpleactor-go/cluster/identitylookup/disthash" + "gitee.com/simplexyz/simpleactor-go/remote" + "k8s.io/utils/env" +) + +func main() { + log.Printf("Starting node\n") + + c := startNode() + defer c.Shutdown(true) + + ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) + defer stop() + + sendMessages(ctx, c) + + log.Printf("Shutting down\n") +} + +func sendMessages(ctx context.Context, c *cluster.Cluster) { + for { + select { + case <-ctx.Done(): + return + case <-time.After(5 * time.Second): + if _, err := c.Call( + "some-actor-123", + "helloKind", + &HelloRequest{Name: fmt.Sprintf("Hello from %s", c.ActorSystem.ID)}); err != nil { + log.Printf("Error calling actor: %v\n", err) + } else { + log.Printf("Successfully called actor\n") + } + } + } +} + +func helloGrainReceive(ctx actor.Context) { + switch msg := ctx.Message().(type) { + case *HelloRequest: + log.Printf("Got hello %v\n", msg) + ctx.Respond(&HelloResponse{}) + } +} + +func startNode() *cluster.Cluster { + host, port, advertisedHost := getHostInformation() + + system := actor.NewActorSystem() + provider, err := k8s.New() + if err != nil { + log.Panic(err) + } + lookup := disthash.New() + + config := remote.Configure(host, port, remote.WithAdvertisedHost(advertisedHost)) + + props := actor.PropsFromFunc(helloGrainReceive) + helloKind := cluster.NewKind("helloKind", props) + + clusterConfig := cluster.Configure("my-cluster", provider, lookup, config, cluster.WithKinds(helloKind)) + + c := cluster.New(system, clusterConfig) + c.StartMember() + + return c +} + +func getHostInformation() (host string, port int, advertisedHost string) { + host = env.GetString("PROTOHOST", "127.0.0.1") + port, err := env.GetInt("PROTOPORT", 0) + if err != nil { + log.Panic(err) + } + advertisedHost = env.GetString("PROTOADVERTISEDHOST", "") + + log.Printf("host: %s, port: %d, advertisedHost: %s", host, port, advertisedHost) + + return +} diff --git a/_examples/kubernetes-sample/protos.pb.go b/_examples/kubernetes-sample/protos.pb.go new file mode 100644 index 0000000000000000000000000000000000000000..a01c847c3739f7a6ae7d44e221226472ca328144 --- /dev/null +++ b/_examples/kubernetes-sample/protos.pb.go @@ -0,0 +1,197 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.26.0 +// protoc v3.20.2 +// source: protos.proto + +package main + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type HelloRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=Name,proto3" json:"Name,omitempty"` +} + +func (x *HelloRequest) Reset() { + *x = HelloRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *HelloRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*HelloRequest) ProtoMessage() {} + +func (x *HelloRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use HelloRequest.ProtoReflect.Descriptor instead. +func (*HelloRequest) Descriptor() ([]byte, []int) { + return file_protos_proto_rawDescGZIP(), []int{0} +} + +func (x *HelloRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +type HelloResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *HelloResponse) Reset() { + *x = HelloResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *HelloResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*HelloResponse) ProtoMessage() {} + +func (x *HelloResponse) ProtoReflect() protoreflect.Message { + mi := &file_protos_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use HelloResponse.ProtoReflect.Descriptor instead. +func (*HelloResponse) Descriptor() ([]byte, []int) { + return file_protos_proto_rawDescGZIP(), []int{1} +} + +var File_protos_proto protoreflect.FileDescriptor + +var file_protos_proto_rawDesc = []byte{ + 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x04, + 0x6d, 0x61, 0x69, 0x6e, 0x22, 0x22, 0x0a, 0x0c, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x0f, 0x0a, 0x0d, 0x48, 0x65, 0x6c, 0x6c, + 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x44, 0x5a, 0x42, 0x67, 0x69, 0x74, + 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x73, 0x79, 0x6e, 0x6b, 0x72, 0x6f, 0x6e, + 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x2d, 0x67, 0x6f, 0x2f, 0x5f, + 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x2f, 0x6b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, + 0x74, 0x65, 0x73, 0x2d, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2f, 0x6d, 0x61, 0x69, 0x6e, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_protos_proto_rawDescOnce sync.Once + file_protos_proto_rawDescData = file_protos_proto_rawDesc +) + +func file_protos_proto_rawDescGZIP() []byte { + file_protos_proto_rawDescOnce.Do(func() { + file_protos_proto_rawDescData = protoimpl.X.CompressGZIP(file_protos_proto_rawDescData) + }) + return file_protos_proto_rawDescData +} + +var file_protos_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_protos_proto_goTypes = []interface{}{ + (*HelloRequest)(nil), // 0: main.HelloRequest + (*HelloResponse)(nil), // 1: main.HelloResponse +} +var file_protos_proto_depIdxs = []int32{ + 0, // [0:0] is the sub-list for method output_type + 0, // [0:0] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_protos_proto_init() } +func file_protos_proto_init() { + if File_protos_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_protos_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*HelloRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*HelloResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_protos_proto_rawDesc, + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_protos_proto_goTypes, + DependencyIndexes: file_protos_proto_depIdxs, + MessageInfos: file_protos_proto_msgTypes, + }.Build() + File_protos_proto = out.File + file_protos_proto_rawDesc = nil + file_protos_proto_goTypes = nil + file_protos_proto_depIdxs = nil +} diff --git a/_examples/kubernetes-sample/protos.proto b/_examples/kubernetes-sample/protos.proto new file mode 100644 index 0000000000000000000000000000000000000000..178e82253427074e07879ba17428d646b097c1ef --- /dev/null +++ b/_examples/kubernetes-sample/protos.proto @@ -0,0 +1,9 @@ +syntax = "proto3"; +package main; +option go_package = "gitee.com/simplexyz/simpleactor-go/_examples/kubernetes-sample/main"; + +message HelloRequest { + string Name = 1; +} + +message HelloResponse { } \ No newline at end of file diff --git a/_examples/opentelemetry-custom-provider/go.mod b/_examples/opentelemetry-custom-provider/go.mod new file mode 100644 index 0000000000000000000000000000000000000000..a2dd0cac21fb0b608457e6371cd00cd97bf1b2a3 --- /dev/null +++ b/_examples/opentelemetry-custom-provider/go.mod @@ -0,0 +1,15 @@ +module opentelemetry-custom-provider + +go 1.21 + +toolchain go1.21.3 + +replace gitee.com/simplexyz/simpleactor-go => ../.. + +require ( + gitee.com/simplexyz/simpleactor-go v0.0.0-00010101000000-000000000000 + github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716 + go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v0.27.0 + go.opentelemetry.io/otel/metric v1.16.0 + go.opentelemetry.io/otel/sdk/metric v0.39.0 +) diff --git a/_examples/opentelemetry-custom-provider/go.sum b/_examples/opentelemetry-custom-provider/go.sum new file mode 100644 index 0000000000000000000000000000000000000000..b73ee5525497bd36b22289b181b8e9c71384b4ce --- /dev/null +++ b/_examples/opentelemetry-custom-provider/go.sum @@ -0,0 +1,1794 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= +cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= +cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= +cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= +cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= +cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= +cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= +cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= +cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= +cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= +cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= +cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= +cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= +cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= +cloud.google.com/go v0.100.1/go.mod h1:fs4QogzfH5n2pBXBP9vRiU+eCny7lD2vmFZy79Iuw1U= +cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= +cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc= +cloud.google.com/go v0.102.1/go.mod h1:XZ77E9qnTEnrgEOvr4xzfdX5TRo7fB4T2F4O6+34hIU= +cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRYtA= +cloud.google.com/go v0.105.0/go.mod h1:PrLgOJNe5nfE9UMxKxgXj4mD3voiP+YQ6gdt6KMFOKM= +cloud.google.com/go v0.107.0/go.mod h1:wpc2eNrD7hXUTy8EKS10jkxpZBjASrORK7goS+3YX2I= +cloud.google.com/go v0.110.0/go.mod h1:SJnCLqQ0FCFGSZMUNUf84MV3Aia54kn7pi8st7tMzaY= +cloud.google.com/go/accessapproval v1.4.0/go.mod h1:zybIuC3KpDOvotz59lFe5qxRZx6C75OtwbisN56xYB4= +cloud.google.com/go/accessapproval v1.5.0/go.mod h1:HFy3tuiGvMdcd/u+Cu5b9NkO1pEICJ46IR82PoUdplw= +cloud.google.com/go/accessapproval v1.6.0/go.mod h1:R0EiYnwV5fsRFiKZkPHr6mwyk2wxUJ30nL4j2pcFY2E= +cloud.google.com/go/accesscontextmanager v1.3.0/go.mod h1:TgCBehyr5gNMz7ZaH9xubp+CE8dkrszb4oK9CWyvD4o= +cloud.google.com/go/accesscontextmanager v1.4.0/go.mod h1:/Kjh7BBu/Gh83sv+K60vN9QE5NJcd80sU33vIe2IFPE= +cloud.google.com/go/accesscontextmanager v1.6.0/go.mod h1:8XCvZWfYw3K/ji0iVnp+6pu7huxoQTLmxAbVjbloTtM= +cloud.google.com/go/aiplatform v1.22.0/go.mod h1:ig5Nct50bZlzV6NvKaTwmplLLddFx0YReh9WfTO5jKw= +cloud.google.com/go/aiplatform v1.24.0/go.mod h1:67UUvRBKG6GTayHKV8DBv2RtR1t93YRu5B1P3x99mYY= +cloud.google.com/go/aiplatform v1.27.0/go.mod h1:Bvxqtl40l0WImSb04d0hXFU7gDOiq9jQmorivIiWcKg= +cloud.google.com/go/aiplatform v1.35.0/go.mod h1:7MFT/vCaOyZT/4IIFfxH4ErVg/4ku6lKv3w0+tFTgXQ= +cloud.google.com/go/analytics v0.11.0/go.mod h1:DjEWCu41bVbYcKyvlws9Er60YE4a//bK6mnhWvQeFNI= +cloud.google.com/go/analytics v0.12.0/go.mod h1:gkfj9h6XRf9+TS4bmuhPEShsh3hH8PAZzm/41OOhQd4= +cloud.google.com/go/analytics v0.18.0/go.mod h1:ZkeHGQlcIPkw0R/GW+boWHhCOR43xz9RN/jn7WcqfIE= +cloud.google.com/go/apigateway v1.3.0/go.mod h1:89Z8Bhpmxu6AmUxuVRg/ECRGReEdiP3vQtk4Z1J9rJk= +cloud.google.com/go/apigateway v1.4.0/go.mod h1:pHVY9MKGaH9PQ3pJ4YLzoj6U5FUDeDFBllIz7WmzJoc= +cloud.google.com/go/apigateway v1.5.0/go.mod h1:GpnZR3Q4rR7LVu5951qfXPJCHquZt02jf7xQx7kpqN8= +cloud.google.com/go/apigeeconnect v1.3.0/go.mod h1:G/AwXFAKo0gIXkPTVfZDd2qA1TxBXJ3MgMRBQkIi9jc= +cloud.google.com/go/apigeeconnect v1.4.0/go.mod h1:kV4NwOKqjvt2JYR0AoIWo2QGfoRtn/pkS3QlHp0Ni04= +cloud.google.com/go/apigeeconnect v1.5.0/go.mod h1:KFaCqvBRU6idyhSNyn3vlHXc8VMDJdRmwDF6JyFRqZ8= +cloud.google.com/go/apigeeregistry v0.4.0/go.mod h1:EUG4PGcsZvxOXAdyEghIdXwAEi/4MEaoqLMLDMIwKXY= +cloud.google.com/go/apigeeregistry v0.5.0/go.mod h1:YR5+s0BVNZfVOUkMa5pAR2xGd0A473vA5M7j247o1wM= +cloud.google.com/go/apikeys v0.4.0/go.mod h1:XATS/yqZbaBK0HOssf+ALHp8jAlNHUgyfprvNcBIszU= +cloud.google.com/go/apikeys v0.5.0/go.mod h1:5aQfwY4D+ewMMWScd3hm2en3hCj+BROlyrt3ytS7KLI= +cloud.google.com/go/appengine v1.4.0/go.mod h1:CS2NhuBuDXM9f+qscZ6V86m1MIIqPj3WC/UoEuR1Sno= +cloud.google.com/go/appengine v1.5.0/go.mod h1:TfasSozdkFI0zeoxW3PTBLiNqRmzraodCWatWI9Dmak= +cloud.google.com/go/appengine v1.6.0/go.mod h1:hg6i0J/BD2cKmDJbaFSYHFyZkgBEfQrDg/X0V5fJn84= +cloud.google.com/go/area120 v0.5.0/go.mod h1:DE/n4mp+iqVyvxHN41Vf1CR602GiHQjFPusMFW6bGR4= +cloud.google.com/go/area120 v0.6.0/go.mod h1:39yFJqWVgm0UZqWTOdqkLhjoC7uFfgXRC8g/ZegeAh0= +cloud.google.com/go/area120 v0.7.1/go.mod h1:j84i4E1RboTWjKtZVWXPqvK5VHQFJRF2c1Nm69pWm9k= +cloud.google.com/go/artifactregistry v1.6.0/go.mod h1:IYt0oBPSAGYj/kprzsBjZ/4LnG/zOcHyFHjWPCi6SAQ= +cloud.google.com/go/artifactregistry v1.7.0/go.mod h1:mqTOFOnGZx8EtSqK/ZWcsm/4U8B77rbcLP6ruDU2Ixk= +cloud.google.com/go/artifactregistry v1.8.0/go.mod h1:w3GQXkJX8hiKN0v+at4b0qotwijQbYUqF2GWkZzAhC0= +cloud.google.com/go/artifactregistry v1.9.0/go.mod h1:2K2RqvA2CYvAeARHRkLDhMDJ3OXy26h3XW+3/Jh2uYc= +cloud.google.com/go/artifactregistry v1.11.2/go.mod h1:nLZns771ZGAwVLzTX/7Al6R9ehma4WUEhZGWV6CeQNQ= +cloud.google.com/go/asset v1.5.0/go.mod h1:5mfs8UvcM5wHhqtSv8J1CtxxaQq3AdBxxQi2jGW/K4o= +cloud.google.com/go/asset v1.7.0/go.mod h1:YbENsRK4+xTiL+Ofoj5Ckf+O17kJtgp3Y3nn4uzZz5s= +cloud.google.com/go/asset v1.8.0/go.mod h1:mUNGKhiqIdbr8X7KNayoYvyc4HbbFO9URsjbytpUaW0= +cloud.google.com/go/asset v1.9.0/go.mod h1:83MOE6jEJBMqFKadM9NLRcs80Gdw76qGuHn8m3h8oHQ= +cloud.google.com/go/asset v1.10.0/go.mod h1:pLz7uokL80qKhzKr4xXGvBQXnzHn5evJAEAtZiIb0wY= +cloud.google.com/go/asset v1.11.1/go.mod h1:fSwLhbRvC9p9CXQHJ3BgFeQNM4c9x10lqlrdEUYXlJo= +cloud.google.com/go/assuredworkloads v1.5.0/go.mod h1:n8HOZ6pff6re5KYfBXcFvSViQjDwxFkAkmUFffJRbbY= +cloud.google.com/go/assuredworkloads v1.6.0/go.mod h1:yo2YOk37Yc89Rsd5QMVECvjaMKymF9OP+QXWlKXUkXw= +cloud.google.com/go/assuredworkloads v1.7.0/go.mod h1:z/736/oNmtGAyU47reJgGN+KVoYoxeLBoj4XkKYscNI= +cloud.google.com/go/assuredworkloads v1.8.0/go.mod h1:AsX2cqyNCOvEQC8RMPnoc0yEarXQk6WEKkxYfL6kGIo= +cloud.google.com/go/assuredworkloads v1.9.0/go.mod h1:kFuI1P78bplYtT77Tb1hi0FMxM0vVpRC7VVoJC3ZoT0= +cloud.google.com/go/assuredworkloads v1.10.0/go.mod h1:kwdUQuXcedVdsIaKgKTp9t0UJkE5+PAVNhdQm4ZVq2E= +cloud.google.com/go/automl v1.5.0/go.mod h1:34EjfoFGMZ5sgJ9EoLsRtdPSNZLcfflJR39VbVNS2M0= +cloud.google.com/go/automl v1.6.0/go.mod h1:ugf8a6Fx+zP0D59WLhqgTDsQI9w07o64uf/Is3Nh5p8= +cloud.google.com/go/automl v1.7.0/go.mod h1:RL9MYCCsJEOmt0Wf3z9uzG0a7adTT1fe+aObgSpkCt8= +cloud.google.com/go/automl v1.8.0/go.mod h1:xWx7G/aPEe/NP+qzYXktoBSDfjO+vnKMGgsApGJJquM= +cloud.google.com/go/automl v1.12.0/go.mod h1:tWDcHDp86aMIuHmyvjuKeeHEGq76lD7ZqfGLN6B0NuU= +cloud.google.com/go/baremetalsolution v0.3.0/go.mod h1:XOrocE+pvK1xFfleEnShBlNAXf+j5blPPxrhjKgnIFc= +cloud.google.com/go/baremetalsolution v0.4.0/go.mod h1:BymplhAadOO/eBa7KewQ0Ppg4A4Wplbn+PsFKRLo0uI= +cloud.google.com/go/baremetalsolution v0.5.0/go.mod h1:dXGxEkmR9BMwxhzBhV0AioD0ULBmuLZI8CdwalUxuss= +cloud.google.com/go/batch v0.3.0/go.mod h1:TR18ZoAekj1GuirsUsR1ZTKN3FC/4UDnScjT8NXImFE= +cloud.google.com/go/batch v0.4.0/go.mod h1:WZkHnP43R/QCGQsZ+0JyG4i79ranE2u8xvjq/9+STPE= +cloud.google.com/go/batch v0.7.0/go.mod h1:vLZN95s6teRUqRQ4s3RLDsH8PvboqBK+rn1oevL159g= +cloud.google.com/go/beyondcorp v0.2.0/go.mod h1:TB7Bd+EEtcw9PCPQhCJtJGjk/7TC6ckmnSFS+xwTfm4= +cloud.google.com/go/beyondcorp v0.3.0/go.mod h1:E5U5lcrcXMsCuoDNyGrpyTm/hn7ne941Jz2vmksAxW8= +cloud.google.com/go/beyondcorp v0.4.0/go.mod h1:3ApA0mbhHx6YImmuubf5pyW8srKnCEPON32/5hj+RmM= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/bigquery v1.42.0/go.mod h1:8dRTJxhtG+vwBKzE5OseQn/hiydoQN3EedCaOdYmxRA= +cloud.google.com/go/bigquery v1.43.0/go.mod h1:ZMQcXHsl+xmU1z36G2jNGZmKp9zNY5BUua5wDgmNCfw= +cloud.google.com/go/bigquery v1.44.0/go.mod h1:0Y33VqXTEsbamHJvJHdFmtqHvMIY28aK1+dFsvaChGc= +cloud.google.com/go/bigquery v1.48.0/go.mod h1:QAwSz+ipNgfL5jxiaK7weyOhzdoAy1zFm0Nf1fysJac= +cloud.google.com/go/billing v1.4.0/go.mod h1:g9IdKBEFlItS8bTtlrZdVLWSSdSyFUZKXNS02zKMOZY= +cloud.google.com/go/billing v1.5.0/go.mod h1:mztb1tBc3QekhjSgmpf/CV4LzWXLzCArwpLmP2Gm88s= +cloud.google.com/go/billing v1.6.0/go.mod h1:WoXzguj+BeHXPbKfNWkqVtDdzORazmCjraY+vrxcyvI= +cloud.google.com/go/billing v1.7.0/go.mod h1:q457N3Hbj9lYwwRbnlD7vUpyjq6u5U1RAOArInEiD5Y= +cloud.google.com/go/billing v1.12.0/go.mod h1:yKrZio/eu+okO/2McZEbch17O5CB5NpZhhXG6Z766ss= +cloud.google.com/go/binaryauthorization v1.1.0/go.mod h1:xwnoWu3Y84jbuHa0zd526MJYmtnVXn0syOjaJgy4+dM= +cloud.google.com/go/binaryauthorization v1.2.0/go.mod h1:86WKkJHtRcv5ViNABtYMhhNWRrD1Vpi//uKEy7aYEfI= +cloud.google.com/go/binaryauthorization v1.3.0/go.mod h1:lRZbKgjDIIQvzYQS1p99A7/U1JqvqeZg0wiI5tp6tg0= +cloud.google.com/go/binaryauthorization v1.4.0/go.mod h1:tsSPQrBd77VLplV70GUhBf/Zm3FsKmgSqgm4UmiDItk= +cloud.google.com/go/binaryauthorization v1.5.0/go.mod h1:OSe4OU1nN/VswXKRBmciKpo9LulY41gch5c68htf3/Q= +cloud.google.com/go/certificatemanager v1.3.0/go.mod h1:n6twGDvcUBFu9uBgt4eYvvf3sQ6My8jADcOVwHmzadg= +cloud.google.com/go/certificatemanager v1.4.0/go.mod h1:vowpercVFyqs8ABSmrdV+GiFf2H/ch3KyudYQEMM590= +cloud.google.com/go/certificatemanager v1.6.0/go.mod h1:3Hh64rCKjRAX8dXgRAyOcY5vQ/fE1sh8o+Mdd6KPgY8= +cloud.google.com/go/channel v1.8.0/go.mod h1:W5SwCXDJsq/rg3tn3oG0LOxpAo6IMxNa09ngphpSlnk= +cloud.google.com/go/channel v1.9.0/go.mod h1:jcu05W0my9Vx4mt3/rEHpfxc9eKi9XwsdDL8yBMbKUk= +cloud.google.com/go/channel v1.11.0/go.mod h1:IdtI0uWGqhEeatSB62VOoJ8FSUhJ9/+iGkJVqp74CGE= +cloud.google.com/go/cloudbuild v1.3.0/go.mod h1:WequR4ULxlqvMsjDEEEFnOG5ZSRSgWOywXYDb1vPE6U= +cloud.google.com/go/cloudbuild v1.4.0/go.mod h1:5Qwa40LHiOXmz3386FrjrYM93rM/hdRr7b53sySrTqA= +cloud.google.com/go/cloudbuild v1.7.0/go.mod h1:zb5tWh2XI6lR9zQmsm1VRA+7OCuve5d8S+zJUul8KTg= +cloud.google.com/go/clouddms v1.3.0/go.mod h1:oK6XsCDdW4Ib3jCCBugx+gVjevp2TMXFtgxvPSee3OM= +cloud.google.com/go/clouddms v1.4.0/go.mod h1:Eh7sUGCC+aKry14O1NRljhjyrr0NFC0G2cjwX0cByRk= +cloud.google.com/go/clouddms v1.5.0/go.mod h1:QSxQnhikCLUw13iAbffF2CZxAER3xDGNHjsTAkQJcQA= +cloud.google.com/go/cloudtasks v1.5.0/go.mod h1:fD92REy1x5woxkKEkLdvavGnPJGEn8Uic9nWuLzqCpY= +cloud.google.com/go/cloudtasks v1.6.0/go.mod h1:C6Io+sxuke9/KNRkbQpihnW93SWDU3uXt92nu85HkYI= +cloud.google.com/go/cloudtasks v1.7.0/go.mod h1:ImsfdYWwlWNJbdgPIIGJWC+gemEGTBK/SunNQQNCAb4= +cloud.google.com/go/cloudtasks v1.8.0/go.mod h1:gQXUIwCSOI4yPVK7DgTVFiiP0ZW/eQkydWzwVMdHxrI= +cloud.google.com/go/cloudtasks v1.9.0/go.mod h1:w+EyLsVkLWHcOaqNEyvcKAsWp9p29dL6uL9Nst1cI7Y= +cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow= +cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM= +cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M= +cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s= +cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU= +cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U= +cloud.google.com/go/compute v1.10.0/go.mod h1:ER5CLbMxl90o2jtNbGSbtfOpQKR0t15FOtRsugnLrlU= +cloud.google.com/go/compute v1.12.0/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU= +cloud.google.com/go/compute v1.12.1/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU= +cloud.google.com/go/compute v1.13.0/go.mod h1:5aPTS0cUNMIc1CE546K+Th6weJUNQErARyZtRXDJ8GE= +cloud.google.com/go/compute v1.14.0/go.mod h1:YfLtxrj9sU4Yxv+sXzZkyPjEyPBZfXHUvjxega5vAdo= +cloud.google.com/go/compute v1.15.1/go.mod h1:bjjoF/NtFUrkD/urWfdHaKuOPDR5nWIs63rR+SXhcpA= +cloud.google.com/go/compute v1.18.0/go.mod h1:1X7yHxec2Ga+Ss6jPyjxRxpu2uu7PLgsOVXvgU0yacs= +cloud.google.com/go/compute/metadata v0.1.0/go.mod h1:Z1VN+bulIf6bt4P/C37K4DyZYZEXYonfTBHHFPO/4UU= +cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= +cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM= +cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= +cloud.google.com/go/contactcenterinsights v1.3.0/go.mod h1:Eu2oemoePuEFc/xKFPjbTuPSj0fYJcPls9TFlPNnHHY= +cloud.google.com/go/contactcenterinsights v1.4.0/go.mod h1:L2YzkGbPsv+vMQMCADxJoT9YiTTnSEd6fEvCeHTYVck= +cloud.google.com/go/contactcenterinsights v1.6.0/go.mod h1:IIDlT6CLcDoyv79kDv8iWxMSTZhLxSCofVV5W6YFM/w= +cloud.google.com/go/container v1.6.0/go.mod h1:Xazp7GjJSeUYo688S+6J5V+n/t+G5sKBTFkKNudGRxg= +cloud.google.com/go/container v1.7.0/go.mod h1:Dp5AHtmothHGX3DwwIHPgq45Y8KmNsgN3amoYfxVkLo= +cloud.google.com/go/container v1.13.1/go.mod h1:6wgbMPeQRw9rSnKBCAJXnds3Pzj03C4JHamr8asWKy4= +cloud.google.com/go/containeranalysis v0.5.1/go.mod h1:1D92jd8gRR/c0fGMlymRgxWD3Qw9C1ff6/T7mLgVL8I= +cloud.google.com/go/containeranalysis v0.6.0/go.mod h1:HEJoiEIu+lEXM+k7+qLCci0h33lX3ZqoYFdmPcoO7s4= +cloud.google.com/go/containeranalysis v0.7.0/go.mod h1:9aUL+/vZ55P2CXfuZjS4UjQ9AgXoSw8Ts6lemfmxBxI= +cloud.google.com/go/datacatalog v1.3.0/go.mod h1:g9svFY6tuR+j+hrTw3J2dNcmI0dzmSiyOzm8kpLq0a0= +cloud.google.com/go/datacatalog v1.5.0/go.mod h1:M7GPLNQeLfWqeIm3iuiruhPzkt65+Bx8dAKvScX8jvs= +cloud.google.com/go/datacatalog v1.6.0/go.mod h1:+aEyF8JKg+uXcIdAmmaMUmZ3q1b/lKLtXCmXdnc0lbc= +cloud.google.com/go/datacatalog v1.7.0/go.mod h1:9mEl4AuDYWw81UGc41HonIHH7/sn52H0/tc8f8ZbZIE= +cloud.google.com/go/datacatalog v1.8.0/go.mod h1:KYuoVOv9BM8EYz/4eMFxrr4DUKhGIOXxZoKYF5wdISM= +cloud.google.com/go/datacatalog v1.8.1/go.mod h1:RJ58z4rMp3gvETA465Vg+ag8BGgBdnRPEMMSTr5Uv+M= +cloud.google.com/go/datacatalog v1.12.0/go.mod h1:CWae8rFkfp6LzLumKOnmVh4+Zle4A3NXLzVJ1d1mRm0= +cloud.google.com/go/dataflow v0.6.0/go.mod h1:9QwV89cGoxjjSR9/r7eFDqqjtvbKxAK2BaYU6PVk9UM= +cloud.google.com/go/dataflow v0.7.0/go.mod h1:PX526vb4ijFMesO1o202EaUmouZKBpjHsTlCtB4parQ= +cloud.google.com/go/dataflow v0.8.0/go.mod h1:Rcf5YgTKPtQyYz8bLYhFoIV/vP39eL7fWNcSOyFfLJE= +cloud.google.com/go/dataform v0.3.0/go.mod h1:cj8uNliRlHpa6L3yVhDOBrUXH+BPAO1+KFMQQNSThKo= +cloud.google.com/go/dataform v0.4.0/go.mod h1:fwV6Y4Ty2yIFL89huYlEkwUPtS7YZinZbzzj5S9FzCE= +cloud.google.com/go/dataform v0.5.0/go.mod h1:GFUYRe8IBa2hcomWplodVmUx/iTL0FrsauObOM3Ipr0= +cloud.google.com/go/dataform v0.6.0/go.mod h1:QPflImQy33e29VuapFdf19oPbE4aYTJxr31OAPV+ulA= +cloud.google.com/go/datafusion v1.4.0/go.mod h1:1Zb6VN+W6ALo85cXnM1IKiPw+yQMKMhB9TsTSRDo/38= +cloud.google.com/go/datafusion v1.5.0/go.mod h1:Kz+l1FGHB0J+4XF2fud96WMmRiq/wj8N9u007vyXZ2w= +cloud.google.com/go/datafusion v1.6.0/go.mod h1:WBsMF8F1RhSXvVM8rCV3AeyWVxcC2xY6vith3iw3S+8= +cloud.google.com/go/datalabeling v0.5.0/go.mod h1:TGcJ0G2NzcsXSE/97yWjIZO0bXj0KbVlINXMG9ud42I= +cloud.google.com/go/datalabeling v0.6.0/go.mod h1:WqdISuk/+WIGeMkpw/1q7bK/tFEZxsrFJOJdY2bXvTQ= +cloud.google.com/go/datalabeling v0.7.0/go.mod h1:WPQb1y08RJbmpM3ww0CSUAGweL0SxByuW2E+FU+wXcM= +cloud.google.com/go/dataplex v1.3.0/go.mod h1:hQuRtDg+fCiFgC8j0zV222HvzFQdRd+SVX8gdmFcZzA= +cloud.google.com/go/dataplex v1.4.0/go.mod h1:X51GfLXEMVJ6UN47ESVqvlsRplbLhcsAt0kZCCKsU0A= +cloud.google.com/go/dataplex v1.5.2/go.mod h1:cVMgQHsmfRoI5KFYq4JtIBEUbYwc3c7tXmIDhRmNNVQ= +cloud.google.com/go/dataproc v1.7.0/go.mod h1:CKAlMjII9H90RXaMpSxQ8EU6dQx6iAYNPcYPOkSbi8s= +cloud.google.com/go/dataproc v1.8.0/go.mod h1:5OW+zNAH0pMpw14JVrPONsxMQYMBqJuzORhIBfBn9uI= +cloud.google.com/go/dataproc v1.12.0/go.mod h1:zrF3aX0uV3ikkMz6z4uBbIKyhRITnxvr4i3IjKsKrw4= +cloud.google.com/go/dataqna v0.5.0/go.mod h1:90Hyk596ft3zUQ8NkFfvICSIfHFh1Bc7C4cK3vbhkeo= +cloud.google.com/go/dataqna v0.6.0/go.mod h1:1lqNpM7rqNLVgWBJyk5NF6Uen2PHym0jtVJonplVsDA= +cloud.google.com/go/dataqna v0.7.0/go.mod h1:Lx9OcIIeqCrw1a6KdO3/5KMP1wAmTc0slZWwP12Qq3c= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/datastore v1.10.0/go.mod h1:PC5UzAmDEkAmkfaknstTYbNpgE49HAgW2J1gcgUfmdM= +cloud.google.com/go/datastream v1.2.0/go.mod h1:i/uTP8/fZwgATHS/XFu0TcNUhuA0twZxxQ3EyCUQMwo= +cloud.google.com/go/datastream v1.3.0/go.mod h1:cqlOX8xlyYF/uxhiKn6Hbv6WjwPPuI9W2M9SAXwaLLQ= +cloud.google.com/go/datastream v1.4.0/go.mod h1:h9dpzScPhDTs5noEMQVWP8Wx8AFBRyS0s8KWPx/9r0g= +cloud.google.com/go/datastream v1.5.0/go.mod h1:6TZMMNPwjUqZHBKPQ1wwXpb0d5VDVPl2/XoS5yi88q4= +cloud.google.com/go/datastream v1.6.0/go.mod h1:6LQSuswqLa7S4rPAOZFVjHIG3wJIjZcZrw8JDEDJuIs= +cloud.google.com/go/deploy v1.4.0/go.mod h1:5Xghikd4VrmMLNaF6FiRFDlHb59VM59YoDQnOUdsH/c= +cloud.google.com/go/deploy v1.5.0/go.mod h1:ffgdD0B89tToyW/U/D2eL0jN2+IEV/3EMuXHA0l4r+s= +cloud.google.com/go/deploy v1.6.0/go.mod h1:f9PTHehG/DjCom3QH0cntOVRm93uGBDt2vKzAPwpXQI= +cloud.google.com/go/dialogflow v1.15.0/go.mod h1:HbHDWs33WOGJgn6rfzBW1Kv807BE3O1+xGbn59zZWI4= +cloud.google.com/go/dialogflow v1.16.1/go.mod h1:po6LlzGfK+smoSmTBnbkIZY2w8ffjz/RcGSS+sh1el0= +cloud.google.com/go/dialogflow v1.17.0/go.mod h1:YNP09C/kXA1aZdBgC/VtXX74G/TKn7XVCcVumTflA+8= +cloud.google.com/go/dialogflow v1.18.0/go.mod h1:trO7Zu5YdyEuR+BhSNOqJezyFQ3aUzz0njv7sMx/iek= +cloud.google.com/go/dialogflow v1.19.0/go.mod h1:JVmlG1TwykZDtxtTXujec4tQ+D8SBFMoosgy+6Gn0s0= +cloud.google.com/go/dialogflow v1.29.0/go.mod h1:b+2bzMe+k1s9V+F2jbJwpHPzrnIyHihAdRFMtn2WXuM= +cloud.google.com/go/dialogflow v1.31.0/go.mod h1:cuoUccuL1Z+HADhyIA7dci3N5zUssgpBJmCzI6fNRB4= +cloud.google.com/go/dlp v1.6.0/go.mod h1:9eyB2xIhpU0sVwUixfBubDoRwP+GjeUoxxeueZmqvmM= +cloud.google.com/go/dlp v1.7.0/go.mod h1:68ak9vCiMBjbasxeVD17hVPxDEck+ExiHavX8kiHG+Q= +cloud.google.com/go/dlp v1.9.0/go.mod h1:qdgmqgTyReTz5/YNSSuueR8pl7hO0o9bQ39ZhtgkWp4= +cloud.google.com/go/documentai v1.7.0/go.mod h1:lJvftZB5NRiFSX4moiye1SMxHx0Bc3x1+p9e/RfXYiU= +cloud.google.com/go/documentai v1.8.0/go.mod h1:xGHNEB7CtsnySCNrCFdCyyMz44RhFEEX2Q7UD0c5IhU= +cloud.google.com/go/documentai v1.9.0/go.mod h1:FS5485S8R00U10GhgBC0aNGrJxBP8ZVpEeJ7PQDZd6k= +cloud.google.com/go/documentai v1.10.0/go.mod h1:vod47hKQIPeCfN2QS/jULIvQTugbmdc0ZvxxfQY1bg4= +cloud.google.com/go/documentai v1.16.0/go.mod h1:o0o0DLTEZ+YnJZ+J4wNfTxmDVyrkzFvttBXXtYRMHkM= +cloud.google.com/go/domains v0.6.0/go.mod h1:T9Rz3GasrpYk6mEGHh4rymIhjlnIuB4ofT1wTxDeT4Y= +cloud.google.com/go/domains v0.7.0/go.mod h1:PtZeqS1xjnXuRPKE/88Iru/LdfoRyEHYA9nFQf4UKpg= +cloud.google.com/go/domains v0.8.0/go.mod h1:M9i3MMDzGFXsydri9/vW+EWz9sWb4I6WyHqdlAk0idE= +cloud.google.com/go/edgecontainer v0.1.0/go.mod h1:WgkZ9tp10bFxqO8BLPqv2LlfmQF1X8lZqwW4r1BTajk= +cloud.google.com/go/edgecontainer v0.2.0/go.mod h1:RTmLijy+lGpQ7BXuTDa4C4ssxyXT34NIuHIgKuP4s5w= +cloud.google.com/go/edgecontainer v0.3.0/go.mod h1:FLDpP4nykgwwIfcLt6zInhprzw0lEi2P1fjO6Ie0qbc= +cloud.google.com/go/errorreporting v0.3.0/go.mod h1:xsP2yaAp+OAW4OIm60An2bbLpqIhKXdWR/tawvl7QzU= +cloud.google.com/go/essentialcontacts v1.3.0/go.mod h1:r+OnHa5jfj90qIfZDO/VztSFqbQan7HV75p8sA+mdGI= +cloud.google.com/go/essentialcontacts v1.4.0/go.mod h1:8tRldvHYsmnBCHdFpvU+GL75oWiBKl80BiqlFh9tp+8= +cloud.google.com/go/essentialcontacts v1.5.0/go.mod h1:ay29Z4zODTuwliK7SnX8E86aUF2CTzdNtvv42niCX0M= +cloud.google.com/go/eventarc v1.7.0/go.mod h1:6ctpF3zTnaQCxUjHUdcfgcA1A2T309+omHZth7gDfmc= +cloud.google.com/go/eventarc v1.8.0/go.mod h1:imbzxkyAU4ubfsaKYdQg04WS1NvncblHEup4kvF+4gw= +cloud.google.com/go/eventarc v1.10.0/go.mod h1:u3R35tmZ9HvswGRBnF48IlYgYeBcPUCjkr4BTdem2Kw= +cloud.google.com/go/filestore v1.3.0/go.mod h1:+qbvHGvXU1HaKX2nD0WEPo92TP/8AQuCVEBXNY9z0+w= +cloud.google.com/go/filestore v1.4.0/go.mod h1:PaG5oDfo9r224f8OYXURtAsY+Fbyq/bLYoINEK8XQAI= +cloud.google.com/go/filestore v1.5.0/go.mod h1:FqBXDWBp4YLHqRnVGveOkHDf8svj9r5+mUDLupOWEDs= +cloud.google.com/go/firestore v1.9.0/go.mod h1:HMkjKHNTtRyZNiMzu7YAsLr9K3X2udY2AMwDaMEQiiE= +cloud.google.com/go/functions v1.6.0/go.mod h1:3H1UA3qiIPRWD7PeZKLvHZ9SaQhR26XIJcC0A5GbvAk= +cloud.google.com/go/functions v1.7.0/go.mod h1:+d+QBcWM+RsrgZfV9xo6KfA1GlzJfxcfZcRPEhDDfzg= +cloud.google.com/go/functions v1.8.0/go.mod h1:RTZ4/HsQjIqIYP9a9YPbU+QFoQsAlYgrwOXJWHn1POY= +cloud.google.com/go/functions v1.9.0/go.mod h1:Y+Dz8yGguzO3PpIjhLTbnqV1CWmgQ5UwtlpzoyquQ08= +cloud.google.com/go/functions v1.10.0/go.mod h1:0D3hEOe3DbEvCXtYOZHQZmD+SzYsi1YbI7dGvHfldXw= +cloud.google.com/go/gaming v1.5.0/go.mod h1:ol7rGcxP/qHTRQE/RO4bxkXq+Fix0j6D4LFPzYTIrDM= +cloud.google.com/go/gaming v1.6.0/go.mod h1:YMU1GEvA39Qt3zWGyAVA9bpYz/yAhTvaQ1t2sK4KPUA= +cloud.google.com/go/gaming v1.7.0/go.mod h1:LrB8U7MHdGgFG851iHAfqUdLcKBdQ55hzXy9xBJz0+w= +cloud.google.com/go/gaming v1.8.0/go.mod h1:xAqjS8b7jAVW0KFYeRUxngo9My3f33kFmua++Pi+ggM= +cloud.google.com/go/gaming v1.9.0/go.mod h1:Fc7kEmCObylSWLO334NcO+O9QMDyz+TKC4v1D7X+Bc0= +cloud.google.com/go/gkebackup v0.2.0/go.mod h1:XKvv/4LfG829/B8B7xRkk8zRrOEbKtEam6yNfuQNH60= +cloud.google.com/go/gkebackup v0.3.0/go.mod h1:n/E671i1aOQvUxT541aTkCwExO/bTer2HDlj4TsBRAo= +cloud.google.com/go/gkebackup v0.4.0/go.mod h1:byAyBGUwYGEEww7xsbnUTBHIYcOPy/PgUWUtOeRm9Vg= +cloud.google.com/go/gkeconnect v0.5.0/go.mod h1:c5lsNAg5EwAy7fkqX/+goqFsU1Da/jQFqArp+wGNr/o= +cloud.google.com/go/gkeconnect v0.6.0/go.mod h1:Mln67KyU/sHJEBY8kFZ0xTeyPtzbq9StAVvEULYK16A= +cloud.google.com/go/gkeconnect v0.7.0/go.mod h1:SNfmVqPkaEi3bF/B3CNZOAYPYdg7sU+obZ+QTky2Myw= +cloud.google.com/go/gkehub v0.9.0/go.mod h1:WYHN6WG8w9bXU0hqNxt8rm5uxnk8IH+lPY9J2TV7BK0= +cloud.google.com/go/gkehub v0.10.0/go.mod h1:UIPwxI0DsrpsVoWpLB0stwKCP+WFVG9+y977wO+hBH0= +cloud.google.com/go/gkehub v0.11.0/go.mod h1:JOWHlmN+GHyIbuWQPl47/C2RFhnFKH38jH9Ascu3n0E= +cloud.google.com/go/gkemulticloud v0.3.0/go.mod h1:7orzy7O0S+5kq95e4Hpn7RysVA7dPs8W/GgfUtsPbrA= +cloud.google.com/go/gkemulticloud v0.4.0/go.mod h1:E9gxVBnseLWCk24ch+P9+B2CoDFJZTyIgLKSalC7tuI= +cloud.google.com/go/gkemulticloud v0.5.0/go.mod h1:W0JDkiyi3Tqh0TJr//y19wyb1yf8llHVto2Htf2Ja3Y= +cloud.google.com/go/grafeas v0.2.0/go.mod h1:KhxgtF2hb0P191HlY5besjYm6MqTSTj3LSI+M+ByZHc= +cloud.google.com/go/gsuiteaddons v1.3.0/go.mod h1:EUNK/J1lZEZO8yPtykKxLXI6JSVN2rg9bN8SXOa0bgM= +cloud.google.com/go/gsuiteaddons v1.4.0/go.mod h1:rZK5I8hht7u7HxFQcFei0+AtfS9uSushomRlg+3ua1o= +cloud.google.com/go/gsuiteaddons v1.5.0/go.mod h1:TFCClYLd64Eaa12sFVmUyG62tk4mdIsI7pAnSXRkcFo= +cloud.google.com/go/iam v0.1.0/go.mod h1:vcUNEa0pEm0qRVpmWepWaFMIAI8/hjB9mO8rNCJtF6c= +cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY= +cloud.google.com/go/iam v0.5.0/go.mod h1:wPU9Vt0P4UmCux7mqtRu6jcpPAb74cP1fh50J3QpkUc= +cloud.google.com/go/iam v0.6.0/go.mod h1:+1AH33ueBne5MzYccyMHtEKqLE4/kJOibtffMHDMFMc= +cloud.google.com/go/iam v0.7.0/go.mod h1:H5Br8wRaDGNc8XP3keLc4unfUUZeyH3Sfl9XpQEYOeg= +cloud.google.com/go/iam v0.8.0/go.mod h1:lga0/y3iH6CX7sYqypWJ33hf7kkfXJag67naqGESjkE= +cloud.google.com/go/iam v0.11.0/go.mod h1:9PiLDanza5D+oWFZiH1uG+RnRCfEGKoyl6yo4cgWZGY= +cloud.google.com/go/iam v0.12.0/go.mod h1:knyHGviacl11zrtZUoDuYpDgLjvr28sLQaG0YB2GYAY= +cloud.google.com/go/iap v1.4.0/go.mod h1:RGFwRJdihTINIe4wZ2iCP0zF/qu18ZwyKxrhMhygBEc= +cloud.google.com/go/iap v1.5.0/go.mod h1:UH/CGgKd4KyohZL5Pt0jSKE4m3FR51qg6FKQ/z/Ix9A= +cloud.google.com/go/iap v1.6.0/go.mod h1:NSuvI9C/j7UdjGjIde7t7HBz+QTwBcapPE07+sSRcLk= +cloud.google.com/go/ids v1.1.0/go.mod h1:WIuwCaYVOzHIj2OhN9HAwvW+DBdmUAdcWlFxRl+KubM= +cloud.google.com/go/ids v1.2.0/go.mod h1:5WXvp4n25S0rA/mQWAg1YEEBBq6/s+7ml1RDCW1IrcY= +cloud.google.com/go/ids v1.3.0/go.mod h1:JBdTYwANikFKaDP6LtW5JAi4gubs57SVNQjemdt6xV4= +cloud.google.com/go/iot v1.3.0/go.mod h1:r7RGh2B61+B8oz0AGE+J72AhA0G7tdXItODWsaA2oLs= +cloud.google.com/go/iot v1.4.0/go.mod h1:dIDxPOn0UvNDUMD8Ger7FIaTuvMkj+aGk94RPP0iV+g= +cloud.google.com/go/iot v1.5.0/go.mod h1:mpz5259PDl3XJthEmh9+ap0affn/MqNSP4My77Qql9o= +cloud.google.com/go/kms v1.4.0/go.mod h1:fajBHndQ+6ubNw6Ss2sSd+SWvjL26RNo/dr7uxsnnOA= +cloud.google.com/go/kms v1.5.0/go.mod h1:QJS2YY0eJGBg3mnDfuaCyLauWwBJiHRboYxJ++1xJNg= +cloud.google.com/go/kms v1.6.0/go.mod h1:Jjy850yySiasBUDi6KFUwUv2n1+o7QZFyuUJg6OgjA0= +cloud.google.com/go/kms v1.9.0/go.mod h1:qb1tPTgfF9RQP8e1wq4cLFErVuTJv7UsSC915J8dh3w= +cloud.google.com/go/language v1.4.0/go.mod h1:F9dRpNFQmJbkaop6g0JhSBXCNlO90e1KWx5iDdxbWic= +cloud.google.com/go/language v1.6.0/go.mod h1:6dJ8t3B+lUYfStgls25GusK04NLh3eDLQnWM3mdEbhI= +cloud.google.com/go/language v1.7.0/go.mod h1:DJ6dYN/W+SQOjF8e1hLQXMF21AkH2w9wiPzPCJa2MIE= +cloud.google.com/go/language v1.8.0/go.mod h1:qYPVHf7SPoNNiCL2Dr0FfEFNil1qi3pQEyygwpgVKB8= +cloud.google.com/go/language v1.9.0/go.mod h1:Ns15WooPM5Ad/5no/0n81yUetis74g3zrbeJBE+ptUY= +cloud.google.com/go/lifesciences v0.5.0/go.mod h1:3oIKy8ycWGPUyZDR/8RNnTOYevhaMLqh5vLUXs9zvT8= +cloud.google.com/go/lifesciences v0.6.0/go.mod h1:ddj6tSX/7BOnhxCSd3ZcETvtNr8NZ6t/iPhY2Tyfu08= +cloud.google.com/go/lifesciences v0.8.0/go.mod h1:lFxiEOMqII6XggGbOnKiyZ7IBwoIqA84ClvoezaA/bo= +cloud.google.com/go/logging v1.6.1/go.mod h1:5ZO0mHHbvm8gEmeEUHrmDlTDSu5imF6MUP9OfilNXBw= +cloud.google.com/go/logging v1.7.0/go.mod h1:3xjP2CjkM3ZkO73aj4ASA5wRPGGCRrPIAeNqVNkzY8M= +cloud.google.com/go/longrunning v0.1.1/go.mod h1:UUFxuDWkv22EuY93jjmDMFT5GPQKeFVJBIF6QlTqdsE= +cloud.google.com/go/longrunning v0.3.0/go.mod h1:qth9Y41RRSUE69rDcOn6DdK3HfQfsUI0YSmW3iIlLJc= +cloud.google.com/go/longrunning v0.4.1/go.mod h1:4iWDqhBZ70CvZ6BfETbvam3T8FMvLK+eFj0E6AaRQTo= +cloud.google.com/go/managedidentities v1.3.0/go.mod h1:UzlW3cBOiPrzucO5qWkNkh0w33KFtBJU281hacNvsdE= +cloud.google.com/go/managedidentities v1.4.0/go.mod h1:NWSBYbEMgqmbZsLIyKvxrYbtqOsxY1ZrGM+9RgDqInM= +cloud.google.com/go/managedidentities v1.5.0/go.mod h1:+dWcZ0JlUmpuxpIDfyP5pP5y0bLdRwOS4Lp7gMni/LA= +cloud.google.com/go/maps v0.1.0/go.mod h1:BQM97WGyfw9FWEmQMpZ5T6cpovXXSd1cGmFma94eubI= +cloud.google.com/go/maps v0.6.0/go.mod h1:o6DAMMfb+aINHz/p/jbcY+mYeXBoZoxTfdSQ8VAJaCw= +cloud.google.com/go/mediatranslation v0.5.0/go.mod h1:jGPUhGTybqsPQn91pNXw0xVHfuJ3leR1wj37oU3y1f4= +cloud.google.com/go/mediatranslation v0.6.0/go.mod h1:hHdBCTYNigsBxshbznuIMFNe5QXEowAuNmmC7h8pu5w= +cloud.google.com/go/mediatranslation v0.7.0/go.mod h1:LCnB/gZr90ONOIQLgSXagp8XUW1ODs2UmUMvcgMfI2I= +cloud.google.com/go/memcache v1.4.0/go.mod h1:rTOfiGZtJX1AaFUrOgsMHX5kAzaTQ8azHiuDoTPzNsE= +cloud.google.com/go/memcache v1.5.0/go.mod h1:dk3fCK7dVo0cUU2c36jKb4VqKPS22BTkf81Xq617aWM= +cloud.google.com/go/memcache v1.6.0/go.mod h1:XS5xB0eQZdHtTuTF9Hf8eJkKtR3pVRCcvJwtm68T3rA= +cloud.google.com/go/memcache v1.7.0/go.mod h1:ywMKfjWhNtkQTxrWxCkCFkoPjLHPW6A7WOTVI8xy3LY= +cloud.google.com/go/memcache v1.9.0/go.mod h1:8oEyzXCu+zo9RzlEaEjHl4KkgjlNDaXbCQeQWlzNFJM= +cloud.google.com/go/metastore v1.5.0/go.mod h1:2ZNrDcQwghfdtCwJ33nM0+GrBGlVuh8rakL3vdPY3XY= +cloud.google.com/go/metastore v1.6.0/go.mod h1:6cyQTls8CWXzk45G55x57DVQ9gWg7RiH65+YgPsNh9s= +cloud.google.com/go/metastore v1.7.0/go.mod h1:s45D0B4IlsINu87/AsWiEVYbLaIMeUSoxlKKDqBGFS8= +cloud.google.com/go/metastore v1.8.0/go.mod h1:zHiMc4ZUpBiM7twCIFQmJ9JMEkDSyZS9U12uf7wHqSI= +cloud.google.com/go/metastore v1.10.0/go.mod h1:fPEnH3g4JJAk+gMRnrAnoqyv2lpUCqJPWOodSaf45Eo= +cloud.google.com/go/monitoring v1.7.0/go.mod h1:HpYse6kkGo//7p6sT0wsIC6IBDET0RhIsnmlA53dvEk= +cloud.google.com/go/monitoring v1.8.0/go.mod h1:E7PtoMJ1kQXWxPjB6mv2fhC5/15jInuulFdYYtlcvT4= +cloud.google.com/go/monitoring v1.12.0/go.mod h1:yx8Jj2fZNEkL/GYZyTLS4ZtZEZN8WtDEiEqG4kLK50w= +cloud.google.com/go/networkconnectivity v1.4.0/go.mod h1:nOl7YL8odKyAOtzNX73/M5/mGZgqqMeryi6UPZTk/rA= +cloud.google.com/go/networkconnectivity v1.5.0/go.mod h1:3GzqJx7uhtlM3kln0+x5wyFvuVH1pIBJjhCpjzSt75o= +cloud.google.com/go/networkconnectivity v1.6.0/go.mod h1:OJOoEXW+0LAxHh89nXd64uGG+FbQoeH8DtxCHVOMlaM= +cloud.google.com/go/networkconnectivity v1.7.0/go.mod h1:RMuSbkdbPwNMQjB5HBWD5MpTBnNm39iAVpC3TmsExt8= +cloud.google.com/go/networkconnectivity v1.10.0/go.mod h1:UP4O4sWXJG13AqrTdQCD9TnLGEbtNRqjuaaA7bNjF5E= +cloud.google.com/go/networkmanagement v1.4.0/go.mod h1:Q9mdLLRn60AsOrPc8rs8iNV6OHXaGcDdsIQe1ohekq8= +cloud.google.com/go/networkmanagement v1.5.0/go.mod h1:ZnOeZ/evzUdUsnvRt792H0uYEnHQEMaz+REhhzJRcf4= +cloud.google.com/go/networkmanagement v1.6.0/go.mod h1:5pKPqyXjB/sgtvB5xqOemumoQNB7y95Q7S+4rjSOPYY= +cloud.google.com/go/networksecurity v0.5.0/go.mod h1:xS6fOCoqpVC5zx15Z/MqkfDwH4+m/61A3ODiDV1xmiQ= +cloud.google.com/go/networksecurity v0.6.0/go.mod h1:Q5fjhTr9WMI5mbpRYEbiexTzROf7ZbDzvzCrNl14nyU= +cloud.google.com/go/networksecurity v0.7.0/go.mod h1:mAnzoxx/8TBSyXEeESMy9OOYwo1v+gZ5eMRnsT5bC8k= +cloud.google.com/go/notebooks v1.2.0/go.mod h1:9+wtppMfVPUeJ8fIWPOq1UnATHISkGXGqTkxeieQ6UY= +cloud.google.com/go/notebooks v1.3.0/go.mod h1:bFR5lj07DtCPC7YAAJ//vHskFBxA5JzYlH68kXVdk34= +cloud.google.com/go/notebooks v1.4.0/go.mod h1:4QPMngcwmgb6uw7Po99B2xv5ufVoIQ7nOGDyL4P8AgA= +cloud.google.com/go/notebooks v1.5.0/go.mod h1:q8mwhnP9aR8Hpfnrc5iN5IBhrXUy8S2vuYs+kBJ/gu0= +cloud.google.com/go/notebooks v1.7.0/go.mod h1:PVlaDGfJgj1fl1S3dUwhFMXFgfYGhYQt2164xOMONmE= +cloud.google.com/go/optimization v1.1.0/go.mod h1:5po+wfvX5AQlPznyVEZjGJTMr4+CAkJf2XSTQOOl9l4= +cloud.google.com/go/optimization v1.2.0/go.mod h1:Lr7SOHdRDENsh+WXVmQhQTrzdu9ybg0NecjHidBq6xs= +cloud.google.com/go/optimization v1.3.1/go.mod h1:IvUSefKiwd1a5p0RgHDbWCIbDFgKuEdB+fPPuP0IDLI= +cloud.google.com/go/orchestration v1.3.0/go.mod h1:Sj5tq/JpWiB//X/q3Ngwdl5K7B7Y0KZ7bfv0wL6fqVA= +cloud.google.com/go/orchestration v1.4.0/go.mod h1:6W5NLFWs2TlniBphAViZEVhrXRSMgUGDfW7vrWKvsBk= +cloud.google.com/go/orchestration v1.6.0/go.mod h1:M62Bevp7pkxStDfFfTuCOaXgaaqRAga1yKyoMtEoWPQ= +cloud.google.com/go/orgpolicy v1.4.0/go.mod h1:xrSLIV4RePWmP9P3tBl8S93lTmlAxjm06NSm2UTmKvE= +cloud.google.com/go/orgpolicy v1.5.0/go.mod h1:hZEc5q3wzwXJaKrsx5+Ewg0u1LxJ51nNFlext7Tanwc= +cloud.google.com/go/orgpolicy v1.10.0/go.mod h1:w1fo8b7rRqlXlIJbVhOMPrwVljyuW5mqssvBtU18ONc= +cloud.google.com/go/osconfig v1.7.0/go.mod h1:oVHeCeZELfJP7XLxcBGTMBvRO+1nQ5tFG9VQTmYS2Fs= +cloud.google.com/go/osconfig v1.8.0/go.mod h1:EQqZLu5w5XA7eKizepumcvWx+m8mJUhEwiPqWiZeEdg= +cloud.google.com/go/osconfig v1.9.0/go.mod h1:Yx+IeIZJ3bdWmzbQU4fxNl8xsZ4amB+dygAwFPlvnNo= +cloud.google.com/go/osconfig v1.10.0/go.mod h1:uMhCzqC5I8zfD9zDEAfvgVhDS8oIjySWh+l4WK6GnWw= +cloud.google.com/go/osconfig v1.11.0/go.mod h1:aDICxrur2ogRd9zY5ytBLV89KEgT2MKB2L/n6x1ooPw= +cloud.google.com/go/oslogin v1.4.0/go.mod h1:YdgMXWRaElXz/lDk1Na6Fh5orF7gvmJ0FGLIs9LId4E= +cloud.google.com/go/oslogin v1.5.0/go.mod h1:D260Qj11W2qx/HVF29zBg+0fd6YCSjSqLUkY/qEenQU= +cloud.google.com/go/oslogin v1.6.0/go.mod h1:zOJ1O3+dTU8WPlGEkFSh7qeHPPSoxrcMbbK1Nm2iX70= +cloud.google.com/go/oslogin v1.7.0/go.mod h1:e04SN0xO1UNJ1M5GP0vzVBFicIe4O53FOfcixIqTyXo= +cloud.google.com/go/oslogin v1.9.0/go.mod h1:HNavntnH8nzrn8JCTT5fj18FuJLFJc4NaZJtBnQtKFs= +cloud.google.com/go/phishingprotection v0.5.0/go.mod h1:Y3HZknsK9bc9dMi+oE8Bim0lczMU6hrX0UpADuMefr0= +cloud.google.com/go/phishingprotection v0.6.0/go.mod h1:9Y3LBLgy0kDTcYET8ZH3bq/7qni15yVUoAxiFxnlSUA= +cloud.google.com/go/phishingprotection v0.7.0/go.mod h1:8qJI4QKHoda/sb/7/YmMQ2omRLSLYSu9bU0EKCNI+Lk= +cloud.google.com/go/policytroubleshooter v1.3.0/go.mod h1:qy0+VwANja+kKrjlQuOzmlvscn4RNsAc0e15GGqfMxg= +cloud.google.com/go/policytroubleshooter v1.4.0/go.mod h1:DZT4BcRw3QoO8ota9xw/LKtPa8lKeCByYeKTIf/vxdE= +cloud.google.com/go/policytroubleshooter v1.5.0/go.mod h1:Rz1WfV+1oIpPdN2VvvuboLVRsB1Hclg3CKQ53j9l8vw= +cloud.google.com/go/privatecatalog v0.5.0/go.mod h1:XgosMUvvPyxDjAVNDYxJ7wBW8//hLDDYmnsNcMGq1K0= +cloud.google.com/go/privatecatalog v0.6.0/go.mod h1:i/fbkZR0hLN29eEWiiwue8Pb+GforiEIBnV9yrRUOKI= +cloud.google.com/go/privatecatalog v0.7.0/go.mod h1:2s5ssIFO69F5csTXcwBP7NPFTZvps26xGzvQ2PQaBYg= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/pubsub v1.26.0/go.mod h1:QgBH3U/jdJy/ftjPhTkyXNj543Tin1pRYcdcPRnFIRI= +cloud.google.com/go/pubsub v1.27.1/go.mod h1:hQN39ymbV9geqBnfQq6Xf63yNhUAhv9CZhzp5O6qsW0= +cloud.google.com/go/pubsub v1.28.0/go.mod h1:vuXFpwaVoIPQMGXqRyUQigu/AX1S3IWugR9xznmcXX8= +cloud.google.com/go/pubsublite v1.5.0/go.mod h1:xapqNQ1CuLfGi23Yda/9l4bBCKz/wC3KIJ5gKcxveZg= +cloud.google.com/go/pubsublite v1.6.0/go.mod h1:1eFCS0U11xlOuMFV/0iBqw3zP12kddMeCbj/F3FSj9k= +cloud.google.com/go/recaptchaenterprise v1.3.1/go.mod h1:OdD+q+y4XGeAlxRaMn1Y7/GveP6zmq76byL6tjPE7d4= +cloud.google.com/go/recaptchaenterprise/v2 v2.1.0/go.mod h1:w9yVqajwroDNTfGuhmOjPDN//rZGySaf6PtFVcSCa7o= +cloud.google.com/go/recaptchaenterprise/v2 v2.2.0/go.mod h1:/Zu5jisWGeERrd5HnlS3EUGb/D335f9k51B/FVil0jk= +cloud.google.com/go/recaptchaenterprise/v2 v2.3.0/go.mod h1:O9LwGCjrhGHBQET5CA7dd5NwwNQUErSgEDit1DLNTdo= +cloud.google.com/go/recaptchaenterprise/v2 v2.4.0/go.mod h1:Am3LHfOuBstrLrNCBrlI5sbwx9LBg3te2N6hGvHn2mE= +cloud.google.com/go/recaptchaenterprise/v2 v2.5.0/go.mod h1:O8LzcHXN3rz0j+LBC91jrwI3R+1ZSZEWrfL7XHgNo9U= +cloud.google.com/go/recaptchaenterprise/v2 v2.6.0/go.mod h1:RPauz9jeLtB3JVzg6nCbe12qNoaa8pXc4d/YukAmcnA= +cloud.google.com/go/recommendationengine v0.5.0/go.mod h1:E5756pJcVFeVgaQv3WNpImkFP8a+RptV6dDLGPILjvg= +cloud.google.com/go/recommendationengine v0.6.0/go.mod h1:08mq2umu9oIqc7tDy8sx+MNJdLG0fUi3vaSVbztHgJ4= +cloud.google.com/go/recommendationengine v0.7.0/go.mod h1:1reUcE3GIu6MeBz/h5xZJqNLuuVjNg1lmWMPyjatzac= +cloud.google.com/go/recommender v1.5.0/go.mod h1:jdoeiBIVrJe9gQjwd759ecLJbxCDED4A6p+mqoqDvTg= +cloud.google.com/go/recommender v1.6.0/go.mod h1:+yETpm25mcoiECKh9DEScGzIRyDKpZ0cEhWGo+8bo+c= +cloud.google.com/go/recommender v1.7.0/go.mod h1:XLHs/W+T8olwlGOgfQenXBTbIseGclClff6lhFVe9Bs= +cloud.google.com/go/recommender v1.8.0/go.mod h1:PkjXrTT05BFKwxaUxQmtIlrtj0kph108r02ZZQ5FE70= +cloud.google.com/go/recommender v1.9.0/go.mod h1:PnSsnZY7q+VL1uax2JWkt/UegHssxjUVVCrX52CuEmQ= +cloud.google.com/go/redis v1.7.0/go.mod h1:V3x5Jq1jzUcg+UNsRvdmsfuFnit1cfe3Z/PGyq/lm4Y= +cloud.google.com/go/redis v1.8.0/go.mod h1:Fm2szCDavWzBk2cDKxrkmWBqoCiL1+Ctwq7EyqBCA/A= +cloud.google.com/go/redis v1.9.0/go.mod h1:HMYQuajvb2D0LvMgZmLDZW8V5aOC/WxstZHiy4g8OiA= +cloud.google.com/go/redis v1.10.0/go.mod h1:ThJf3mMBQtW18JzGgh41/Wld6vnDDc/F/F35UolRZPM= +cloud.google.com/go/redis v1.11.0/go.mod h1:/X6eicana+BWcUda5PpwZC48o37SiFVTFSs0fWAJ7uQ= +cloud.google.com/go/resourcemanager v1.3.0/go.mod h1:bAtrTjZQFJkiWTPDb1WBjzvc6/kifjj4QBYuKCCoqKA= +cloud.google.com/go/resourcemanager v1.4.0/go.mod h1:MwxuzkumyTX7/a3n37gmsT3py7LIXwrShilPh3P1tR0= +cloud.google.com/go/resourcemanager v1.5.0/go.mod h1:eQoXNAiAvCf5PXxWxXjhKQoTMaUSNrEfg+6qdf/wots= +cloud.google.com/go/resourcesettings v1.3.0/go.mod h1:lzew8VfESA5DQ8gdlHwMrqZs1S9V87v3oCnKCWoOuQU= +cloud.google.com/go/resourcesettings v1.4.0/go.mod h1:ldiH9IJpcrlC3VSuCGvjR5of/ezRrOxFtpJoJo5SmXg= +cloud.google.com/go/resourcesettings v1.5.0/go.mod h1:+xJF7QSG6undsQDfsCJyqWXyBwUoJLhetkRMDRnIoXA= +cloud.google.com/go/retail v1.8.0/go.mod h1:QblKS8waDmNUhghY2TI9O3JLlFk8jybHeV4BF19FrE4= +cloud.google.com/go/retail v1.9.0/go.mod h1:g6jb6mKuCS1QKnH/dpu7isX253absFl6iE92nHwlBUY= +cloud.google.com/go/retail v1.10.0/go.mod h1:2gDk9HsL4HMS4oZwz6daui2/jmKvqShXKQuB2RZ+cCc= +cloud.google.com/go/retail v1.11.0/go.mod h1:MBLk1NaWPmh6iVFSz9MeKG/Psyd7TAgm6y/9L2B4x9Y= +cloud.google.com/go/retail v1.12.0/go.mod h1:UMkelN/0Z8XvKymXFbD4EhFJlYKRx1FGhQkVPU5kF14= +cloud.google.com/go/run v0.2.0/go.mod h1:CNtKsTA1sDcnqqIFR3Pb5Tq0usWxJJvsWOCPldRU3Do= +cloud.google.com/go/run v0.3.0/go.mod h1:TuyY1+taHxTjrD0ZFk2iAR+xyOXEA0ztb7U3UNA0zBo= +cloud.google.com/go/run v0.8.0/go.mod h1:VniEnuBwqjigv0A7ONfQUaEItaiCRVujlMqerPPiktM= +cloud.google.com/go/scheduler v1.4.0/go.mod h1:drcJBmxF3aqZJRhmkHQ9b3uSSpQoltBPGPxGAWROx6s= +cloud.google.com/go/scheduler v1.5.0/go.mod h1:ri073ym49NW3AfT6DZi21vLZrG07GXr5p3H1KxN5QlI= +cloud.google.com/go/scheduler v1.6.0/go.mod h1:SgeKVM7MIwPn3BqtcBntpLyrIJftQISRrYB5ZtT+KOk= +cloud.google.com/go/scheduler v1.7.0/go.mod h1:jyCiBqWW956uBjjPMMuX09n3x37mtyPJegEWKxRsn44= +cloud.google.com/go/scheduler v1.8.0/go.mod h1:TCET+Y5Gp1YgHT8py4nlg2Sew8nUHMqcpousDgXJVQc= +cloud.google.com/go/secretmanager v1.6.0/go.mod h1:awVa/OXF6IiyaU1wQ34inzQNc4ISIDIrId8qE5QGgKA= +cloud.google.com/go/secretmanager v1.8.0/go.mod h1:hnVgi/bN5MYHd3Gt0SPuTPPp5ENina1/LxM+2W9U9J4= +cloud.google.com/go/secretmanager v1.9.0/go.mod h1:b71qH2l1yHmWQHt9LC80akm86mX8AL6X1MA01dW8ht4= +cloud.google.com/go/secretmanager v1.10.0/go.mod h1:MfnrdvKMPNra9aZtQFvBcvRU54hbPD8/HayQdlUgJpU= +cloud.google.com/go/security v1.5.0/go.mod h1:lgxGdyOKKjHL4YG3/YwIL2zLqMFCKs0UbQwgyZmfJl4= +cloud.google.com/go/security v1.7.0/go.mod h1:mZklORHl6Bg7CNnnjLH//0UlAlaXqiG7Lb9PsPXLfD0= +cloud.google.com/go/security v1.8.0/go.mod h1:hAQOwgmaHhztFhiQ41CjDODdWP0+AE1B3sX4OFlq+GU= +cloud.google.com/go/security v1.9.0/go.mod h1:6Ta1bO8LXI89nZnmnsZGp9lVoVWXqsVbIq/t9dzI+2Q= +cloud.google.com/go/security v1.10.0/go.mod h1:QtOMZByJVlibUT2h9afNDWRZ1G96gVywH8T5GUSb9IA= +cloud.google.com/go/security v1.12.0/go.mod h1:rV6EhrpbNHrrxqlvW0BWAIawFWq3X90SduMJdFwtLB8= +cloud.google.com/go/securitycenter v1.13.0/go.mod h1:cv5qNAqjY84FCN6Y9z28WlkKXyWsgLO832YiWwkCWcU= +cloud.google.com/go/securitycenter v1.14.0/go.mod h1:gZLAhtyKv85n52XYWt6RmeBdydyxfPeTrpToDPw4Auc= +cloud.google.com/go/securitycenter v1.15.0/go.mod h1:PeKJ0t8MoFmmXLXWm41JidyzI3PJjd8sXWaVqg43WWk= +cloud.google.com/go/securitycenter v1.16.0/go.mod h1:Q9GMaLQFUD+5ZTabrbujNWLtSLZIZF7SAR0wWECrjdk= +cloud.google.com/go/securitycenter v1.18.1/go.mod h1:0/25gAzCM/9OL9vVx4ChPeM/+DlfGQJDwBy/UC8AKK0= +cloud.google.com/go/servicecontrol v1.4.0/go.mod h1:o0hUSJ1TXJAmi/7fLJAedOovnujSEvjKCAFNXPQ1RaU= +cloud.google.com/go/servicecontrol v1.5.0/go.mod h1:qM0CnXHhyqKVuiZnGKrIurvVImCs8gmqWsDoqe9sU1s= +cloud.google.com/go/servicecontrol v1.11.0/go.mod h1:kFmTzYzTUIuZs0ycVqRHNaNhgR+UMUpw9n02l/pY+mc= +cloud.google.com/go/servicedirectory v1.4.0/go.mod h1:gH1MUaZCgtP7qQiI+F+A+OpeKF/HQWgtAddhTbhL2bs= +cloud.google.com/go/servicedirectory v1.5.0/go.mod h1:QMKFL0NUySbpZJ1UZs3oFAmdvVxhhxB6eJ/Vlp73dfg= +cloud.google.com/go/servicedirectory v1.6.0/go.mod h1:pUlbnWsLH9c13yGkxCmfumWEPjsRs1RlmJ4pqiNjVL4= +cloud.google.com/go/servicedirectory v1.7.0/go.mod h1:5p/U5oyvgYGYejufvxhgwjL8UVXjkuw7q5XcG10wx1U= +cloud.google.com/go/servicedirectory v1.8.0/go.mod h1:srXodfhY1GFIPvltunswqXpVxFPpZjf8nkKQT7XcXaY= +cloud.google.com/go/servicemanagement v1.4.0/go.mod h1:d8t8MDbezI7Z2R1O/wu8oTggo3BI2GKYbdG4y/SJTco= +cloud.google.com/go/servicemanagement v1.5.0/go.mod h1:XGaCRe57kfqu4+lRxaFEAuqmjzF0r+gWHjWqKqBvKFo= +cloud.google.com/go/servicemanagement v1.6.0/go.mod h1:aWns7EeeCOtGEX4OvZUWCCJONRZeFKiptqKf1D0l/Jc= +cloud.google.com/go/serviceusage v1.3.0/go.mod h1:Hya1cozXM4SeSKTAgGXgj97GlqUvF5JaoXacR1JTP/E= +cloud.google.com/go/serviceusage v1.4.0/go.mod h1:SB4yxXSaYVuUBYUml6qklyONXNLt83U0Rb+CXyhjEeU= +cloud.google.com/go/serviceusage v1.5.0/go.mod h1:w8U1JvqUqwJNPEOTQjrMHkw3IaIFLoLsPLvsE3xueec= +cloud.google.com/go/shell v1.3.0/go.mod h1:VZ9HmRjZBsjLGXusm7K5Q5lzzByZmJHf1d0IWHEN5X4= +cloud.google.com/go/shell v1.4.0/go.mod h1:HDxPzZf3GkDdhExzD/gs8Grqk+dmYcEjGShZgYa9URw= +cloud.google.com/go/shell v1.6.0/go.mod h1:oHO8QACS90luWgxP3N9iZVuEiSF84zNyLytb+qE2f9A= +cloud.google.com/go/spanner v1.41.0/go.mod h1:MLYDBJR/dY4Wt7ZaMIQ7rXOTLjYrmxLE/5ve9vFfWos= +cloud.google.com/go/spanner v1.44.0/go.mod h1:G8XIgYdOK+Fbcpbs7p2fiprDw4CaZX63whnSMLVBxjk= +cloud.google.com/go/speech v1.6.0/go.mod h1:79tcr4FHCimOp56lwC01xnt/WPJZc4v3gzyT7FoBkCM= +cloud.google.com/go/speech v1.7.0/go.mod h1:KptqL+BAQIhMsj1kOP2la5DSEEerPDuOP/2mmkhHhZQ= +cloud.google.com/go/speech v1.8.0/go.mod h1:9bYIl1/tjsAnMgKGHKmBZzXKEkGgtU+MpdDPTE9f7y0= +cloud.google.com/go/speech v1.9.0/go.mod h1:xQ0jTcmnRFFM2RfX/U+rk6FQNUF6DQlydUSyoooSpco= +cloud.google.com/go/speech v1.14.1/go.mod h1:gEosVRPJ9waG7zqqnsHpYTOoAS4KouMRLDFMekpJ0J0= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= +cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y= +cloud.google.com/go/storage v1.23.0/go.mod h1:vOEEDNFnciUMhBeT6hsJIn3ieU5cFRmzeLgDvXzfIXc= +cloud.google.com/go/storage v1.27.0/go.mod h1:x9DOL8TK/ygDUMieqwfhdpQryTeEkhGKMi80i/iqR2s= +cloud.google.com/go/storage v1.28.1/go.mod h1:Qnisd4CqDdo6BGs2AD5LLnEsmSQ80wQ5ogcBBKhU86Y= +cloud.google.com/go/storagetransfer v1.5.0/go.mod h1:dxNzUopWy7RQevYFHewchb29POFv3/AaBgnhqzqiK0w= +cloud.google.com/go/storagetransfer v1.6.0/go.mod h1:y77xm4CQV/ZhFZH75PLEXY0ROiS7Gh6pSKrM8dJyg6I= +cloud.google.com/go/storagetransfer v1.7.0/go.mod h1:8Giuj1QNb1kfLAiWM1bN6dHzfdlDAVC9rv9abHot2W4= +cloud.google.com/go/talent v1.1.0/go.mod h1:Vl4pt9jiHKvOgF9KoZo6Kob9oV4lwd/ZD5Cto54zDRw= +cloud.google.com/go/talent v1.2.0/go.mod h1:MoNF9bhFQbiJ6eFD3uSsg0uBALw4n4gaCaEjBw9zo8g= +cloud.google.com/go/talent v1.3.0/go.mod h1:CmcxwJ/PKfRgd1pBjQgU6W3YBwiewmUzQYH5HHmSCmM= +cloud.google.com/go/talent v1.4.0/go.mod h1:ezFtAgVuRf8jRsvyE6EwmbTK5LKciD4KVnHuDEFmOOA= +cloud.google.com/go/talent v1.5.0/go.mod h1:G+ODMj9bsasAEJkQSzO2uHQWXHHXUomArjWQQYkqK6c= +cloud.google.com/go/texttospeech v1.4.0/go.mod h1:FX8HQHA6sEpJ7rCMSfXuzBcysDAuWusNNNvN9FELDd8= +cloud.google.com/go/texttospeech v1.5.0/go.mod h1:oKPLhR4n4ZdQqWKURdwxMy0uiTS1xU161C8W57Wkea4= +cloud.google.com/go/texttospeech v1.6.0/go.mod h1:YmwmFT8pj1aBblQOI3TfKmwibnsfvhIBzPXcW4EBovc= +cloud.google.com/go/tpu v1.3.0/go.mod h1:aJIManG0o20tfDQlRIej44FcwGGl/cD0oiRyMKG19IQ= +cloud.google.com/go/tpu v1.4.0/go.mod h1:mjZaX8p0VBgllCzF6wcU2ovUXN9TONFLd7iz227X2Xg= +cloud.google.com/go/tpu v1.5.0/go.mod h1:8zVo1rYDFuW2l4yZVY0R0fb/v44xLh3llq7RuV61fPM= +cloud.google.com/go/trace v1.3.0/go.mod h1:FFUE83d9Ca57C+K8rDl/Ih8LwOzWIV1krKgxg6N0G28= +cloud.google.com/go/trace v1.4.0/go.mod h1:UG0v8UBqzusp+z63o7FK74SdFE+AXpCLdFb1rshXG+Y= +cloud.google.com/go/trace v1.8.0/go.mod h1:zH7vcsbAhklH8hWFig58HvxcxyQbaIqMarMg9hn5ECA= +cloud.google.com/go/translate v1.3.0/go.mod h1:gzMUwRjvOqj5i69y/LYLd8RrNQk+hOmIXTi9+nb3Djs= +cloud.google.com/go/translate v1.4.0/go.mod h1:06Dn/ppvLD6WvA5Rhdp029IX2Mi3Mn7fpMRLPvXT5Wg= +cloud.google.com/go/translate v1.6.0/go.mod h1:lMGRudH1pu7I3n3PETiOB2507gf3HnfLV8qlkHZEyos= +cloud.google.com/go/video v1.8.0/go.mod h1:sTzKFc0bUSByE8Yoh8X0mn8bMymItVGPfTuUBUyRgxk= +cloud.google.com/go/video v1.9.0/go.mod h1:0RhNKFRF5v92f8dQt0yhaHrEuH95m068JYOvLZYnJSw= +cloud.google.com/go/video v1.13.0/go.mod h1:ulzkYlYgCp15N2AokzKjy7MQ9ejuynOJdf1tR5lGthk= +cloud.google.com/go/videointelligence v1.6.0/go.mod h1:w0DIDlVRKtwPCn/C4iwZIJdvC69yInhW0cfi+p546uU= +cloud.google.com/go/videointelligence v1.7.0/go.mod h1:k8pI/1wAhjznARtVT9U1llUaFNPh7muw8QyOUpavru4= +cloud.google.com/go/videointelligence v1.8.0/go.mod h1:dIcCn4gVDdS7yte/w+koiXn5dWVplOZkE+xwG9FgK+M= +cloud.google.com/go/videointelligence v1.9.0/go.mod h1:29lVRMPDYHikk3v8EdPSaL8Ku+eMzDljjuvRs105XoU= +cloud.google.com/go/videointelligence v1.10.0/go.mod h1:LHZngX1liVtUhZvi2uNS0VQuOzNi2TkY1OakiuoUOjU= +cloud.google.com/go/vision v1.2.0/go.mod h1:SmNwgObm5DpFBme2xpyOyasvBc1aPdjvMk2bBk0tKD0= +cloud.google.com/go/vision/v2 v2.2.0/go.mod h1:uCdV4PpN1S0jyCyq8sIM42v2Y6zOLkZs+4R9LrGYwFo= +cloud.google.com/go/vision/v2 v2.3.0/go.mod h1:UO61abBx9QRMFkNBbf1D8B1LXdS2cGiiCRx0vSpZoUo= +cloud.google.com/go/vision/v2 v2.4.0/go.mod h1:VtI579ll9RpVTrdKdkMzckdnwMyX2JILb+MhPqRbPsY= +cloud.google.com/go/vision/v2 v2.5.0/go.mod h1:MmaezXOOE+IWa+cS7OhRRLK2cNv1ZL98zhqFFZaaH2E= +cloud.google.com/go/vision/v2 v2.6.0/go.mod h1:158Hes0MvOS9Z/bDMSFpjwsUrZ5fPrdwuyyvKSGAGMY= +cloud.google.com/go/vmmigration v1.2.0/go.mod h1:IRf0o7myyWFSmVR1ItrBSFLFD/rJkfDCUTO4vLlJvsE= +cloud.google.com/go/vmmigration v1.3.0/go.mod h1:oGJ6ZgGPQOFdjHuocGcLqX4lc98YQ7Ygq8YQwHh9A7g= +cloud.google.com/go/vmmigration v1.5.0/go.mod h1:E4YQ8q7/4W9gobHjQg4JJSgXXSgY21nA5r8swQV+Xxc= +cloud.google.com/go/vmwareengine v0.1.0/go.mod h1:RsdNEf/8UDvKllXhMz5J40XxDrNJNN4sagiox+OI208= +cloud.google.com/go/vmwareengine v0.2.2/go.mod h1:sKdctNJxb3KLZkE/6Oui94iw/xs9PRNC2wnNLXsHvH8= +cloud.google.com/go/vpcaccess v1.4.0/go.mod h1:aQHVbTWDYUR1EbTApSVvMq1EnT57ppDmQzZ3imqIk4w= +cloud.google.com/go/vpcaccess v1.5.0/go.mod h1:drmg4HLk9NkZpGfCmZ3Tz0Bwnm2+DKqViEpeEpOq0m8= +cloud.google.com/go/vpcaccess v1.6.0/go.mod h1:wX2ILaNhe7TlVa4vC5xce1bCnqE3AeH27RV31lnmZes= +cloud.google.com/go/webrisk v1.4.0/go.mod h1:Hn8X6Zr+ziE2aNd8SliSDWpEnSS1u4R9+xXZmFiHmGE= +cloud.google.com/go/webrisk v1.5.0/go.mod h1:iPG6fr52Tv7sGk0H6qUFzmL3HHZev1htXuWDEEsqMTg= +cloud.google.com/go/webrisk v1.6.0/go.mod h1:65sW9V9rOosnc9ZY7A7jsy1zoHS5W9IAXv6dGqhMQMc= +cloud.google.com/go/webrisk v1.7.0/go.mod h1:mVMHgEYH0r337nmt1JyLthzMr6YxwN1aAIEc2fTcq7A= +cloud.google.com/go/webrisk v1.8.0/go.mod h1:oJPDuamzHXgUc+b8SiHRcVInZQuybnvEW72PqTc7sSg= +cloud.google.com/go/websecurityscanner v1.3.0/go.mod h1:uImdKm2wyeXQevQJXeh8Uun/Ym1VqworNDlBXQevGMo= +cloud.google.com/go/websecurityscanner v1.4.0/go.mod h1:ebit/Fp0a+FWu5j4JOmJEV8S8CzdTkAS77oDsiSqYWQ= +cloud.google.com/go/websecurityscanner v1.5.0/go.mod h1:Y6xdCPy81yi0SQnDY1xdNTNpfY1oAgXUlcfN3B3eSng= +cloud.google.com/go/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1Vwf+KmJENM0= +cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoISEXH2bcHC3M= +cloud.google.com/go/workflows v1.8.0/go.mod h1:ysGhmEajwZxGn1OhGOGKsTXc5PyxOc0vfKf5Af+to4M= +cloud.google.com/go/workflows v1.9.0/go.mod h1:ZGkj1aFIOd9c8Gerkjjq7OW7I5+l6cSvT3ujaO/WwSA= +cloud.google.com/go/workflows v1.10.0/go.mod h1:fZ8LmRmZQWacon9UCX1r/g/DfAXx5VcPALq2CxzdePw= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8= +git.sr.ht/~sbinet/gg v0.3.1/go.mod h1:KGYtlADtqsqANL9ueOFkWymvzUvLMQllU5Ixo+8v3pc= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= +github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c/go.mod h1:X0CRv0ky0k6m906ixxpzmDRLvX58TFUKS2eePweuyxk= +github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/Workiva/go-datastructures v1.1.1/go.mod h1:1yZL+zfsztete+ePzZz/Zb1/t5BnDuE2Ya2MMGhzP6A= +github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9/go.mod h1:JynElWSGnm/4RlzPXRlREEwqTHAN3T56Bv2ITsFT3gY= +github.com/ajstarks/deck/generate v0.0.0-20210309230005-c3f852c02e19/go.mod h1:T13YZdzov6OU0A1+RfKZiZN9ca6VeKdBdyDV+BY97Tk= +github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= +github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b/go.mod h1:1KcenG0jGWcpt8ov532z81sp/kMMUG485J2InIOyADM= +github.com/alecthomas/kingpin/v2 v2.3.1/go.mod h1:oYL5vtsvEHZGHxU7DMp32Dvx+qL+ptGn6lWaot2vCNE= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= +github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/apache/arrow/go/v10 v10.0.1/go.mod h1:YvhnlEePVnBS4+0z3fhPfUy7W1Ikj0Ih0vcRo/gZ1M0= +github.com/apache/thrift v0.16.0/go.mod h1:PHK3hniurgQaNMZYaCLEqXKsYK8upmhPbmdP2FXSqgU= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-metrics v0.4.0/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716/go.mod h1:/zSlF0T2ArAsTG6SVu8d8qlK+19jjudjA3wWsxnGFHg= +github.com/asynkron/gofun v0.0.0-20220329210725-34fed760f4c2/go.mod h1:5GMOSqaYxNWwuVRWyampTPJEntwz7Mj9J8v1a7gSU2E= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= +github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= +github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= +github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20220314180256-7f1daf1720fc/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20230105202645-06c439db220b/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20230310173818-32f1caf87195/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/couchbase/gocb v1.6.7/go.mod h1:AtRhXLpjgHmkRgG3e0K9t41qnWFonb8iohS/u/TZzxM= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= +github.com/emicklei/go-restful/v3 v3.8.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= +github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= +github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= +github.com/envoyproxy/go-control-plane v0.10.3/go.mod h1:fJJn/j26vwOu972OllsvAgJJM//w9BV6Fxbg2LuVd34= +github.com/envoyproxy/go-control-plane v0.11.0/go.mod h1:VnHyVMpzcLvCFt9yUz1UnCwHLhwx1WguiVDV7pTG/tI= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/envoyproxy/protoc-gen-validate v0.6.7/go.mod h1:dyJXwwfPK2VSqiB9Klm1J6romD608Ba7Hij42vrOBCo= +github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w= +github.com/envoyproxy/protoc-gen-validate v0.10.0/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss= +github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/flowstack/go-jsonschema v0.1.1/go.mod h1:yL7fNggx1o8rm9RlgXv7hTBWxdBM0rVwpMwimd3F3N0= +github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= +github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-fonts/dejavu v0.1.0/go.mod h1:4Wt4I4OU2Nq9asgDCteaAaWZOV24E+0/Pwo0gppep4g= +github.com/go-fonts/latin-modern v0.2.0/go.mod h1:rQVLdDMK+mK1xscDwsqM5J8U2jrRa3T0ecnM9pNujks= +github.com/go-fonts/liberation v0.1.1/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= +github.com/go-fonts/liberation v0.2.0/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= +github.com/go-fonts/stix v0.1.0/go.mod h1:w/c1f0ldAUlJmLBvlbkvVXLAD+tAMqobIIQpmnUIzUY= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= +github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= +github.com/go-latex/latex v0.0.0-20210118124228-b3d85cf34e07/go.mod h1:CO1AlKB2CSIqUrmQPqA0gdRIlnLEY0gK5JGjh37zN5U= +github.com/go-latex/latex v0.0.0-20210823091927-c0d11ff05a81/go.mod h1:SX0U8uGpxhq9o2S/CELCSUxEWWAuoCUcVCQWv7G2OCk= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= +github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= +github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= +github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= +github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-openapi/swag v0.21.1/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-pdf/fpdf v0.5.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= +github.com/go-pdf/fpdf v0.6.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/go-zookeeper/zk v1.0.3/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw= +github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= +github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.4/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= +github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= +github.com/google/flatbuffers v2.0.8+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= +github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ= +github.com/google/gnostic v0.6.9/go.mod h1:Nm8234We1lq6iB9OmlgNv3nH91XLLVZHCDayfA3xq+E= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= +github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= +github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= +github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg= +github.com/googleapis/enterprise-certificate-proxy v0.2.1/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= +github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= +github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= +github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM= +github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM= +github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c= +github.com/googleapis/gax-go/v2 v2.5.1/go.mod h1:h6B0KMMFNtI2ddbGJn3T3ZbwkeT6yqEF02fYlzkUCyo= +github.com/googleapis/gax-go/v2 v2.6.0/go.mod h1:1mjbznJAPHFpesgE5ucqfYEscaz5kMdcIDwU/6+DDoY= +github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8= +github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= +github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w= +github.com/hashicorp/consul/api v1.18.0/go.mod h1:owRRGJ9M5xReDC5nfT8FTJrNAPbT4NM6p/k+d03q2v4= +github.com/hashicorp/consul/sdk v0.13.0/go.mod h1:0hs/l5fOVhJy/VdcoaNqUSi2AUs95eF5WKtv+EYIQqE= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-hclog v1.2.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= +github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= +github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= +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/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= +github.com/hashicorp/memberlist v0.5.0/go.mod h1:yvyXLpo0QaGE59Y7hDTsTzDD25JYBZ4mHgHUZ8lrOI0= +github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= +github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/asmfmt v1.3.2/go.mod h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j0HLHbNSE= +github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/labstack/echo v3.3.10+incompatible/go.mod h1:0INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s= +github.com/labstack/gommon v0.3.1/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM= +github.com/lithammer/shortuuid/v4 v4.0.0/go.mod h1:Zs8puNcrvf2rV9rTH51ZLLcj7ZXqQI3lv67aw4KiB1Y= +github.com/lyft/protoc-gen-star v0.6.0/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= +github.com/lyft/protoc-gen-star v0.6.1/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= +github.com/lyft/protoc-gen-star/v2 v2.0.1/go.mod h1:RcCdONR2ScXaYnQC5tUzxzlpA3WVYF7/opLeUgcQs/o= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= +github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= +github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= +github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY= +github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3/go.mod h1:RagcQ7I8IeTMnF8JTXieKnO4Z6JCsikNEzj0DwauVzE= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/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= +github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= +github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= +github.com/onsi/ginkgo/v2 v2.1.4/go.mod h1:um6tUpWM/cxCK3/FK8BXqEiUMUwRgSM4JXG47RKZmLU= +github.com/onsi/ginkgo/v2 v2.1.6/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk= +github.com/onsi/ginkgo/v2 v2.3.0/go.mod h1:Eew0uilEqZmIEZr8JrvYlvOM7Rr6xzTmMV8AyFNU9d0= +github.com/onsi/ginkgo/v2 v2.4.0/go.mod h1:iHkDK1fKGcBoEHT5W7YBq4RFWaQulw+caOMkAt4OrFo= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= +github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= +github.com/onsi/gomega v1.20.1/go.mod h1:DtrZpjmvpn2mPm4YWQa0/ALMDj9v4YxLgojwPeREyVo= +github.com/onsi/gomega v1.21.1/go.mod h1:iYAIXgPSaDHak0LCMA+AWBpIKBr8WZicMxnE8luStNc= +github.com/onsi/gomega v1.22.1/go.mod h1:x6n7VNe4hw0vkyYUM4mjIXx3JbLiPaBPNgB7PRQ1tuM= +github.com/onsi/gomega v1.23.0/go.mod h1:Z/NWtiqwBrwUt4/2loMmHL63EDLnYHmVbuBpDr2vQAg= +github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= +github.com/orcaman/concurrent-map v1.0.0/go.mod h1:Lu3tH6HLW3feq74c2GC+jIMS/K2CFcDWnWD9XkenwhI= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= +github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY= +github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= +github.com/phpdave11/gofpdi v1.0.13/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= +github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= +github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= +github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= +github.com/prometheus/client_golang v1.15.1/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk= +github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= +github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= +github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= +github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= +github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= +github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= +github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= +github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= +github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w= +github.com/ruudk/golang-pdf417 v0.0.0-20201230142125-a7e3863a1245/go.mod h1:pQAZKsJ8yyVxGRWYNEm9oFB8ieLgKFnamEyDmSA0BRk= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/serialx/hashring v0.0.0-20200727003509-22c0c7ab6b1b/go.mod h1:/yeG0My1xr/u+HZrFQ1tOQQQQrOawfyMUH13ai5brBc= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= +github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= +github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/tinylib/msgp v1.1.5/go.mod h1:eQsjooMTnV42mHu917E26IogZ2930nFyBQdofk10Udg= +github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q= +github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= +github.com/twmb/murmur3 v1.1.6/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= +github.com/xhit/go-str2duration v1.2.0/go.mod h1:3cPSlfZlUHVlneIVfePFWcJZsuwf+P1v2SRTV4cUmp4= +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= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= +github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA= +go.etcd.io/etcd/api/v3 v3.5.7/go.mod h1:9qew1gCdDDLu+VwmeG+iFpL+QlpHTo7iubavdVDgCAA= +go.etcd.io/etcd/client/pkg/v3 v3.5.7/go.mod h1:o0Abi1MK86iad3YrWhgUsbGx1pmTS+hrORWc2CamuhY= +go.etcd.io/etcd/client/v3 v3.5.7/go.mod h1:sOWmj9DZUMyAngS7QQwCyAXXAL6WhgTOPLNS/NabQgw= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/otel v1.4.0/go.mod h1:jeAqMFKy2uLIxCtKxoFj0FAL5zAPKQagc3+GtBWakzk= +go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4= +go.opentelemetry.io/otel/exporters/prometheus v0.39.0/go.mod h1:4jo5Q4CROlCpSPsXLhymi+LYrDXd2ObU5wbKayfZs7Y= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v0.27.0/go.mod h1:2T1VrynTXNdK4uB/hHqnvjTYL09wJWDGp8sOcJIMqjA= +go.opentelemetry.io/otel/internal/metric v0.27.0/go.mod h1:n1CVxRqKqYZtqyTh9U/onvKapPGv7y/rpyOTI+LFNzw= +go.opentelemetry.io/otel/metric v0.27.0/go.mod h1:raXDJ7uP2/Jc0nVZWQjJtzoyssOYWu/+pjZqRzfvZ7g= +go.opentelemetry.io/otel/metric v1.16.0/go.mod h1:QE47cpOmkwipPiefDwo2wDzwJrlfxxNYodqc4xnGCo4= +go.opentelemetry.io/otel/sdk v1.4.0/go.mod h1:71GJPNJh4Qju6zJuYl1CrYtXbrgfau/M9UAggqiy1UE= +go.opentelemetry.io/otel/sdk v1.16.0/go.mod h1:tMsIuKXuuIWPBAOrH+eHtvhTL+SntFtXF9QD68aP6p4= +go.opentelemetry.io/otel/sdk/metric v0.27.0/go.mod h1:lOgrT5C3ORdbqp2LsDrx+pBj6gbZtQ5Omk27vH3EaW0= +go.opentelemetry.io/otel/sdk/metric v0.39.0/go.mod h1:piDIRgjcK7u0HCL5pCA4e74qpK/jk3NiUoAHATVAmiI= +go.opentelemetry.io/otel/trace v1.4.0/go.mod h1:uc3eRsqDfWs9R7b92xbQbU42/eTNz4N+gLP8qJCi4aE= +go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= +go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= +go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= +go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3/go.mod h1:NOZ3BPKG0ec/BKJQgnvsSFpcKLM5xXVWnvZS97DWHgE= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/exp v0.0.0-20220325121720-054d8573a5d8/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE= +golang.org/x/exp v0.0.0-20220518171630-0b5c67f07fdf/go.mod h1:yh0Ynu2b5ZUe3MQfp2nM0ecK7wsgouWTDN0FNeJuIys= +golang.org/x/exp v0.0.0-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= +golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20200119044424-58c23975cae1/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20200618115811-c13761719519/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20201208152932-35266b937fa6/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20210216034530-4410531fe030/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20210607152325-775e3b0c77b9/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= +golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= +golang.org/x/image v0.0.0-20211028202545-6944b10bf410/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= +golang.org/x/image v0.0.0-20220302094943-723b81ca9867/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= +golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= +golang.org/x/mod v0.6.0-dev.0.20211013180041-c96bc1413d57/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= +golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= +golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= +golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220617184016-355a448f1bc9/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.0.0-20221012135044-0b7e1fb9d458/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= +golang.org/x/net v0.3.1-0.20221206200815-1e63c2f08a10/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= +golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= +golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= +golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= +golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.0.0-20221006150949-b44042a4b9c1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec= +golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I= +golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210304124612-50617c2ba197/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= +golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= +golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20220411224347-583f2d630306/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +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-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= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190927191325-030b2cf1153e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.8-0.20211029000441-d6a9af8af023/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= +golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= +golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= +golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= +gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= +gonum.org/v1/gonum v0.9.3/go.mod h1:TZumC3NeyVQskjXqmyWt4S3bINhy7B4eYwW69EbyX+0= +gonum.org/v1/gonum v0.11.0/go.mod h1:fSG4YDCxxUZQJ7rKsQrj0gMOg00Il0Z96/qMA4bVQhA= +gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= +gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= +gonum.org/v1/plot v0.9.0/go.mod h1:3Pcqqmp6RHvJI72kgb8fThyUnav364FOsdDo2aGW5lY= +gonum.org/v1/plot v0.10.1/go.mod h1:VZW5OlhkL1mysU9vaqNHnsy86inf6Ot+jB3r+BczCEo= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= +google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= +google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= +google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= +google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= +google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= +google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= +google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= +google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= +google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= +google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= +google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= +google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= +google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= +google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo= +google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g= +google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA= +google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8= +google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs= +google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= +google.golang.org/api v0.77.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= +google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw= +google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg= +google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o= +google.golang.org/api v0.85.0/go.mod h1:AqZf8Ep9uZ2pyTvgL+x0D3Zt0eoT9b5E8fmzfu6FO2g= +google.golang.org/api v0.90.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= +google.golang.org/api v0.93.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= +google.golang.org/api v0.95.0/go.mod h1:eADj+UBuxkh5zlrSntJghuNeg8HwQ1w5lTKkuqaETEI= +google.golang.org/api v0.96.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= +google.golang.org/api v0.97.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= +google.golang.org/api v0.98.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= +google.golang.org/api v0.99.0/go.mod h1:1YOf74vkVndF7pG6hIHuINsM7eWwpVTAfNMNiL91A08= +google.golang.org/api v0.100.0/go.mod h1:ZE3Z2+ZOr87Rx7dqFsdRQkRBk36kDtp/h+QpHbB7a70= +google.golang.org/api v0.102.0/go.mod h1:3VFl6/fzoA+qNuS1N1/VfXY4LjoXN/wzeIp7TweWwGo= +google.golang.org/api v0.103.0/go.mod h1:hGtW6nK1AC+d9si/UBhw8Xli+QMOf6xyNAyJw4qU9w0= +google.golang.org/api v0.106.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= +google.golang.org/api v0.107.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= +google.golang.org/api v0.108.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= +google.golang.org/api v0.110.0/go.mod h1:7FC4Vvx1Mooxh8C5HWjzZHcavuS2f6pmJpZx60ca7iI= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= +google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= +google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= +google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= +google.golang.org/genproto v0.0.0-20220329172620-7be39ac1afc7/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220617124728-180714bec0ad/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220628213854-d9e0b6570c03/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220722212130-b98a9ff5e252/go.mod h1:GkXuJDJ6aQ7lnJcRF+SJVgFdQhypqgl3LB1C9vabdRE= +google.golang.org/genproto v0.0.0-20220801145646-83ce21fca29f/go.mod h1:iHe1svFLAZg9VWz891+QbRMwUv9O/1Ww+/mngYeThbc= +google.golang.org/genproto v0.0.0-20220815135757-37a418bb8959/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220817144833-d7fd3f11b9b1/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220822174746-9e6da59bd2fc/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220829144015-23454907ede3/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220829175752-36a9c930ecbf/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220913154956-18f8339a66a5/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220914142337-ca0e39ece12f/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220915135415-7fd63a7952de/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220916172020-2692e8806bfa/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220919141832-68c03719ef51/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220920201722-2b89144ce006/go.mod h1:ht8XFiar2npT/g4vkk7O0WYS1sHOHbdujxbEp7CJWbw= +google.golang.org/genproto v0.0.0-20220926165614-551eb538f295/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI= +google.golang.org/genproto v0.0.0-20220926220553-6981cbe3cfce/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI= +google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e/go.mod h1:3526vdqwhZAwq4wsRUaVG555sVgsNmIjRtO7t/JH29U= +google.golang.org/genproto v0.0.0-20221014173430-6e2ab493f96b/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= +google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= +google.golang.org/genproto v0.0.0-20221024153911-1573dae28c9c/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= +google.golang.org/genproto v0.0.0-20221024183307-1bc688fe9f3e/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= +google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c/go.mod h1:CGI5F/G+E5bKwmfYo09AXuVN4dD894kIKUFmVbP2/Fo= +google.golang.org/genproto v0.0.0-20221109142239-94d6d90a7d66/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221114212237-e4508ebdbee1/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221117204609-8f9c96812029/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221201164419-0e50fba7f41c/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221201204527-e3fa12d562f3/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221202195650-67e5cbc046fd/go.mod h1:cTsE614GARnxrLsqKREzmNYJACSWWpAWdNMwnD7c2BE= +google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230112194545-e10362b5ecf9/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230113154510-dbe35b8444a5/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230123190316-2c411cf9d197/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230124163310-31e0e69b6fc2/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230125152338-dcaf20b6aeaa/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230127162408-596548ed4efa/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230209215440-0dfe4f8abfcc/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230216225411-c8e22ba71e44/go.mod h1:8B0gmkoRebU8ukX6HP+4wrVQUY1+6PkQ44BSyIlflHA= +google.golang.org/genproto v0.0.0-20230222225845-10f96fb3dbec/go.mod h1:3Dl5ZL0q0isWJt+FVcfpQyirqemEuLAK/iFvg1UP1Hw= +google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k= +google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= +google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= +google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= +google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= +google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= +google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= +google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww= +google.golang.org/grpc v1.52.0/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5vorUY= +google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= +google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/couchbase/gocbcore.v7 v7.1.18/go.mod h1:48d2Be0MxRtsyuvn+mWzqmoGUG9uA00ghopzOs148/E= +gopkg.in/couchbaselabs/gocbconnstr.v1 v1.0.4/go.mod h1:ZjII0iKx4Veo6N6da+pEZu/ptNyKLg9QTVt7fFmR6sw= +gopkg.in/couchbaselabs/gojcbmock.v1 v1.0.4/go.mod h1:jl/gd/aQ2S8whKVSTnsPs6n7BPeaAuw9UglBD/OF7eo= +gopkg.in/couchbaselabs/jsonx.v1 v1.0.1/go.mod h1:oR201IRovxvLW/eISevH12/+MiKHtNQAKfcX8iWZvJY= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1/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-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= +k8s.io/api v0.26.1/go.mod h1:xd/GBNgR0f707+ATNyPmQ1oyKSgndzXij81FzWGsejg= +k8s.io/apimachinery v0.26.1/go.mod h1:tnPmbONNJ7ByJNz9+n9kMjNP8ON+1qoAIIC70lztu74= +k8s.io/client-go v0.26.1/go.mod h1:IWNSglg+rQ3OcvDkhY6+QLeasV4OYHDjdqeWkDQZwGE= +k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= +k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= +k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= +k8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280/go.mod h1:+Axhij7bCpeqhklhUTe3xmOn6bWxolyZEeyaFpjGtl4= +k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +k8s.io/utils v0.0.0-20221107191617-1a15be271d1d/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= +lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= +modernc.org/cc/v3 v3.36.0/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= +modernc.org/cc/v3 v3.36.2/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= +modernc.org/cc/v3 v3.36.3/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= +modernc.org/ccgo/v3 v3.0.0-20220428102840-41399a37e894/go.mod h1:eI31LL8EwEBKPpNpA4bU1/i+sKOwOrQy8D87zWUcRZc= +modernc.org/ccgo/v3 v3.0.0-20220430103911-bc99d88307be/go.mod h1:bwdAnOoaIt8Ax9YdWGjxWsdkPcZyRPHqrOvJxaKAKGw= +modernc.org/ccgo/v3 v3.16.4/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ= +modernc.org/ccgo/v3 v3.16.6/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ= +modernc.org/ccgo/v3 v3.16.8/go.mod h1:zNjwkizS+fIFDrDjIAgBSCLkWbJuHF+ar3QRn+Z9aws= +modernc.org/ccgo/v3 v3.16.9/go.mod h1:zNMzC9A9xeNUepy6KuZBbugn3c0Mc9TeiJO4lgvkJDo= +modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ= +modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= +modernc.org/libc v0.0.0-20220428101251-2d5f3daf273b/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA= +modernc.org/libc v1.16.0/go.mod h1:N4LD6DBE9cf+Dzf9buBlzVJndKr/iJHG97vGLHYnb5A= +modernc.org/libc v1.16.1/go.mod h1:JjJE0eu4yeK7tab2n4S1w8tlWd9MxXLRzheaRnAKymU= +modernc.org/libc v1.16.17/go.mod h1:hYIV5VZczAmGZAnG15Vdngn5HSF5cSkbvfz2B7GRuVU= +modernc.org/libc v1.16.19/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA= +modernc.org/libc v1.17.0/go.mod h1:XsgLldpP4aWlPlsjqKRdHPqCxCjISdHfM/yeWC5GyW0= +modernc.org/libc v1.17.1/go.mod h1:FZ23b+8LjxZs7XtFMbSzL/EhPxNbfZbErxEHc7cbD9s= +modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/memory v1.1.1/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw= +modernc.org/memory v1.2.0/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw= +modernc.org/memory v1.2.1/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= +modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= +modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= +modernc.org/sqlite v1.18.1/go.mod h1:6ho+Gow7oX5V+OiOQ6Tr4xeqbx13UZ6t+Fw9IRUG4d4= +modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw= +modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw= +modernc.org/tcl v1.13.1/go.mod h1:XOLfOwzhkljL4itZkK6T72ckMgvj0BDsnKNdZVUOecw= +modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= +modernc.org/z v1.5.1/go.mod h1:eWFB510QWW5Th9YGZT81s+LwvaAs3Q2yr4sP0rmLkv8= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= +sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= +sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/_examples/opentelemetry-custom-provider/main.go b/_examples/opentelemetry-custom-provider/main.go new file mode 100644 index 0000000000000000000000000000000000000000..581c1ce80ceb649d540df59f22fe5ec1bce1a235 --- /dev/null +++ b/_examples/opentelemetry-custom-provider/main.go @@ -0,0 +1,68 @@ +package main + +import ( + "context" + "fmt" + "log" + "time" + + "gitee.com/simplexyz/simpleactor-go/actor" + console "github.com/asynkron/goconsole" + "go.opentelemetry.io/otel" + stdout "go.opentelemetry.io/otel/exporters/stdout/stdoutmetric" + "go.opentelemetry.io/otel/metric" + controller "go.opentelemetry.io/otel/sdk/metric/controller/basic" + processor "go.opentelemetry.io/otel/sdk/metric/processor/basic" + "go.opentelemetry.io/otel/sdk/metric/selector/simple" +) + +type ( + hello struct{ Who string } + helloActor struct{} +) + +func (state *helloActor) Receive(context actor.Context) { + switch msg := context.Message().(type) { + case *hello: + fmt.Printf("Hello %s\n", msg.Who) + } +} + +func main() { + ctx := context.Background() + provider := stdoutExporter(ctx) + defer func() { + if err := provider.(*controller.Controller).Stop(ctx); err != nil { + log.Fatalf("could not stop push controller: %v", err) + } + }() + + config := actor.Configure(actor.WithMetricProviders(provider)) + system := actor.NewActorSystemWithConfig(config) + props := actor.PropsFromProducer(func() actor.Actor { + return &helloActor{} + }) + + pid := system.Root.Spawn(props) + system.Root.Request(pid, &hello{Who: "Stdout Exporter"}) + time.Sleep(100 * time.Millisecond) + _, _ = console.ReadLine() +} + +func stdoutExporter(ctx context.Context) metric.MeterProvider { + exporter, _ := stdout.New(stdout.WithPrettyPrint()) + provider := controller.New( + processor.NewFactory( + simple.NewWithInexpensiveDistribution(), + exporter, + ), + controller.WithExporter(exporter), + ) + + if err := provider.Start(ctx); err != nil { + log.Fatalf("could not start push controller: %v", err) + } + otel.SetMeterProvider(provider) + + return provider +} diff --git a/_examples/opentelemetry/go.mod b/_examples/opentelemetry/go.mod new file mode 100644 index 0000000000000000000000000000000000000000..a8d54f82a998e33a5b01d9878623b9072fa78bff --- /dev/null +++ b/_examples/opentelemetry/go.mod @@ -0,0 +1,39 @@ +module opentelemetry + +go 1.21 + +toolchain go1.21.2 + +replace gitee.com/simplexyz/simpleactor-go => ../.. + +require ( + gitee.com/simplexyz/simpleactor-go v0.0.0-00010101000000-000000000000 + github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716 +) + +require ( + github.com/Workiva/go-datastructures v1.1.1 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/emirpasic/gods v1.18.1 // indirect + github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/lithammer/shortuuid/v4 v4.0.0 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/orcaman/concurrent-map v1.0.0 // indirect + github.com/prometheus/client_golang v1.16.0 // indirect + github.com/prometheus/client_model v0.4.0 // indirect + github.com/prometheus/common v0.42.0 // indirect + github.com/prometheus/procfs v0.10.1 // indirect + github.com/twmb/murmur3 v1.1.6 // indirect + go.opentelemetry.io/otel v1.16.0 // indirect + go.opentelemetry.io/otel/exporters/prometheus v0.39.0 // indirect + go.opentelemetry.io/otel/metric v1.16.0 // indirect + go.opentelemetry.io/otel/sdk v1.16.0 // indirect + go.opentelemetry.io/otel/sdk/metric v0.39.0 // indirect + go.opentelemetry.io/otel/trace v1.16.0 // indirect + golang.org/x/sys v0.13.0 // indirect + google.golang.org/protobuf v1.31.0 // indirect +) diff --git a/_examples/opentelemetry/go.sum b/_examples/opentelemetry/go.sum new file mode 100644 index 0000000000000000000000000000000000000000..72e1596d2e6bb1dc5da2329b876b42c528bf5f7a --- /dev/null +++ b/_examples/opentelemetry/go.sum @@ -0,0 +1,100 @@ +github.com/Workiva/go-datastructures v1.1.1 h1:9G5u1UqKt6ABseAffHGNfbNQd7omRlWE5QaxNruzhE0= +github.com/Workiva/go-datastructures v1.1.1/go.mod h1:1yZL+zfsztete+ePzZz/Zb1/t5BnDuE2Ya2MMGhzP6A= +github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716 h1:SgyG4sXkrlalMoCfp20LiNPNhfJS7ez3opNdtihIxPc= +github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716/go.mod h1:/zSlF0T2ArAsTG6SVu8d8qlK+19jjudjA3wWsxnGFHg= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/lithammer/shortuuid/v4 v4.0.0 h1:QRbbVkfgNippHOS8PXDkti4NaWeyYfcBTHtw7k08o4c= +github.com/lithammer/shortuuid/v4 v4.0.0/go.mod h1:Zs8puNcrvf2rV9rTH51ZLLcj7ZXqQI3lv67aw4KiB1Y= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/orcaman/concurrent-map v1.0.0 h1:I/2A2XPCb4IuQWcQhBhSwGfiuybl/J0ev9HDbW65HOY= +github.com/orcaman/concurrent-map v1.0.0/go.mod h1:Lu3tH6HLW3feq74c2GC+jIMS/K2CFcDWnWD9XkenwhI= +github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= +github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= +github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= +github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= +github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= +github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= +github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/tinylib/msgp v1.1.5/go.mod h1:eQsjooMTnV42mHu917E26IogZ2930nFyBQdofk10Udg= +github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q= +github.com/twmb/murmur3 v1.1.6 h1:mqrRot1BRxm+Yct+vavLMou2/iJt0tNVTTC0QoIjaZg= +github.com/twmb/murmur3 v1.1.6/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s= +go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4= +go.opentelemetry.io/otel/exporters/prometheus v0.39.0 h1:whAaiHxOatgtKd+w0dOi//1KUxj3KoPINZdtDaDj3IA= +go.opentelemetry.io/otel/exporters/prometheus v0.39.0/go.mod h1:4jo5Q4CROlCpSPsXLhymi+LYrDXd2ObU5wbKayfZs7Y= +go.opentelemetry.io/otel/metric v1.16.0 h1:RbrpwVG1Hfv85LgnZ7+txXioPDoh6EdbZHo26Q3hqOo= +go.opentelemetry.io/otel/metric v1.16.0/go.mod h1:QE47cpOmkwipPiefDwo2wDzwJrlfxxNYodqc4xnGCo4= +go.opentelemetry.io/otel/sdk v1.16.0 h1:Z1Ok1YsijYL0CSJpHt4cS3wDDh7p572grzNrBMiMWgE= +go.opentelemetry.io/otel/sdk v1.16.0/go.mod h1:tMsIuKXuuIWPBAOrH+eHtvhTL+SntFtXF9QD68aP6p4= +go.opentelemetry.io/otel/sdk/metric v0.39.0 h1:Kun8i1eYf48kHH83RucG93ffz0zGV1sh46FAScOTuDI= +go.opentelemetry.io/otel/sdk/metric v0.39.0/go.mod h1:piDIRgjcK7u0HCL5pCA4e74qpK/jk3NiUoAHATVAmiI= +go.opentelemetry.io/otel/trace v1.16.0 h1:8JRpaObFoW0pxuVPapkgH8UhHQj+bJW8jJsCZEu5MQs= +go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/_examples/opentelemetry/main.go b/_examples/opentelemetry/main.go new file mode 100644 index 0000000000000000000000000000000000000000..b35909a405b1e5bd6828000b5e60121fb29af91c --- /dev/null +++ b/_examples/opentelemetry/main.go @@ -0,0 +1,34 @@ +package main + +import ( + "fmt" + "time" + + "gitee.com/simplexyz/simpleactor-go/actor" + console "github.com/asynkron/goconsole" +) + +type ( + hello struct{ Who string } + helloActor struct{} +) + +func (state *helloActor) Receive(context actor.Context) { + switch msg := context.Message().(type) { + case *hello: + fmt.Printf("Hello %s\n", msg.Who) + } +} + +func main() { + system := actor.NewActorSystem(actor.WithDefaultPrometheusProvider(2222)) + props := actor.PropsFromProducer(func() actor.Actor { + return &helloActor{} + }) + + pid := system.Root.Spawn(props) + system.Root.Request(pid, &hello{Who: "Prometheus Exporter"}) + time.Sleep(100 * time.Millisecond) + fmt.Println("Visit http://localhost:2222") + _, _ = console.ReadLine() +} diff --git a/_examples/persistence/go.mod b/_examples/persistence/go.mod new file mode 100644 index 0000000000000000000000000000000000000000..159e8aeabb34c4513acb9f8de1d985828a60239e --- /dev/null +++ b/_examples/persistence/go.mod @@ -0,0 +1,39 @@ +module persistence + +go 1.21 + +toolchain go1.21.2 + +replace gitee.com/simplexyz/simpleactor-go => ../.. + +require ( + gitee.com/simplexyz/simpleactor-go v0.0.0-00010101000000-000000000000 + github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716 + google.golang.org/protobuf v1.31.0 +) + +require ( + github.com/Workiva/go-datastructures v1.1.1 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/emirpasic/gods v1.18.1 // indirect + github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/lithammer/shortuuid/v4 v4.0.0 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/orcaman/concurrent-map v1.0.0 // indirect + github.com/prometheus/client_golang v1.16.0 // indirect + github.com/prometheus/client_model v0.4.0 // indirect + github.com/prometheus/common v0.42.0 // indirect + github.com/prometheus/procfs v0.10.1 // indirect + github.com/twmb/murmur3 v1.1.6 // indirect + go.opentelemetry.io/otel v1.16.0 // indirect + go.opentelemetry.io/otel/exporters/prometheus v0.39.0 // indirect + go.opentelemetry.io/otel/metric v1.16.0 // indirect + go.opentelemetry.io/otel/sdk v1.16.0 // indirect + go.opentelemetry.io/otel/sdk/metric v0.39.0 // indirect + go.opentelemetry.io/otel/trace v1.16.0 // indirect + golang.org/x/sys v0.13.0 // indirect +) diff --git a/_examples/persistence/go.sum b/_examples/persistence/go.sum new file mode 100644 index 0000000000000000000000000000000000000000..72e1596d2e6bb1dc5da2329b876b42c528bf5f7a --- /dev/null +++ b/_examples/persistence/go.sum @@ -0,0 +1,100 @@ +github.com/Workiva/go-datastructures v1.1.1 h1:9G5u1UqKt6ABseAffHGNfbNQd7omRlWE5QaxNruzhE0= +github.com/Workiva/go-datastructures v1.1.1/go.mod h1:1yZL+zfsztete+ePzZz/Zb1/t5BnDuE2Ya2MMGhzP6A= +github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716 h1:SgyG4sXkrlalMoCfp20LiNPNhfJS7ez3opNdtihIxPc= +github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716/go.mod h1:/zSlF0T2ArAsTG6SVu8d8qlK+19jjudjA3wWsxnGFHg= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/lithammer/shortuuid/v4 v4.0.0 h1:QRbbVkfgNippHOS8PXDkti4NaWeyYfcBTHtw7k08o4c= +github.com/lithammer/shortuuid/v4 v4.0.0/go.mod h1:Zs8puNcrvf2rV9rTH51ZLLcj7ZXqQI3lv67aw4KiB1Y= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/orcaman/concurrent-map v1.0.0 h1:I/2A2XPCb4IuQWcQhBhSwGfiuybl/J0ev9HDbW65HOY= +github.com/orcaman/concurrent-map v1.0.0/go.mod h1:Lu3tH6HLW3feq74c2GC+jIMS/K2CFcDWnWD9XkenwhI= +github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= +github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= +github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= +github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= +github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= +github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= +github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/tinylib/msgp v1.1.5/go.mod h1:eQsjooMTnV42mHu917E26IogZ2930nFyBQdofk10Udg= +github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q= +github.com/twmb/murmur3 v1.1.6 h1:mqrRot1BRxm+Yct+vavLMou2/iJt0tNVTTC0QoIjaZg= +github.com/twmb/murmur3 v1.1.6/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s= +go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4= +go.opentelemetry.io/otel/exporters/prometheus v0.39.0 h1:whAaiHxOatgtKd+w0dOi//1KUxj3KoPINZdtDaDj3IA= +go.opentelemetry.io/otel/exporters/prometheus v0.39.0/go.mod h1:4jo5Q4CROlCpSPsXLhymi+LYrDXd2ObU5wbKayfZs7Y= +go.opentelemetry.io/otel/metric v1.16.0 h1:RbrpwVG1Hfv85LgnZ7+txXioPDoh6EdbZHo26Q3hqOo= +go.opentelemetry.io/otel/metric v1.16.0/go.mod h1:QE47cpOmkwipPiefDwo2wDzwJrlfxxNYodqc4xnGCo4= +go.opentelemetry.io/otel/sdk v1.16.0 h1:Z1Ok1YsijYL0CSJpHt4cS3wDDh7p572grzNrBMiMWgE= +go.opentelemetry.io/otel/sdk v1.16.0/go.mod h1:tMsIuKXuuIWPBAOrH+eHtvhTL+SntFtXF9QD68aP6p4= +go.opentelemetry.io/otel/sdk/metric v0.39.0 h1:Kun8i1eYf48kHH83RucG93ffz0zGV1sh46FAScOTuDI= +go.opentelemetry.io/otel/sdk/metric v0.39.0/go.mod h1:piDIRgjcK7u0HCL5pCA4e74qpK/jk3NiUoAHATVAmiI= +go.opentelemetry.io/otel/trace v1.16.0 h1:8JRpaObFoW0pxuVPapkgH8UhHQj+bJW8jJsCZEu5MQs= +go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/_examples/persistence/main.go b/_examples/persistence/main.go new file mode 100644 index 0000000000000000000000000000000000000000..3dbfe84c010fe49b50689706187409cda2babf15 --- /dev/null +++ b/_examples/persistence/main.go @@ -0,0 +1,222 @@ +package main + +import ( + "fmt" + "log" + "strconv" + + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/persistence" + console "github.com/asynkron/goconsole" + "google.golang.org/protobuf/encoding/prototext" + "google.golang.org/protobuf/reflect/protodesc" + "google.golang.org/protobuf/reflect/protoreflect" + "google.golang.org/protobuf/runtime/protoiface" + "google.golang.org/protobuf/types/descriptorpb" +) + +type Provider struct { + providerState persistence.ProviderState +} + +func NewProvider(snapshotInterval int) *Provider { + return &Provider{ + providerState: persistence.NewInMemoryProvider(snapshotInterval), + } +} + +func (p *Provider) InitState(actorName string, eventNum, eventIndexAfterSnapshot int) { + for i := 0; i < eventNum; i++ { + p.providerState.PersistEvent( + actorName, + i, + &Message{protoMsg: protoMsg{state: "state" + strconv.Itoa(i)}}, + ) + } + p.providerState.PersistSnapshot( + actorName, + eventIndexAfterSnapshot, + &Snapshot{protoMsg: protoMsg{state: "state" + strconv.Itoa(eventIndexAfterSnapshot-1)}}, + ) +} + +func (p *Provider) GetState() persistence.ProviderState { + return p.providerState +} + +type protoMsg struct { + state string + set bool + value string +} + +func (p *protoMsg) Reset() {} +func (p *protoMsg) String() string { return p.state } +func (p *protoMsg) ProtoMessage() {} + +type ( + Message struct{ protoMsg } + Snapshot struct{ protoMsg } +) + +func (m *protoMsg) ProtoReflect() protoreflect.Message { return (*message)(m) } + +type message protoMsg + +type messageType struct{} + +func (messageType) New() protoreflect.Message { return &message{} } +func (messageType) Zero() protoreflect.Message { return (*message)(nil) } +func (messageType) Descriptor() protoreflect.MessageDescriptor { return fileDesc.Messages().Get(0) } + +func (m *message) New() protoreflect.Message { return &message{} } +func (m *message) Descriptor() protoreflect.MessageDescriptor { return fileDesc.Messages().Get(0) } +func (m *message) Type() protoreflect.MessageType { return messageType{} } +func (m *message) Interface() protoreflect.ProtoMessage { return (*protoMsg)(m) } +func (m *message) ProtoMethods() *protoiface.Methods { return nil } + +var fieldDescS = fileDesc.Messages().Get(0).Fields().Get(0) + +func (m *message) Range(f func(protoreflect.FieldDescriptor, protoreflect.Value) bool) { + if m.set { + f(fieldDescS, protoreflect.ValueOf(m.value)) + } +} + +func (m *message) Has(fd protoreflect.FieldDescriptor) bool { + if fd == fieldDescS { + return m.set + } + panic("invalid field descriptor") +} + +func (m *message) Clear(fd protoreflect.FieldDescriptor) { + if fd == fieldDescS { + m.value = "" + m.set = false + return + } + panic("invalid field descriptor") +} + +func (m *message) Get(fd protoreflect.FieldDescriptor) protoreflect.Value { + if fd == fieldDescS { + return protoreflect.ValueOf(m.value) + } + panic("invalid field descriptor") +} + +func (m *message) Set(fd protoreflect.FieldDescriptor, v protoreflect.Value) { + if fd == fieldDescS { + m.value = v.String() + m.set = true + return + } + panic("invalid field descriptor") +} + +func (m *message) Mutable(protoreflect.FieldDescriptor) protoreflect.Value { + panic("invalid field descriptor") +} + +func (m *message) NewField(protoreflect.FieldDescriptor) protoreflect.Value { + panic("invalid field descriptor") +} + +func (m *message) WhichOneof(protoreflect.OneofDescriptor) protoreflect.FieldDescriptor { + panic("invalid oneof descriptor") +} + +func (m *message) GetUnknown() protoreflect.RawFields { return nil } + +// func (m *message) SetUnknown(protoreflect.RawFields) { return } +func (m *message) SetUnknown(protoreflect.RawFields) {} + +func (m *message) IsValid() bool { + return m != nil +} + +var fileDesc = func() protoreflect.FileDescriptor { + p := &descriptorpb.FileDescriptorProto{} + if err := prototext.Unmarshal([]byte(descriptorText), p); err != nil { + panic(err) + } + file, err := protodesc.NewFile(p, nil) + if err != nil { + panic(err) + } + return file +}() + +const descriptorText = ` + name: "internal/testprotos/irregular/irregular.proto" + package: "goproto.proto.thirdparty" + message_type { + name: "IrregularMessage" + field { + name: "s" + number: 1 + label: LABEL_OPTIONAL + type: TYPE_STRING + json_name: "s" + } + } + options { + go_package: "google.golang.org/protobuf/internal/testprotos/irregular" + } +` + +type AberrantMessage int + +func (m AberrantMessage) ProtoMessage() {} +func (m AberrantMessage) Reset() {} +func (m AberrantMessage) String() string { return "" } +func (m AberrantMessage) Marshal() ([]byte, error) { return nil, nil } +func (m AberrantMessage) Unmarshal([]byte) error { return nil } + +type Actor struct { + persistence.Mixin + state string +} + +func (a *Actor) Receive(ctx actor.Context) { + switch msg := ctx.Message().(type) { + case *actor.Started: + log.Println("actor started") + case *persistence.RequestSnapshot: + log.Printf("snapshot internal state '%v'", a.state) + a.PersistSnapshot(&Snapshot{protoMsg: protoMsg{state: a.state}}) + case *Snapshot: + a.state = msg.state + log.Printf("recovered from snapshot, internal state changed to '%v'", a.state) + case *persistence.ReplayComplete: + log.Printf("replay completed, internal state changed to '%v'", a.state) + case *Message: + scenario := "received replayed event" + if !a.Recovering() { + a.PersistReceive(msg) + scenario = "received new message" + } + a.state = msg.state + log.Printf("%s, internal state changed to '%v'\n", scenario, a.state) + } +} + +func main() { + system := actor.NewActorSystem() + provider := NewProvider(3) + provider.InitState("persistent", 4, 3) + + rootContext := system.Root + props := actor.PropsFromProducer(func() actor.Actor { return &Actor{} }, + actor.WithReceiverMiddleware(persistence.Using(provider))) + pid, _ := rootContext.SpawnNamed(props, "persistent") + rootContext.Send(pid, &Message{protoMsg: protoMsg{state: "state4"}}) + rootContext.Send(pid, &Message{protoMsg: protoMsg{state: "state5"}}) + + rootContext.PoisonFuture(pid).Wait() + fmt.Printf("*** restart ***\n") + pid, _ = rootContext.SpawnNamed(props, "persistent") + + _, _ = console.ReadLine() +} diff --git a/_examples/remote-activate/Makefile b/_examples/remote-activate/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..a4c35f1d4ba7a4575c1c4a92937addde15c2eec2 --- /dev/null +++ b/_examples/remote-activate/Makefile @@ -0,0 +1,11 @@ +start: + tmux new-session -d -s eg + tmux split-window -t "eg:0" -v + tmux send-keys -t "eg:0.0" "go run node2/main.go" Enter + tmux send-keys -t "eg:0.1" "go run node1/main.go" Enter + tmux attach -t eg + tmux kill-session -t eg + + +stop: + tmux kill-session -t eg diff --git a/_examples/remote-activate/go.mod b/_examples/remote-activate/go.mod new file mode 100644 index 0000000000000000000000000000000000000000..1998f7f1354eabea81a0793f154701e1d9b12ebf --- /dev/null +++ b/_examples/remote-activate/go.mod @@ -0,0 +1,46 @@ +module remoteactivate + +go 1.21 + +toolchain go1.21.2 + +replace gitee.com/simplexyz/simpleactor-go => ../.. + +require ( + gitee.com/simplexyz/simpleactor-go v0.0.0-00010101000000-000000000000 + github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716 + google.golang.org/protobuf v1.31.0 +) + +require ( + github.com/Workiva/go-datastructures v1.1.1 // indirect + github.com/asynkron/gofun v0.0.0-20220329210725-34fed760f4c2 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/emirpasic/gods v1.18.1 // indirect + github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/lithammer/shortuuid/v4 v4.0.0 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/orcaman/concurrent-map v1.0.0 // indirect + github.com/prometheus/client_golang v1.16.0 // indirect + github.com/prometheus/client_model v0.4.0 // indirect + github.com/prometheus/common v0.42.0 // indirect + github.com/prometheus/procfs v0.10.1 // indirect + github.com/twmb/murmur3 v1.1.6 // indirect + go.opentelemetry.io/otel v1.16.0 // indirect + go.opentelemetry.io/otel/exporters/prometheus v0.39.0 // indirect + go.opentelemetry.io/otel/metric v1.16.0 // indirect + go.opentelemetry.io/otel/sdk v1.16.0 // indirect + go.opentelemetry.io/otel/sdk/metric v0.39.0 // indirect + go.opentelemetry.io/otel/trace v1.16.0 // indirect + golang.org/x/exp v0.0.0-20220827204233-334a2380cb91 // indirect + golang.org/x/net v0.17.0 // indirect + golang.org/x/sys v0.13.0 // indirect + golang.org/x/text v0.13.0 // indirect + google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97 // indirect + google.golang.org/grpc v1.58.3 // indirect +) diff --git a/_examples/remote-activate/go.sum b/_examples/remote-activate/go.sum new file mode 100644 index 0000000000000000000000000000000000000000..af32feca5fbf879242aa12c72a2d4e3f94331558 --- /dev/null +++ b/_examples/remote-activate/go.sum @@ -0,0 +1,125 @@ +github.com/Workiva/go-datastructures v1.1.1 h1:9G5u1UqKt6ABseAffHGNfbNQd7omRlWE5QaxNruzhE0= +github.com/Workiva/go-datastructures v1.1.1/go.mod h1:1yZL+zfsztete+ePzZz/Zb1/t5BnDuE2Ya2MMGhzP6A= +github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716 h1:SgyG4sXkrlalMoCfp20LiNPNhfJS7ez3opNdtihIxPc= +github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716/go.mod h1:/zSlF0T2ArAsTG6SVu8d8qlK+19jjudjA3wWsxnGFHg= +github.com/asynkron/gofun v0.0.0-20220329210725-34fed760f4c2 h1:jEsFZ9d/ieJGVrx3fSPi8oe/qv21fRmyUL5cS3ZEn5A= +github.com/asynkron/gofun v0.0.0-20220329210725-34fed760f4c2/go.mod h1:5GMOSqaYxNWwuVRWyampTPJEntwz7Mj9J8v1a7gSU2E= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/lithammer/shortuuid/v4 v4.0.0 h1:QRbbVkfgNippHOS8PXDkti4NaWeyYfcBTHtw7k08o4c= +github.com/lithammer/shortuuid/v4 v4.0.0/go.mod h1:Zs8puNcrvf2rV9rTH51ZLLcj7ZXqQI3lv67aw4KiB1Y= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/orcaman/concurrent-map v1.0.0 h1:I/2A2XPCb4IuQWcQhBhSwGfiuybl/J0ev9HDbW65HOY= +github.com/orcaman/concurrent-map v1.0.0/go.mod h1:Lu3tH6HLW3feq74c2GC+jIMS/K2CFcDWnWD9XkenwhI= +github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= +github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= +github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= +github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= +github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= +github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= +github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/tinylib/msgp v1.1.5/go.mod h1:eQsjooMTnV42mHu917E26IogZ2930nFyBQdofk10Udg= +github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q= +github.com/twmb/murmur3 v1.1.6 h1:mqrRot1BRxm+Yct+vavLMou2/iJt0tNVTTC0QoIjaZg= +github.com/twmb/murmur3 v1.1.6/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s= +go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4= +go.opentelemetry.io/otel/exporters/prometheus v0.39.0 h1:whAaiHxOatgtKd+w0dOi//1KUxj3KoPINZdtDaDj3IA= +go.opentelemetry.io/otel/exporters/prometheus v0.39.0/go.mod h1:4jo5Q4CROlCpSPsXLhymi+LYrDXd2ObU5wbKayfZs7Y= +go.opentelemetry.io/otel/metric v1.16.0 h1:RbrpwVG1Hfv85LgnZ7+txXioPDoh6EdbZHo26Q3hqOo= +go.opentelemetry.io/otel/metric v1.16.0/go.mod h1:QE47cpOmkwipPiefDwo2wDzwJrlfxxNYodqc4xnGCo4= +go.opentelemetry.io/otel/sdk v1.16.0 h1:Z1Ok1YsijYL0CSJpHt4cS3wDDh7p572grzNrBMiMWgE= +go.opentelemetry.io/otel/sdk v1.16.0/go.mod h1:tMsIuKXuuIWPBAOrH+eHtvhTL+SntFtXF9QD68aP6p4= +go.opentelemetry.io/otel/sdk/metric v0.39.0 h1:Kun8i1eYf48kHH83RucG93ffz0zGV1sh46FAScOTuDI= +go.opentelemetry.io/otel/sdk/metric v0.39.0/go.mod h1:piDIRgjcK7u0HCL5pCA4e74qpK/jk3NiUoAHATVAmiI= +go.opentelemetry.io/otel/trace v1.16.0 h1:8JRpaObFoW0pxuVPapkgH8UhHQj+bJW8jJsCZEu5MQs= +go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20220827204233-334a2380cb91 h1:tnebWN09GYg9OLPss1KXj8txwZc6X6uMr6VFdcGNbHw= +golang.org/x/exp v0.0.0-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 h1:DdoeryqhaXp1LtT/emMP1BRJPHHKFi5akj/nbx/zNTA= +google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= +google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97/go.mod h1:t1VqOqqvce95G3hIDCT5FeO3YUc6Q4Oe24L/+rNMxRk= +google.golang.org/grpc v1.55.0 h1:3Oj82/tFSCeUrRTg/5E/7d/W5A1tj6Ky1ABAuZuv5ag= +google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8= +google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/_examples/remote-activate/messages/build.sh b/_examples/remote-activate/messages/build.sh new file mode 100644 index 0000000000000000000000000000000000000000..7f4860098b75fc0c7ca705886c2c10e74e818d31 --- /dev/null +++ b/_examples/remote-activate/messages/build.sh @@ -0,0 +1,2 @@ +protoc -I="../../../actor" --go_out=. --go_opt=paths=source_relative --proto_path=. protos.proto + diff --git a/_examples/remote-activate/messages/protos.pb.go b/_examples/remote-activate/messages/protos.pb.go new file mode 100644 index 0000000000000000000000000000000000000000..ef16843e9b2c4c3bd1cd25208c706f478ad14901 --- /dev/null +++ b/_examples/remote-activate/messages/protos.pb.go @@ -0,0 +1,198 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc v3.19.1 +// source: protos.proto + +package messages + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type HelloRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *HelloRequest) Reset() { + *x = HelloRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *HelloRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*HelloRequest) ProtoMessage() {} + +func (x *HelloRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use HelloRequest.ProtoReflect.Descriptor instead. +func (*HelloRequest) Descriptor() ([]byte, []int) { + return file_protos_proto_rawDescGZIP(), []int{0} +} + +type HelloResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Message string `protobuf:"bytes,1,opt,name=Message,proto3" json:"Message,omitempty"` +} + +func (x *HelloResponse) Reset() { + *x = HelloResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *HelloResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*HelloResponse) ProtoMessage() {} + +func (x *HelloResponse) ProtoReflect() protoreflect.Message { + mi := &file_protos_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use HelloResponse.ProtoReflect.Descriptor instead. +func (*HelloResponse) Descriptor() ([]byte, []int) { + return file_protos_proto_rawDescGZIP(), []int{1} +} + +func (x *HelloResponse) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + +var File_protos_proto protoreflect.FileDescriptor + +var file_protos_proto_rawDesc = []byte{ + 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, + 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x22, 0x0e, 0x0a, 0x0c, 0x48, 0x65, 0x6c, 0x6c, + 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x29, 0x0a, 0x0d, 0x48, 0x65, 0x6c, 0x6c, + 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x4d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x4d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x42, 0x46, 0x5a, 0x44, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x61, 0x73, 0x79, 0x6e, 0x6b, 0x72, 0x6f, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x61, 0x63, 0x74, 0x6f, 0x72, 0x2d, 0x67, 0x6f, 0x2f, 0x5f, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, + 0x65, 0x73, 0x2f, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x2d, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, + 0x74, 0x65, 0x2f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x33, +} + +var ( + file_protos_proto_rawDescOnce sync.Once + file_protos_proto_rawDescData = file_protos_proto_rawDesc +) + +func file_protos_proto_rawDescGZIP() []byte { + file_protos_proto_rawDescOnce.Do(func() { + file_protos_proto_rawDescData = protoimpl.X.CompressGZIP(file_protos_proto_rawDescData) + }) + return file_protos_proto_rawDescData +} + +var file_protos_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_protos_proto_goTypes = []interface{}{ + (*HelloRequest)(nil), // 0: messages.HelloRequest + (*HelloResponse)(nil), // 1: messages.HelloResponse +} +var file_protos_proto_depIdxs = []int32{ + 0, // [0:0] is the sub-list for method output_type + 0, // [0:0] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_protos_proto_init() } +func file_protos_proto_init() { + if File_protos_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_protos_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*HelloRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*HelloResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_protos_proto_rawDesc, + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_protos_proto_goTypes, + DependencyIndexes: file_protos_proto_depIdxs, + MessageInfos: file_protos_proto_msgTypes, + }.Build() + File_protos_proto = out.File + file_protos_proto_rawDesc = nil + file_protos_proto_goTypes = nil + file_protos_proto_depIdxs = nil +} diff --git a/_examples/remote-activate/messages/protos.proto b/_examples/remote-activate/messages/protos.proto new file mode 100644 index 0000000000000000000000000000000000000000..4c902f7f1c0af5f2d2d957113953a41fb6b79bb8 --- /dev/null +++ b/_examples/remote-activate/messages/protos.proto @@ -0,0 +1,8 @@ +syntax = "proto3"; +package messages; +option go_package = "gitee.com/simplexyz/simpleactor-go/_examples/remote-activate/messages"; + +message HelloRequest {} +message HelloResponse { + string Message = 1; +} \ No newline at end of file diff --git a/_examples/remote-activate/node1/main.go b/_examples/remote-activate/node1/main.go new file mode 100644 index 0000000000000000000000000000000000000000..fecf532996a6233e4fc7b476a14fc9ab94a1e984 --- /dev/null +++ b/_examples/remote-activate/node1/main.go @@ -0,0 +1,45 @@ +package main + +import ( + "fmt" + "time" + + "remoteactivate/messages" + + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/remote" + console "github.com/asynkron/goconsole" +) + +func main() { + timeout := 5 * time.Second + + system := actor.NewActorSystem() + remoteConfig := remote.Configure("127.0.0.1", 8081) + r := remote.NewRemote(system, remoteConfig) + r.Start() + + rootContext := system.Root + + // props := actor. + // PropsFromFunc(func(context actor.Context) { + // switch context.Message().(type) { + // case *actor.Started: + // log.Printf("actor started " + context.Self().String()) + // case *messages.HelloRequest: + // log.Println("Received pong from sender") + // message := &messages.HelloResponse{Message: "hello from remote"} + // context.Request(context.Sender(), message) + // } + // }) + + pidResp, _ := r.SpawnNamed("127.0.0.1:8080", "world", "hello", timeout) + + res, _ := rootContext.RequestFuture(pidResp.Pid, &messages.HelloRequest{}, timeout).Result() + + response := res.(*messages.HelloResponse) + + fmt.Printf("Response from remote %v, sender=%s", response.Message, pidResp.Pid.String()) + + console.ReadLine() +} diff --git a/_examples/remote-activate/node2/main.go b/_examples/remote-activate/node2/main.go new file mode 100644 index 0000000000000000000000000000000000000000..7a39f4ce1997102e110f2b4897a99abe638c2ab8 --- /dev/null +++ b/_examples/remote-activate/node2/main.go @@ -0,0 +1,39 @@ +package main + +import ( + "runtime" + + "remoteactivate/messages" + + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/remote" + console "github.com/asynkron/goconsole" +) + +type helloActor struct{} + +func (*helloActor) Receive(ctx actor.Context) { + switch ctx.Message().(type) { + case *messages.HelloRequest: + ctx.Respond(&messages.HelloResponse{ + Message: "Hello from remote node", + }) + } +} + +func newHelloActor() actor.Actor { + return &helloActor{} +} + +func main() { + runtime.GOMAXPROCS(runtime.NumCPU()) + + system := actor.NewActorSystem() + remoteConfig := remote.Configure("127.0.0.1", 8080, + remote.WithKinds(remote.NewKind("hello", actor.PropsFromProducer(newHelloActor)))) + + remoter := remote.NewRemote(system, remoteConfig) + remoter.Start() + + console.ReadLine() +} diff --git a/_examples/remote-advertised-address/Makefile b/_examples/remote-advertised-address/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..a4c35f1d4ba7a4575c1c4a92937addde15c2eec2 --- /dev/null +++ b/_examples/remote-advertised-address/Makefile @@ -0,0 +1,11 @@ +start: + tmux new-session -d -s eg + tmux split-window -t "eg:0" -v + tmux send-keys -t "eg:0.0" "go run node2/main.go" Enter + tmux send-keys -t "eg:0.1" "go run node1/main.go" Enter + tmux attach -t eg + tmux kill-session -t eg + + +stop: + tmux kill-session -t eg diff --git a/_examples/remote-advertised-address/go.mod b/_examples/remote-advertised-address/go.mod new file mode 100644 index 0000000000000000000000000000000000000000..325a43369f10a0d7511c560b07328ef4893c5be2 --- /dev/null +++ b/_examples/remote-advertised-address/go.mod @@ -0,0 +1,46 @@ +module remoteadvertisedaddress + +go 1.21 + +toolchain go1.21.2 + +replace gitee.com/simplexyz/simpleactor-go => ../.. + +require ( + gitee.com/simplexyz/simpleactor-go v0.0.0-00010101000000-000000000000 + github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716 + google.golang.org/protobuf v1.31.0 +) + +require ( + github.com/Workiva/go-datastructures v1.1.1 // indirect + github.com/asynkron/gofun v0.0.0-20220329210725-34fed760f4c2 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/emirpasic/gods v1.18.1 // indirect + github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/lithammer/shortuuid/v4 v4.0.0 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/orcaman/concurrent-map v1.0.0 // indirect + github.com/prometheus/client_golang v1.16.0 // indirect + github.com/prometheus/client_model v0.4.0 // indirect + github.com/prometheus/common v0.42.0 // indirect + github.com/prometheus/procfs v0.10.1 // indirect + github.com/twmb/murmur3 v1.1.6 // indirect + go.opentelemetry.io/otel v1.16.0 // indirect + go.opentelemetry.io/otel/exporters/prometheus v0.39.0 // indirect + go.opentelemetry.io/otel/metric v1.16.0 // indirect + go.opentelemetry.io/otel/sdk v1.16.0 // indirect + go.opentelemetry.io/otel/sdk/metric v0.39.0 // indirect + go.opentelemetry.io/otel/trace v1.16.0 // indirect + golang.org/x/exp v0.0.0-20220518171630-0b5c67f07fdf // indirect + golang.org/x/net v0.17.0 // indirect + golang.org/x/sys v0.13.0 // indirect + golang.org/x/text v0.13.0 // indirect + google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97 // indirect + google.golang.org/grpc v1.58.3 // indirect +) diff --git a/_examples/remote-advertised-address/go.sum b/_examples/remote-advertised-address/go.sum new file mode 100644 index 0000000000000000000000000000000000000000..a2b3557700cd9f38d7fffd4be0c6b4e677683c36 --- /dev/null +++ b/_examples/remote-advertised-address/go.sum @@ -0,0 +1,125 @@ +github.com/Workiva/go-datastructures v1.1.1 h1:9G5u1UqKt6ABseAffHGNfbNQd7omRlWE5QaxNruzhE0= +github.com/Workiva/go-datastructures v1.1.1/go.mod h1:1yZL+zfsztete+ePzZz/Zb1/t5BnDuE2Ya2MMGhzP6A= +github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716 h1:SgyG4sXkrlalMoCfp20LiNPNhfJS7ez3opNdtihIxPc= +github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716/go.mod h1:/zSlF0T2ArAsTG6SVu8d8qlK+19jjudjA3wWsxnGFHg= +github.com/asynkron/gofun v0.0.0-20220329210725-34fed760f4c2 h1:jEsFZ9d/ieJGVrx3fSPi8oe/qv21fRmyUL5cS3ZEn5A= +github.com/asynkron/gofun v0.0.0-20220329210725-34fed760f4c2/go.mod h1:5GMOSqaYxNWwuVRWyampTPJEntwz7Mj9J8v1a7gSU2E= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/lithammer/shortuuid/v4 v4.0.0 h1:QRbbVkfgNippHOS8PXDkti4NaWeyYfcBTHtw7k08o4c= +github.com/lithammer/shortuuid/v4 v4.0.0/go.mod h1:Zs8puNcrvf2rV9rTH51ZLLcj7ZXqQI3lv67aw4KiB1Y= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/orcaman/concurrent-map v1.0.0 h1:I/2A2XPCb4IuQWcQhBhSwGfiuybl/J0ev9HDbW65HOY= +github.com/orcaman/concurrent-map v1.0.0/go.mod h1:Lu3tH6HLW3feq74c2GC+jIMS/K2CFcDWnWD9XkenwhI= +github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= +github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= +github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= +github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= +github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= +github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= +github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/tinylib/msgp v1.1.5/go.mod h1:eQsjooMTnV42mHu917E26IogZ2930nFyBQdofk10Udg= +github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q= +github.com/twmb/murmur3 v1.1.6 h1:mqrRot1BRxm+Yct+vavLMou2/iJt0tNVTTC0QoIjaZg= +github.com/twmb/murmur3 v1.1.6/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s= +go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4= +go.opentelemetry.io/otel/exporters/prometheus v0.39.0 h1:whAaiHxOatgtKd+w0dOi//1KUxj3KoPINZdtDaDj3IA= +go.opentelemetry.io/otel/exporters/prometheus v0.39.0/go.mod h1:4jo5Q4CROlCpSPsXLhymi+LYrDXd2ObU5wbKayfZs7Y= +go.opentelemetry.io/otel/metric v1.16.0 h1:RbrpwVG1Hfv85LgnZ7+txXioPDoh6EdbZHo26Q3hqOo= +go.opentelemetry.io/otel/metric v1.16.0/go.mod h1:QE47cpOmkwipPiefDwo2wDzwJrlfxxNYodqc4xnGCo4= +go.opentelemetry.io/otel/sdk v1.16.0 h1:Z1Ok1YsijYL0CSJpHt4cS3wDDh7p572grzNrBMiMWgE= +go.opentelemetry.io/otel/sdk v1.16.0/go.mod h1:tMsIuKXuuIWPBAOrH+eHtvhTL+SntFtXF9QD68aP6p4= +go.opentelemetry.io/otel/sdk/metric v0.39.0 h1:Kun8i1eYf48kHH83RucG93ffz0zGV1sh46FAScOTuDI= +go.opentelemetry.io/otel/sdk/metric v0.39.0/go.mod h1:piDIRgjcK7u0HCL5pCA4e74qpK/jk3NiUoAHATVAmiI= +go.opentelemetry.io/otel/trace v1.16.0 h1:8JRpaObFoW0pxuVPapkgH8UhHQj+bJW8jJsCZEu5MQs= +go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20220518171630-0b5c67f07fdf h1:oXVg4h2qJDd9htKxb5SCpFBHLipW6hXmL3qpUixS2jw= +golang.org/x/exp v0.0.0-20220518171630-0b5c67f07fdf/go.mod h1:yh0Ynu2b5ZUe3MQfp2nM0ecK7wsgouWTDN0FNeJuIys= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 h1:DdoeryqhaXp1LtT/emMP1BRJPHHKFi5akj/nbx/zNTA= +google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= +google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97/go.mod h1:t1VqOqqvce95G3hIDCT5FeO3YUc6Q4Oe24L/+rNMxRk= +google.golang.org/grpc v1.55.0 h1:3Oj82/tFSCeUrRTg/5E/7d/W5A1tj6Ky1ABAuZuv5ag= +google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8= +google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/_examples/remote-advertised-address/messages/build.sh b/_examples/remote-advertised-address/messages/build.sh new file mode 100644 index 0000000000000000000000000000000000000000..7f4860098b75fc0c7ca705886c2c10e74e818d31 --- /dev/null +++ b/_examples/remote-advertised-address/messages/build.sh @@ -0,0 +1,2 @@ +protoc -I="../../../actor" --go_out=. --go_opt=paths=source_relative --proto_path=. protos.proto + diff --git a/_examples/remote-advertised-address/messages/protos.pb.go b/_examples/remote-advertised-address/messages/protos.pb.go new file mode 100644 index 0000000000000000000000000000000000000000..9854fb27053cdea9434744975c024ff76e99c66b --- /dev/null +++ b/_examples/remote-advertised-address/messages/protos.pb.go @@ -0,0 +1,305 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc v3.19.1 +// source: protos.proto + +package messages + +import ( + actor "gitee.com/simplexyz/simpleactor-go/actor" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type Start struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *Start) Reset() { + *x = Start{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Start) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Start) ProtoMessage() {} + +func (x *Start) ProtoReflect() protoreflect.Message { + mi := &file_protos_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Start.ProtoReflect.Descriptor instead. +func (*Start) Descriptor() ([]byte, []int) { + return file_protos_proto_rawDescGZIP(), []int{0} +} + +type StartRemote struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Sender *actor.PID `protobuf:"bytes,1,opt,name=Sender,proto3" json:"Sender,omitempty"` +} + +func (x *StartRemote) Reset() { + *x = StartRemote{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StartRemote) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StartRemote) ProtoMessage() {} + +func (x *StartRemote) ProtoReflect() protoreflect.Message { + mi := &file_protos_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StartRemote.ProtoReflect.Descriptor instead. +func (*StartRemote) Descriptor() ([]byte, []int) { + return file_protos_proto_rawDescGZIP(), []int{1} +} + +func (x *StartRemote) GetSender() *actor.PID { + if x != nil { + return x.Sender + } + return nil +} + +type Ping struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *Ping) Reset() { + *x = Ping{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Ping) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Ping) ProtoMessage() {} + +func (x *Ping) ProtoReflect() protoreflect.Message { + mi := &file_protos_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Ping.ProtoReflect.Descriptor instead. +func (*Ping) Descriptor() ([]byte, []int) { + return file_protos_proto_rawDescGZIP(), []int{2} +} + +type Pong struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *Pong) Reset() { + *x = Pong{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Pong) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Pong) ProtoMessage() {} + +func (x *Pong) ProtoReflect() protoreflect.Message { + mi := &file_protos_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Pong.ProtoReflect.Descriptor instead. +func (*Pong) Descriptor() ([]byte, []int) { + return file_protos_proto_rawDescGZIP(), []int{3} +} + +var File_protos_proto protoreflect.FileDescriptor + +var file_protos_proto_rawDesc = []byte{ + 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, + 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x1a, 0x0b, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x07, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x72, 0x74, 0x22, 0x31, + 0x0a, 0x0b, 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x12, 0x22, 0x0a, + 0x06, 0x53, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, + 0x61, 0x63, 0x74, 0x6f, 0x72, 0x2e, 0x50, 0x49, 0x44, 0x52, 0x06, 0x53, 0x65, 0x6e, 0x64, 0x65, + 0x72, 0x22, 0x06, 0x0a, 0x04, 0x50, 0x69, 0x6e, 0x67, 0x22, 0x06, 0x0a, 0x04, 0x50, 0x6f, 0x6e, + 0x67, 0x42, 0x50, 0x5a, 0x4e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x61, 0x73, 0x79, 0x6e, 0x6b, 0x72, 0x6f, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x61, 0x63, + 0x74, 0x6f, 0x72, 0x2d, 0x67, 0x6f, 0x2f, 0x5f, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, + 0x2f, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x2d, 0x61, 0x64, 0x76, 0x65, 0x72, 0x74, 0x69, 0x73, + 0x65, 0x64, 0x2d, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x2f, 0x6d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_protos_proto_rawDescOnce sync.Once + file_protos_proto_rawDescData = file_protos_proto_rawDesc +) + +func file_protos_proto_rawDescGZIP() []byte { + file_protos_proto_rawDescOnce.Do(func() { + file_protos_proto_rawDescData = protoimpl.X.CompressGZIP(file_protos_proto_rawDescData) + }) + return file_protos_proto_rawDescData +} + +var file_protos_proto_msgTypes = make([]protoimpl.MessageInfo, 4) +var file_protos_proto_goTypes = []interface{}{ + (*Start)(nil), // 0: messages.Start + (*StartRemote)(nil), // 1: messages.StartRemote + (*Ping)(nil), // 2: messages.Ping + (*Pong)(nil), // 3: messages.Pong + (*actor.PID)(nil), // 4: actor.PID +} +var file_protos_proto_depIdxs = []int32{ + 4, // 0: messages.StartRemote.Sender:type_name -> actor.PID + 1, // [1:1] is the sub-list for method output_type + 1, // [1:1] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name +} + +func init() { file_protos_proto_init() } +func file_protos_proto_init() { + if File_protos_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_protos_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Start); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StartRemote); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Ping); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Pong); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_protos_proto_rawDesc, + NumEnums: 0, + NumMessages: 4, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_protos_proto_goTypes, + DependencyIndexes: file_protos_proto_depIdxs, + MessageInfos: file_protos_proto_msgTypes, + }.Build() + File_protos_proto = out.File + file_protos_proto_rawDesc = nil + file_protos_proto_goTypes = nil + file_protos_proto_depIdxs = nil +} diff --git a/_examples/remote-advertised-address/messages/protos.proto b/_examples/remote-advertised-address/messages/protos.proto new file mode 100644 index 0000000000000000000000000000000000000000..2097f6b76c4957bd5dcc51179ed418ed3e444b8d --- /dev/null +++ b/_examples/remote-advertised-address/messages/protos.proto @@ -0,0 +1,11 @@ +syntax = "proto3"; +package messages; +option go_package = "gitee.com/simplexyz/simpleactor-go/_examples/remote-advertised-address/messages"; +import "actor.proto"; + +message Start {} +message StartRemote { + actor.PID Sender = 1; +} +message Ping {} +message Pong {} diff --git a/_examples/remote-advertised-address/node1/main.go b/_examples/remote-advertised-address/node1/main.go new file mode 100644 index 0000000000000000000000000000000000000000..bb26829216d1e73838b8a6f510a3848dc7a838c3 --- /dev/null +++ b/_examples/remote-advertised-address/node1/main.go @@ -0,0 +1,40 @@ +package main + +import ( + "log" + + "remoteadvertisedaddress/messages" + + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/remote" + console "github.com/asynkron/goconsole" +) + +var ( + system = actor.NewActorSystem() + rootContext = system.Root +) + +func main() { + cfg := remote.Configure("0.0.0.0", 8081, remote.WithAdvertisedHost("localhost:8081")) + r := remote.NewRemote(system, cfg) + r.Start() + + remotePid := actor.NewPID("127.0.0.1:8080", "remote") + + props := actor. + PropsFromFunc(func(context actor.Context) { + switch context.Message().(type) { + case *actor.Started: + message := &messages.Ping{} + context.Request(remotePid, message) + + case *messages.Pong: + log.Println("Received pong from sender") + } + }) + + rootContext.Spawn(props) + + console.ReadLine() +} diff --git a/_examples/remote-advertised-address/node2/main.go b/_examples/remote-advertised-address/node2/main.go new file mode 100644 index 0000000000000000000000000000000000000000..21850e00574a35a0cf0510b2a7a2abd8f7e7d2eb --- /dev/null +++ b/_examples/remote-advertised-address/node2/main.go @@ -0,0 +1,36 @@ +package main + +import ( + "fmt" + + "remoteadvertisedaddress/messages" + + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/remote" + console "github.com/asynkron/goconsole" +) + +var ( + system = actor.NewActorSystem() + rootContext = system.Root +) + +func main() { + cfg := remote.Configure("0.0.0.0", 8080, remote.WithAdvertisedHost("localhost:8080")) + r := remote.NewRemote(system, cfg) + r.Start() + + props := actor. + PropsFromFunc( + func(context actor.Context) { + switch context.Message().(type) { + case *messages.Ping: + fmt.Println("Received ping from sender with address: " + context.Sender().Address) + context.Respond(&messages.Pong{}) + } + }) + + rootContext.SpawnNamed(props, "remote") + + console.ReadLine() +} diff --git a/_examples/remote-benchmark/Makefile b/_examples/remote-benchmark/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..a4c35f1d4ba7a4575c1c4a92937addde15c2eec2 --- /dev/null +++ b/_examples/remote-benchmark/Makefile @@ -0,0 +1,11 @@ +start: + tmux new-session -d -s eg + tmux split-window -t "eg:0" -v + tmux send-keys -t "eg:0.0" "go run node2/main.go" Enter + tmux send-keys -t "eg:0.1" "go run node1/main.go" Enter + tmux attach -t eg + tmux kill-session -t eg + + +stop: + tmux kill-session -t eg diff --git a/_examples/remote-benchmark/go.mod b/_examples/remote-benchmark/go.mod new file mode 100644 index 0000000000000000000000000000000000000000..2e4dc1b1619a55126b630e0c9cc55bce3ffa9f2e --- /dev/null +++ b/_examples/remote-benchmark/go.mod @@ -0,0 +1,46 @@ +module remotebenchmark + +go 1.21 + +toolchain go1.21.2 + +replace gitee.com/simplexyz/simpleactor-go => ../.. + +require ( + gitee.com/simplexyz/simpleactor-go v0.0.0-00010101000000-000000000000 + github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716 + google.golang.org/protobuf v1.31.0 +) + +require ( + github.com/Workiva/go-datastructures v1.1.1 // indirect + github.com/asynkron/gofun v0.0.0-20220329210725-34fed760f4c2 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/emirpasic/gods v1.18.1 // indirect + github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/lithammer/shortuuid/v4 v4.0.0 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/orcaman/concurrent-map v1.0.0 // indirect + github.com/prometheus/client_golang v1.16.0 // indirect + github.com/prometheus/client_model v0.4.0 // indirect + github.com/prometheus/common v0.42.0 // indirect + github.com/prometheus/procfs v0.10.1 // indirect + github.com/twmb/murmur3 v1.1.6 // indirect + go.opentelemetry.io/otel v1.16.0 // indirect + go.opentelemetry.io/otel/exporters/prometheus v0.39.0 // indirect + go.opentelemetry.io/otel/metric v1.16.0 // indirect + go.opentelemetry.io/otel/sdk v1.16.0 // indirect + go.opentelemetry.io/otel/sdk/metric v0.39.0 // indirect + go.opentelemetry.io/otel/trace v1.16.0 // indirect + golang.org/x/exp v0.0.0-20220518171630-0b5c67f07fdf // indirect + golang.org/x/net v0.17.0 // indirect + golang.org/x/sys v0.13.0 // indirect + golang.org/x/text v0.13.0 // indirect + google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97 // indirect + google.golang.org/grpc v1.58.3 // indirect +) diff --git a/_examples/remote-benchmark/go.sum b/_examples/remote-benchmark/go.sum new file mode 100644 index 0000000000000000000000000000000000000000..a2b3557700cd9f38d7fffd4be0c6b4e677683c36 --- /dev/null +++ b/_examples/remote-benchmark/go.sum @@ -0,0 +1,125 @@ +github.com/Workiva/go-datastructures v1.1.1 h1:9G5u1UqKt6ABseAffHGNfbNQd7omRlWE5QaxNruzhE0= +github.com/Workiva/go-datastructures v1.1.1/go.mod h1:1yZL+zfsztete+ePzZz/Zb1/t5BnDuE2Ya2MMGhzP6A= +github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716 h1:SgyG4sXkrlalMoCfp20LiNPNhfJS7ez3opNdtihIxPc= +github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716/go.mod h1:/zSlF0T2ArAsTG6SVu8d8qlK+19jjudjA3wWsxnGFHg= +github.com/asynkron/gofun v0.0.0-20220329210725-34fed760f4c2 h1:jEsFZ9d/ieJGVrx3fSPi8oe/qv21fRmyUL5cS3ZEn5A= +github.com/asynkron/gofun v0.0.0-20220329210725-34fed760f4c2/go.mod h1:5GMOSqaYxNWwuVRWyampTPJEntwz7Mj9J8v1a7gSU2E= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/lithammer/shortuuid/v4 v4.0.0 h1:QRbbVkfgNippHOS8PXDkti4NaWeyYfcBTHtw7k08o4c= +github.com/lithammer/shortuuid/v4 v4.0.0/go.mod h1:Zs8puNcrvf2rV9rTH51ZLLcj7ZXqQI3lv67aw4KiB1Y= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/orcaman/concurrent-map v1.0.0 h1:I/2A2XPCb4IuQWcQhBhSwGfiuybl/J0ev9HDbW65HOY= +github.com/orcaman/concurrent-map v1.0.0/go.mod h1:Lu3tH6HLW3feq74c2GC+jIMS/K2CFcDWnWD9XkenwhI= +github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= +github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= +github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= +github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= +github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= +github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= +github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/tinylib/msgp v1.1.5/go.mod h1:eQsjooMTnV42mHu917E26IogZ2930nFyBQdofk10Udg= +github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q= +github.com/twmb/murmur3 v1.1.6 h1:mqrRot1BRxm+Yct+vavLMou2/iJt0tNVTTC0QoIjaZg= +github.com/twmb/murmur3 v1.1.6/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s= +go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4= +go.opentelemetry.io/otel/exporters/prometheus v0.39.0 h1:whAaiHxOatgtKd+w0dOi//1KUxj3KoPINZdtDaDj3IA= +go.opentelemetry.io/otel/exporters/prometheus v0.39.0/go.mod h1:4jo5Q4CROlCpSPsXLhymi+LYrDXd2ObU5wbKayfZs7Y= +go.opentelemetry.io/otel/metric v1.16.0 h1:RbrpwVG1Hfv85LgnZ7+txXioPDoh6EdbZHo26Q3hqOo= +go.opentelemetry.io/otel/metric v1.16.0/go.mod h1:QE47cpOmkwipPiefDwo2wDzwJrlfxxNYodqc4xnGCo4= +go.opentelemetry.io/otel/sdk v1.16.0 h1:Z1Ok1YsijYL0CSJpHt4cS3wDDh7p572grzNrBMiMWgE= +go.opentelemetry.io/otel/sdk v1.16.0/go.mod h1:tMsIuKXuuIWPBAOrH+eHtvhTL+SntFtXF9QD68aP6p4= +go.opentelemetry.io/otel/sdk/metric v0.39.0 h1:Kun8i1eYf48kHH83RucG93ffz0zGV1sh46FAScOTuDI= +go.opentelemetry.io/otel/sdk/metric v0.39.0/go.mod h1:piDIRgjcK7u0HCL5pCA4e74qpK/jk3NiUoAHATVAmiI= +go.opentelemetry.io/otel/trace v1.16.0 h1:8JRpaObFoW0pxuVPapkgH8UhHQj+bJW8jJsCZEu5MQs= +go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20220518171630-0b5c67f07fdf h1:oXVg4h2qJDd9htKxb5SCpFBHLipW6hXmL3qpUixS2jw= +golang.org/x/exp v0.0.0-20220518171630-0b5c67f07fdf/go.mod h1:yh0Ynu2b5ZUe3MQfp2nM0ecK7wsgouWTDN0FNeJuIys= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 h1:DdoeryqhaXp1LtT/emMP1BRJPHHKFi5akj/nbx/zNTA= +google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= +google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97/go.mod h1:t1VqOqqvce95G3hIDCT5FeO3YUc6Q4Oe24L/+rNMxRk= +google.golang.org/grpc v1.55.0 h1:3Oj82/tFSCeUrRTg/5E/7d/W5A1tj6Ky1ABAuZuv5ag= +google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8= +google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/_examples/remote-benchmark/messages/build.sh b/_examples/remote-benchmark/messages/build.sh new file mode 100644 index 0000000000000000000000000000000000000000..7f4860098b75fc0c7ca705886c2c10e74e818d31 --- /dev/null +++ b/_examples/remote-benchmark/messages/build.sh @@ -0,0 +1,2 @@ +protoc -I="../../../actor" --go_out=. --go_opt=paths=source_relative --proto_path=. protos.proto + diff --git a/_examples/remote-benchmark/messages/protos.pb.go b/_examples/remote-benchmark/messages/protos.pb.go new file mode 100644 index 0000000000000000000000000000000000000000..96f87c0d7a29fae9e40cf7bde4401f98521e9353 --- /dev/null +++ b/_examples/remote-benchmark/messages/protos.pb.go @@ -0,0 +1,305 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc v3.19.1 +// source: protos.proto + +package messages + +import ( + actor "gitee.com/simplexyz/simpleactor-go/actor" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type Start struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *Start) Reset() { + *x = Start{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Start) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Start) ProtoMessage() {} + +func (x *Start) ProtoReflect() protoreflect.Message { + mi := &file_protos_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Start.ProtoReflect.Descriptor instead. +func (*Start) Descriptor() ([]byte, []int) { + return file_protos_proto_rawDescGZIP(), []int{0} +} + +type StartRemote struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Sender *actor.PID `protobuf:"bytes,1,opt,name=Sender,proto3" json:"Sender,omitempty"` +} + +func (x *StartRemote) Reset() { + *x = StartRemote{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StartRemote) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StartRemote) ProtoMessage() {} + +func (x *StartRemote) ProtoReflect() protoreflect.Message { + mi := &file_protos_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StartRemote.ProtoReflect.Descriptor instead. +func (*StartRemote) Descriptor() ([]byte, []int) { + return file_protos_proto_rawDescGZIP(), []int{1} +} + +func (x *StartRemote) GetSender() *actor.PID { + if x != nil { + return x.Sender + } + return nil +} + +type Ping struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *Ping) Reset() { + *x = Ping{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Ping) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Ping) ProtoMessage() {} + +func (x *Ping) ProtoReflect() protoreflect.Message { + mi := &file_protos_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Ping.ProtoReflect.Descriptor instead. +func (*Ping) Descriptor() ([]byte, []int) { + return file_protos_proto_rawDescGZIP(), []int{2} +} + +type Pong struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *Pong) Reset() { + *x = Pong{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Pong) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Pong) ProtoMessage() {} + +func (x *Pong) ProtoReflect() protoreflect.Message { + mi := &file_protos_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Pong.ProtoReflect.Descriptor instead. +func (*Pong) Descriptor() ([]byte, []int) { + return file_protos_proto_rawDescGZIP(), []int{3} +} + +var File_protos_proto protoreflect.FileDescriptor + +var file_protos_proto_rawDesc = []byte{ + 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, + 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x1a, 0x0b, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x07, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x72, 0x74, 0x22, 0x31, + 0x0a, 0x0b, 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x12, 0x22, 0x0a, + 0x06, 0x53, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, + 0x61, 0x63, 0x74, 0x6f, 0x72, 0x2e, 0x50, 0x49, 0x44, 0x52, 0x06, 0x53, 0x65, 0x6e, 0x64, 0x65, + 0x72, 0x22, 0x06, 0x0a, 0x04, 0x50, 0x69, 0x6e, 0x67, 0x22, 0x06, 0x0a, 0x04, 0x50, 0x6f, 0x6e, + 0x67, 0x42, 0x47, 0x5a, 0x45, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x61, 0x73, 0x79, 0x6e, 0x6b, 0x72, 0x6f, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x61, 0x63, + 0x74, 0x6f, 0x72, 0x2d, 0x67, 0x6f, 0x2f, 0x5f, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, + 0x2f, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x2d, 0x62, 0x65, 0x6e, 0x63, 0x68, 0x6d, 0x61, 0x72, + 0x6b, 0x2f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, +} + +var ( + file_protos_proto_rawDescOnce sync.Once + file_protos_proto_rawDescData = file_protos_proto_rawDesc +) + +func file_protos_proto_rawDescGZIP() []byte { + file_protos_proto_rawDescOnce.Do(func() { + file_protos_proto_rawDescData = protoimpl.X.CompressGZIP(file_protos_proto_rawDescData) + }) + return file_protos_proto_rawDescData +} + +var file_protos_proto_msgTypes = make([]protoimpl.MessageInfo, 4) +var file_protos_proto_goTypes = []interface{}{ + (*Start)(nil), // 0: messages.Start + (*StartRemote)(nil), // 1: messages.StartRemote + (*Ping)(nil), // 2: messages.Ping + (*Pong)(nil), // 3: messages.Pong + (*actor.PID)(nil), // 4: actor.PID +} +var file_protos_proto_depIdxs = []int32{ + 4, // 0: messages.StartRemote.Sender:type_name -> actor.PID + 1, // [1:1] is the sub-list for method output_type + 1, // [1:1] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name +} + +func init() { file_protos_proto_init() } +func file_protos_proto_init() { + if File_protos_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_protos_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Start); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StartRemote); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Ping); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Pong); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_protos_proto_rawDesc, + NumEnums: 0, + NumMessages: 4, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_protos_proto_goTypes, + DependencyIndexes: file_protos_proto_depIdxs, + MessageInfos: file_protos_proto_msgTypes, + }.Build() + File_protos_proto = out.File + file_protos_proto_rawDesc = nil + file_protos_proto_goTypes = nil + file_protos_proto_depIdxs = nil +} diff --git a/_examples/remote-benchmark/messages/protos.proto b/_examples/remote-benchmark/messages/protos.proto new file mode 100644 index 0000000000000000000000000000000000000000..1328b5c58579e30be74ed7b4eb0a56aba9cfc7d4 --- /dev/null +++ b/_examples/remote-benchmark/messages/protos.proto @@ -0,0 +1,11 @@ +syntax = "proto3"; +package messages; +option go_package = "gitee.com/simplexyz/simpleactor-go/_examples/remote-benchmark/messages"; +import "actor.proto"; + +message Start {} +message StartRemote { + actor.PID Sender = 1; +} +message Ping {} +message Pong {} diff --git a/_examples/remote-benchmark/node1/main.go b/_examples/remote-benchmark/node1/main.go new file mode 100644 index 0000000000000000000000000000000000000000..cabe0c45405cb1fce025de84b750cf9ad7052cfa --- /dev/null +++ b/_examples/remote-benchmark/node1/main.go @@ -0,0 +1,128 @@ +package main + +import ( + "flag" + "log" + "os" + "runtime" + "runtime/pprof" + "sync" + "time" + + "remotebenchmark/messages" + + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/remote" + console "github.com/asynkron/goconsole" +) + +type localActor struct { + count int + wgStop *sync.WaitGroup + messageCount int +} + +func (state *localActor) Receive(context actor.Context) { + switch context.Message().(type) { + case *messages.Pong: + state.count++ + //if state.count%50000 == 0 { + // log.Println(state.count) + //} + if state.count == state.messageCount { + state.wgStop.Done() + } + } +} + +func newLocalActor(stop *sync.WaitGroup, messageCount int) actor.Producer { + return func() actor.Actor { + return &localActor{ + wgStop: stop, + messageCount: messageCount, + } + } +} + +var ( + cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file") + blockProfile = flag.String("blockprof", "", "execute contention profiling and save results here") +) + +func main() { + flag.Parse() + if *cpuprofile != "" { + f, err := os.Create(*cpuprofile) + if err != nil { + log.Fatal(err) + } + pprof.StartCPUProfile(f) + defer pprof.StopCPUProfile() + } + + // Check for lock contention profiling + if *blockProfile != "" { + prof, err := os.Create(*blockProfile) + if err != nil { + log.Fatal(err) + } + runtime.SetBlockProfileRate(1) + defer func() { + pprof.Lookup("block").WriteTo(prof, 0) + }() + } + + // runtime.GOMAXPROCS(runtime.NumCPU() * 1) + // runtime.GC() + + messageCount := 1000000 + // remote.DefaultSerializerID = 1 + system := actor.NewActorSystem() + r := remote.NewRemote(system, remote.Configure("127.0.0.1", 0 /*, remote.WithCallOptions(grpc.UseCompressor(gzip.Name))*/)) + r.Start() + + rootContext := system.Root + + run := true + go func() { + for run == true { + var wg sync.WaitGroup + props := actor. + PropsFromProducer(newLocalActor(&wg, messageCount), + actor.WithMailbox(actor.Bounded(1000000))) + + pid := rootContext.Spawn(props) + + pidResponse, err := r.Spawn("127.0.0.1:12000", "echo", time.Second*2000) + if err != nil || pidResponse.StatusCode != 0 { + rootContext.Stop(pid) + return + } + remotePid := pidResponse.Pid + msg := messages.StartRemote{Sender: pid} + rootContext.RequestFuture(remotePid, &msg, 5*time.Second).Wait() + wg.Add(1) + + start := time.Now() + log.Println("Starting to send") + + message := &messages.Ping{} + for i := 0; i < messageCount; i++ { + rootContext.Send(remotePid, message) + } + + wg.Wait() + elapsed := time.Since(start) + log.Printf("Elapsed %s", elapsed) + + x := int(float32(messageCount*2) / (float32(elapsed) / float32(time.Second))) + log.Printf("Msg per sec %v", x) + rootContext.Stop(remotePid) + rootContext.Stop(pid) + } + }() + console.ReadLine() + run = false + console.ReadLine() + r.Shutdown(true) +} diff --git a/_examples/remote-benchmark/node2/main.go b/_examples/remote-benchmark/node2/main.go new file mode 100644 index 0000000000000000000000000000000000000000..7e88e6029c98a932e6f64fece2f6711bdfb2bce0 --- /dev/null +++ b/_examples/remote-benchmark/node2/main.go @@ -0,0 +1,47 @@ +package main + +import ( + "log" + + "remotebenchmark/messages" + + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/remote" + console "github.com/asynkron/goconsole" +) + +type echoActor struct { + sender *actor.PID +} + +func (state *echoActor) Receive(context actor.Context) { + switch msg := context.Message().(type) { + case *messages.StartRemote: + log.Printf("Starting for %s", msg.Sender) + state.sender = msg.Sender + context.Respond(&messages.Start{}) + case *messages.Ping: + context.Send(state.sender, &messages.Pong{}) + } +} + +func main() { + // runtime.GOMAXPROCS(runtime.NumCPU() * 1) + // runtime.GC() + + props := actor. + PropsFromProducer(func() actor.Actor { return &echoActor{} }, + actor.WithMailbox(actor.Bounded(1000000))) + + system := actor.NewActorSystem() + r := remote.NewRemote(system, remote.Configure("127.0.0.1", 12000 /*, remote.WithCallOptions(grpc.UseCompressor(gzip.Name))*/)) + r.Register("echo", props) + r.Start() + + rootContext := system.Root + + rootContext.SpawnNamed(props, "remote") + + console.ReadLine() + r.Shutdown(true) +} diff --git a/_examples/remote-channels/Makefile b/_examples/remote-channels/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..0bb231412a50360c6f984c3a358922c50ce05e49 --- /dev/null +++ b/_examples/remote-channels/Makefile @@ -0,0 +1,11 @@ +start: + tmux new-session -d -s eg + tmux split-window -t "eg:0" -v + tmux send-keys -t "eg:0.0" "go run node1/main.go" Enter + tmux send-keys -t "eg:0.1" "go run node2/main.go" Enter + tmux attach -t eg + tmux kill-session -t eg + + +stop: + tmux kill-session -t eg diff --git a/_examples/remote-channels/go.mod b/_examples/remote-channels/go.mod new file mode 100644 index 0000000000000000000000000000000000000000..a27b3faf382c8cac46c1bea306024fd871b33bd2 --- /dev/null +++ b/_examples/remote-channels/go.mod @@ -0,0 +1,46 @@ +module distributedchannels + +go 1.21 + +toolchain go1.21.2 + +replace gitee.com/simplexyz/simpleactor-go => ../.. + +require ( + gitee.com/simplexyz/simpleactor-go v0.0.0-00010101000000-000000000000 + github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716 + google.golang.org/protobuf v1.31.0 +) + +require ( + github.com/Workiva/go-datastructures v1.1.1 // indirect + github.com/asynkron/gofun v0.0.0-20220329210725-34fed760f4c2 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/emirpasic/gods v1.18.1 // indirect + github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/lithammer/shortuuid/v4 v4.0.0 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/orcaman/concurrent-map v1.0.0 // indirect + github.com/prometheus/client_golang v1.16.0 // indirect + github.com/prometheus/client_model v0.4.0 // indirect + github.com/prometheus/common v0.42.0 // indirect + github.com/prometheus/procfs v0.10.1 // indirect + github.com/twmb/murmur3 v1.1.6 // indirect + go.opentelemetry.io/otel v1.16.0 // indirect + go.opentelemetry.io/otel/exporters/prometheus v0.39.0 // indirect + go.opentelemetry.io/otel/metric v1.16.0 // indirect + go.opentelemetry.io/otel/sdk v1.16.0 // indirect + go.opentelemetry.io/otel/sdk/metric v0.39.0 // indirect + go.opentelemetry.io/otel/trace v1.16.0 // indirect + golang.org/x/exp v0.0.0-20220827204233-334a2380cb91 // indirect + golang.org/x/net v0.17.0 // indirect + golang.org/x/sys v0.13.0 // indirect + golang.org/x/text v0.13.0 // indirect + google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97 // indirect + google.golang.org/grpc v1.58.3 // indirect +) diff --git a/_examples/remote-channels/go.sum b/_examples/remote-channels/go.sum new file mode 100644 index 0000000000000000000000000000000000000000..af32feca5fbf879242aa12c72a2d4e3f94331558 --- /dev/null +++ b/_examples/remote-channels/go.sum @@ -0,0 +1,125 @@ +github.com/Workiva/go-datastructures v1.1.1 h1:9G5u1UqKt6ABseAffHGNfbNQd7omRlWE5QaxNruzhE0= +github.com/Workiva/go-datastructures v1.1.1/go.mod h1:1yZL+zfsztete+ePzZz/Zb1/t5BnDuE2Ya2MMGhzP6A= +github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716 h1:SgyG4sXkrlalMoCfp20LiNPNhfJS7ez3opNdtihIxPc= +github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716/go.mod h1:/zSlF0T2ArAsTG6SVu8d8qlK+19jjudjA3wWsxnGFHg= +github.com/asynkron/gofun v0.0.0-20220329210725-34fed760f4c2 h1:jEsFZ9d/ieJGVrx3fSPi8oe/qv21fRmyUL5cS3ZEn5A= +github.com/asynkron/gofun v0.0.0-20220329210725-34fed760f4c2/go.mod h1:5GMOSqaYxNWwuVRWyampTPJEntwz7Mj9J8v1a7gSU2E= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/lithammer/shortuuid/v4 v4.0.0 h1:QRbbVkfgNippHOS8PXDkti4NaWeyYfcBTHtw7k08o4c= +github.com/lithammer/shortuuid/v4 v4.0.0/go.mod h1:Zs8puNcrvf2rV9rTH51ZLLcj7ZXqQI3lv67aw4KiB1Y= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/orcaman/concurrent-map v1.0.0 h1:I/2A2XPCb4IuQWcQhBhSwGfiuybl/J0ev9HDbW65HOY= +github.com/orcaman/concurrent-map v1.0.0/go.mod h1:Lu3tH6HLW3feq74c2GC+jIMS/K2CFcDWnWD9XkenwhI= +github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= +github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= +github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= +github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= +github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= +github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= +github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/tinylib/msgp v1.1.5/go.mod h1:eQsjooMTnV42mHu917E26IogZ2930nFyBQdofk10Udg= +github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q= +github.com/twmb/murmur3 v1.1.6 h1:mqrRot1BRxm+Yct+vavLMou2/iJt0tNVTTC0QoIjaZg= +github.com/twmb/murmur3 v1.1.6/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s= +go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4= +go.opentelemetry.io/otel/exporters/prometheus v0.39.0 h1:whAaiHxOatgtKd+w0dOi//1KUxj3KoPINZdtDaDj3IA= +go.opentelemetry.io/otel/exporters/prometheus v0.39.0/go.mod h1:4jo5Q4CROlCpSPsXLhymi+LYrDXd2ObU5wbKayfZs7Y= +go.opentelemetry.io/otel/metric v1.16.0 h1:RbrpwVG1Hfv85LgnZ7+txXioPDoh6EdbZHo26Q3hqOo= +go.opentelemetry.io/otel/metric v1.16.0/go.mod h1:QE47cpOmkwipPiefDwo2wDzwJrlfxxNYodqc4xnGCo4= +go.opentelemetry.io/otel/sdk v1.16.0 h1:Z1Ok1YsijYL0CSJpHt4cS3wDDh7p572grzNrBMiMWgE= +go.opentelemetry.io/otel/sdk v1.16.0/go.mod h1:tMsIuKXuuIWPBAOrH+eHtvhTL+SntFtXF9QD68aP6p4= +go.opentelemetry.io/otel/sdk/metric v0.39.0 h1:Kun8i1eYf48kHH83RucG93ffz0zGV1sh46FAScOTuDI= +go.opentelemetry.io/otel/sdk/metric v0.39.0/go.mod h1:piDIRgjcK7u0HCL5pCA4e74qpK/jk3NiUoAHATVAmiI= +go.opentelemetry.io/otel/trace v1.16.0 h1:8JRpaObFoW0pxuVPapkgH8UhHQj+bJW8jJsCZEu5MQs= +go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20220827204233-334a2380cb91 h1:tnebWN09GYg9OLPss1KXj8txwZc6X6uMr6VFdcGNbHw= +golang.org/x/exp v0.0.0-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 h1:DdoeryqhaXp1LtT/emMP1BRJPHHKFi5akj/nbx/zNTA= +google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= +google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97/go.mod h1:t1VqOqqvce95G3hIDCT5FeO3YUc6Q4Oe24L/+rNMxRk= +google.golang.org/grpc v1.55.0 h1:3Oj82/tFSCeUrRTg/5E/7d/W5A1tj6Ky1ABAuZuv5ag= +google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8= +google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/_examples/remote-channels/messages/build.sh b/_examples/remote-channels/messages/build.sh new file mode 100644 index 0000000000000000000000000000000000000000..7f4860098b75fc0c7ca705886c2c10e74e818d31 --- /dev/null +++ b/_examples/remote-channels/messages/build.sh @@ -0,0 +1,2 @@ +protoc -I="../../../actor" --go_out=. --go_opt=paths=source_relative --proto_path=. protos.proto + diff --git a/_examples/remote-channels/messages/protos.pb.go b/_examples/remote-channels/messages/protos.pb.go new file mode 100644 index 0000000000000000000000000000000000000000..fafe722d8253d28d7af645b263499d7d429a365e --- /dev/null +++ b/_examples/remote-channels/messages/protos.pb.go @@ -0,0 +1,145 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc v3.19.1 +// source: protos.proto + +package messages + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type MyMessage struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Message string `protobuf:"bytes,1,opt,name=Message,proto3" json:"Message,omitempty"` +} + +func (x *MyMessage) Reset() { + *x = MyMessage{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *MyMessage) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MyMessage) ProtoMessage() {} + +func (x *MyMessage) ProtoReflect() protoreflect.Message { + mi := &file_protos_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use MyMessage.ProtoReflect.Descriptor instead. +func (*MyMessage) Descriptor() ([]byte, []int) { + return file_protos_proto_rawDescGZIP(), []int{0} +} + +func (x *MyMessage) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + +var File_protos_proto protoreflect.FileDescriptor + +var file_protos_proto_rawDesc = []byte{ + 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, + 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x22, 0x25, 0x0a, 0x09, 0x4d, 0x79, 0x4d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x42, + 0x46, 0x5a, 0x44, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x73, + 0x79, 0x6e, 0x6b, 0x72, 0x6f, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x61, 0x63, 0x74, 0x6f, + 0x72, 0x2d, 0x67, 0x6f, 0x2f, 0x5f, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x2f, 0x72, + 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x2d, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x2f, 0x6d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_protos_proto_rawDescOnce sync.Once + file_protos_proto_rawDescData = file_protos_proto_rawDesc +) + +func file_protos_proto_rawDescGZIP() []byte { + file_protos_proto_rawDescOnce.Do(func() { + file_protos_proto_rawDescData = protoimpl.X.CompressGZIP(file_protos_proto_rawDescData) + }) + return file_protos_proto_rawDescData +} + +var file_protos_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_protos_proto_goTypes = []interface{}{ + (*MyMessage)(nil), // 0: messages.MyMessage +} +var file_protos_proto_depIdxs = []int32{ + 0, // [0:0] is the sub-list for method output_type + 0, // [0:0] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_protos_proto_init() } +func file_protos_proto_init() { + if File_protos_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_protos_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*MyMessage); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_protos_proto_rawDesc, + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_protos_proto_goTypes, + DependencyIndexes: file_protos_proto_depIdxs, + MessageInfos: file_protos_proto_msgTypes, + }.Build() + File_protos_proto = out.File + file_protos_proto_rawDesc = nil + file_protos_proto_goTypes = nil + file_protos_proto_depIdxs = nil +} diff --git a/_examples/remote-channels/messages/protos.proto b/_examples/remote-channels/messages/protos.proto new file mode 100644 index 0000000000000000000000000000000000000000..7166c254cdccabcca351d9be841fde957bb3fdd6 --- /dev/null +++ b/_examples/remote-channels/messages/protos.proto @@ -0,0 +1,7 @@ +syntax = "proto3"; +package messages; +option go_package = "gitee.com/simplexyz/simpleactor-go/_examples/remote-channels/messages"; + +message MyMessage { + string Message = 1; +} \ No newline at end of file diff --git a/_examples/remote-channels/node1/main.go b/_examples/remote-channels/node1/main.go new file mode 100644 index 0000000000000000000000000000000000000000..2b0cb86172673eb04dc0890937b37801f8cfa78d --- /dev/null +++ b/_examples/remote-channels/node1/main.go @@ -0,0 +1,41 @@ +package main + +import ( + "fmt" + + "distributedchannels/messages" + + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/remote" + console "github.com/asynkron/goconsole" +) + +func newMyMessageSenderChannel(context actor.SenderContext) chan<- *messages.MyMessage { + channel := make(chan *messages.MyMessage) + remoteChannel := actor.NewPID("127.0.0.1:8080", "MyMessage") + go func() { + for msg := range channel { + context.Send(remoteChannel, msg) + } + }() + + return channel +} + +func main() { + system := actor.NewActorSystem() + remoteConfig := remote.Configure("127.0.0.1", 0) + remoting := remote.NewRemote(system, remoteConfig) + remoting.Start() + + channel := newMyMessageSenderChannel(system.Root) + + for i := 0; i < 10; i++ { + message := &messages.MyMessage{ + Message: fmt.Sprintf("hello %v", i), + } + channel <- message + } + + console.ReadLine() +} diff --git a/_examples/remote-channels/node2/main.go b/_examples/remote-channels/node2/main.go new file mode 100644 index 0000000000000000000000000000000000000000..f5fad1cc7fbabb402d2d0eff3984a0bb1106e24b --- /dev/null +++ b/_examples/remote-channels/node2/main.go @@ -0,0 +1,40 @@ +package main + +import ( + "log" + + "distributedchannels/messages" + + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/remote" + console "github.com/asynkron/goconsole" +) + +func main() { + system := actor.NewActorSystem() + remoteConfig := remote.Configure("127.0.0.1", 8080) + remoting := remote.NewRemote(system, remoteConfig) + remoting.Start() + + // create the channel + channel := make(chan *messages.MyMessage) + + // create an actor receiving messages and pushing them onto the channel + props := actor.PropsFromFunc(func(context actor.Context) { + if msg, ok := context.Message().(*messages.MyMessage); ok { + channel <- msg + } + }) + + // spawn + _, _ = system.Root.SpawnNamed(props, "MyMessage") + + // consume the channel just like you use to + go func() { + for msg := range channel { + log.Println(msg) + } + }() + + _, _ = console.ReadLine() +} diff --git a/_examples/remote-chat/Makefile b/_examples/remote-chat/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..6def3182bcb1823c11c952164db4d6184e130e40 --- /dev/null +++ b/_examples/remote-chat/Makefile @@ -0,0 +1,13 @@ +start: + tmux new-session -d -s eg + tmux split-window -t "eg:0" -v + tmux split-window -t "eg:0.1" -h + tmux send-keys -t "eg:0.0" "go run server/main.go" Enter + tmux send-keys -t "eg:0.1" "go run client/main.go" Enter + tmux send-keys -t "eg:0.2" "go run client/main.go" Enter + tmux attach -t eg + tmux kill-session -t eg + + +stop: + tmux kill-session -t eg diff --git a/_examples/remote-chat/client/main.go b/_examples/remote-chat/client/main.go new file mode 100644 index 0000000000000000000000000000000000000000..e14f8ba4cf5b0da166379f2a8f437de47e2051e6 --- /dev/null +++ b/_examples/remote-chat/client/main.go @@ -0,0 +1,58 @@ +package main + +import ( + "log" + + "chat/messages" + + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/remote" + console "github.com/asynkron/goconsole" +) + +func main() { + system := actor.NewActorSystem() + config := remote.Configure("127.0.0.1", 0) + remoter := remote.NewRemote(system, config) + remoter.Start() + + server := actor.NewPID("127.0.0.1:8080", "chatserver") + + // define root context + rootContext := system.Root + + // spawn our chat client inline + props := actor.PropsFromFunc(func(context actor.Context) { + switch msg := context.Message().(type) { + case *messages.Connected: + log.Println(msg.Message) + case *messages.SayResponse: + log.Printf("%v: %v", msg.UserName, msg.Message) + case *messages.NickResponse: + log.Printf("%v is now known as %v", msg.OldUserName, msg.NewUserName) + } + }) + + client := rootContext.Spawn(props) + + rootContext.Send(server, &messages.Connect{ + Sender: client, + }) + + nick := "Roger" + cons := console.NewConsole(func(text string) { + rootContext.Send(server, &messages.SayRequest{ + UserName: nick, + Message: text, + }) + }) + // write /nick NAME to change your chat username + cons.Command("/nick", func(newNick string) { + rootContext.Send(server, &messages.NickRequest{ + OldUserName: nick, + NewUserName: newNick, + }) + nick = newNick + }) + cons.Run() +} diff --git a/_examples/remote-chat/go.mod b/_examples/remote-chat/go.mod new file mode 100644 index 0000000000000000000000000000000000000000..760757e4a15906933c9b86cb99f6e603d3eeccc6 --- /dev/null +++ b/_examples/remote-chat/go.mod @@ -0,0 +1,46 @@ +module chat + +go 1.21 + +toolchain go1.21.2 + +replace gitee.com/simplexyz/simpleactor-go => ../.. + +require ( + gitee.com/simplexyz/simpleactor-go v0.0.0-00010101000000-000000000000 + github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716 + google.golang.org/protobuf v1.31.0 +) + +require ( + github.com/Workiva/go-datastructures v1.1.1 // indirect + github.com/asynkron/gofun v0.0.0-20220329210725-34fed760f4c2 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/emirpasic/gods v1.18.1 // indirect + github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/lithammer/shortuuid/v4 v4.0.0 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/orcaman/concurrent-map v1.0.0 // indirect + github.com/prometheus/client_golang v1.16.0 // indirect + github.com/prometheus/client_model v0.4.0 // indirect + github.com/prometheus/common v0.42.0 // indirect + github.com/prometheus/procfs v0.10.1 // indirect + github.com/twmb/murmur3 v1.1.6 // indirect + go.opentelemetry.io/otel v1.16.0 // indirect + go.opentelemetry.io/otel/exporters/prometheus v0.39.0 // indirect + go.opentelemetry.io/otel/metric v1.16.0 // indirect + go.opentelemetry.io/otel/sdk v1.16.0 // indirect + go.opentelemetry.io/otel/sdk/metric v0.39.0 // indirect + go.opentelemetry.io/otel/trace v1.16.0 // indirect + golang.org/x/exp v0.0.0-20220827204233-334a2380cb91 // indirect + golang.org/x/net v0.17.0 // indirect + golang.org/x/sys v0.13.0 // indirect + golang.org/x/text v0.13.0 // indirect + google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97 // indirect + google.golang.org/grpc v1.58.3 // indirect +) diff --git a/_examples/remote-chat/go.sum b/_examples/remote-chat/go.sum new file mode 100644 index 0000000000000000000000000000000000000000..af32feca5fbf879242aa12c72a2d4e3f94331558 --- /dev/null +++ b/_examples/remote-chat/go.sum @@ -0,0 +1,125 @@ +github.com/Workiva/go-datastructures v1.1.1 h1:9G5u1UqKt6ABseAffHGNfbNQd7omRlWE5QaxNruzhE0= +github.com/Workiva/go-datastructures v1.1.1/go.mod h1:1yZL+zfsztete+ePzZz/Zb1/t5BnDuE2Ya2MMGhzP6A= +github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716 h1:SgyG4sXkrlalMoCfp20LiNPNhfJS7ez3opNdtihIxPc= +github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716/go.mod h1:/zSlF0T2ArAsTG6SVu8d8qlK+19jjudjA3wWsxnGFHg= +github.com/asynkron/gofun v0.0.0-20220329210725-34fed760f4c2 h1:jEsFZ9d/ieJGVrx3fSPi8oe/qv21fRmyUL5cS3ZEn5A= +github.com/asynkron/gofun v0.0.0-20220329210725-34fed760f4c2/go.mod h1:5GMOSqaYxNWwuVRWyampTPJEntwz7Mj9J8v1a7gSU2E= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/lithammer/shortuuid/v4 v4.0.0 h1:QRbbVkfgNippHOS8PXDkti4NaWeyYfcBTHtw7k08o4c= +github.com/lithammer/shortuuid/v4 v4.0.0/go.mod h1:Zs8puNcrvf2rV9rTH51ZLLcj7ZXqQI3lv67aw4KiB1Y= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/orcaman/concurrent-map v1.0.0 h1:I/2A2XPCb4IuQWcQhBhSwGfiuybl/J0ev9HDbW65HOY= +github.com/orcaman/concurrent-map v1.0.0/go.mod h1:Lu3tH6HLW3feq74c2GC+jIMS/K2CFcDWnWD9XkenwhI= +github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= +github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= +github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= +github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= +github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= +github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= +github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/tinylib/msgp v1.1.5/go.mod h1:eQsjooMTnV42mHu917E26IogZ2930nFyBQdofk10Udg= +github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q= +github.com/twmb/murmur3 v1.1.6 h1:mqrRot1BRxm+Yct+vavLMou2/iJt0tNVTTC0QoIjaZg= +github.com/twmb/murmur3 v1.1.6/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s= +go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4= +go.opentelemetry.io/otel/exporters/prometheus v0.39.0 h1:whAaiHxOatgtKd+w0dOi//1KUxj3KoPINZdtDaDj3IA= +go.opentelemetry.io/otel/exporters/prometheus v0.39.0/go.mod h1:4jo5Q4CROlCpSPsXLhymi+LYrDXd2ObU5wbKayfZs7Y= +go.opentelemetry.io/otel/metric v1.16.0 h1:RbrpwVG1Hfv85LgnZ7+txXioPDoh6EdbZHo26Q3hqOo= +go.opentelemetry.io/otel/metric v1.16.0/go.mod h1:QE47cpOmkwipPiefDwo2wDzwJrlfxxNYodqc4xnGCo4= +go.opentelemetry.io/otel/sdk v1.16.0 h1:Z1Ok1YsijYL0CSJpHt4cS3wDDh7p572grzNrBMiMWgE= +go.opentelemetry.io/otel/sdk v1.16.0/go.mod h1:tMsIuKXuuIWPBAOrH+eHtvhTL+SntFtXF9QD68aP6p4= +go.opentelemetry.io/otel/sdk/metric v0.39.0 h1:Kun8i1eYf48kHH83RucG93ffz0zGV1sh46FAScOTuDI= +go.opentelemetry.io/otel/sdk/metric v0.39.0/go.mod h1:piDIRgjcK7u0HCL5pCA4e74qpK/jk3NiUoAHATVAmiI= +go.opentelemetry.io/otel/trace v1.16.0 h1:8JRpaObFoW0pxuVPapkgH8UhHQj+bJW8jJsCZEu5MQs= +go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20220827204233-334a2380cb91 h1:tnebWN09GYg9OLPss1KXj8txwZc6X6uMr6VFdcGNbHw= +golang.org/x/exp v0.0.0-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 h1:DdoeryqhaXp1LtT/emMP1BRJPHHKFi5akj/nbx/zNTA= +google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= +google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97/go.mod h1:t1VqOqqvce95G3hIDCT5FeO3YUc6Q4Oe24L/+rNMxRk= +google.golang.org/grpc v1.55.0 h1:3Oj82/tFSCeUrRTg/5E/7d/W5A1tj6Ky1ABAuZuv5ag= +google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8= +google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/_examples/remote-chat/messages/build.sh b/_examples/remote-chat/messages/build.sh new file mode 100644 index 0000000000000000000000000000000000000000..7f4860098b75fc0c7ca705886c2c10e74e818d31 --- /dev/null +++ b/_examples/remote-chat/messages/build.sh @@ -0,0 +1,2 @@ +protoc -I="../../../actor" --go_out=. --go_opt=paths=source_relative --proto_path=. protos.proto + diff --git a/_examples/remote-chat/messages/protos.pb.go b/_examples/remote-chat/messages/protos.pb.go new file mode 100644 index 0000000000000000000000000000000000000000..616c76ff0c669a3ab003c68940ddcb083e0cb30e --- /dev/null +++ b/_examples/remote-chat/messages/protos.pb.go @@ -0,0 +1,503 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc v3.19.1 +// source: protos.proto + +package messages + +import ( + actor "gitee.com/simplexyz/simpleactor-go/actor" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type Connect struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Sender *actor.PID `protobuf:"bytes,1,opt,name=Sender,proto3" json:"Sender,omitempty"` +} + +func (x *Connect) Reset() { + *x = Connect{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Connect) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Connect) ProtoMessage() {} + +func (x *Connect) ProtoReflect() protoreflect.Message { + mi := &file_protos_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Connect.ProtoReflect.Descriptor instead. +func (*Connect) Descriptor() ([]byte, []int) { + return file_protos_proto_rawDescGZIP(), []int{0} +} + +func (x *Connect) GetSender() *actor.PID { + if x != nil { + return x.Sender + } + return nil +} + +type Connected struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Message string `protobuf:"bytes,1,opt,name=Message,proto3" json:"Message,omitempty"` +} + +func (x *Connected) Reset() { + *x = Connected{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Connected) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Connected) ProtoMessage() {} + +func (x *Connected) ProtoReflect() protoreflect.Message { + mi := &file_protos_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Connected.ProtoReflect.Descriptor instead. +func (*Connected) Descriptor() ([]byte, []int) { + return file_protos_proto_rawDescGZIP(), []int{1} +} + +func (x *Connected) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + +type SayRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + UserName string `protobuf:"bytes,1,opt,name=UserName,proto3" json:"UserName,omitempty"` + Message string `protobuf:"bytes,2,opt,name=Message,proto3" json:"Message,omitempty"` +} + +func (x *SayRequest) Reset() { + *x = SayRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SayRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SayRequest) ProtoMessage() {} + +func (x *SayRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SayRequest.ProtoReflect.Descriptor instead. +func (*SayRequest) Descriptor() ([]byte, []int) { + return file_protos_proto_rawDescGZIP(), []int{2} +} + +func (x *SayRequest) GetUserName() string { + if x != nil { + return x.UserName + } + return "" +} + +func (x *SayRequest) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + +type SayResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + UserName string `protobuf:"bytes,1,opt,name=UserName,proto3" json:"UserName,omitempty"` + Message string `protobuf:"bytes,2,opt,name=Message,proto3" json:"Message,omitempty"` +} + +func (x *SayResponse) Reset() { + *x = SayResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SayResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SayResponse) ProtoMessage() {} + +func (x *SayResponse) ProtoReflect() protoreflect.Message { + mi := &file_protos_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SayResponse.ProtoReflect.Descriptor instead. +func (*SayResponse) Descriptor() ([]byte, []int) { + return file_protos_proto_rawDescGZIP(), []int{3} +} + +func (x *SayResponse) GetUserName() string { + if x != nil { + return x.UserName + } + return "" +} + +func (x *SayResponse) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + +type NickRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + OldUserName string `protobuf:"bytes,1,opt,name=OldUserName,proto3" json:"OldUserName,omitempty"` + NewUserName string `protobuf:"bytes,2,opt,name=NewUserName,proto3" json:"NewUserName,omitempty"` +} + +func (x *NickRequest) Reset() { + *x = NickRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *NickRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NickRequest) ProtoMessage() {} + +func (x *NickRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use NickRequest.ProtoReflect.Descriptor instead. +func (*NickRequest) Descriptor() ([]byte, []int) { + return file_protos_proto_rawDescGZIP(), []int{4} +} + +func (x *NickRequest) GetOldUserName() string { + if x != nil { + return x.OldUserName + } + return "" +} + +func (x *NickRequest) GetNewUserName() string { + if x != nil { + return x.NewUserName + } + return "" +} + +type NickResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + OldUserName string `protobuf:"bytes,1,opt,name=OldUserName,proto3" json:"OldUserName,omitempty"` + NewUserName string `protobuf:"bytes,2,opt,name=NewUserName,proto3" json:"NewUserName,omitempty"` +} + +func (x *NickResponse) Reset() { + *x = NickResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *NickResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NickResponse) ProtoMessage() {} + +func (x *NickResponse) ProtoReflect() protoreflect.Message { + mi := &file_protos_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use NickResponse.ProtoReflect.Descriptor instead. +func (*NickResponse) Descriptor() ([]byte, []int) { + return file_protos_proto_rawDescGZIP(), []int{5} +} + +func (x *NickResponse) GetOldUserName() string { + if x != nil { + return x.OldUserName + } + return "" +} + +func (x *NickResponse) GetNewUserName() string { + if x != nil { + return x.NewUserName + } + return "" +} + +var File_protos_proto protoreflect.FileDescriptor + +var file_protos_proto_rawDesc = []byte{ + 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, + 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x1a, 0x0b, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x2d, 0x0a, 0x07, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, + 0x12, 0x22, 0x0a, 0x06, 0x53, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x0a, 0x2e, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x2e, 0x50, 0x49, 0x44, 0x52, 0x06, 0x53, 0x65, + 0x6e, 0x64, 0x65, 0x72, 0x22, 0x25, 0x0a, 0x09, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, + 0x64, 0x12, 0x18, 0x0a, 0x07, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x07, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x42, 0x0a, 0x0a, 0x53, + 0x61, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x55, 0x73, 0x65, + 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x55, 0x73, 0x65, + 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, + 0x43, 0x0a, 0x0b, 0x53, 0x61, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, + 0x0a, 0x08, 0x55, 0x73, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x55, 0x73, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x4d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x4d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x22, 0x51, 0x0a, 0x0b, 0x4e, 0x69, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x4f, 0x6c, 0x64, 0x55, 0x73, 0x65, 0x72, 0x4e, 0x61, + 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x4f, 0x6c, 0x64, 0x55, 0x73, 0x65, + 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x4e, 0x65, 0x77, 0x55, 0x73, 0x65, 0x72, + 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x4e, 0x65, 0x77, 0x55, + 0x73, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x52, 0x0a, 0x0c, 0x4e, 0x69, 0x63, 0x6b, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x4f, 0x6c, 0x64, 0x55, 0x73, + 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x4f, 0x6c, + 0x64, 0x55, 0x73, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x4e, 0x65, 0x77, + 0x55, 0x73, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, + 0x4e, 0x65, 0x77, 0x55, 0x73, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x42, 0x42, 0x5a, 0x40, 0x67, + 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x73, 0x79, 0x6e, 0x6b, 0x72, + 0x6f, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x2d, 0x67, 0x6f, + 0x2f, 0x5f, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x2f, 0x72, 0x65, 0x6d, 0x6f, 0x74, + 0x65, 0x2d, 0x63, 0x68, 0x61, 0x74, 0x2f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_protos_proto_rawDescOnce sync.Once + file_protos_proto_rawDescData = file_protos_proto_rawDesc +) + +func file_protos_proto_rawDescGZIP() []byte { + file_protos_proto_rawDescOnce.Do(func() { + file_protos_proto_rawDescData = protoimpl.X.CompressGZIP(file_protos_proto_rawDescData) + }) + return file_protos_proto_rawDescData +} + +var file_protos_proto_msgTypes = make([]protoimpl.MessageInfo, 6) +var file_protos_proto_goTypes = []interface{}{ + (*Connect)(nil), // 0: messages.Connect + (*Connected)(nil), // 1: messages.Connected + (*SayRequest)(nil), // 2: messages.SayRequest + (*SayResponse)(nil), // 3: messages.SayResponse + (*NickRequest)(nil), // 4: messages.NickRequest + (*NickResponse)(nil), // 5: messages.NickResponse + (*actor.PID)(nil), // 6: actor.PID +} +var file_protos_proto_depIdxs = []int32{ + 6, // 0: messages.Connect.Sender:type_name -> actor.PID + 1, // [1:1] is the sub-list for method output_type + 1, // [1:1] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name +} + +func init() { file_protos_proto_init() } +func file_protos_proto_init() { + if File_protos_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_protos_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Connect); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Connected); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SayRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SayResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NickRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NickResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_protos_proto_rawDesc, + NumEnums: 0, + NumMessages: 6, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_protos_proto_goTypes, + DependencyIndexes: file_protos_proto_depIdxs, + MessageInfos: file_protos_proto_msgTypes, + }.Build() + File_protos_proto = out.File + file_protos_proto_rawDesc = nil + file_protos_proto_goTypes = nil + file_protos_proto_depIdxs = nil +} diff --git a/_examples/remote-chat/messages/protos.proto b/_examples/remote-chat/messages/protos.proto new file mode 100644 index 0000000000000000000000000000000000000000..a50f133696ad958d6502630343c8e62b2771b411 --- /dev/null +++ b/_examples/remote-chat/messages/protos.proto @@ -0,0 +1,32 @@ +syntax = "proto3"; +package messages; +option go_package = "gitee.com/simplexyz/simpleactor-go/_examples/remote-chat/messages"; +import "actor.proto"; + +message Connect { + actor.PID Sender = 1; +} + +message Connected { + string Message = 1; +} + +message SayRequest { + string UserName = 1; + string Message = 2; +} + +message SayResponse { + string UserName = 1; + string Message = 2; +} + +message NickRequest { + string OldUserName = 1; + string NewUserName = 2; +} + +message NickResponse { + string OldUserName = 1; + string NewUserName = 2; +} \ No newline at end of file diff --git a/_examples/remote-chat/server/main.go b/_examples/remote-chat/server/main.go new file mode 100644 index 0000000000000000000000000000000000000000..4d58ad4a0fc805860d6021c9083ab64e4d963bc7 --- /dev/null +++ b/_examples/remote-chat/server/main.go @@ -0,0 +1,50 @@ +package main + +import ( + "log" + + "chat/messages" + + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/remote" + console "github.com/asynkron/goconsole" +) + +// define root context + +func notifyAll(context actor.Context, clients *actor.PIDSet, message interface{}) { + for _, client := range clients.Values() { + context.Send(client, message) + } +} + +func main() { + system := actor.NewActorSystem() + config := remote.Configure("127.0.0.1", 8080) + remoter := remote.NewRemote(system, config) + remoter.Start() + + clients := actor.NewPIDSet() + + props := actor.PropsFromFunc(func(context actor.Context) { + switch msg := context.Message().(type) { + case *messages.Connect: + log.Printf("Client %v connected", msg.Sender) + clients.Add(msg.Sender) + context.Send(msg.Sender, &messages.Connected{Message: "Welcome!"}) + case *messages.SayRequest: + notifyAll(context, clients, &messages.SayResponse{ + UserName: msg.UserName, + Message: msg.Message, + }) + case *messages.NickRequest: + notifyAll(context, clients, &messages.NickResponse{ + OldUserName: msg.OldUserName, + NewUserName: msg.NewUserName, + }) + } + }) + + _, _ = system.Root.SpawnNamed(props, "chatserver") + _, _ = console.ReadLine() +} diff --git a/_examples/remote-header/Makefile b/_examples/remote-header/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..a4c35f1d4ba7a4575c1c4a92937addde15c2eec2 --- /dev/null +++ b/_examples/remote-header/Makefile @@ -0,0 +1,11 @@ +start: + tmux new-session -d -s eg + tmux split-window -t "eg:0" -v + tmux send-keys -t "eg:0.0" "go run node2/main.go" Enter + tmux send-keys -t "eg:0.1" "go run node1/main.go" Enter + tmux attach -t eg + tmux kill-session -t eg + + +stop: + tmux kill-session -t eg diff --git a/_examples/remote-header/go.mod b/_examples/remote-header/go.mod new file mode 100644 index 0000000000000000000000000000000000000000..dc60a6291b08ca2257cced617849e4f7910123a6 --- /dev/null +++ b/_examples/remote-header/go.mod @@ -0,0 +1,46 @@ +module remoteheader + +go 1.21 + +toolchain go1.21.2 + +replace gitee.com/simplexyz/simpleactor-go => ../.. + +require ( + gitee.com/simplexyz/simpleactor-go v0.0.0-00010101000000-000000000000 + github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716 + google.golang.org/protobuf v1.31.0 +) + +require ( + github.com/Workiva/go-datastructures v1.1.1 // indirect + github.com/asynkron/gofun v0.0.0-20220329210725-34fed760f4c2 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/emirpasic/gods v1.18.1 // indirect + github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/lithammer/shortuuid/v4 v4.0.0 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/orcaman/concurrent-map v1.0.0 // indirect + github.com/prometheus/client_golang v1.16.0 // indirect + github.com/prometheus/client_model v0.4.0 // indirect + github.com/prometheus/common v0.42.0 // indirect + github.com/prometheus/procfs v0.10.1 // indirect + github.com/twmb/murmur3 v1.1.6 // indirect + go.opentelemetry.io/otel v1.16.0 // indirect + go.opentelemetry.io/otel/exporters/prometheus v0.39.0 // indirect + go.opentelemetry.io/otel/metric v1.16.0 // indirect + go.opentelemetry.io/otel/sdk v1.16.0 // indirect + go.opentelemetry.io/otel/sdk/metric v0.39.0 // indirect + go.opentelemetry.io/otel/trace v1.16.0 // indirect + golang.org/x/exp v0.0.0-20220827204233-334a2380cb91 // indirect + golang.org/x/net v0.17.0 // indirect + golang.org/x/sys v0.13.0 // indirect + golang.org/x/text v0.13.0 // indirect + google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97 // indirect + google.golang.org/grpc v1.58.3 // indirect +) diff --git a/_examples/remote-header/go.sum b/_examples/remote-header/go.sum new file mode 100644 index 0000000000000000000000000000000000000000..af32feca5fbf879242aa12c72a2d4e3f94331558 --- /dev/null +++ b/_examples/remote-header/go.sum @@ -0,0 +1,125 @@ +github.com/Workiva/go-datastructures v1.1.1 h1:9G5u1UqKt6ABseAffHGNfbNQd7omRlWE5QaxNruzhE0= +github.com/Workiva/go-datastructures v1.1.1/go.mod h1:1yZL+zfsztete+ePzZz/Zb1/t5BnDuE2Ya2MMGhzP6A= +github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716 h1:SgyG4sXkrlalMoCfp20LiNPNhfJS7ez3opNdtihIxPc= +github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716/go.mod h1:/zSlF0T2ArAsTG6SVu8d8qlK+19jjudjA3wWsxnGFHg= +github.com/asynkron/gofun v0.0.0-20220329210725-34fed760f4c2 h1:jEsFZ9d/ieJGVrx3fSPi8oe/qv21fRmyUL5cS3ZEn5A= +github.com/asynkron/gofun v0.0.0-20220329210725-34fed760f4c2/go.mod h1:5GMOSqaYxNWwuVRWyampTPJEntwz7Mj9J8v1a7gSU2E= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/lithammer/shortuuid/v4 v4.0.0 h1:QRbbVkfgNippHOS8PXDkti4NaWeyYfcBTHtw7k08o4c= +github.com/lithammer/shortuuid/v4 v4.0.0/go.mod h1:Zs8puNcrvf2rV9rTH51ZLLcj7ZXqQI3lv67aw4KiB1Y= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/orcaman/concurrent-map v1.0.0 h1:I/2A2XPCb4IuQWcQhBhSwGfiuybl/J0ev9HDbW65HOY= +github.com/orcaman/concurrent-map v1.0.0/go.mod h1:Lu3tH6HLW3feq74c2GC+jIMS/K2CFcDWnWD9XkenwhI= +github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= +github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= +github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= +github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= +github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= +github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= +github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/tinylib/msgp v1.1.5/go.mod h1:eQsjooMTnV42mHu917E26IogZ2930nFyBQdofk10Udg= +github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q= +github.com/twmb/murmur3 v1.1.6 h1:mqrRot1BRxm+Yct+vavLMou2/iJt0tNVTTC0QoIjaZg= +github.com/twmb/murmur3 v1.1.6/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s= +go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4= +go.opentelemetry.io/otel/exporters/prometheus v0.39.0 h1:whAaiHxOatgtKd+w0dOi//1KUxj3KoPINZdtDaDj3IA= +go.opentelemetry.io/otel/exporters/prometheus v0.39.0/go.mod h1:4jo5Q4CROlCpSPsXLhymi+LYrDXd2ObU5wbKayfZs7Y= +go.opentelemetry.io/otel/metric v1.16.0 h1:RbrpwVG1Hfv85LgnZ7+txXioPDoh6EdbZHo26Q3hqOo= +go.opentelemetry.io/otel/metric v1.16.0/go.mod h1:QE47cpOmkwipPiefDwo2wDzwJrlfxxNYodqc4xnGCo4= +go.opentelemetry.io/otel/sdk v1.16.0 h1:Z1Ok1YsijYL0CSJpHt4cS3wDDh7p572grzNrBMiMWgE= +go.opentelemetry.io/otel/sdk v1.16.0/go.mod h1:tMsIuKXuuIWPBAOrH+eHtvhTL+SntFtXF9QD68aP6p4= +go.opentelemetry.io/otel/sdk/metric v0.39.0 h1:Kun8i1eYf48kHH83RucG93ffz0zGV1sh46FAScOTuDI= +go.opentelemetry.io/otel/sdk/metric v0.39.0/go.mod h1:piDIRgjcK7u0HCL5pCA4e74qpK/jk3NiUoAHATVAmiI= +go.opentelemetry.io/otel/trace v1.16.0 h1:8JRpaObFoW0pxuVPapkgH8UhHQj+bJW8jJsCZEu5MQs= +go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20220827204233-334a2380cb91 h1:tnebWN09GYg9OLPss1KXj8txwZc6X6uMr6VFdcGNbHw= +golang.org/x/exp v0.0.0-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 h1:DdoeryqhaXp1LtT/emMP1BRJPHHKFi5akj/nbx/zNTA= +google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= +google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97/go.mod h1:t1VqOqqvce95G3hIDCT5FeO3YUc6Q4Oe24L/+rNMxRk= +google.golang.org/grpc v1.55.0 h1:3Oj82/tFSCeUrRTg/5E/7d/W5A1tj6Ky1ABAuZuv5ag= +google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8= +google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/_examples/remote-header/messages/build.sh b/_examples/remote-header/messages/build.sh new file mode 100644 index 0000000000000000000000000000000000000000..7f4860098b75fc0c7ca705886c2c10e74e818d31 --- /dev/null +++ b/_examples/remote-header/messages/build.sh @@ -0,0 +1,2 @@ +protoc -I="../../../actor" --go_out=. --go_opt=paths=source_relative --proto_path=. protos.proto + diff --git a/_examples/remote-header/messages/protos.pb.go b/_examples/remote-header/messages/protos.pb.go new file mode 100644 index 0000000000000000000000000000000000000000..63b7187c881958615f0d9bddf162b02d50d407ed --- /dev/null +++ b/_examples/remote-header/messages/protos.pb.go @@ -0,0 +1,304 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc v3.19.1 +// source: protos.proto + +package messages + +import ( + actor "gitee.com/simplexyz/simpleactor-go/actor" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type Start struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *Start) Reset() { + *x = Start{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Start) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Start) ProtoMessage() {} + +func (x *Start) ProtoReflect() protoreflect.Message { + mi := &file_protos_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Start.ProtoReflect.Descriptor instead. +func (*Start) Descriptor() ([]byte, []int) { + return file_protos_proto_rawDescGZIP(), []int{0} +} + +type StartRemote struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Sender *actor.PID `protobuf:"bytes,1,opt,name=Sender,proto3" json:"Sender,omitempty"` +} + +func (x *StartRemote) Reset() { + *x = StartRemote{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StartRemote) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StartRemote) ProtoMessage() {} + +func (x *StartRemote) ProtoReflect() protoreflect.Message { + mi := &file_protos_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StartRemote.ProtoReflect.Descriptor instead. +func (*StartRemote) Descriptor() ([]byte, []int) { + return file_protos_proto_rawDescGZIP(), []int{1} +} + +func (x *StartRemote) GetSender() *actor.PID { + if x != nil { + return x.Sender + } + return nil +} + +type Ping struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *Ping) Reset() { + *x = Ping{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Ping) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Ping) ProtoMessage() {} + +func (x *Ping) ProtoReflect() protoreflect.Message { + mi := &file_protos_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Ping.ProtoReflect.Descriptor instead. +func (*Ping) Descriptor() ([]byte, []int) { + return file_protos_proto_rawDescGZIP(), []int{2} +} + +type Pong struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *Pong) Reset() { + *x = Pong{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Pong) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Pong) ProtoMessage() {} + +func (x *Pong) ProtoReflect() protoreflect.Message { + mi := &file_protos_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Pong.ProtoReflect.Descriptor instead. +func (*Pong) Descriptor() ([]byte, []int) { + return file_protos_proto_rawDescGZIP(), []int{3} +} + +var File_protos_proto protoreflect.FileDescriptor + +var file_protos_proto_rawDesc = []byte{ + 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, + 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x1a, 0x0b, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x07, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x72, 0x74, 0x22, 0x31, + 0x0a, 0x0b, 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x12, 0x22, 0x0a, + 0x06, 0x53, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, + 0x61, 0x63, 0x74, 0x6f, 0x72, 0x2e, 0x50, 0x49, 0x44, 0x52, 0x06, 0x53, 0x65, 0x6e, 0x64, 0x65, + 0x72, 0x22, 0x06, 0x0a, 0x04, 0x50, 0x69, 0x6e, 0x67, 0x22, 0x06, 0x0a, 0x04, 0x50, 0x6f, 0x6e, + 0x67, 0x42, 0x44, 0x5a, 0x42, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x61, 0x73, 0x79, 0x6e, 0x6b, 0x72, 0x6f, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x61, 0x63, + 0x74, 0x6f, 0x72, 0x2d, 0x67, 0x6f, 0x2f, 0x5f, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, + 0x2f, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x2d, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x2f, 0x6d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_protos_proto_rawDescOnce sync.Once + file_protos_proto_rawDescData = file_protos_proto_rawDesc +) + +func file_protos_proto_rawDescGZIP() []byte { + file_protos_proto_rawDescOnce.Do(func() { + file_protos_proto_rawDescData = protoimpl.X.CompressGZIP(file_protos_proto_rawDescData) + }) + return file_protos_proto_rawDescData +} + +var file_protos_proto_msgTypes = make([]protoimpl.MessageInfo, 4) +var file_protos_proto_goTypes = []interface{}{ + (*Start)(nil), // 0: messages.Start + (*StartRemote)(nil), // 1: messages.StartRemote + (*Ping)(nil), // 2: messages.Ping + (*Pong)(nil), // 3: messages.Pong + (*actor.PID)(nil), // 4: actor.PID +} +var file_protos_proto_depIdxs = []int32{ + 4, // 0: messages.StartRemote.Sender:type_name -> actor.PID + 1, // [1:1] is the sub-list for method output_type + 1, // [1:1] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name +} + +func init() { file_protos_proto_init() } +func file_protos_proto_init() { + if File_protos_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_protos_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Start); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StartRemote); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Ping); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Pong); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_protos_proto_rawDesc, + NumEnums: 0, + NumMessages: 4, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_protos_proto_goTypes, + DependencyIndexes: file_protos_proto_depIdxs, + MessageInfos: file_protos_proto_msgTypes, + }.Build() + File_protos_proto = out.File + file_protos_proto_rawDesc = nil + file_protos_proto_goTypes = nil + file_protos_proto_depIdxs = nil +} diff --git a/_examples/remote-header/messages/protos.proto b/_examples/remote-header/messages/protos.proto new file mode 100644 index 0000000000000000000000000000000000000000..dff3b83f58bba2d5981cd9fb01e8b9b0807e6c5f --- /dev/null +++ b/_examples/remote-header/messages/protos.proto @@ -0,0 +1,11 @@ +syntax = "proto3"; +package messages; +option go_package = "gitee.com/simplexyz/simpleactor-go/_examples/remote-header/messages"; +import "actor.proto"; + +message Start {} +message StartRemote { + actor.PID Sender = 1; +} +message Ping {} +message Pong {} diff --git a/_examples/remote-header/node1/main.go b/_examples/remote-header/node1/main.go new file mode 100644 index 0000000000000000000000000000000000000000..d62a1ff306f5c8bfccacf81bfe3e2e245495368e --- /dev/null +++ b/_examples/remote-header/node1/main.go @@ -0,0 +1,45 @@ +package main + +import ( + "log" + "time" + + "remoteheader/messages" + + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/remote" + console "github.com/asynkron/goconsole" +) + +var ( + system = actor.NewActorSystem() + rootContext = system.Root +) + +func main() { + cfg := remote.Configure("127.0.0.1", 8081) + r := remote.NewRemote(system, cfg) + r.Start() + + props := actor. + PropsFromFunc(func(context actor.Context) { + switch context.Message().(type) { + case *messages.Pong: + v := context.MessageHeader().Get("test_header") + log.Println("Receive pong message with header:" + v) + } + }) + + pid := rootContext.Spawn(props) + + remotePid := actor.NewPID("127.0.0.1:8080", "remote") + rootContext.RequestFuture(remotePid, &messages.StartRemote{ + Sender: pid, + }, 5*time.Second). + Wait() + + message := &messages.Ping{} + rootContext.Send(remotePid, message) + + console.ReadLine() +} diff --git a/_examples/remote-header/node2/main.go b/_examples/remote-header/node2/main.go new file mode 100644 index 0000000000000000000000000000000000000000..0732b0d5eea0423a73812d8281e4411ff40e7813 --- /dev/null +++ b/_examples/remote-header/node2/main.go @@ -0,0 +1,48 @@ +package main + +import ( + "log" + + "remoteheader/messages" + + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/remote" + console "github.com/asynkron/goconsole" +) + +var ( + system = actor.NewActorSystem() + rootContext = system.Root +) + +func main() { + cfg := remote.Configure("127.0.0.1", 8080) + r := remote.NewRemote(system, cfg) + r.Start() + + var sender *actor.PID + props := actor. + PropsFromFunc( + func(context actor.Context) { + switch msg := context.Message().(type) { + case *messages.StartRemote: + log.Println("Starting") + sender = msg.Sender + context.Respond(&messages.Start{}) + case *messages.Ping: + context.Send(sender, &messages.Pong{}) + } + }, + actor.WithSenderMiddleware( + func(next actor.SenderFunc) actor.SenderFunc { + return func(ctx actor.SenderContext, target *actor.PID, envelope *actor.MessageEnvelope) { + envelope.SetHeader("test_header", "header_from_node2") + log.Println("set header") + next(ctx, target, envelope) + } + })) + + rootContext.SpawnNamed(props, "remote") + + console.ReadLine() +} diff --git a/_examples/remote-routing/Makefile b/_examples/remote-routing/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..d87df430e0495a25b30b3f700eb38b13ab3caeb0 --- /dev/null +++ b/_examples/remote-routing/Makefile @@ -0,0 +1,13 @@ +start: + tmux new-session -d -s eg + tmux split-window -t "eg:0" -v + tmux split-window -t "eg:0.0" -h + tmux send-keys -t "eg:0.0" "go run server/main.go --name node-1 --bind=127.0.0.1:8101" Enter + tmux send-keys -t "eg:0.1" "go run server/main.go --name node-2 --bind=127.0.0.1:8102" Enter + tmux send-keys -t "eg:0.2" "go run client/*.go" Enter + tmux attach -t eg + tmux kill-session -t eg + + +stop: + tmux kill-session -t eg diff --git a/_examples/remote-routing/README.md b/_examples/remote-routing/README.md new file mode 100644 index 0000000000000000000000000000000000000000..c3988d4ab2d720fe1ead7ffad97c0188b385e1dc --- /dev/null +++ b/_examples/remote-routing/README.md @@ -0,0 +1,12 @@ +# start servers + +```bash +go run server/main.go --name node-1 --bind=127.0.0.1:8101 +go run server/main.go --name node-2 --bind=127.0.0.1:8102 +``` + +# start client + +```bash +go run client/local.go client/main.go +``` diff --git a/_examples/remote-routing/client/local.go b/_examples/remote-routing/client/local.go new file mode 100644 index 0000000000000000000000000000000000000000..d02fb44ca1abd74eb16332e1a4d7aa525b2c7ca5 --- /dev/null +++ b/_examples/remote-routing/client/local.go @@ -0,0 +1,40 @@ +package main + +import ( + "log" + "sync" + + "remoterouting/messages" + + "gitee.com/simplexyz/simpleactor-go/actor" +) + +type localActor struct { + count int + wgStop *sync.WaitGroup + messageCount int +} + +func (state *localActor) Receive(context actor.Context) { + switch context.Message().(type) { + case *messages.Pong: + state.count++ + if state.count%50000 == 0 { + log.Println(state.count) + } + if state.count == state.messageCount { + log.Println("Done") + state.wgStop.Done() + } + } +} + +func newLocalActor(stop *sync.WaitGroup, messageCount int) actor.Producer { + stop.Add(1) + return func() actor.Actor { + return &localActor{ + wgStop: stop, + messageCount: messageCount, + } + } +} diff --git a/_examples/remote-routing/client/main.go b/_examples/remote-routing/client/main.go new file mode 100644 index 0000000000000000000000000000000000000000..8c90bf955e7a198aee52baae85320e942dc8c2ae --- /dev/null +++ b/_examples/remote-routing/client/main.go @@ -0,0 +1,62 @@ +package main + +import ( + "fmt" + "log" + "runtime" + "sync" + "time" + + "remoterouting/messages" + + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/remote" + "gitee.com/simplexyz/simpleactor-go/router" + console "github.com/asynkron/goconsole" +) + +var ( + system = actor.NewActorSystem() + rootContext = system.Root +) + +func main() { + cfg := remote.Configure("127.0.0.1", 8100) + r := remote.NewRemote(system, cfg) + r.Start() + + runtime.GOMAXPROCS(runtime.NumCPU()) + runtime.GC() + + p1 := actor.NewPID("127.0.0.1:8101", "remote") + p2 := actor.NewPID("127.0.0.1:8102", "remote") + + remotePID := rootContext.Spawn(router.NewConsistentHashGroup(p1, p2)) + + messageCount := 1000000 + + var wgStop sync.WaitGroup + + props := actor. + PropsFromProducer(newLocalActor(&wgStop, messageCount), + actor.WithMailbox(actor.Bounded(10000))) + + pid := rootContext.Spawn(props) + + log.Println("Starting to send") + + t := time.Now() + + for i := 0; i < messageCount; i++ { + message := &messages.Ping{User: fmt.Sprintf("User_%d", i)} + rootContext.RequestWithCustomSender(remotePID, message, pid) + } + + wgStop.Wait() + + rootContext.Stop(pid) + + fmt.Printf("elapsed: %v\n", time.Since(t)) + + console.ReadLine() +} diff --git a/_examples/remote-routing/go.mod b/_examples/remote-routing/go.mod new file mode 100644 index 0000000000000000000000000000000000000000..74a9bf63b236f055957b1e5fe7a6b6ece3243c11 --- /dev/null +++ b/_examples/remote-routing/go.mod @@ -0,0 +1,47 @@ +module remoterouting + +go 1.21 + +toolchain go1.21.2 + +replace gitee.com/simplexyz/simpleactor-go => ../.. + +require ( + gitee.com/simplexyz/simpleactor-go v0.0.0-00010101000000-000000000000 + github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716 + google.golang.org/protobuf v1.31.0 +) + +require ( + github.com/Workiva/go-datastructures v1.1.1 // indirect + github.com/asynkron/gofun v0.0.0-20220329210725-34fed760f4c2 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/emirpasic/gods v1.18.1 // indirect + github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/lithammer/shortuuid/v4 v4.0.0 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/orcaman/concurrent-map v1.0.0 // indirect + github.com/prometheus/client_golang v1.16.0 // indirect + github.com/prometheus/client_model v0.4.0 // indirect + github.com/prometheus/common v0.42.0 // indirect + github.com/prometheus/procfs v0.10.1 // indirect + github.com/serialx/hashring v0.0.0-20200727003509-22c0c7ab6b1b // indirect + github.com/twmb/murmur3 v1.1.6 // indirect + go.opentelemetry.io/otel v1.16.0 // indirect + go.opentelemetry.io/otel/exporters/prometheus v0.39.0 // indirect + go.opentelemetry.io/otel/metric v1.16.0 // indirect + go.opentelemetry.io/otel/sdk v1.16.0 // indirect + go.opentelemetry.io/otel/sdk/metric v0.39.0 // indirect + go.opentelemetry.io/otel/trace v1.16.0 // indirect + golang.org/x/exp v0.0.0-20220827204233-334a2380cb91 // indirect + golang.org/x/net v0.17.0 // indirect + golang.org/x/sys v0.13.0 // indirect + golang.org/x/text v0.13.0 // indirect + google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97 // indirect + google.golang.org/grpc v1.58.3 // indirect +) diff --git a/_examples/remote-routing/go.sum b/_examples/remote-routing/go.sum new file mode 100644 index 0000000000000000000000000000000000000000..cee93d2919cdc2df54d2dfecea9d5f4b3150a1ec --- /dev/null +++ b/_examples/remote-routing/go.sum @@ -0,0 +1,127 @@ +github.com/Workiva/go-datastructures v1.1.1 h1:9G5u1UqKt6ABseAffHGNfbNQd7omRlWE5QaxNruzhE0= +github.com/Workiva/go-datastructures v1.1.1/go.mod h1:1yZL+zfsztete+ePzZz/Zb1/t5BnDuE2Ya2MMGhzP6A= +github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716 h1:SgyG4sXkrlalMoCfp20LiNPNhfJS7ez3opNdtihIxPc= +github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716/go.mod h1:/zSlF0T2ArAsTG6SVu8d8qlK+19jjudjA3wWsxnGFHg= +github.com/asynkron/gofun v0.0.0-20220329210725-34fed760f4c2 h1:jEsFZ9d/ieJGVrx3fSPi8oe/qv21fRmyUL5cS3ZEn5A= +github.com/asynkron/gofun v0.0.0-20220329210725-34fed760f4c2/go.mod h1:5GMOSqaYxNWwuVRWyampTPJEntwz7Mj9J8v1a7gSU2E= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/lithammer/shortuuid/v4 v4.0.0 h1:QRbbVkfgNippHOS8PXDkti4NaWeyYfcBTHtw7k08o4c= +github.com/lithammer/shortuuid/v4 v4.0.0/go.mod h1:Zs8puNcrvf2rV9rTH51ZLLcj7ZXqQI3lv67aw4KiB1Y= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/orcaman/concurrent-map v1.0.0 h1:I/2A2XPCb4IuQWcQhBhSwGfiuybl/J0ev9HDbW65HOY= +github.com/orcaman/concurrent-map v1.0.0/go.mod h1:Lu3tH6HLW3feq74c2GC+jIMS/K2CFcDWnWD9XkenwhI= +github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= +github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= +github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= +github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= +github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= +github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= +github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= +github.com/serialx/hashring v0.0.0-20200727003509-22c0c7ab6b1b h1:h+3JX2VoWTFuyQEo87pStk/a99dzIO1mM9KxIyLPGTU= +github.com/serialx/hashring v0.0.0-20200727003509-22c0c7ab6b1b/go.mod h1:/yeG0My1xr/u+HZrFQ1tOQQQQrOawfyMUH13ai5brBc= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/tinylib/msgp v1.1.5/go.mod h1:eQsjooMTnV42mHu917E26IogZ2930nFyBQdofk10Udg= +github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q= +github.com/twmb/murmur3 v1.1.6 h1:mqrRot1BRxm+Yct+vavLMou2/iJt0tNVTTC0QoIjaZg= +github.com/twmb/murmur3 v1.1.6/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s= +go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4= +go.opentelemetry.io/otel/exporters/prometheus v0.39.0 h1:whAaiHxOatgtKd+w0dOi//1KUxj3KoPINZdtDaDj3IA= +go.opentelemetry.io/otel/exporters/prometheus v0.39.0/go.mod h1:4jo5Q4CROlCpSPsXLhymi+LYrDXd2ObU5wbKayfZs7Y= +go.opentelemetry.io/otel/metric v1.16.0 h1:RbrpwVG1Hfv85LgnZ7+txXioPDoh6EdbZHo26Q3hqOo= +go.opentelemetry.io/otel/metric v1.16.0/go.mod h1:QE47cpOmkwipPiefDwo2wDzwJrlfxxNYodqc4xnGCo4= +go.opentelemetry.io/otel/sdk v1.16.0 h1:Z1Ok1YsijYL0CSJpHt4cS3wDDh7p572grzNrBMiMWgE= +go.opentelemetry.io/otel/sdk v1.16.0/go.mod h1:tMsIuKXuuIWPBAOrH+eHtvhTL+SntFtXF9QD68aP6p4= +go.opentelemetry.io/otel/sdk/metric v0.39.0 h1:Kun8i1eYf48kHH83RucG93ffz0zGV1sh46FAScOTuDI= +go.opentelemetry.io/otel/sdk/metric v0.39.0/go.mod h1:piDIRgjcK7u0HCL5pCA4e74qpK/jk3NiUoAHATVAmiI= +go.opentelemetry.io/otel/trace v1.16.0 h1:8JRpaObFoW0pxuVPapkgH8UhHQj+bJW8jJsCZEu5MQs= +go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20220827204233-334a2380cb91 h1:tnebWN09GYg9OLPss1KXj8txwZc6X6uMr6VFdcGNbHw= +golang.org/x/exp v0.0.0-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 h1:DdoeryqhaXp1LtT/emMP1BRJPHHKFi5akj/nbx/zNTA= +google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= +google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97/go.mod h1:t1VqOqqvce95G3hIDCT5FeO3YUc6Q4Oe24L/+rNMxRk= +google.golang.org/grpc v1.55.0 h1:3Oj82/tFSCeUrRTg/5E/7d/W5A1tj6Ky1ABAuZuv5ag= +google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8= +google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/_examples/remote-routing/messages/build.sh b/_examples/remote-routing/messages/build.sh new file mode 100644 index 0000000000000000000000000000000000000000..8539de66481ea39cc4f5694f78feccec3f440e03 --- /dev/null +++ b/_examples/remote-routing/messages/build.sh @@ -0,0 +1,2 @@ +protoc -I="../../../actor" --go_out=. --go_opt=paths=source_relative --proto_path=. messages.proto + diff --git a/_examples/remote-routing/messages/messages.go b/_examples/remote-routing/messages/messages.go new file mode 100644 index 0000000000000000000000000000000000000000..a14ff310f391a26a56fbe06e5415e886ef198611 --- /dev/null +++ b/_examples/remote-routing/messages/messages.go @@ -0,0 +1,5 @@ +package messages + +func (m *Ping) Hash() string { + return m.User +} diff --git a/_examples/remote-routing/messages/messages.pb.go b/_examples/remote-routing/messages/messages.pb.go new file mode 100644 index 0000000000000000000000000000000000000000..a367633cf09bcdb7d2fe71ba3f7d95da0ea6dfe5 --- /dev/null +++ b/_examples/remote-routing/messages/messages.pb.go @@ -0,0 +1,196 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc v3.19.1 +// source: messages.proto + +package messages + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type Ping struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + User string `protobuf:"bytes,1,opt,name=User,proto3" json:"User,omitempty"` +} + +func (x *Ping) Reset() { + *x = Ping{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Ping) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Ping) ProtoMessage() {} + +func (x *Ping) ProtoReflect() protoreflect.Message { + mi := &file_messages_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Ping.ProtoReflect.Descriptor instead. +func (*Ping) Descriptor() ([]byte, []int) { + return file_messages_proto_rawDescGZIP(), []int{0} +} + +func (x *Ping) GetUser() string { + if x != nil { + return x.User + } + return "" +} + +type Pong struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *Pong) Reset() { + *x = Pong{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Pong) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Pong) ProtoMessage() {} + +func (x *Pong) ProtoReflect() protoreflect.Message { + mi := &file_messages_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Pong.ProtoReflect.Descriptor instead. +func (*Pong) Descriptor() ([]byte, []int) { + return file_messages_proto_rawDescGZIP(), []int{1} +} + +var File_messages_proto protoreflect.FileDescriptor + +var file_messages_proto_rawDesc = []byte{ + 0x0a, 0x0e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x12, 0x08, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x22, 0x1a, 0x0a, 0x04, 0x50, 0x69, + 0x6e, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x55, 0x73, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x55, 0x73, 0x65, 0x72, 0x22, 0x06, 0x0a, 0x04, 0x50, 0x6f, 0x6e, 0x67, 0x42, 0x45, + 0x5a, 0x43, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x73, 0x79, + 0x6e, 0x6b, 0x72, 0x6f, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x61, 0x63, 0x74, 0x6f, 0x72, + 0x2d, 0x67, 0x6f, 0x2f, 0x5f, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x2f, 0x72, 0x65, + 0x6d, 0x6f, 0x74, 0x65, 0x2d, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x2f, 0x6d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_messages_proto_rawDescOnce sync.Once + file_messages_proto_rawDescData = file_messages_proto_rawDesc +) + +func file_messages_proto_rawDescGZIP() []byte { + file_messages_proto_rawDescOnce.Do(func() { + file_messages_proto_rawDescData = protoimpl.X.CompressGZIP(file_messages_proto_rawDescData) + }) + return file_messages_proto_rawDescData +} + +var file_messages_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_messages_proto_goTypes = []interface{}{ + (*Ping)(nil), // 0: messages.Ping + (*Pong)(nil), // 1: messages.Pong +} +var file_messages_proto_depIdxs = []int32{ + 0, // [0:0] is the sub-list for method output_type + 0, // [0:0] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_messages_proto_init() } +func file_messages_proto_init() { + if File_messages_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_messages_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Ping); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Pong); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_messages_proto_rawDesc, + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_messages_proto_goTypes, + DependencyIndexes: file_messages_proto_depIdxs, + MessageInfos: file_messages_proto_msgTypes, + }.Build() + File_messages_proto = out.File + file_messages_proto_rawDesc = nil + file_messages_proto_goTypes = nil + file_messages_proto_depIdxs = nil +} diff --git a/_examples/remote-routing/messages/messages.proto b/_examples/remote-routing/messages/messages.proto new file mode 100644 index 0000000000000000000000000000000000000000..cd5623c22f230962f9875d550b1c28adc33deae4 --- /dev/null +++ b/_examples/remote-routing/messages/messages.proto @@ -0,0 +1,12 @@ +syntax = "proto3"; +option go_package = "gitee.com/simplexyz/simpleactor-go/_examples/remote-routing/messages"; +package messages; + + + +message Ping { + string User = 1; +} + +message Pong {} + diff --git a/_examples/remote-routing/server/main.go b/_examples/remote-routing/server/main.go new file mode 100644 index 0000000000000000000000000000000000000000..502bf8125fb9d604db4fc6d253f84ec6c2038830 --- /dev/null +++ b/_examples/remote-routing/server/main.go @@ -0,0 +1,76 @@ +package main + +import ( + "flag" + "log" + "net" + "runtime" + "strconv" + + "remoterouting/messages" + + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/remote" + console "github.com/asynkron/goconsole" +) + +var ( + flagBind = flag.String("bind", "localhost:8100", "Bind to address") + flagName = flag.String("name", "node1", "Name") + + system = actor.NewActorSystem() + context = system.Root +) + +type remoteActor struct { + name string + count int +} + +func (a *remoteActor) Receive(context actor.Context) { + switch context.Message().(type) { + case *messages.Ping: + context.Respond(&messages.Pong{}) + } +} + +func newRemoteActor(name string) actor.Producer { + return func() actor.Actor { + return &remoteActor{ + name: name, + } + } +} + +func newRemote(bind, name string) { + host, _port, err := net.SplitHostPort(bind) + if err != nil { + panic(err) + } + port, err := strconv.Atoi(_port) + if err != nil { + panic(err) + } + + r := remote.NewRemote(system, remote.Configure(host, port)) + r.Start() + + props := actor. + PropsFromProducer(newRemoteActor(name), + actor.WithMailbox(actor.Bounded(10000))) + + context.SpawnNamed(props, "remote") + + log.Println(name, "Ready") +} + +func main() { + runtime.GOMAXPROCS(runtime.NumCPU()) + runtime.GC() + + flag.Parse() + + newRemote(*flagBind, *flagName) + + console.ReadLine() +} diff --git a/_examples/remote-routing/start.bat b/_examples/remote-routing/start.bat new file mode 100644 index 0000000000000000000000000000000000000000..8d7b41b02ac3e7ce1a049df250fcf64b62f2f752 --- /dev/null +++ b/_examples/remote-routing/start.bat @@ -0,0 +1,4 @@ + +start go run server/main.go --name node-1 --bind=127.0.0.1:8101 +start go run server/main.go --name node-2 --bind=127.0.0.1:8102 +start go run client/local.go client/main.go \ No newline at end of file diff --git a/_examples/remote-ssl/.gitignore b/_examples/remote-ssl/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..148f7bca678cd1d4f62e97bdf23af520337070be --- /dev/null +++ b/_examples/remote-ssl/.gitignore @@ -0,0 +1,2 @@ +node1 +node2 diff --git a/_examples/remote-ssl/Makefile b/_examples/remote-ssl/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..c5b166feff4c30a54098561acc7824eff9ccbe9c --- /dev/null +++ b/_examples/remote-ssl/Makefile @@ -0,0 +1,25 @@ +all: nodes + +clean: + @rm -f cert/localhost.key + @rm -f cert/localhost.crt + @rm -f node1 + @rm -f node2 + +ssl: + @openssl req \ + -config cert/localhost.conf \ + -new \ + -newkey rsa:4096 \ + -days 365 \ + -nodes \ + -x509 \ + -subj "/C=US/ST=California/L=SanFrancisco/O=Dis/CN=localhost" \ + -keyout cert/localhost.key \ + -out cert/localhost.crt + +nodes: + go build -o node1 nodes/node1/main.go + go build -o node2 nodes/node2/main.go + +.PHONY: clean ssl nodes diff --git a/_examples/remote-ssl/README.md b/_examples/remote-ssl/README.md new file mode 100644 index 0000000000000000000000000000000000000000..2f98d04ece3fbcc937ab610ef393d0bed0b75bc7 --- /dev/null +++ b/_examples/remote-ssl/README.md @@ -0,0 +1,91 @@ +# Remote SSL Example + +In this example we'll use SSL/TLS to authenticate and encrypt exchanges between remote clients and servers using +Proto.Actor-Go. + +# Requirements + +* OpenSSL 1.1.0g+ +* GNU Make 4.1+ + +# Setup + +The `remote` package in Proto.Actor-Go utilizes [gRPC][0] under the hood to enable remote connections between nodes, and +when creating a server with `remote.Start()` it is possible to pass in several [ServerOption][1] arguments which can be +used to pass [TransportCredentials][2] to the [gRPC Server][3]. + +For this example we'll create an SSL certificate using [OpenSSL][4]. You can either use the +local [Makefile](https://www.gnu.org/software/make/manual/html_node/Introduction.html) provided: + +```shell +make ssl +``` + +Or you can do it manually: + +```shell + @openssl req \ + -config cert/localhost.conf \ + -new \ + -newkey rsa:4096 \ + -days 365 \ + -nodes \ + -x509 \ + -subj "/C=US/ST=California/L=SanFrancisco/O=Dis/CN=localhost" \ + -keyout cert/localhost.key \ + -out cert/localhost.crt +``` + +This will place the files `cert/localhost.key` and `cert/localhost.crt` which both nodes will use to communicate with +one another via TLS. + +Now you can use the Makefile to compile the nodes: + +``` +make nodes +``` + +Or run `go build` manually: + +``` +go build -o node1 nodes/node1/main.go +go build -o node2 nodes/node2/main.go +``` + +# Running + +For this demo, `node2` will send a message to `node1`, which `node1` will respond to, all over TLS. + +You'll want to make sure `node1` is up first: + +```shell +./node1 +``` + +And then run `node2` in another terminal: + +```shell +./node2 +``` + +If everything is working properly you should see output like the following from `node1`: + +```shell +127.0.0.1:8090/node1 received SYN from 127.0.0.1:8091/node2 +``` + +And similarly for `node2`: + +```shell +127.0.0.1:8091/node2 received ACK from 127.0.0.1:8090/node1 +``` + +[0]:https://google.golang.org/grpc + +[1]:https://godoc.org/google.golang.org/grpc#ServerOption + +[2]:https://godoc.org/google.golang.org/grpc/credentials#TransportCredentials + +[3]:https://godoc.org/google.golang.org/grpc#Server + +[4]:https://www.openssl.org/ diff --git a/_examples/remote-ssl/cert/.gitignore b/_examples/remote-ssl/cert/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..aab6efcf8e4f6e4b0cd9eae03d19c5993820b317 --- /dev/null +++ b/_examples/remote-ssl/cert/.gitignore @@ -0,0 +1,4 @@ +* +.* +!localhost.conf +!.gitignore diff --git a/_examples/remote-ssl/cert/localhost.conf b/_examples/remote-ssl/cert/localhost.conf new file mode 100644 index 0000000000000000000000000000000000000000..b216e2ccedc36756dc2d9378cb696f6fe2cdfc24 --- /dev/null +++ b/_examples/remote-ssl/cert/localhost.conf @@ -0,0 +1,43 @@ +[ req ] +default_bits = 4096 +default_keyfile = localhost.key +distinguished_name = subject +req_extensions = extensions +x509_extensions = extensions +string_mask = utf8only + +[ subject ] +countryName = Country Name (2 letter code) +countryName_default = US + +stateOrProvinceName = State or Province Name (full name) +stateOrProvinceName_default = California + +localityName = Locality Name (eg, city) +localityName_default = San Franscisco + +organizationName = Organization Name (eg, company) +organizationName_default = Example, LLC + +commonName = Common Name (e.g. server FQDN or YOUR name) +commonName_default = localhost + +emailAddress = Email Address +emailAddress_default = root@localhost + +[ extensions ] + +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid,issuer + +basicConstraints = CA:FALSE +keyUsage = nonRepudiation, digitalSignature, keyEncipherment +extendedKeyUsage = serverAuth +subjectAltName = @alternate_names +nsComment = "OpenSSL Generated Certificate" + +[ alternate_names ] + +DNS.1 = localhost +IP.2 = 127.0.0.1 +IP.3 = ::1 diff --git a/_examples/remote-ssl/go.mod b/_examples/remote-ssl/go.mod new file mode 100644 index 0000000000000000000000000000000000000000..c6d72a22f17b83f7477e281ad1faef1eb3170538 --- /dev/null +++ b/_examples/remote-ssl/go.mod @@ -0,0 +1,5 @@ +module remotessl + +go 1.16 + +replace gitee.com/simplexyz/simpleactor-go => ../.. diff --git a/_examples/remote-ssl/messages/messages.proto b/_examples/remote-ssl/messages/messages.proto new file mode 100644 index 0000000000000000000000000000000000000000..87337061bf7ed13951979ac361c7612df9b762c6 --- /dev/null +++ b/_examples/remote-ssl/messages/messages.proto @@ -0,0 +1,7 @@ +syntax = "proto3"; +package messages; +import "gitee.com/simplexyz/simpleactor-go/actor/protos.proto"; + +message EncryptedMessage { + string Message = 1; +} diff --git a/_examples/remote-ssl/messages/protobuf.sh b/_examples/remote-ssl/messages/protobuf.sh new file mode 100644 index 0000000000000000000000000000000000000000..aca9857dad351cfeb581608478e558357f15561d --- /dev/null +++ b/_examples/remote-ssl/messages/protobuf.sh @@ -0,0 +1,2 @@ +#!/bin/bash +protoc -I=. -I=$GOPATH/src --gogoslick_out=plugins=grpc:. messages.proto diff --git a/_examples/remote-watch/Makefile b/_examples/remote-watch/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..a4c35f1d4ba7a4575c1c4a92937addde15c2eec2 --- /dev/null +++ b/_examples/remote-watch/Makefile @@ -0,0 +1,11 @@ +start: + tmux new-session -d -s eg + tmux split-window -t "eg:0" -v + tmux send-keys -t "eg:0.0" "go run node2/main.go" Enter + tmux send-keys -t "eg:0.1" "go run node1/main.go" Enter + tmux attach -t eg + tmux kill-session -t eg + + +stop: + tmux kill-session -t eg diff --git a/_examples/remote-watch/go.mod b/_examples/remote-watch/go.mod new file mode 100644 index 0000000000000000000000000000000000000000..70dca78968d31ad2cb007bc83574202c0ce13839 --- /dev/null +++ b/_examples/remote-watch/go.mod @@ -0,0 +1,46 @@ +module remotewatch + +go 1.21 + +toolchain go1.21.2 + +replace gitee.com/simplexyz/simpleactor-go => ../.. + +require ( + gitee.com/simplexyz/simpleactor-go v0.0.0-00010101000000-000000000000 + github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716 + google.golang.org/protobuf v1.31.0 +) + +require ( + github.com/Workiva/go-datastructures v1.1.1 // indirect + github.com/asynkron/gofun v0.0.0-20220329210725-34fed760f4c2 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/emirpasic/gods v1.18.1 // indirect + github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/lithammer/shortuuid/v4 v4.0.0 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/orcaman/concurrent-map v1.0.0 // indirect + github.com/prometheus/client_golang v1.16.0 // indirect + github.com/prometheus/client_model v0.4.0 // indirect + github.com/prometheus/common v0.42.0 // indirect + github.com/prometheus/procfs v0.10.1 // indirect + github.com/twmb/murmur3 v1.1.6 // indirect + go.opentelemetry.io/otel v1.16.0 // indirect + go.opentelemetry.io/otel/exporters/prometheus v0.39.0 // indirect + go.opentelemetry.io/otel/metric v1.16.0 // indirect + go.opentelemetry.io/otel/sdk v1.16.0 // indirect + go.opentelemetry.io/otel/sdk/metric v0.39.0 // indirect + go.opentelemetry.io/otel/trace v1.16.0 // indirect + golang.org/x/exp v0.0.0-20220827204233-334a2380cb91 // indirect + golang.org/x/net v0.17.0 // indirect + golang.org/x/sys v0.13.0 // indirect + golang.org/x/text v0.13.0 // indirect + google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97 // indirect + google.golang.org/grpc v1.58.3 // indirect +) diff --git a/_examples/remote-watch/go.sum b/_examples/remote-watch/go.sum new file mode 100644 index 0000000000000000000000000000000000000000..af32feca5fbf879242aa12c72a2d4e3f94331558 --- /dev/null +++ b/_examples/remote-watch/go.sum @@ -0,0 +1,125 @@ +github.com/Workiva/go-datastructures v1.1.1 h1:9G5u1UqKt6ABseAffHGNfbNQd7omRlWE5QaxNruzhE0= +github.com/Workiva/go-datastructures v1.1.1/go.mod h1:1yZL+zfsztete+ePzZz/Zb1/t5BnDuE2Ya2MMGhzP6A= +github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716 h1:SgyG4sXkrlalMoCfp20LiNPNhfJS7ez3opNdtihIxPc= +github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716/go.mod h1:/zSlF0T2ArAsTG6SVu8d8qlK+19jjudjA3wWsxnGFHg= +github.com/asynkron/gofun v0.0.0-20220329210725-34fed760f4c2 h1:jEsFZ9d/ieJGVrx3fSPi8oe/qv21fRmyUL5cS3ZEn5A= +github.com/asynkron/gofun v0.0.0-20220329210725-34fed760f4c2/go.mod h1:5GMOSqaYxNWwuVRWyampTPJEntwz7Mj9J8v1a7gSU2E= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/lithammer/shortuuid/v4 v4.0.0 h1:QRbbVkfgNippHOS8PXDkti4NaWeyYfcBTHtw7k08o4c= +github.com/lithammer/shortuuid/v4 v4.0.0/go.mod h1:Zs8puNcrvf2rV9rTH51ZLLcj7ZXqQI3lv67aw4KiB1Y= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/orcaman/concurrent-map v1.0.0 h1:I/2A2XPCb4IuQWcQhBhSwGfiuybl/J0ev9HDbW65HOY= +github.com/orcaman/concurrent-map v1.0.0/go.mod h1:Lu3tH6HLW3feq74c2GC+jIMS/K2CFcDWnWD9XkenwhI= +github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= +github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= +github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= +github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= +github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= +github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= +github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/tinylib/msgp v1.1.5/go.mod h1:eQsjooMTnV42mHu917E26IogZ2930nFyBQdofk10Udg= +github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q= +github.com/twmb/murmur3 v1.1.6 h1:mqrRot1BRxm+Yct+vavLMou2/iJt0tNVTTC0QoIjaZg= +github.com/twmb/murmur3 v1.1.6/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s= +go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4= +go.opentelemetry.io/otel/exporters/prometheus v0.39.0 h1:whAaiHxOatgtKd+w0dOi//1KUxj3KoPINZdtDaDj3IA= +go.opentelemetry.io/otel/exporters/prometheus v0.39.0/go.mod h1:4jo5Q4CROlCpSPsXLhymi+LYrDXd2ObU5wbKayfZs7Y= +go.opentelemetry.io/otel/metric v1.16.0 h1:RbrpwVG1Hfv85LgnZ7+txXioPDoh6EdbZHo26Q3hqOo= +go.opentelemetry.io/otel/metric v1.16.0/go.mod h1:QE47cpOmkwipPiefDwo2wDzwJrlfxxNYodqc4xnGCo4= +go.opentelemetry.io/otel/sdk v1.16.0 h1:Z1Ok1YsijYL0CSJpHt4cS3wDDh7p572grzNrBMiMWgE= +go.opentelemetry.io/otel/sdk v1.16.0/go.mod h1:tMsIuKXuuIWPBAOrH+eHtvhTL+SntFtXF9QD68aP6p4= +go.opentelemetry.io/otel/sdk/metric v0.39.0 h1:Kun8i1eYf48kHH83RucG93ffz0zGV1sh46FAScOTuDI= +go.opentelemetry.io/otel/sdk/metric v0.39.0/go.mod h1:piDIRgjcK7u0HCL5pCA4e74qpK/jk3NiUoAHATVAmiI= +go.opentelemetry.io/otel/trace v1.16.0 h1:8JRpaObFoW0pxuVPapkgH8UhHQj+bJW8jJsCZEu5MQs= +go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20220827204233-334a2380cb91 h1:tnebWN09GYg9OLPss1KXj8txwZc6X6uMr6VFdcGNbHw= +golang.org/x/exp v0.0.0-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 h1:DdoeryqhaXp1LtT/emMP1BRJPHHKFi5akj/nbx/zNTA= +google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= +google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97/go.mod h1:t1VqOqqvce95G3hIDCT5FeO3YUc6Q4Oe24L/+rNMxRk= +google.golang.org/grpc v1.55.0 h1:3Oj82/tFSCeUrRTg/5E/7d/W5A1tj6Ky1ABAuZuv5ag= +google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8= +google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/_examples/remote-watch/messages/build.sh b/_examples/remote-watch/messages/build.sh new file mode 100644 index 0000000000000000000000000000000000000000..7f4860098b75fc0c7ca705886c2c10e74e818d31 --- /dev/null +++ b/_examples/remote-watch/messages/build.sh @@ -0,0 +1,2 @@ +protoc -I="../../../actor" --go_out=. --go_opt=paths=source_relative --proto_path=. protos.proto + diff --git a/_examples/remote-watch/messages/protos.pb.go b/_examples/remote-watch/messages/protos.pb.go new file mode 100644 index 0000000000000000000000000000000000000000..091ee1c0edb74be309e75bd45dc4c75c49bcbe0d --- /dev/null +++ b/_examples/remote-watch/messages/protos.pb.go @@ -0,0 +1,197 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc v3.19.1 +// source: protos.proto + +package messages + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type HelloRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *HelloRequest) Reset() { + *x = HelloRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *HelloRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*HelloRequest) ProtoMessage() {} + +func (x *HelloRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use HelloRequest.ProtoReflect.Descriptor instead. +func (*HelloRequest) Descriptor() ([]byte, []int) { + return file_protos_proto_rawDescGZIP(), []int{0} +} + +type HelloResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Message string `protobuf:"bytes,1,opt,name=Message,proto3" json:"Message,omitempty"` +} + +func (x *HelloResponse) Reset() { + *x = HelloResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *HelloResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*HelloResponse) ProtoMessage() {} + +func (x *HelloResponse) ProtoReflect() protoreflect.Message { + mi := &file_protos_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use HelloResponse.ProtoReflect.Descriptor instead. +func (*HelloResponse) Descriptor() ([]byte, []int) { + return file_protos_proto_rawDescGZIP(), []int{1} +} + +func (x *HelloResponse) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + +var File_protos_proto protoreflect.FileDescriptor + +var file_protos_proto_rawDesc = []byte{ + 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, + 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x22, 0x0e, 0x0a, 0x0c, 0x48, 0x65, 0x6c, 0x6c, + 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x29, 0x0a, 0x0d, 0x48, 0x65, 0x6c, 0x6c, + 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x4d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x4d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x42, 0x43, 0x5a, 0x41, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x61, 0x73, 0x79, 0x6e, 0x6b, 0x72, 0x6f, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x61, 0x63, 0x74, 0x6f, 0x72, 0x2d, 0x67, 0x6f, 0x2f, 0x5f, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, + 0x65, 0x73, 0x2f, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x2d, 0x77, 0x61, 0x74, 0x63, 0x68, 0x2f, + 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_protos_proto_rawDescOnce sync.Once + file_protos_proto_rawDescData = file_protos_proto_rawDesc +) + +func file_protos_proto_rawDescGZIP() []byte { + file_protos_proto_rawDescOnce.Do(func() { + file_protos_proto_rawDescData = protoimpl.X.CompressGZIP(file_protos_proto_rawDescData) + }) + return file_protos_proto_rawDescData +} + +var file_protos_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_protos_proto_goTypes = []interface{}{ + (*HelloRequest)(nil), // 0: messages.HelloRequest + (*HelloResponse)(nil), // 1: messages.HelloResponse +} +var file_protos_proto_depIdxs = []int32{ + 0, // [0:0] is the sub-list for method output_type + 0, // [0:0] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_protos_proto_init() } +func file_protos_proto_init() { + if File_protos_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_protos_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*HelloRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*HelloResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_protos_proto_rawDesc, + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_protos_proto_goTypes, + DependencyIndexes: file_protos_proto_depIdxs, + MessageInfos: file_protos_proto_msgTypes, + }.Build() + File_protos_proto = out.File + file_protos_proto_rawDesc = nil + file_protos_proto_goTypes = nil + file_protos_proto_depIdxs = nil +} diff --git a/_examples/remote-watch/messages/protos.proto b/_examples/remote-watch/messages/protos.proto new file mode 100644 index 0000000000000000000000000000000000000000..aa02b5c7d0f182a2b3616f9e0c4d3c95f9aecd69 --- /dev/null +++ b/_examples/remote-watch/messages/protos.proto @@ -0,0 +1,9 @@ +syntax = "proto3"; +package messages; +option go_package = "gitee.com/simplexyz/simpleactor-go/_examples/remote-watch/messages"; + + +message HelloRequest {} +message HelloResponse { + string Message = 1; +} \ No newline at end of file diff --git a/_examples/remote-watch/node1/main.go b/_examples/remote-watch/node1/main.go new file mode 100644 index 0000000000000000000000000000000000000000..0f8e43155fa16c301bdd21eaa80980c42b7e8811 --- /dev/null +++ b/_examples/remote-watch/node1/main.go @@ -0,0 +1,43 @@ +package main + +import ( + "log" + "time" + + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/remote" + console "github.com/asynkron/goconsole" +) + +var ( + system = actor.NewActorSystem() + context = system.Root +) + +func main() { + cfg := remote.Configure("127.0.0.1", 8081) + r := remote.NewRemote(system, cfg) + r.Start() + + timeout := 5 * time.Second + + props := actor.PropsFromFunc(func(ctx actor.Context) { + switch msg := ctx.Message().(type) { + case *actor.Started: + log.Println("Local actor started") + pidResp, err := r.SpawnNamed("127.0.0.1:8080", "myRemote", "remote", timeout) + if err != nil { + log.Print("Local failed to spawn remote actor") + return + } + log.Println("Local spawned remote actor") + ctx.Watch(pidResp.Pid) + log.Println("Local is watching remote actor") + case *actor.Terminated: + log.Printf("Local got terminated message %+v", msg) + } + }) + + context.Spawn(props) + console.ReadLine() +} diff --git a/_examples/remote-watch/node2/main.go b/_examples/remote-watch/node2/main.go new file mode 100644 index 0000000000000000000000000000000000000000..d67f60116d7a512142af7b51cb14b37f49bd9b11 --- /dev/null +++ b/_examples/remote-watch/node2/main.go @@ -0,0 +1,29 @@ +package main + +import ( + "runtime" + + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/remote" + console "github.com/asynkron/goconsole" +) + +var ( + system = actor.NewActorSystem() + context = system.Root +) + +func main() { + runtime.GOMAXPROCS(runtime.NumCPU()) + + props := actor.PropsFromFunc(func(ctx actor.Context) {}) + cfg := remote.Configure("127.0.0.1", 8080, remote.WithKinds(remote.NewKind("remote", props))) + + r := remote.NewRemote(system, cfg) + r.Register("remote", props) + r.Start() + + // empty actor just to have something to remote spawn + + console.ReadLine() +} diff --git a/_examples/router-demo/go.mod b/_examples/router-demo/go.mod new file mode 100644 index 0000000000000000000000000000000000000000..5c81ce9968c093ca9c966c4087c7d6aeb4e5e56c --- /dev/null +++ b/_examples/router-demo/go.mod @@ -0,0 +1,40 @@ +module routing + +go 1.21 + +toolchain go1.21.2 + +replace gitee.com/simplexyz/simpleactor-go => ../.. + +require ( + gitee.com/simplexyz/simpleactor-go v0.0.0-00010101000000-000000000000 + github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716 +) + +require ( + github.com/Workiva/go-datastructures v1.1.1 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/emirpasic/gods v1.18.1 // indirect + github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/lithammer/shortuuid/v4 v4.0.0 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/orcaman/concurrent-map v1.0.0 // indirect + github.com/prometheus/client_golang v1.16.0 // indirect + github.com/prometheus/client_model v0.4.0 // indirect + github.com/prometheus/common v0.42.0 // indirect + github.com/prometheus/procfs v0.10.1 // indirect + github.com/serialx/hashring v0.0.0-20200727003509-22c0c7ab6b1b // indirect + github.com/twmb/murmur3 v1.1.6 // indirect + go.opentelemetry.io/otel v1.16.0 // indirect + go.opentelemetry.io/otel/exporters/prometheus v0.39.0 // indirect + go.opentelemetry.io/otel/metric v1.16.0 // indirect + go.opentelemetry.io/otel/sdk v1.16.0 // indirect + go.opentelemetry.io/otel/sdk/metric v0.39.0 // indirect + go.opentelemetry.io/otel/trace v1.16.0 // indirect + golang.org/x/sys v0.13.0 // indirect + google.golang.org/protobuf v1.31.0 // indirect +) diff --git a/_examples/router-demo/go.sum b/_examples/router-demo/go.sum new file mode 100644 index 0000000000000000000000000000000000000000..539dcf9dde2b37a66910ee98d6683d9439b9cb7f --- /dev/null +++ b/_examples/router-demo/go.sum @@ -0,0 +1,102 @@ +github.com/Workiva/go-datastructures v1.1.1 h1:9G5u1UqKt6ABseAffHGNfbNQd7omRlWE5QaxNruzhE0= +github.com/Workiva/go-datastructures v1.1.1/go.mod h1:1yZL+zfsztete+ePzZz/Zb1/t5BnDuE2Ya2MMGhzP6A= +github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716 h1:SgyG4sXkrlalMoCfp20LiNPNhfJS7ez3opNdtihIxPc= +github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716/go.mod h1:/zSlF0T2ArAsTG6SVu8d8qlK+19jjudjA3wWsxnGFHg= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/lithammer/shortuuid/v4 v4.0.0 h1:QRbbVkfgNippHOS8PXDkti4NaWeyYfcBTHtw7k08o4c= +github.com/lithammer/shortuuid/v4 v4.0.0/go.mod h1:Zs8puNcrvf2rV9rTH51ZLLcj7ZXqQI3lv67aw4KiB1Y= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/orcaman/concurrent-map v1.0.0 h1:I/2A2XPCb4IuQWcQhBhSwGfiuybl/J0ev9HDbW65HOY= +github.com/orcaman/concurrent-map v1.0.0/go.mod h1:Lu3tH6HLW3feq74c2GC+jIMS/K2CFcDWnWD9XkenwhI= +github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= +github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= +github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= +github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= +github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= +github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= +github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= +github.com/serialx/hashring v0.0.0-20200727003509-22c0c7ab6b1b h1:h+3JX2VoWTFuyQEo87pStk/a99dzIO1mM9KxIyLPGTU= +github.com/serialx/hashring v0.0.0-20200727003509-22c0c7ab6b1b/go.mod h1:/yeG0My1xr/u+HZrFQ1tOQQQQrOawfyMUH13ai5brBc= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/tinylib/msgp v1.1.5/go.mod h1:eQsjooMTnV42mHu917E26IogZ2930nFyBQdofk10Udg= +github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q= +github.com/twmb/murmur3 v1.1.6 h1:mqrRot1BRxm+Yct+vavLMou2/iJt0tNVTTC0QoIjaZg= +github.com/twmb/murmur3 v1.1.6/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s= +go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4= +go.opentelemetry.io/otel/exporters/prometheus v0.39.0 h1:whAaiHxOatgtKd+w0dOi//1KUxj3KoPINZdtDaDj3IA= +go.opentelemetry.io/otel/exporters/prometheus v0.39.0/go.mod h1:4jo5Q4CROlCpSPsXLhymi+LYrDXd2ObU5wbKayfZs7Y= +go.opentelemetry.io/otel/metric v1.16.0 h1:RbrpwVG1Hfv85LgnZ7+txXioPDoh6EdbZHo26Q3hqOo= +go.opentelemetry.io/otel/metric v1.16.0/go.mod h1:QE47cpOmkwipPiefDwo2wDzwJrlfxxNYodqc4xnGCo4= +go.opentelemetry.io/otel/sdk v1.16.0 h1:Z1Ok1YsijYL0CSJpHt4cS3wDDh7p572grzNrBMiMWgE= +go.opentelemetry.io/otel/sdk v1.16.0/go.mod h1:tMsIuKXuuIWPBAOrH+eHtvhTL+SntFtXF9QD68aP6p4= +go.opentelemetry.io/otel/sdk/metric v0.39.0 h1:Kun8i1eYf48kHH83RucG93ffz0zGV1sh46FAScOTuDI= +go.opentelemetry.io/otel/sdk/metric v0.39.0/go.mod h1:piDIRgjcK7u0HCL5pCA4e74qpK/jk3NiUoAHATVAmiI= +go.opentelemetry.io/otel/trace v1.16.0 h1:8JRpaObFoW0pxuVPapkgH8UhHQj+bJW8jJsCZEu5MQs= +go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/_examples/router-demo/main.go b/_examples/router-demo/main.go new file mode 100644 index 0000000000000000000000000000000000000000..f8afe573d02503548dbfd83d402442415ab79f91 --- /dev/null +++ b/_examples/router-demo/main.go @@ -0,0 +1,53 @@ +package main + +import ( + "log" + "strconv" + "time" + + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/router" + console "github.com/asynkron/goconsole" +) + +type myMessage struct{ i int } + +func (m *myMessage) Hash() string { + return strconv.Itoa(m.i) +} + +func main() { + log.Println("Round robin routing:") + system := actor.NewActorSystem() + rootContext := system.Root + act := func(context actor.Context) { + switch msg := context.Message().(type) { + case *myMessage: + log.Printf("%v got message %d", context.Self(), msg.i) + } + } + + pid := rootContext.Spawn(router.NewRoundRobinPool(5, actor.WithFunc(act))) + for i := 0; i < 10; i++ { + rootContext.Send(pid, &myMessage{i}) + } + time.Sleep(1 * time.Second) + log.Println("Random routing:") + pid = rootContext.Spawn(router.NewRandomPool(5, actor.WithFunc(act))) + for i := 0; i < 10; i++ { + rootContext.Send(pid, &myMessage{i}) + } + time.Sleep(1 * time.Second) + log.Println("ConsistentHash routing:") + pid = rootContext.Spawn(router.NewConsistentHashPool(5, actor.WithFunc(act))) + for i := 0; i < 10; i++ { + rootContext.Send(pid, &myMessage{i}) + } + time.Sleep(1 * time.Second) + log.Println("BroadcastPool routing:") + pid = rootContext.Spawn(router.NewBroadcastPool(5, actor.WithFunc(act))) + for i := 0; i < 10; i++ { + rootContext.Send(pid, &myMessage{i}) + } + _, _ = console.ReadLine() +} diff --git a/_examples/router-limitconcurrency/go.mod b/_examples/router-limitconcurrency/go.mod new file mode 100644 index 0000000000000000000000000000000000000000..57798845bbfc806e32287807c5ce8e12a12f3891 --- /dev/null +++ b/_examples/router-limitconcurrency/go.mod @@ -0,0 +1,40 @@ +module limitconcurrency + +go 1.21 + +toolchain go1.21.2 + +replace gitee.com/simplexyz/simpleactor-go => ../.. + +require ( + gitee.com/simplexyz/simpleactor-go v0.0.0-00010101000000-000000000000 + github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716 +) + +require ( + github.com/Workiva/go-datastructures v1.1.1 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/emirpasic/gods v1.18.1 // indirect + github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/lithammer/shortuuid/v4 v4.0.0 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/orcaman/concurrent-map v1.0.0 // indirect + github.com/prometheus/client_golang v1.16.0 // indirect + github.com/prometheus/client_model v0.4.0 // indirect + github.com/prometheus/common v0.42.0 // indirect + github.com/prometheus/procfs v0.10.1 // indirect + github.com/serialx/hashring v0.0.0-20200727003509-22c0c7ab6b1b // indirect + github.com/twmb/murmur3 v1.1.6 // indirect + go.opentelemetry.io/otel v1.16.0 // indirect + go.opentelemetry.io/otel/exporters/prometheus v0.39.0 // indirect + go.opentelemetry.io/otel/metric v1.16.0 // indirect + go.opentelemetry.io/otel/sdk v1.16.0 // indirect + go.opentelemetry.io/otel/sdk/metric v0.39.0 // indirect + go.opentelemetry.io/otel/trace v1.16.0 // indirect + golang.org/x/sys v0.13.0 // indirect + google.golang.org/protobuf v1.31.0 // indirect +) diff --git a/_examples/router-limitconcurrency/go.sum b/_examples/router-limitconcurrency/go.sum new file mode 100644 index 0000000000000000000000000000000000000000..539dcf9dde2b37a66910ee98d6683d9439b9cb7f --- /dev/null +++ b/_examples/router-limitconcurrency/go.sum @@ -0,0 +1,102 @@ +github.com/Workiva/go-datastructures v1.1.1 h1:9G5u1UqKt6ABseAffHGNfbNQd7omRlWE5QaxNruzhE0= +github.com/Workiva/go-datastructures v1.1.1/go.mod h1:1yZL+zfsztete+ePzZz/Zb1/t5BnDuE2Ya2MMGhzP6A= +github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716 h1:SgyG4sXkrlalMoCfp20LiNPNhfJS7ez3opNdtihIxPc= +github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716/go.mod h1:/zSlF0T2ArAsTG6SVu8d8qlK+19jjudjA3wWsxnGFHg= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/lithammer/shortuuid/v4 v4.0.0 h1:QRbbVkfgNippHOS8PXDkti4NaWeyYfcBTHtw7k08o4c= +github.com/lithammer/shortuuid/v4 v4.0.0/go.mod h1:Zs8puNcrvf2rV9rTH51ZLLcj7ZXqQI3lv67aw4KiB1Y= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/orcaman/concurrent-map v1.0.0 h1:I/2A2XPCb4IuQWcQhBhSwGfiuybl/J0ev9HDbW65HOY= +github.com/orcaman/concurrent-map v1.0.0/go.mod h1:Lu3tH6HLW3feq74c2GC+jIMS/K2CFcDWnWD9XkenwhI= +github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= +github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= +github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= +github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= +github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= +github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= +github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= +github.com/serialx/hashring v0.0.0-20200727003509-22c0c7ab6b1b h1:h+3JX2VoWTFuyQEo87pStk/a99dzIO1mM9KxIyLPGTU= +github.com/serialx/hashring v0.0.0-20200727003509-22c0c7ab6b1b/go.mod h1:/yeG0My1xr/u+HZrFQ1tOQQQQrOawfyMUH13ai5brBc= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/tinylib/msgp v1.1.5/go.mod h1:eQsjooMTnV42mHu917E26IogZ2930nFyBQdofk10Udg= +github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q= +github.com/twmb/murmur3 v1.1.6 h1:mqrRot1BRxm+Yct+vavLMou2/iJt0tNVTTC0QoIjaZg= +github.com/twmb/murmur3 v1.1.6/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s= +go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4= +go.opentelemetry.io/otel/exporters/prometheus v0.39.0 h1:whAaiHxOatgtKd+w0dOi//1KUxj3KoPINZdtDaDj3IA= +go.opentelemetry.io/otel/exporters/prometheus v0.39.0/go.mod h1:4jo5Q4CROlCpSPsXLhymi+LYrDXd2ObU5wbKayfZs7Y= +go.opentelemetry.io/otel/metric v1.16.0 h1:RbrpwVG1Hfv85LgnZ7+txXioPDoh6EdbZHo26Q3hqOo= +go.opentelemetry.io/otel/metric v1.16.0/go.mod h1:QE47cpOmkwipPiefDwo2wDzwJrlfxxNYodqc4xnGCo4= +go.opentelemetry.io/otel/sdk v1.16.0 h1:Z1Ok1YsijYL0CSJpHt4cS3wDDh7p572grzNrBMiMWgE= +go.opentelemetry.io/otel/sdk v1.16.0/go.mod h1:tMsIuKXuuIWPBAOrH+eHtvhTL+SntFtXF9QD68aP6p4= +go.opentelemetry.io/otel/sdk/metric v0.39.0 h1:Kun8i1eYf48kHH83RucG93ffz0zGV1sh46FAScOTuDI= +go.opentelemetry.io/otel/sdk/metric v0.39.0/go.mod h1:piDIRgjcK7u0HCL5pCA4e74qpK/jk3NiUoAHATVAmiI= +go.opentelemetry.io/otel/trace v1.16.0 h1:8JRpaObFoW0pxuVPapkgH8UhHQj+bJW8jJsCZEu5MQs= +go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/_examples/router-limitconcurrency/main.go b/_examples/router-limitconcurrency/main.go new file mode 100644 index 0000000000000000000000000000000000000000..7a238b7cc649a7cf542602212730eff5d66d1553 --- /dev/null +++ b/_examples/router-limitconcurrency/main.go @@ -0,0 +1,29 @@ +package main + +import ( + "log" + + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/router" + console "github.com/asynkron/goconsole" +) + +type workItem struct{ i int } + +const maxConcurrency = 5 + +func doWork(ctx actor.Context) { + if msg, ok := ctx.Message().(*workItem); ok { + // this is guaranteed to only execute with a max concurrency level of `maxConcurrency` + log.Printf("%v got message %d", ctx.Self(), msg.i) + } +} + +func main() { + system := actor.NewActorSystem() + pid := system.Root.Spawn(router.NewRoundRobinPool(maxConcurrency).Configure(actor.WithFunc(doWork))) + for i := 0; i < 1000; i++ { + system.Root.Send(pid, &workItem{i}) + } + _, _ = console.ReadLine() +} diff --git a/_examples/scheduler/go.mod b/_examples/scheduler/go.mod new file mode 100644 index 0000000000000000000000000000000000000000..e5ae0c59ef164726462b53e262aba61e3f04f9b9 --- /dev/null +++ b/_examples/scheduler/go.mod @@ -0,0 +1,39 @@ +module scheduler + +go 1.21 + +toolchain go1.21.2 + +replace gitee.com/simplexyz/simpleactor-go => ../.. + +require ( + gitee.com/simplexyz/simpleactor-go v0.0.0-00010101000000-000000000000 + github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716 +) + +require ( + github.com/Workiva/go-datastructures v1.1.1 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/emirpasic/gods v1.18.1 // indirect + github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/lithammer/shortuuid/v4 v4.0.0 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/orcaman/concurrent-map v1.0.0 // indirect + github.com/prometheus/client_golang v1.16.0 // indirect + github.com/prometheus/client_model v0.4.0 // indirect + github.com/prometheus/common v0.42.0 // indirect + github.com/prometheus/procfs v0.10.1 // indirect + github.com/twmb/murmur3 v1.1.6 // indirect + go.opentelemetry.io/otel v1.16.0 // indirect + go.opentelemetry.io/otel/exporters/prometheus v0.39.0 // indirect + go.opentelemetry.io/otel/metric v1.16.0 // indirect + go.opentelemetry.io/otel/sdk v1.16.0 // indirect + go.opentelemetry.io/otel/sdk/metric v0.39.0 // indirect + go.opentelemetry.io/otel/trace v1.16.0 // indirect + golang.org/x/sys v0.13.0 // indirect + google.golang.org/protobuf v1.31.0 // indirect +) diff --git a/_examples/scheduler/go.sum b/_examples/scheduler/go.sum new file mode 100644 index 0000000000000000000000000000000000000000..72e1596d2e6bb1dc5da2329b876b42c528bf5f7a --- /dev/null +++ b/_examples/scheduler/go.sum @@ -0,0 +1,100 @@ +github.com/Workiva/go-datastructures v1.1.1 h1:9G5u1UqKt6ABseAffHGNfbNQd7omRlWE5QaxNruzhE0= +github.com/Workiva/go-datastructures v1.1.1/go.mod h1:1yZL+zfsztete+ePzZz/Zb1/t5BnDuE2Ya2MMGhzP6A= +github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716 h1:SgyG4sXkrlalMoCfp20LiNPNhfJS7ez3opNdtihIxPc= +github.com/asynkron/goconsole v0.0.0-20160504192649-bfa12eebf716/go.mod h1:/zSlF0T2ArAsTG6SVu8d8qlK+19jjudjA3wWsxnGFHg= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/lithammer/shortuuid/v4 v4.0.0 h1:QRbbVkfgNippHOS8PXDkti4NaWeyYfcBTHtw7k08o4c= +github.com/lithammer/shortuuid/v4 v4.0.0/go.mod h1:Zs8puNcrvf2rV9rTH51ZLLcj7ZXqQI3lv67aw4KiB1Y= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/orcaman/concurrent-map v1.0.0 h1:I/2A2XPCb4IuQWcQhBhSwGfiuybl/J0ev9HDbW65HOY= +github.com/orcaman/concurrent-map v1.0.0/go.mod h1:Lu3tH6HLW3feq74c2GC+jIMS/K2CFcDWnWD9XkenwhI= +github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= +github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= +github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= +github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= +github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= +github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= +github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/tinylib/msgp v1.1.5/go.mod h1:eQsjooMTnV42mHu917E26IogZ2930nFyBQdofk10Udg= +github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q= +github.com/twmb/murmur3 v1.1.6 h1:mqrRot1BRxm+Yct+vavLMou2/iJt0tNVTTC0QoIjaZg= +github.com/twmb/murmur3 v1.1.6/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s= +go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4= +go.opentelemetry.io/otel/exporters/prometheus v0.39.0 h1:whAaiHxOatgtKd+w0dOi//1KUxj3KoPINZdtDaDj3IA= +go.opentelemetry.io/otel/exporters/prometheus v0.39.0/go.mod h1:4jo5Q4CROlCpSPsXLhymi+LYrDXd2ObU5wbKayfZs7Y= +go.opentelemetry.io/otel/metric v1.16.0 h1:RbrpwVG1Hfv85LgnZ7+txXioPDoh6EdbZHo26Q3hqOo= +go.opentelemetry.io/otel/metric v1.16.0/go.mod h1:QE47cpOmkwipPiefDwo2wDzwJrlfxxNYodqc4xnGCo4= +go.opentelemetry.io/otel/sdk v1.16.0 h1:Z1Ok1YsijYL0CSJpHt4cS3wDDh7p572grzNrBMiMWgE= +go.opentelemetry.io/otel/sdk v1.16.0/go.mod h1:tMsIuKXuuIWPBAOrH+eHtvhTL+SntFtXF9QD68aP6p4= +go.opentelemetry.io/otel/sdk/metric v0.39.0 h1:Kun8i1eYf48kHH83RucG93ffz0zGV1sh46FAScOTuDI= +go.opentelemetry.io/otel/sdk/metric v0.39.0/go.mod h1:piDIRgjcK7u0HCL5pCA4e74qpK/jk3NiUoAHATVAmiI= +go.opentelemetry.io/otel/trace v1.16.0 h1:8JRpaObFoW0pxuVPapkgH8UhHQj+bJW8jJsCZEu5MQs= +go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/_examples/scheduler/main.go b/_examples/scheduler/main.go new file mode 100644 index 0000000000000000000000000000000000000000..4b85b3127606539c5b7c5184e15ed9775cd24d78 --- /dev/null +++ b/_examples/scheduler/main.go @@ -0,0 +1,70 @@ +package main + +import ( + "log" + "math/rand" + "sync" + "time" + + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/scheduler" + console "github.com/asynkron/goconsole" +) + +var HelloMessages = []string{ + "Hello", + "Bonjour", + "Hola", + "Zdravstvuyte", + "Nǐn hǎo", + "Salve", + "Konnichiwa", + "Olá", +} + +func main() { + var wg sync.WaitGroup + wg.Add(5) + + rand.Seed(time.Now().UnixMicro()) + system := actor.NewActorSystem() + + log.SetFlags(log.LstdFlags | log.Lmicroseconds) + + count := 0 + props := actor.PropsFromFunc(func(ctx actor.Context) { + switch t := ctx.Message().(type) { + case []string: + count++ + log.Printf("\t%s, counter value: %d", t[rand.Intn(len(t))], count) + wg.Done() + case string: + log.Printf("\t%s\n", t) + } + }) + + pid := system.Root.Spawn(props) + + s := scheduler.NewTimerScheduler(system.Root) + cancel := s.SendRepeatedly(1*time.Millisecond, 1*time.Millisecond, pid, HelloMessages) + + wg.Wait() + cancel() + + wg.Add(100) // add 100 to our waiting group + cancel = s.RequestRepeatedly(1*time.Millisecond, 1*time.Millisecond, pid, HelloMessages) + + // the following timer will fire before the + // wait group is consumed and will stop the scheduler + time.Sleep(10 * time.Millisecond) + cancel() + + s.SendOnce(1*time.Millisecond, pid, "Hello Once") + + // this message will never show as we cancel it before it can be fired + cancel = s.RequestOnce(500*time.Millisecond, pid, "Hello Once Again") + time.Sleep(250 * time.Millisecond) + cancel() + + _, _ = console.ReadLine() +} diff --git a/actor/actor.pb.go b/actor/actor.pb.go new file mode 100644 index 0000000000000000000000000000000000000000..f9b665bd64167e15e03ed5e9eb0f8fe349df844b --- /dev/null +++ b/actor/actor.pb.go @@ -0,0 +1,710 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.31.0 +// protoc v4.24.3 +// source: actor.proto + +package actor + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type TerminatedReason int32 + +const ( + TerminatedReason_Stopped TerminatedReason = 0 + TerminatedReason_AddressTerminated TerminatedReason = 1 + TerminatedReason_NotFound TerminatedReason = 2 +) + +// Enum value maps for TerminatedReason. +var ( + TerminatedReason_name = map[int32]string{ + 0: "Stopped", + 1: "AddressTerminated", + 2: "NotFound", + } + TerminatedReason_value = map[string]int32{ + "Stopped": 0, + "AddressTerminated": 1, + "NotFound": 2, + } +) + +func (x TerminatedReason) Enum() *TerminatedReason { + p := new(TerminatedReason) + *p = x + return p +} + +func (x TerminatedReason) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (TerminatedReason) Descriptor() protoreflect.EnumDescriptor { + return file_actor_proto_enumTypes[0].Descriptor() +} + +func (TerminatedReason) Type() protoreflect.EnumType { + return &file_actor_proto_enumTypes[0] +} + +func (x TerminatedReason) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use TerminatedReason.Descriptor instead. +func (TerminatedReason) EnumDescriptor() ([]byte, []int) { + return file_actor_proto_rawDescGZIP(), []int{0} +} + +type PID struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Address string `protobuf:"bytes,1,opt,name=Address,proto3" json:"Address,omitempty"` + ID string `protobuf:"bytes,2,opt,name=ID,proto3" json:"ID,omitempty"` + RequestID uint32 `protobuf:"varint,3,opt,name=RequestID,proto3" json:"RequestID,omitempty"` + + //manually added + p *Process +} + +func (x *PID) Reset() { + *x = PID{} + if protoimpl.UnsafeEnabled { + mi := &file_actor_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PID) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PID) ProtoMessage() {} + +func (x *PID) ProtoReflect() protoreflect.Message { + mi := &file_actor_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PID.ProtoReflect.Descriptor instead. +func (*PID) Descriptor() ([]byte, []int) { + return file_actor_proto_rawDescGZIP(), []int{0} +} + +func (x *PID) GetAddress() string { + if x != nil { + return x.Address + } + return "" +} + +func (x *PID) GetID() string { + if x != nil { + return x.ID + } + return "" +} + +func (x *PID) GetRequestID() uint32 { + if x != nil { + return x.RequestID + } + return 0 +} + +// user messages +type PoisonPill struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *PoisonPill) Reset() { + *x = PoisonPill{} + if protoimpl.UnsafeEnabled { + mi := &file_actor_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PoisonPill) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PoisonPill) ProtoMessage() {} + +func (x *PoisonPill) ProtoReflect() protoreflect.Message { + mi := &file_actor_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PoisonPill.ProtoReflect.Descriptor instead. +func (*PoisonPill) Descriptor() ([]byte, []int) { + return file_actor_proto_rawDescGZIP(), []int{1} +} + +type DeadLetterResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Target *PID `protobuf:"bytes,1,opt,name=Target,proto3" json:"Target,omitempty"` +} + +func (x *DeadLetterResponse) Reset() { + *x = DeadLetterResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_actor_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DeadLetterResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeadLetterResponse) ProtoMessage() {} + +func (x *DeadLetterResponse) ProtoReflect() protoreflect.Message { + mi := &file_actor_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeadLetterResponse.ProtoReflect.Descriptor instead. +func (*DeadLetterResponse) Descriptor() ([]byte, []int) { + return file_actor_proto_rawDescGZIP(), []int{2} +} + +func (x *DeadLetterResponse) GetTarget() *PID { + if x != nil { + return x.Target + } + return nil +} + +// system messages +type Watch struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Watcher *PID `protobuf:"bytes,1,opt,name=Watcher,proto3" json:"Watcher,omitempty"` +} + +func (x *Watch) Reset() { + *x = Watch{} + if protoimpl.UnsafeEnabled { + mi := &file_actor_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Watch) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Watch) ProtoMessage() {} + +func (x *Watch) ProtoReflect() protoreflect.Message { + mi := &file_actor_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Watch.ProtoReflect.Descriptor instead. +func (*Watch) Descriptor() ([]byte, []int) { + return file_actor_proto_rawDescGZIP(), []int{3} +} + +func (x *Watch) GetWatcher() *PID { + if x != nil { + return x.Watcher + } + return nil +} + +type Unwatch struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Watcher *PID `protobuf:"bytes,1,opt,name=Watcher,proto3" json:"Watcher,omitempty"` +} + +func (x *Unwatch) Reset() { + *x = Unwatch{} + if protoimpl.UnsafeEnabled { + mi := &file_actor_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Unwatch) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Unwatch) ProtoMessage() {} + +func (x *Unwatch) ProtoReflect() protoreflect.Message { + mi := &file_actor_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Unwatch.ProtoReflect.Descriptor instead. +func (*Unwatch) Descriptor() ([]byte, []int) { + return file_actor_proto_rawDescGZIP(), []int{4} +} + +func (x *Unwatch) GetWatcher() *PID { + if x != nil { + return x.Watcher + } + return nil +} + +type Terminated struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Who *PID `protobuf:"bytes,1,opt,name=Who,proto3" json:"Who,omitempty"` + Why TerminatedReason `protobuf:"varint,2,opt,name=Why,proto3,enum=actor.TerminatedReason" json:"Why,omitempty"` +} + +func (x *Terminated) Reset() { + *x = Terminated{} + if protoimpl.UnsafeEnabled { + mi := &file_actor_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Terminated) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Terminated) ProtoMessage() {} + +func (x *Terminated) ProtoReflect() protoreflect.Message { + mi := &file_actor_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Terminated.ProtoReflect.Descriptor instead. +func (*Terminated) Descriptor() ([]byte, []int) { + return file_actor_proto_rawDescGZIP(), []int{5} +} + +func (x *Terminated) GetWho() *PID { + if x != nil { + return x.Who + } + return nil +} + +func (x *Terminated) GetWhy() TerminatedReason { + if x != nil { + return x.Why + } + return TerminatedReason_Stopped +} + +type Stop struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *Stop) Reset() { + *x = Stop{} + if protoimpl.UnsafeEnabled { + mi := &file_actor_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Stop) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Stop) ProtoMessage() {} + +func (x *Stop) ProtoReflect() protoreflect.Message { + mi := &file_actor_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Stop.ProtoReflect.Descriptor instead. +func (*Stop) Descriptor() ([]byte, []int) { + return file_actor_proto_rawDescGZIP(), []int{6} +} + +type Touch struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *Touch) Reset() { + *x = Touch{} + if protoimpl.UnsafeEnabled { + mi := &file_actor_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Touch) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Touch) ProtoMessage() {} + +func (x *Touch) ProtoReflect() protoreflect.Message { + mi := &file_actor_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Touch.ProtoReflect.Descriptor instead. +func (*Touch) Descriptor() ([]byte, []int) { + return file_actor_proto_rawDescGZIP(), []int{7} +} + +type Touched struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Who *PID `protobuf:"bytes,1,opt,name=Who,proto3" json:"Who,omitempty"` +} + +func (x *Touched) Reset() { + *x = Touched{} + if protoimpl.UnsafeEnabled { + mi := &file_actor_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Touched) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Touched) ProtoMessage() {} + +func (x *Touched) ProtoReflect() protoreflect.Message { + mi := &file_actor_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Touched.ProtoReflect.Descriptor instead. +func (*Touched) Descriptor() ([]byte, []int) { + return file_actor_proto_rawDescGZIP(), []int{8} +} + +func (x *Touched) GetWho() *PID { + if x != nil { + return x.Who + } + return nil +} + +var File_actor_proto protoreflect.FileDescriptor + +var file_actor_proto_rawDesc = []byte{ + 0x0a, 0x0b, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x05, 0x61, + 0x63, 0x74, 0x6f, 0x72, 0x22, 0x4d, 0x0a, 0x03, 0x50, 0x49, 0x44, 0x12, 0x18, 0x0a, 0x07, 0x41, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x41, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x02, 0x49, 0x44, 0x12, 0x1c, 0x0a, 0x09, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x49, 0x44, 0x22, 0x0c, 0x0a, 0x0a, 0x50, 0x6f, 0x69, 0x73, 0x6f, 0x6e, 0x50, 0x69, 0x6c, + 0x6c, 0x22, 0x38, 0x0a, 0x12, 0x44, 0x65, 0x61, 0x64, 0x4c, 0x65, 0x74, 0x74, 0x65, 0x72, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x22, 0x0a, 0x06, 0x54, 0x61, 0x72, 0x67, 0x65, + 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x2e, + 0x50, 0x49, 0x44, 0x52, 0x06, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x22, 0x2d, 0x0a, 0x05, 0x57, + 0x61, 0x74, 0x63, 0x68, 0x12, 0x24, 0x0a, 0x07, 0x57, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x2e, 0x50, 0x49, + 0x44, 0x52, 0x07, 0x57, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x22, 0x2f, 0x0a, 0x07, 0x55, 0x6e, + 0x77, 0x61, 0x74, 0x63, 0x68, 0x12, 0x24, 0x0a, 0x07, 0x57, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x2e, 0x50, + 0x49, 0x44, 0x52, 0x07, 0x57, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x22, 0x55, 0x0a, 0x0a, 0x54, + 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x65, 0x64, 0x12, 0x1c, 0x0a, 0x03, 0x57, 0x68, 0x6f, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x2e, 0x50, + 0x49, 0x44, 0x52, 0x03, 0x57, 0x68, 0x6f, 0x12, 0x29, 0x0a, 0x03, 0x57, 0x68, 0x79, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x2e, 0x54, 0x65, 0x72, + 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x65, 0x64, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x52, 0x03, 0x57, + 0x68, 0x79, 0x22, 0x06, 0x0a, 0x04, 0x53, 0x74, 0x6f, 0x70, 0x22, 0x07, 0x0a, 0x05, 0x54, 0x6f, + 0x75, 0x63, 0x68, 0x22, 0x27, 0x0a, 0x07, 0x54, 0x6f, 0x75, 0x63, 0x68, 0x65, 0x64, 0x12, 0x1c, + 0x0a, 0x03, 0x57, 0x68, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x61, 0x63, + 0x74, 0x6f, 0x72, 0x2e, 0x50, 0x49, 0x44, 0x52, 0x03, 0x57, 0x68, 0x6f, 0x2a, 0x44, 0x0a, 0x10, + 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x65, 0x64, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, + 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x74, 0x6f, 0x70, 0x70, 0x65, 0x64, 0x10, 0x00, 0x12, 0x15, 0x0a, + 0x11, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x74, + 0x65, 0x64, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x4e, 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, 0x64, + 0x10, 0x02, 0x42, 0x2a, 0x5a, 0x28, 0x67, 0x69, 0x74, 0x65, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x73, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x78, 0x79, 0x7a, 0x2f, 0x73, 0x69, 0x6d, 0x70, 0x6c, 0x65, + 0x61, 0x63, 0x74, 0x6f, 0x72, 0x2d, 0x67, 0x6f, 0x2f, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x62, 0x06, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_actor_proto_rawDescOnce sync.Once + file_actor_proto_rawDescData = file_actor_proto_rawDesc +) + +func file_actor_proto_rawDescGZIP() []byte { + file_actor_proto_rawDescOnce.Do(func() { + file_actor_proto_rawDescData = protoimpl.X.CompressGZIP(file_actor_proto_rawDescData) + }) + return file_actor_proto_rawDescData +} + +var file_actor_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_actor_proto_msgTypes = make([]protoimpl.MessageInfo, 9) +var file_actor_proto_goTypes = []interface{}{ + (TerminatedReason)(0), // 0: actor.TerminatedReason + (*PID)(nil), // 1: actor.PID + (*PoisonPill)(nil), // 2: actor.PoisonPill + (*DeadLetterResponse)(nil), // 3: actor.DeadLetterResponse + (*Watch)(nil), // 4: actor.Watch + (*Unwatch)(nil), // 5: actor.Unwatch + (*Terminated)(nil), // 6: actor.Terminated + (*Stop)(nil), // 7: actor.Stop + (*Touch)(nil), // 8: actor.Touch + (*Touched)(nil), // 9: actor.Touched +} +var file_actor_proto_depIdxs = []int32{ + 1, // 0: actor.DeadLetterResponse.Target:type_name -> actor.PID + 1, // 1: actor.Watch.Watcher:type_name -> actor.PID + 1, // 2: actor.Unwatch.Watcher:type_name -> actor.PID + 1, // 3: actor.Terminated.Who:type_name -> actor.PID + 0, // 4: actor.Terminated.Why:type_name -> actor.TerminatedReason + 1, // 5: actor.Touched.Who:type_name -> actor.PID + 6, // [6:6] is the sub-list for method output_type + 6, // [6:6] is the sub-list for method input_type + 6, // [6:6] is the sub-list for extension type_name + 6, // [6:6] is the sub-list for extension extendee + 0, // [0:6] is the sub-list for field type_name +} + +func init() { file_actor_proto_init() } +func file_actor_proto_init() { + if File_actor_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_actor_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PID); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_actor_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PoisonPill); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_actor_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DeadLetterResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_actor_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Watch); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_actor_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Unwatch); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_actor_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Terminated); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_actor_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Stop); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_actor_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Touch); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_actor_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Touched); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_actor_proto_rawDesc, + NumEnums: 1, + NumMessages: 9, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_actor_proto_goTypes, + DependencyIndexes: file_actor_proto_depIdxs, + EnumInfos: file_actor_proto_enumTypes, + MessageInfos: file_actor_proto_msgTypes, + }.Build() + File_actor_proto = out.File + file_actor_proto_rawDesc = nil + file_actor_proto_goTypes = nil + file_actor_proto_depIdxs = nil +} diff --git a/actor/actor.proto b/actor/actor.proto new file mode 100644 index 0000000000000000000000000000000000000000..c0cc9ebc988a65ffad6d42639b8b4951a7213813 --- /dev/null +++ b/actor/actor.proto @@ -0,0 +1,47 @@ +syntax = "proto3"; +package actor; +option go_package = "gitee.com/simplexyz/simpleactor-go/actor"; + +message PID { + string Address = 1; + string Id = 2; + uint32 request_id = 3; +} + +//user messages +message PoisonPill { +} + +message DeadLetterResponse { + PID Target = 1; +} + +//system messages +message Watch { + PID Watcher = 1; +} + +message Unwatch { + PID Watcher = 1; +} + +message Terminated { + PID who = 1; + TerminatedReason Why = 2; +} + +enum TerminatedReason { + Stopped = 0; + AddressTerminated = 1; + NotFound = 2; +} + +message Stop { +} + +message Touch { +} + +message Touched { + PID who = 1; +} \ No newline at end of file diff --git a/actor/actor_context.go b/actor/actor_context.go new file mode 100644 index 0000000000000000000000000000000000000000..791c903f6c2203cbfa9fa121ecd53432fbeb3190 --- /dev/null +++ b/actor/actor_context.go @@ -0,0 +1,769 @@ +package actor + +import ( + "context" + "errors" + "fmt" + "sync/atomic" + "time" + + "gitee.com/simplexyz/simpleactor-go/ctxext" + "gitee.com/simplexyz/simpleactor-go/log" + "gitee.com/simplexyz/simpleactor-go/metrics" + "github.com/emirpasic/gods/stacks/linkedliststack" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/metric" +) + +const ( + stateAlive int32 = iota + stateRestarting + stateStopping + stateStopped +) + +type actorContextExtras struct { + children PIDSet + receiveTimeoutTimer *time.Timer + rs *RestartStatistics + stash *linkedliststack.Stack + watchers PIDSet + context Context + extensions *ctxext.ContextExtensions +} + +func newActorContextExtras(context Context) *actorContextExtras { + this := &actorContextExtras{ + context: context, + extensions: ctxext.NewContextExtensions(), + } + + return this +} + +func (ctxExt *actorContextExtras) restartStats() *RestartStatistics { + // lazy initialize the child restart stats if this is the first time + // further mutations are handled within "restart" + if ctxExt.rs == nil { + ctxExt.rs = NewRestartStatistics() + } + + return ctxExt.rs +} + +func (ctxExt *actorContextExtras) initReceiveTimeoutTimer(timer *time.Timer) { + ctxExt.receiveTimeoutTimer = timer +} + +func (ctxExt *actorContextExtras) resetReceiveTimeoutTimer(time time.Duration) { + if ctxExt.receiveTimeoutTimer == nil { + return + } + + ctxExt.receiveTimeoutTimer.Reset(time) +} + +func (ctxExt *actorContextExtras) stopReceiveTimeoutTimer() { + if ctxExt.receiveTimeoutTimer == nil { + return + } + + ctxExt.receiveTimeoutTimer.Stop() +} + +func (ctxExt *actorContextExtras) killReceiveTimeoutTimer() { + if ctxExt.receiveTimeoutTimer == nil { + return + } + + ctxExt.receiveTimeoutTimer.Stop() + ctxExt.receiveTimeoutTimer = nil +} + +func (ctxExt *actorContextExtras) addChild(pid *PID) { + ctxExt.children.Add(pid) +} + +func (ctxExt *actorContextExtras) removeChild(pid *PID) { + ctxExt.children.Remove(pid) +} + +func (ctxExt *actorContextExtras) watch(watcher *PID) { + ctxExt.watchers.Add(watcher) +} + +func (ctxExt *actorContextExtras) unwatch(watcher *PID) { + ctxExt.watchers.Remove(watcher) +} + +type actorContext struct { + actor Actor + actorSystem *ActorSystem + extras *actorContextExtras + props *Props + parent *PID + self *PID + receiveTimeout time.Duration + messageOrEnvelope interface{} + state int32 +} + +var ( + _ SenderContext = &actorContext{} + _ ReceiverContext = &actorContext{} + _ SpawnerContext = &actorContext{} + _ basePart = &actorContext{} + _ stopperPart = &actorContext{} +) + +func newActorContext(actorSystem *ActorSystem, props *Props, parent *PID) *actorContext { + this := &actorContext{ + parent: parent, + props: props, + actorSystem: actorSystem, + } + + this.incarnateActor() + + return this +} + +func (ctx *actorContext) ensureExtras() *actorContextExtras { + if ctx.extras == nil { + ctxd := Context(ctx) + if ctx.props != nil && ctx.props.contextDecoratorChain != nil { + ctxd = ctx.props.contextDecoratorChain(ctxd) + } + + ctx.extras = newActorContextExtras(ctxd) + } + + return ctx.extras +} + +// +// Interface: Context +// + +func (ctx *actorContext) ActorSystem() *ActorSystem { + return ctx.actorSystem +} + +func (ctx *actorContext) Parent() *PID { + return ctx.parent +} + +func (ctx *actorContext) Self() *PID { + return ctx.self +} + +func (ctx *actorContext) Sender() *PID { + return UnwrapEnvelopeSender(ctx.messageOrEnvelope) +} + +func (ctx *actorContext) Actor() Actor { + return ctx.actor +} + +func (ctx *actorContext) ReceiveTimeout() time.Duration { + return ctx.receiveTimeout +} + +func (ctx *actorContext) Children() []*PID { + if ctx.extras == nil { + return make([]*PID, 0) + } + + return ctx.extras.children.Values() +} + +func (ctx *actorContext) Respond(response interface{}) { + // If the message is addressed to nil forward it to the dead letter channel + if ctx.Sender() == nil { + ctx.actorSystem.DeadLetter.SendUserMessage(nil, response) + + return + } + + ctx.Send(ctx.Sender(), response) +} + +func (ctx *actorContext) Stash() { + extra := ctx.ensureExtras() + if extra.stash == nil { + extra.stash = linkedliststack.New() + } + + extra.stash.Push(ctx.Message()) +} + +func (ctx *actorContext) Watch(who *PID) { + who.sendSystemMessage(ctx.actorSystem, &Watch{ + Watcher: ctx.self, + }) +} + +func (ctx *actorContext) Unwatch(who *PID) { + who.sendSystemMessage(ctx.actorSystem, &Unwatch{ + Watcher: ctx.self, + }) +} + +func (ctx *actorContext) SetReceiveTimeout(d time.Duration) { + if d <= 0 { + panic("Duration must be greater than zero") + } + + if d == ctx.receiveTimeout { + return + } + + if d < time.Millisecond { + // anything less than 1 millisecond is set to zero + d = 0 + } + + ctx.receiveTimeout = d + + ctx.ensureExtras() + ctx.extras.stopReceiveTimeoutTimer() + + if d > 0 { + if ctx.extras.receiveTimeoutTimer == nil { + ctx.extras.initReceiveTimeoutTimer(time.AfterFunc(d, ctx.receiveTimeoutHandler)) + } else { + ctx.extras.resetReceiveTimeoutTimer(d) + } + } +} + +func (ctx *actorContext) CancelReceiveTimeout() { + if ctx.extras == nil || ctx.extras.receiveTimeoutTimer == nil { + return + } + + ctx.extras.killReceiveTimeoutTimer() + ctx.receiveTimeout = 0 +} + +func (ctx *actorContext) receiveTimeoutHandler() { + if ctx.extras != nil && ctx.extras.receiveTimeoutTimer != nil { + ctx.CancelReceiveTimeout() + ctx.Send(ctx.self, receiveTimeoutMessage) + } +} + +func (ctx *actorContext) Forward(pid *PID) { + if msg, ok := ctx.messageOrEnvelope.(SystemMessage); ok { + // SystemMessage cannot be forwarded + plog.Error("SystemMessage cannot be forwarded", log.Message(msg)) + + return + } + + ctx.sendUserMessage(pid, ctx.messageOrEnvelope) +} + +func (ctx *actorContext) ReenterAfter(f *Future, cont func(res interface{}, err error)) { + wrapper := func() { + cont(f.result, f.err) + } + + message := ctx.messageOrEnvelope + // invoke the callback when the future completes + f.continueWith(func(res interface{}, err error) { + // send the wrapped callback as a continuation message to self + ctx.self.sendSystemMessage(ctx.actorSystem, &continuation{ + f: wrapper, + message: message, + }) + }) +} + +// +// Interface: sender +// + +func (ctx *actorContext) Message() interface{} { + return UnwrapEnvelopeMessage(ctx.messageOrEnvelope) +} + +func (ctx *actorContext) MessageHeader() ReadonlyMessageHeader { + return UnwrapEnvelopeHeader(ctx.messageOrEnvelope) +} + +func (ctx *actorContext) Send(pid *PID, message interface{}) { + ctx.sendUserMessage(pid, message) +} + +func (ctx *actorContext) sendUserMessage(pid *PID, message interface{}) { + if ctx.props.senderMiddlewareChain != nil { + ctx.props.senderMiddlewareChain(ctx.ensureExtras().context, pid, WrapEnvelope(message)) + } else { + pid.sendUserMessage(ctx.actorSystem, message) + } +} + +func (ctx *actorContext) Request(pid *PID, message interface{}) { + env := &MessageEnvelope{ + Header: nil, + Message: message, + Sender: ctx.Self(), + } + + ctx.sendUserMessage(pid, env) +} + +func (ctx *actorContext) RequestWithCustomSender(pid *PID, message interface{}, sender *PID) { + env := &MessageEnvelope{ + Header: nil, + Message: message, + Sender: sender, + } + ctx.sendUserMessage(pid, env) +} + +func (ctx *actorContext) RequestFuture(pid *PID, message interface{}, timeout time.Duration) *Future { + future := NewFuture(ctx.actorSystem, timeout) + env := &MessageEnvelope{ + Header: nil, + Message: message, + Sender: future.PID(), + } + ctx.sendUserMessage(pid, env) + + return future +} + +// +// Interface: receiver +// + +func (ctx *actorContext) Receive(envelope *MessageEnvelope) { + ctx.messageOrEnvelope = envelope + ctx.defaultReceive() + ctx.messageOrEnvelope = nil +} + +func (ctx *actorContext) defaultReceive() { + switch msg := ctx.Message().(type) { + case *PoisonPill: + ctx.Stop(ctx.self) + + case AutoRespond: + if ctx.props.contextDecoratorChain != nil { + ctx.actor.Receive(ctx.ensureExtras().context) + } else { + ctx.actor.Receive(ctx) + } + + res := msg.GetAutoResponse(ctx) + ctx.Respond(res) + + default: + // are we using decorators, if so, ensure it has been created + if ctx.props.contextDecoratorChain != nil { + ctx.actor.Receive(ctx.ensureExtras().context) + + return + } + + ctx.actor.Receive(ctx) + } +} + +// +// Interface: spawner +// + +func (ctx *actorContext) Spawn(props *Props) *PID { + pid, err := ctx.SpawnNamed(props, ctx.actorSystem.ProcessRegistry.NextId()) + if err != nil { + panic(err) + } + + return pid +} + +func (ctx *actorContext) SpawnPrefix(props *Props, prefix string) *PID { + pid, err := ctx.SpawnNamed(props, prefix+ctx.actorSystem.ProcessRegistry.NextId()) + if err != nil { + panic(err) + } + + return pid +} + +func (ctx *actorContext) SpawnNamed(props *Props, name string) (*PID, error) { + if props.guardianStrategy != nil { + panic(errors.New("props used to spawn child cannot have GuardianStrategy")) + } + + var pid *PID + + var err error + + if ctx.props.spawnMiddlewareChain != nil { + pid, err = ctx.props.spawnMiddlewareChain(ctx.actorSystem, ctx.self.ID+"/"+name, props, ctx) + } else { + pid, err = props.spawn(ctx.actorSystem, ctx.self.ID+"/"+name, ctx) + } + + if err != nil { + return pid, err + } + + ctx.ensureExtras().addChild(pid) + + return pid, nil +} + +// +// Interface: stopper +// + +// Stop will stop actor immediately regardless of existing user messages in mailbox. +func (ctx *actorContext) Stop(pid *PID) { + if ctx.actorSystem.Config.MetricsProvider != nil { + metricsSystem, ok := ctx.actorSystem.Extensions.Get(extensionId).(*Metrics) + if ok && metricsSystem.enabled { + _ctx := context.Background() + if instruments := metricsSystem.metrics.Get(metrics.InternalActorMetrics); instruments != nil { + instruments.ActorStoppedCount.Add(_ctx, 1, metric.WithAttributes(metricsSystem.CommonLabels(ctx)...)) + } + } + } + + pid.ref(ctx.actorSystem).Stop(pid) +} + +// StopFuture will stop actor immediately regardless of existing user messages in mailbox, and return its future. +func (ctx *actorContext) StopFuture(pid *PID) *Future { + future := NewFuture(ctx.actorSystem, 10*time.Second) + + pid.sendSystemMessage(ctx.actorSystem, &Watch{Watcher: future.pid}) + ctx.Stop(pid) + + return future +} + +// Poison will tell actor to stop after processing current user messages in mailbox. +func (ctx *actorContext) Poison(pid *PID) { + pid.sendUserMessage(ctx.actorSystem, poisonPillMessage) +} + +// PoisonFuture will tell actor to stop after processing current user messages in mailbox, and return its future. +func (ctx *actorContext) PoisonFuture(pid *PID) *Future { + future := NewFuture(ctx.actorSystem, 10*time.Second) + + pid.sendSystemMessage(ctx.actorSystem, &Watch{Watcher: future.pid}) + ctx.Poison(pid) + + return future +} + +// +// Interface: MessageInvoker +// + +func (ctx *actorContext) InvokeUserMessage(md interface{}) { + if atomic.LoadInt32(&ctx.state) == stateStopped { + // already stopped + return + } + + influenceTimeout := true + if ctx.receiveTimeout > 0 { + _, influenceTimeout = md.(NotInfluenceReceiveTimeout) + influenceTimeout = !influenceTimeout + + if influenceTimeout { + ctx.extras.stopReceiveTimeoutTimer() + } + } + + systemMetrics, ok := ctx.actorSystem.Extensions.Get(extensionId).(*Metrics) + if ok && systemMetrics.enabled { + t := time.Now() + + ctx.processMessage(md) + + delta := time.Since(t) + _ctx := context.Background() + + if instruments := systemMetrics.metrics.Get(metrics.InternalActorMetrics); instruments != nil { + histogram := instruments.ActorMessageReceiveHistogram + + labels := append( + systemMetrics.CommonLabels(ctx), + attribute.String("messagetype", fmt.Sprintf("%T", md)), + ) + histogram.Record(_ctx, delta.Seconds(), metric.WithAttributes(labels...)) + } + } else { + ctx.processMessage(md) + } + + if ctx.receiveTimeout > 0 && influenceTimeout { + ctx.extras.resetReceiveTimeoutTimer(ctx.receiveTimeout) + } +} + +func (ctx *actorContext) processMessage(m interface{}) { + if ctx.props.receiverMiddlewareChain != nil { + ctx.props.receiverMiddlewareChain(ctx.ensureExtras().context, WrapEnvelope(m)) + + return + } + + if ctx.props.contextDecoratorChain != nil { + ctx.ensureExtras().context.Receive(WrapEnvelope(m)) + + return + } + + ctx.messageOrEnvelope = m + ctx.defaultReceive() + ctx.messageOrEnvelope = nil // release message +} + +func (ctx *actorContext) incarnateActor() { + atomic.StoreInt32(&ctx.state, stateAlive) + ctx.actor = ctx.props.producer() + + metricsSystem, ok := ctx.actorSystem.Extensions.Get(extensionId).(*Metrics) + if ok && metricsSystem.enabled { + _ctx := context.Background() + if instruments := metricsSystem.metrics.Get(metrics.InternalActorMetrics); instruments != nil { + instruments.ActorSpawnCount.Add(_ctx, 1, metric.WithAttributes(metricsSystem.CommonLabels(ctx)...)) + } + } +} + +func (ctx *actorContext) InvokeSystemMessage(message interface{}) { + //goland:noinspection GrazieInspection + switch msg := message.(type) { + case *continuation: + ctx.messageOrEnvelope = msg.message // apply the message that was present when we started the await + msg.f() // invoke the continuation in the current actor context + + ctx.messageOrEnvelope = nil // release the message + case *Started: + ctx.InvokeUserMessage(msg) // forward + case *Watch: + ctx.handleWatch(msg) + case *Unwatch: + ctx.handleUnwatch(msg) + case *Stop: + ctx.handleStop() + case *Terminated: + ctx.handleTerminated(msg) + case *Failure: + ctx.handleFailure(msg) + case *Restart: + ctx.handleRestart() + default: + plog.Error("unknown system message", log.Message(msg)) + } +} + +func (ctx *actorContext) handleRootFailure(failure *Failure) { + defaultSupervisionStrategy.HandleFailure(ctx.actorSystem, ctx, failure.Who, failure.RestartStats, failure.Reason, failure.Message) +} + +func (ctx *actorContext) handleWatch(msg *Watch) { + if atomic.LoadInt32(&ctx.state) >= stateStopping { + msg.Watcher.sendSystemMessage(ctx.actorSystem, &Terminated{ + Who: ctx.self, + }) + } else { + ctx.ensureExtras().watch(msg.Watcher) + } +} + +func (ctx *actorContext) handleUnwatch(msg *Unwatch) { + if ctx.extras == nil { + return + } + + ctx.extras.unwatch(msg.Watcher) +} + +func (ctx *actorContext) handleRestart() { + atomic.StoreInt32(&ctx.state, stateRestarting) + ctx.InvokeUserMessage(restartingMessage) + ctx.stopAllChildren() + ctx.tryRestartOrTerminate() + + metricsSystem, ok := ctx.actorSystem.Extensions.Get(extensionId).(*Metrics) + if ok && metricsSystem.enabled { + _ctx := context.Background() + if instruments := metricsSystem.metrics.Get(metrics.InternalActorMetrics); instruments != nil { + instruments.ActorRestartedCount.Add(_ctx, 1, metric.WithAttributes(metricsSystem.CommonLabels(ctx)...)) + } + } +} + +// I am stopping. +func (ctx *actorContext) handleStop() { + if atomic.LoadInt32(&ctx.state) >= stateStopping { + // already stopping or stopped + return + } + + atomic.StoreInt32(&ctx.state, stateStopping) + + ctx.InvokeUserMessage(stoppingMessage) + ctx.stopAllChildren() + ctx.tryRestartOrTerminate() +} + +// child stopped, check if we can stop or restart (if needed). +func (ctx *actorContext) handleTerminated(msg *Terminated) { + if ctx.extras != nil { + ctx.extras.removeChild(msg.Who) + } + + ctx.InvokeUserMessage(msg) + ctx.tryRestartOrTerminate() +} + +// offload the supervision completely to the supervisor strategy. +func (ctx *actorContext) handleFailure(msg *Failure) { + if strategy, ok := ctx.actor.(SupervisorStrategy); ok { + strategy.HandleFailure(ctx.actorSystem, ctx, msg.Who, msg.RestartStats, msg.Reason, msg.Message) + + return + } + + ctx.props.getSupervisor().HandleFailure(ctx.actorSystem, ctx, msg.Who, msg.RestartStats, msg.Reason, msg.Message) +} + +func (ctx *actorContext) stopAllChildren() { + if ctx.extras == nil { + return + } + + ctx.extras.children.ForEach(func(_ int, pid *PID) { + ctx.Stop(pid) + }) +} + +func (ctx *actorContext) tryRestartOrTerminate() { + if ctx.extras != nil && !ctx.extras.children.Empty() { + return + } + + switch atomic.LoadInt32(&ctx.state) { + case stateRestarting: + ctx.CancelReceiveTimeout() + ctx.restart() + case stateStopping: + ctx.CancelReceiveTimeout() + ctx.finalizeStop() + } +} + +func (ctx *actorContext) restart() { + ctx.incarnateActor() + ctx.self.sendSystemMessage(ctx.actorSystem, resumeMailboxMessage) + ctx.InvokeUserMessage(startedMessage) + + if ctx.extras != nil && ctx.extras.stash != nil { + for !ctx.extras.stash.Empty() { + msg, _ := ctx.extras.stash.Pop() + ctx.InvokeUserMessage(msg) + } + } +} + +func (ctx *actorContext) finalizeStop() { + ctx.actorSystem.ProcessRegistry.Remove(ctx.self) + ctx.InvokeUserMessage(stoppedMessage) + + otherStopped := &Terminated{Who: ctx.self} + // Notify watchers + if ctx.extras != nil { + ctx.extras.watchers.ForEach(func(i int, pid *PID) { + pid.sendSystemMessage(ctx.actorSystem, otherStopped) + }) + } + // Notify parent + if ctx.parent != nil { + ctx.parent.sendSystemMessage(ctx.actorSystem, otherStopped) + } + + atomic.StoreInt32(&ctx.state, stateStopped) +} + +// +// Interface: Supervisor +// + +func (ctx *actorContext) EscalateFailure(reason interface{}, message interface{}) { + // debug setting, allows to output supervision failures in console/error level + if ctx.actorSystem.Config.DeveloperSupervisionLogging { + fmt.Println("[Supervision] Actor:", ctx.self, " failed with message:", message, " exception:", reason) + plog.Error("[Supervision]", log.Stringer("actor", ctx.self), log.Object("message", message), log.Object("exception", reason)) + } + + metricsSystem, ok := ctx.actorSystem.Extensions.Get(extensionId).(*Metrics) + if ok && metricsSystem.enabled { + _ctx := context.Background() + if instruments := metricsSystem.metrics.Get(metrics.InternalActorMetrics); instruments != nil { + instruments.ActorFailureCount.Add(_ctx, 1, metric.WithAttributes(metricsSystem.CommonLabels(ctx)...)) + } + } + + failure := &Failure{Reason: reason, Who: ctx.self, RestartStats: ctx.ensureExtras().restartStats(), Message: message} + + ctx.self.sendSystemMessage(ctx.actorSystem, suspendMailboxMessage) + + if ctx.parent == nil { + ctx.handleRootFailure(failure) + } else { + ctx.parent.sendSystemMessage(ctx.actorSystem, failure) + } +} + +func (ctx *actorContext) RestartChildren(pids ...*PID) { + for _, pid := range pids { + pid.sendSystemMessage(ctx.actorSystem, restartMessage) + } +} + +func (ctx *actorContext) StopChildren(pids ...*PID) { + for _, pid := range pids { + pid.sendSystemMessage(ctx.actorSystem, stopMessage) + } +} + +func (ctx *actorContext) ResumeChildren(pids ...*PID) { + for _, pid := range pids { + pid.sendSystemMessage(ctx.actorSystem, resumeMailboxMessage) + } +} + +// +// Miscellaneous +// + +func (ctx *actorContext) GoString() string { + return ctx.self.String() +} + +func (ctx *actorContext) String() string { + return ctx.self.String() +} + +func (ctx *actorContext) Get(id ctxext.ContextExtensionID) ctxext.ContextExtension { + extras := ctx.ensureExtras() + ext := extras.extensions.Get(id) + + return ext +} + +func (ctx *actorContext) Set(ext ctxext.ContextExtension) { + extras := ctx.ensureExtras() + extras.extensions.Set(ext) +} diff --git a/actor/actor_context_test.go b/actor/actor_context_test.go new file mode 100644 index 0000000000000000000000000000000000000000..a275d5dcaa1f4d6bd8c5e82540743fe0d20399a8 --- /dev/null +++ b/actor/actor_context_test.go @@ -0,0 +1,309 @@ +package actor + +import ( + "fmt" + "sync" + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func FuzzSpawnNamed(f *testing.F) { + f.Add("parent", "child") + + f.Fuzz(func(t *testing.T, parentName string, childName string) { + combined := parentName + "/" + childName + + pid, _ := spawnMockProcess(parentName) + + defer removeMockProcess(pid) + + props := &Props{ + spawner: func(actorSystem *ActorSystem, id string, _ *Props, _ SpawnerContext) (*PID, error) { + assert.Equal(t, combined, id) + + return NewPID(actorSystem.Address(), id), nil + }, + } + + parent := &actorContext{self: NewPID(localAddress, parentName), props: props, actorSystem: system} + child, err := parent.SpawnNamed(props, childName) + assert.NoError(t, err) + assert.Equal(t, parent.Children()[0], child) + }) +} + +// TestActorContext_Stop verifies if context is stopping and receives a Watch message, it should +// immediately respond with a Terminated message. +func TestActorContext_Stop(t *testing.T) { + t.Parallel() + + pid, p := spawnMockProcess("foo") + defer removeMockProcess(pid) + + other, o := spawnMockProcess("watcher") + defer removeMockProcess(other) + + o.On("SendSystemMessage", other, &Terminated{Who: pid}) + + props := PropsFromProducer(nullProducer, WithSupervisor(DefaultSupervisorStrategy())) + lc := newActorContext(system, props, nil) + lc.self = pid + lc.InvokeSystemMessage(&Stop{}) + lc.InvokeSystemMessage(&Watch{Watcher: other}) + + p.AssertExpectations(t) + o.AssertExpectations(t) +} + +func TestActorContext_SendMessage_WithSenderMiddleware(t *testing.T) { + t.Parallel() + + var wg sync.WaitGroup + + wg.Add(1) + + // Define a local context with no-op sender middleware + mw := func(next SenderFunc) SenderFunc { + return func(ctx SenderContext, target *PID, envelope *MessageEnvelope) { + next(ctx, target, envelope) + } + } + + props := PropsFromProducer(nullProducer, WithSupervisor(DefaultSupervisorStrategy()), WithSenderMiddleware(mw)) + ctx := newActorContext(system, props, nil) + + // Define a receiver to which the local context will send a message + var counter int + + receiver := rootContext.Spawn(PropsFromFunc(func(ctx Context) { + if _, ok := ctx.Message().(bool); ok { + counter++ + wg.Done() + } + })) + + // Send a message with Tell + // Then wait a little to allow the receiver to process the message + // TODO: There should be a better way to wait. + timeout := 3 * time.Millisecond + + ctx.Send(receiver, true) + wg.Wait() + assert.Equal(t, 1, counter) + + // Send a message with Request + counter = 0 // Reset the counter + + wg.Add(1) + ctx.Request(receiver, true) + wg.Wait() + assert.Equal(t, 1, counter) + + // Send a message with RequestFuture + counter = 0 // Reset the counter + + wg.Add(1) + + _ = ctx.RequestFuture(receiver, true, timeout).Wait() + wg.Wait() + assert.Equal(t, 1, counter) +} + +func BenchmarkActorContext_ProcessMessageNoMiddleware(b *testing.B) { + var m interface{} = 1 + + ctx := newActorContext(system, PropsFromFunc(nullReceive), nil) + for i := 0; i < b.N; i++ { + ctx.processMessage(m) + } +} + +func TestActorContext_Respond(t *testing.T) { + t.Parallel() + + var wg sync.WaitGroup + + wg.Add(1) + + // Defined a responder actor + // It simply echoes a received string. + responder := rootContext.Spawn(PropsFromFunc(func(ctx Context) { + if m, ok := ctx.Message().(string); ok { + ctx.Respond(fmt.Sprintf("Got a string: %v", m)) + } + })) + + // Be prepared to catch a response that the responder will send to nil + var gotResponseToNil bool + + deadLetterSubscriber := system.EventStream.Subscribe(func(msg interface{}) { + if deadLetter, ok := msg.(*DeadLetterEvent); ok { + if deadLetter.PID == nil { + gotResponseToNil = true + wg.Done() + } + } + }) + + // Send a message to the responder using Request + // The responder should send something back. + timeout := 3 * time.Millisecond + res, err := rootContext.RequestFuture(responder, "hello", timeout).Result() + assert.Nil(t, err) + assert.NotNil(t, res) + + resStr, ok := res.(string) + assert.True(t, ok) + assert.Equal(t, "Got a string: hello", resStr) + + // Ensure that the responder did not send anything to nil + assert.False(t, gotResponseToNil) + + // Send a message using Tell + rootContext.Send(responder, "hello") + + // Ensure that the responder actually send something to nil + wg.Wait() + assert.True(t, gotResponseToNil) + + // Cleanup + system.EventStream.Unsubscribe(deadLetterSubscriber) +} + +func TestActorContext_Forward(t *testing.T) { + t.Parallel() + // Defined a response actor + // It simply responds to the string message + responder := rootContext.Spawn(PropsFromFunc(func(ctx Context) { + if m, ok := ctx.Message().(string); ok { + ctx.Respond(fmt.Sprintf("Got a string: %v", m)) + } + })) + + // Defined a forwarder actor + // It simply forward the string message to responder + forwarder := rootContext.Spawn(PropsFromFunc(func(ctx Context) { + if _, ok := ctx.Message().(string); ok { + ctx.Forward(responder) + } + })) + + // Send a message to the responder using Request + // The responder should send something back. + timeout := 3 * time.Millisecond + res, err := rootContext.RequestFuture(forwarder, "hello", timeout).Result() + assert.Nil(t, err) + assert.NotNil(t, res) + + resStr, ok := res.(string) + assert.True(t, ok) + assert.Equal(t, "Got a string: hello", resStr) +} + +func BenchmarkActorContext_ProcessMessageWithMiddleware(b *testing.B) { + var m interface{} = 1 + + fn := func(next ReceiverFunc) ReceiverFunc { + return func(ctx ReceiverContext, env *MessageEnvelope) { + next(ctx, env) + } + } + + props := PropsFromProducer(nullProducer, WithSupervisor(DefaultSupervisorStrategy()), WithReceiverMiddleware(fn)) + ctx := newActorContext(system, props, nil) + + for i := 0; i < b.N; i++ { + ctx.processMessage(m) + } +} + +func benchmarkactorcontextSpawnwithmiddlewaren(n int, b *testing.B) { + middlewareFn := func(next SenderFunc) SenderFunc { + return func(ctx SenderContext, pid *PID, env *MessageEnvelope) { + next(ctx, pid, env) + } + } + + props := PropsFromProducer(nullProducer) + for i := 0; i < n; i++ { + props = props.Configure(WithSenderMiddleware(middlewareFn)) + } + + system := NewActorSystem() + parent := &actorContext{self: NewPID(localAddress, "foo"), props: props, actorSystem: system} + + for i := 0; i < b.N; i++ { + parent.Spawn(props) + } +} + +func BenchmarkActorContext_SpawnWithMiddleware0(b *testing.B) { + benchmarkactorcontextSpawnwithmiddlewaren(0, b) +} + +func BenchmarkActorContext_SpawnWithMiddleware1(b *testing.B) { + benchmarkactorcontextSpawnwithmiddlewaren(1, b) +} + +func BenchmarkActorContext_SpawnWithMiddleware2(b *testing.B) { + benchmarkactorcontextSpawnwithmiddlewaren(2, b) +} + +func BenchmarkActorContext_SpawnWithMiddleware5(b *testing.B) { + benchmarkactorcontextSpawnwithmiddlewaren(5, b) +} + +func TestActorContinueFutureInActor(t *testing.T) { + t.Parallel() + + pid := rootContext.Spawn(PropsFromFunc(func(ctx Context) { + if ctx.Message() == "request" { + ctx.Respond("done") + } + if ctx.Message() == "start" { + f := ctx.RequestFuture(ctx.Self(), "request", 5*time.Second) + ctx.ReenterAfter(f, func(res interface{}, err error) { + ctx.Respond(res) + }) + } + })) + res, err := rootContext.RequestFuture(pid, "start", time.Second).Result() + assert.NoError(t, err) + assert.Equal(t, "done", res) +} + +type dummyAutoRespond struct{} + +func (*dummyAutoRespond) GetAutoResponse(_ Context) interface{} { + return &dummyResponse{} +} + +func TestActorContextAutoRespondMessage(t *testing.T) { + t.Parallel() + + pid := rootContext.Spawn(PropsFromFunc(func(ctx Context) {})) + + var msg AutoRespond = &dummyAutoRespond{} + + res, err := rootContext.RequestFuture(pid, msg, 1*time.Second).Result() + assert.NoError(t, err) + assert.IsType(t, &dummyResponse{}, res) +} + +func TestActorContextAutoRespondTouchedMessage(t *testing.T) { + t.Parallel() + + pid := rootContext.Spawn(PropsFromFunc(func(ctx Context) {})) + + var msg AutoRespond = &Touch{} + + res, err := rootContext.RequestFuture(pid, msg, 1*time.Second).Result() + + res2, _ := res.(*Touched) + + assert.NoError(t, err) + assert.IsType(t, &Touched{}, res) + assert.True(t, res2.Who.Equal(pid)) +} diff --git a/actor/actor_example_test.go b/actor/actor_example_test.go new file mode 100644 index 0000000000000000000000000000000000000000..8de28baed1e9c0f3c9a7b3de41f9b8e0552aee72 --- /dev/null +++ b/actor/actor_example_test.go @@ -0,0 +1,67 @@ +package actor_test + +import ( + "fmt" + "sync" + "time" + + "gitee.com/simplexyz/simpleactor-go/actor" +) + +// Demonstrates how to create an actor using a function literal and how to send a message asynchronously +func Example() { + context := system.Root + props := actor.PropsFromFunc(func(c actor.Context) { + if msg, ok := c.Message().(string); ok { + fmt.Println(msg) // outputs "Hello World" + } + }) + + pid := context.Spawn(props) + + context.Send(pid, "Hello World") + time.Sleep(time.Millisecond * 100) + + _ = context.StopFuture(pid).Wait() // wait for the actor to stop + + // Output: Hello World +} + +// Demonstrates how to send a message from one actor to another and for the caller to wait for a response before +// proceeding +func Example_synchronous() { + var wg sync.WaitGroup + + wg.Add(1) + + // callee will wait for the PING message + callee := system.Root.Spawn(actor.PropsFromFunc(func(c actor.Context) { + if msg, ok := c.Message().(string); ok { + fmt.Println(msg) // outputs PING + c.Respond("PONG") + } + })) + + // caller will send a PING message and wait for the PONG + caller := system.Root.Spawn(actor.PropsFromFunc(func(c actor.Context) { + switch msg := c.Message().(type) { + // the first message an actor receives after it has started + case *actor.Started: + // send a PING to the callee, and specify the response + // is sent to Self, which is this actor'pids PID + c.Request(callee, "PING") + + case string: + fmt.Println(msg) // PONG + wg.Done() + } + })) + + wg.Wait() + _ = system.Root.StopFuture(callee).Wait() + _ = system.Root.StopFuture(caller).Wait() + + // Output: + // PING + // PONG +} diff --git a/actor/actor_process.go b/actor/actor_process.go new file mode 100644 index 0000000000000000000000000000000000000000..e8483fb077ae75823244e66a01187243e3d6ee5e --- /dev/null +++ b/actor/actor_process.go @@ -0,0 +1,31 @@ +package actor + +import ( + "sync/atomic" +) + +type ActorProcess struct { + mailbox Mailbox + dead int32 +} + +var _ Process = &ActorProcess{} + +func NewActorProcess(mailbox Mailbox) *ActorProcess { + return &ActorProcess{ + mailbox: mailbox, + } +} + +func (ref *ActorProcess) SendUserMessage(_ *PID, message interface{}) { + ref.mailbox.PostUserMessage(message) +} + +func (ref *ActorProcess) SendSystemMessage(_ *PID, message interface{}) { + ref.mailbox.PostSystemMessage(message) +} + +func (ref *ActorProcess) Stop(pid *PID) { + atomic.StoreInt32(&ref.dead, 1) + ref.SendSystemMessage(pid, stopMessage) +} diff --git a/actor/actor_system.go b/actor/actor_system.go new file mode 100644 index 0000000000000000000000000000000000000000..09b9f2de3d2c61e7118bb0ecadacb9a43ae70fbd --- /dev/null +++ b/actor/actor_system.go @@ -0,0 +1,86 @@ +package actor + +import ( + "net" + "strconv" + + "gitee.com/simplexyz/simpleactor-go/eventstream" + "gitee.com/simplexyz/simpleactor-go/extensions" + "github.com/lithammer/shortuuid/v4" +) + +//goland:noinspection GoNameStartsWithPackageName +type ActorSystem struct { + ProcessRegistry *ProcessRegistryValue + Root *RootContext + EventStream *eventstream.EventStream + Guardians *guardiansValue + DeadLetter *deadLetterProcess + Extensions *extensions.Extensions + Config *Config + ID string + stopper chan struct{} +} + +func (as *ActorSystem) NewLocalPID(id string) *PID { + return NewPID(as.ProcessRegistry.Address, id) +} + +func (as *ActorSystem) Address() string { + return as.ProcessRegistry.Address +} + +func (as *ActorSystem) GetHostPort() (host string, port int, err error) { + addr := as.ProcessRegistry.Address + if h, p, e := net.SplitHostPort(addr); e != nil { + if addr != localAddress { + err = e + } + + host = localAddress + port = -1 + } else { + host = h + port, err = strconv.Atoi(p) + } + + return +} + +func (as *ActorSystem) Shutdown() { + close(as.stopper) +} + +func (as *ActorSystem) IsStopped() bool { + select { + case <-as.stopper: + return true + default: + return false + } +} + +func NewActorSystem(options ...ConfigOption) *ActorSystem { + config := Configure(options...) + + return NewActorSystemWithConfig(config) +} + +func NewActorSystemWithConfig(config *Config) *ActorSystem { + system := &ActorSystem{} + system.ID = shortuuid.New() + system.Config = config + system.ProcessRegistry = NewProcessRegistry(system) + system.Root = NewRootContext(system, EmptyMessageHeader) + system.Guardians = NewGuardians(system) + system.EventStream = eventstream.NewEventStream() + system.DeadLetter = NewDeadLetter(system) + system.Extensions = extensions.NewExtensions() + SubscribeSupervision(system) + system.Extensions.Register(NewMetrics(config.MetricsProvider)) + + system.ProcessRegistry.Add(NewEventStreamProcess(system), "eventstream") + system.stopper = make(chan struct{}) + + return system +} diff --git a/actor/auto_respond.go b/actor/auto_respond.go new file mode 100644 index 0000000000000000000000000000000000000000..22031c49df5074f3d0367981713f15c225bdfcdc --- /dev/null +++ b/actor/auto_respond.go @@ -0,0 +1,5 @@ +package actor + +type AutoRespond interface { + GetAutoResponse(context Context) interface{} +} diff --git a/actor/behavior.go b/actor/behavior.go new file mode 100644 index 0000000000000000000000000000000000000000..6eb450ec6b64df1c5b26369a11df33ce766f1fa8 --- /dev/null +++ b/actor/behavior.go @@ -0,0 +1,73 @@ +package actor + +import "gitee.com/simplexyz/simpleactor-go/log" + +type Behavior []ReceiveFunc + +func NewBehavior() Behavior { + return make(Behavior, 0) +} + +func (b *Behavior) Become(receive ReceiveFunc) { + b.clear() + b.push(receive) +} + +func (b *Behavior) BecomeStacked(receive ReceiveFunc) { + b.push(receive) +} + +func (b *Behavior) UnbecomeStacked() { + b.pop() +} + +func (b *Behavior) Receive(context Context) { + behavior, ok := b.peek() + if ok { + behavior(context) + } else { + plog.Error("empty behavior called", log.Stringer("pid", context.Self())) + } +} + +func (b *Behavior) clear() { + if len(*b) == 0 { + return + } + + for i := range *b { + (*b)[i] = nil + } + + *b = (*b)[:0] +} + +func (b *Behavior) peek() (v ReceiveFunc, ok bool) { + if l := b.len(); l > 0 { + ok = true + v = (*b)[l-1] + } + + return +} + +func (b *Behavior) push(v ReceiveFunc) { + *b = append(*b, v) +} + +func (b *Behavior) pop() (v ReceiveFunc, ok bool) { + if l := b.len(); l > 0 { + l-- + + ok = true + v = (*b)[l] + (*b)[l] = nil + *b = (*b)[:l] + } + + return +} + +func (b *Behavior) len() int { + return len(*b) +} diff --git a/actor/behavior_test.go b/actor/behavior_test.go new file mode 100644 index 0000000000000000000000000000000000000000..7a813e30aba3800eaac7fc0bf082182cf8303a8d --- /dev/null +++ b/actor/behavior_test.go @@ -0,0 +1,63 @@ +package actor + +import ( + "testing" +) + +type BehaviorMessage struct{} + +type EchoSetBehaviorActor struct { + behavior Behavior +} + +func NewEchoBehaviorActor() Actor { + state := &EchoSetBehaviorActor{ + behavior: NewBehavior(), + } + state.behavior.Become(state.one) + + return state +} + +func (state *EchoSetBehaviorActor) Receive(context Context) { + state.behavior.Receive(context) +} + +func (state *EchoSetBehaviorActor) one(context Context) { + if _, ok := context.Message().(BehaviorMessage); ok { + state.behavior.Become(state.other) + } +} + +func (EchoSetBehaviorActor) other(context Context) { + if _, ok := context.Message().(EchoRequest); ok { + context.Respond(EchoResponse{}) + } +} + +func TestActorCanSetBehavior(t *testing.T) { + pid := rootContext.Spawn(PropsFromProducer(NewEchoBehaviorActor)) + defer rootContext.Stop(pid) + rootContext.Send(pid, BehaviorMessage{}) + fut := rootContext.RequestFuture(pid, EchoRequest{}, testTimeout) + assertFutureSuccess(fut, t) +} + +type PopBehaviorMessage struct{} + +func NewEchoUnbecomeActor() Actor { + state := &EchoSetBehaviorActor{ + behavior: NewBehavior(), + } + state.behavior.Become(state.one) + + return state +} + +func TestActorCanPopBehavior(t *testing.T) { + a := rootContext.Spawn(PropsFromProducer(NewEchoUnbecomeActor)) + rootContext.Send(a, BehaviorMessage{}) + rootContext.Send(a, PopBehaviorMessage{}) + fut := rootContext.RequestFuture(a, EchoRequest{}, testTimeout) + assertFutureSuccess(fut, t) +} diff --git a/actor/behaviorlogic_test.go b/actor/behaviorlogic_test.go new file mode 100644 index 0000000000000000000000000000000000000000..fe6b27791feabfdb9bbcde0e94325f2eac349ddd --- /dev/null +++ b/actor/behaviorlogic_test.go @@ -0,0 +1,93 @@ +package actor + +import ( + "strconv" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestBehavior_Len(t *testing.T) { + var bs Behavior + + assert.Len(t, bs, 0) + bs.push(func(Context) {}) + bs.push(func(Context) {}) + assert.Len(t, bs, 2) +} + +func TestBehavior_Push(t *testing.T) { + var bs Behavior + + assert.Len(t, bs, 0) + bs.push(func(Context) {}) + assert.Len(t, bs, 1) + bs.push(func(Context) {}) + assert.Len(t, bs, 2) +} + +func TestBehavior_Clear(t *testing.T) { + var bs Behavior + + bs.push(func(Context) {}) + bs.push(func(Context) {}) + assert.Len(t, bs, 2) + bs.clear() + assert.Len(t, bs, 0) +} + +func TestBehavior_Peek(t *testing.T) { + called := 0 + fn1 := ReceiveFunc(func(Context) { called = 1 }) + fn2 := ReceiveFunc(func(Context) { called = 2 }) + + cases := []struct { + items []ReceiveFunc + expected int + }{ + {[]ReceiveFunc{fn1, fn2}, 2}, + {[]ReceiveFunc{fn2, fn1}, 1}, + } + + for _, tc := range cases { + t.Run("", func(t *testing.T) { + var bs Behavior + for _, fn := range tc.items { + bs.push(fn) + } + a, _ := bs.peek() + a(nil) + assert.Equal(t, tc.expected, called) + }) + } +} + +func TestBehaviorStack_Pop_ExpectedOrder(t *testing.T) { + called := 0 + fn1 := ReceiveFunc(func(Context) { called = 1 }) + fn2 := ReceiveFunc(func(Context) { called = 2 }) + + cases := []struct { + items []ReceiveFunc + expected []int + }{ + {[]ReceiveFunc{fn1, fn2}, []int{2, 1}}, + {[]ReceiveFunc{fn2, fn1}, []int{1, 2}}, + } + + for i, tc := range cases { + t.Run("order "+strconv.Itoa(i), func(t *testing.T) { + var bs Behavior + for _, fn := range tc.items { + bs.push(fn) + } + + for _, e := range tc.expected { + a, _ := bs.pop() + a(nil) + assert.Equal(t, e, called) + called = 0 + } + }) + } +} diff --git a/actor/bounded.go b/actor/bounded.go new file mode 100644 index 0000000000000000000000000000000000000000..0c6eeee8c7b3af565feb563ad14c2c34ee85e4f9 --- /dev/null +++ b/actor/bounded.go @@ -0,0 +1,56 @@ +package actor + +import ( + "gitee.com/simplexyz/simpleactor-go/internal/queue/mpsc" + rbqueue "github.com/Workiva/go-datastructures/queue" +) + +type boundedMailboxQueue struct { + userMailbox *rbqueue.RingBuffer + dropping bool +} + +func (q *boundedMailboxQueue) Push(m interface{}) { + if q.dropping { + if q.userMailbox.Len() > 0 && q.userMailbox.Cap()-1 == q.userMailbox.Len() { + _, _ = q.userMailbox.Get() + } + } + + _ = q.userMailbox.Put(m) +} + +func (q *boundedMailboxQueue) Pop() interface{} { + if q.userMailbox.Len() > 0 { + m, _ := q.userMailbox.Get() + + return m + } + + return nil +} + +// Bounded returns a producer which creates a bounded mailbox of the specified size. +func Bounded(size int, mailboxStats ...MailboxMiddleware) MailboxProducer { + return bounded(size, false, mailboxStats...) +} + +// BoundedDropping returns a producer which creates a bounded mailbox of the specified size that drops front element on push. +func BoundedDropping(size int, mailboxStats ...MailboxMiddleware) MailboxProducer { + return bounded(size, true, mailboxStats...) +} + +func bounded(size int, dropping bool, mailboxStats ...MailboxMiddleware) MailboxProducer { + return func() Mailbox { + q := &boundedMailboxQueue{ + userMailbox: rbqueue.NewRingBuffer(uint64(size)), + dropping: dropping, + } + + return &defaultMailbox{ + systemMailbox: mpsc.New(), + userMailbox: q, + middlewares: mailboxStats, + } + } +} diff --git a/actor/child_restart_stats.go b/actor/child_restart_stats.go new file mode 100644 index 0000000000000000000000000000000000000000..082db0fff45ee5b7492eabe5f77502d5c9b7fc5c --- /dev/null +++ b/actor/child_restart_stats.go @@ -0,0 +1,48 @@ +package actor + +import ( + "time" +) + +// RestartStatistics keeps track of how many times an actor have restarted and when +type RestartStatistics struct { + failureTimes []time.Time +} + +// NewRestartStatistics construct a RestartStatistics +func NewRestartStatistics() *RestartStatistics { + return &RestartStatistics{[]time.Time{}} +} + +// FailureCount returns failure count +func (rs *RestartStatistics) FailureCount() int { + return len(rs.failureTimes) +} + +// Fail increases the associated actors' failure count +func (rs *RestartStatistics) Fail() { + rs.failureTimes = append(rs.failureTimes, time.Now()) +} + +// Reset the associated actors' failure count +func (rs *RestartStatistics) Reset() { + rs.failureTimes = []time.Time{} +} + +// NumberOfFailures returns number of failures within a given duration +func (rs *RestartStatistics) NumberOfFailures(withinDuration time.Duration) int { + if withinDuration == 0 { + return len(rs.failureTimes) + } + + num := 0 + currTime := time.Now() + + for _, t := range rs.failureTimes { + if currTime.Sub(t) < withinDuration { + num++ + } + } + + return num +} diff --git a/actor/child_test.go b/actor/child_test.go new file mode 100644 index 0000000000000000000000000000000000000000..9cd66fc93b619e11427656ad67cc610fbd831ed6 --- /dev/null +++ b/actor/child_test.go @@ -0,0 +1,139 @@ +package actor + +import ( + "sync" + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +type ( + CreateChildMessage struct{} + GetChildCountRequest struct{} + GetChildCountResponse struct{ ChildCount int } + CreateChildActor struct{} +) + +func (*CreateChildActor) Receive(context Context) { + switch context.Message().(type) { + case CreateChildMessage: + context.Spawn(PropsFromProducer(NewBlackHoleActor)) + case GetChildCountRequest: + reply := GetChildCountResponse{ChildCount: len(context.Children())} + context.Respond(reply) + } +} + +func NewCreateChildActor() Actor { + return &CreateChildActor{} +} + +func TestActorCanCreateChildren(t *testing.T) { + a := rootContext.Spawn(PropsFromProducer(NewCreateChildActor)) + defer rootContext.Stop(a) + expected := 10 + for i := 0; i < expected; i++ { + rootContext.Send(a, CreateChildMessage{}) + } + fut := rootContext.RequestFuture(a, GetChildCountRequest{}, testTimeout) + response := assertFutureSuccess(fut, t) + assert.Equal(t, expected, response.(GetChildCountResponse).ChildCount) +} + +type CreateChildThenStopActor struct { + replyTo *PID +} + +type GetChildCountMessage2 struct { + ReplyDirectly *PID + ReplyAfterStop *PID +} + +func (state *CreateChildThenStopActor) Receive(context Context) { + switch msg := context.Message().(type) { + case CreateChildMessage: + context.Spawn(PropsFromProducer(NewBlackHoleActor)) + case GetChildCountMessage2: + context.Send(msg.ReplyDirectly, true) + state.replyTo = msg.ReplyAfterStop + case *Stopped: + reply := GetChildCountResponse{ChildCount: len(context.Children())} + context.Send(state.replyTo, reply) + } +} + +func NewCreateChildThenStopActor() Actor { + return &CreateChildThenStopActor{} +} + +func TestActorCanStopChildren(t *testing.T) { + actor := rootContext.Spawn(PropsFromProducer(NewCreateChildThenStopActor)) + count := 10 + for i := 0; i < count; i++ { + rootContext.Send(actor, CreateChildMessage{}) + } + + future := NewFuture(system, testTimeout) + future2 := NewFuture(system, testTimeout) + rootContext.Send(actor, GetChildCountMessage2{ReplyDirectly: future.PID(), ReplyAfterStop: future2.PID()}) + + // wait for the actor to reply to the first responsePID + assertFutureSuccess(future, t) + + // then send a stop command + rootContext.Stop(actor) + + // wait for the actor to stop and get the result from the stopped handler + response := assertFutureSuccess(future2, t) + // we should have 0 children when the actor is stopped + assert.Equal(t, 0, response.(GetChildCountResponse).ChildCount) +} + +func TestActorReceivesTerminatedFromWatched(t *testing.T) { + child := rootContext.Spawn(PropsFromFunc(nullReceive)) + future := NewFuture(system, testTimeout) + var wg sync.WaitGroup + wg.Add(1) + + var r ReceiveFunc = func(c Context) { + switch msg := c.Message().(type) { + case *Started: + c.Watch(child) + wg.Done() + + case *Terminated: + ac := c.(*actorContext) + if msg.Who.Equal(child) && ac.ensureExtras().watchers.Empty() { + c.Send(future.PID(), true) + } + } + } + + rootContext.Spawn(PropsFromFunc(r)) + wg.Wait() + rootContext.Stop(child) + + assertFutureSuccess(future, t) +} + +func TestFutureDoesTimeout(t *testing.T) { + pid := rootContext.Spawn(PropsFromFunc(nullReceive)) + _, err := rootContext.RequestFuture(pid, "", time.Millisecond).Result() + assert.EqualError(t, err, ErrTimeout.Error()) +} + +func TestFutureDoesNotTimeout(t *testing.T) { + var r ReceiveFunc = func(c Context) { + if _, ok := c.Message().(string); !ok { + return + } + + time.Sleep(50 * time.Millisecond) + c.Respond("foo") + } + pid := rootContext.Spawn(PropsFromFunc(r)) + reply, err := rootContext.RequestFuture(pid, "", 2*time.Second).Result() + assert.NoError(t, err) + assert.Equal(t, "foo", reply) +} diff --git a/actor/common_test.go b/actor/common_test.go new file mode 100644 index 0000000000000000000000000000000000000000..84dd17e66ed31cc883ea0f9ec75e1b3844cd85f0 --- /dev/null +++ b/actor/common_test.go @@ -0,0 +1,213 @@ +package actor + +import ( + "fmt" + "time" + + "gitee.com/simplexyz/simpleactor-go/ctxext" + + "github.com/stretchr/testify/mock" +) + +var ( + nullProducer Producer = func() Actor { return nullReceive } + nullReceive ReceiveFunc = func(Context) {} + system = NewActorSystem() + rootContext = system.Root +) + +// mockContext +type mockContext struct { + mock.Mock +} + +// +// Interface: Context +// + +func (m *mockContext) ActorSystem() *ActorSystem { + args := m.Called() + return args.Get(0).(*ActorSystem) +} + +func (m *mockContext) Get(id ctxext.ContextExtensionID) ctxext.ContextExtension { + args := m.Called(id) + return args.Get(0).(ctxext.ContextExtension) +} + +func (m *mockContext) Set(ext ctxext.ContextExtension) { + m.Called(ext) +} + +func (m *mockContext) Parent() *PID { + args := m.Called() + return args.Get(0).(*PID) +} + +func (m *mockContext) Self() *PID { + args := m.Called() + return args.Get(0).(*PID) +} + +func (m *mockContext) Sender() *PID { + args := m.Called() + return args.Get(0).(*PID) +} + +func (m *mockContext) Actor() Actor { + args := m.Called() + return args.Get(0).(Actor) +} + +func (m *mockContext) ReceiveTimeout() time.Duration { + args := m.Called() + return args.Get(0).(time.Duration) +} + +func (m *mockContext) Children() []*PID { + args := m.Called() + return args.Get(0).([]*PID) +} + +func (m *mockContext) Respond(response interface{}) { + m.Called(response) +} + +func (m *mockContext) Stash() { + m.Called() +} + +func (m *mockContext) Watch(pid *PID) { + m.Called(pid) +} + +func (m *mockContext) Unwatch(pid *PID) { + m.Called(pid) +} + +func (m *mockContext) SetReceiveTimeout(d time.Duration) { + m.Called(d) +} + +func (m *mockContext) CancelReceiveTimeout() { + m.Called() +} + +func (m *mockContext) Forward(_ *PID) { + m.Called() +} + +func (m *mockContext) ReenterAfter(f *Future, cont func(res interface{}, err error)) { + m.Called(f, cont) +} + +// +// Interface: SenderContext +// + +func (m *mockContext) Message() interface{} { + args := m.Called() + + return args.Get(0) +} + +func (m *mockContext) MessageHeader() ReadonlyMessageHeader { + args := m.Called() + + return args.Get(0).(ReadonlyMessageHeader) +} + +func (m *mockContext) Send(_ *PID, _ interface{}) { + m.Called() +} + +func (m *mockContext) Request(pid *PID, message interface{}) { + args := m.Called() + + p, _ := system.ProcessRegistry.Get(pid) + env := &MessageEnvelope{ + Header: nil, + Message: message, + Sender: args.Get(0).(*PID), + } + p.SendUserMessage(pid, env) +} + +func (m *mockContext) RequestWithCustomSender(pid *PID, message interface{}, sender *PID) { + m.Called() + + p, _ := system.ProcessRegistry.Get(pid) + env := &MessageEnvelope{ + Header: nil, + Message: message, + Sender: sender, + } + p.SendUserMessage(pid, env) +} + +func (m *mockContext) RequestFuture(_ *PID, _ interface{}, _ time.Duration) *Future { + args := m.Called() + + return args.Get(0).(*Future) +} + +// +// Interface: ReceiverContext +// + +func (m *mockContext) Receive(envelope *MessageEnvelope) { + m.Called(envelope) +} + +// +// Interface: SpawnerContext +// + +func (m *mockContext) Spawn(p *Props) *PID { + args := m.Called(p) + return args.Get(0).(*PID) +} + +func (m *mockContext) SpawnPrefix(p *Props, prefix string) *PID { + args := m.Called(p, prefix) + + return args.Get(0).(*PID) +} + +func (m *mockContext) SpawnNamed(p *Props, name string) (*PID, error) { + args := m.Called(p, name) + + return args.Get(0).(*PID), args.Get(1).(error) +} + +// mockProcess +type mockProcess struct { + mock.Mock +} + +func spawnMockProcess(name string) (*PID, *mockProcess) { + p := &mockProcess{} + + pid, ok := system.ProcessRegistry.Add(p, name) + if !ok { + panic(fmt.Errorf("did not spawn named process '%vids'", name)) + } + + return pid, p +} + +func removeMockProcess(pid *PID) { + system.ProcessRegistry.Remove(pid) +} + +func (m *mockProcess) SendUserMessage(pid *PID, message interface{}) { + m.Called(pid, message) +} + +func (m *mockProcess) SendSystemMessage(pid *PID, message interface{}) { + m.Called(pid, message) +} + +func (m *mockProcess) Stop(pid *PID) { + m.Called(pid) +} diff --git a/actor/config.go b/actor/config.go new file mode 100644 index 0000000000000000000000000000000000000000..6f54b6c477fe99a75b6de9aed530361cece7d5f7 --- /dev/null +++ b/actor/config.go @@ -0,0 +1,65 @@ +package actor + +import ( + "fmt" + "net/http" + "time" + + "gitee.com/simplexyz/simpleactor-go/log" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/exporters/prometheus" + "go.opentelemetry.io/otel/metric" + sdkmetric "go.opentelemetry.io/otel/sdk/metric" + + "github.com/prometheus/client_golang/prometheus/promhttp" +) + +type Config struct { + DeadLetterThrottleInterval time.Duration // throttle deadletter logging after this interval + DeadLetterThrottleCount int32 // throttle deadletter logging after this count + DeadLetterRequestLogging bool // do not log dead-letters with sender + DeveloperSupervisionLogging bool // console log and promote supervision logs to Warning level + DiagnosticsSerializer func(Actor) string // extract diagnostics from actor and return as string + MetricsProvider metric.MeterProvider +} + +func defaultConfig() *Config { + return &Config{ + MetricsProvider: nil, + DeadLetterThrottleInterval: 1 * time.Second, + DeadLetterThrottleCount: 3, + DeadLetterRequestLogging: true, + DeveloperSupervisionLogging: false, + DiagnosticsSerializer: func(actor Actor) string { + return "" + }, + } +} + +func defaultPrometheusProvider(port int) metric.MeterProvider { + exporter, err := prometheus.New() + if err != nil { + err = fmt.Errorf("failed to initialize prometheus exporter: %w", err) + plog.Error(err.Error(), log.Error(err)) + + return nil + } + + provider := sdkmetric.NewMeterProvider(sdkmetric.WithReader(exporter.Reader)) + otel.SetMeterProvider(provider) + + http.Handle("/", promhttp.Handler()) + _port := fmt.Sprintf(":%d", port) + + go func() { + _ = http.ListenAndServe(_port, nil) + }() + + plog.Debug(fmt.Sprintf("Prometheus server running on %s", _port)) + + return provider +} + +func NewConfig() *Config { + return defaultConfig() +} diff --git a/actor/config_opts.go b/actor/config_opts.go new file mode 100644 index 0000000000000000000000000000000000000000..ebaec6f88594a33f755929b9682bb58d4a13ed96 --- /dev/null +++ b/actor/config_opts.go @@ -0,0 +1,63 @@ +package actor + +import ( + "time" + + "go.opentelemetry.io/otel/metric" +) + +type ConfigOption func(config *Config) + +func Configure(options ...ConfigOption) *Config { + config := defaultConfig() + for _, option := range options { + option(config) + } + + return config +} + +func WithDeadLetterThrottleInterval(duration time.Duration) ConfigOption { + return func(config *Config) { + config.DeadLetterThrottleInterval = duration + } +} + +func WithDeadLetterThrottleCount(count int32) ConfigOption { + return func(config *Config) { + config.DeadLetterThrottleCount = count + } +} + +func WithDeadLetterRequestLogging(enabled bool) ConfigOption { + return func(config *Config) { + config.DeadLetterRequestLogging = enabled + } +} + +func WithDeveloperSupervisionLogging(enabled bool) ConfigOption { + return func(config *Config) { + config.DeveloperSupervisionLogging = enabled + } +} + +func WithDiagnosticsSerializer(serializer func(Actor) string) ConfigOption { + return func(config *Config) { + config.DiagnosticsSerializer = serializer + } +} + +func WithMetricProviders(provider metric.MeterProvider) ConfigOption { + return func(config *Config) { + config.MetricsProvider = provider + } +} + +func WithDefaultPrometheusProvider(port ...int) ConfigOption { + _port := 2222 + if len(port) > 0 { + _port = port[0] + } + + return WithMetricProviders(defaultPrometheusProvider(_port)) +} diff --git a/actor/context.go b/actor/context.go new file mode 100644 index 0000000000000000000000000000000000000000..10f5a6dfca8af7f8515605f6fdae038f66fe55a2 --- /dev/null +++ b/actor/context.go @@ -0,0 +1,152 @@ +package actor + +import ( + "time" + + "gitee.com/simplexyz/simpleactor-go/ctxext" +) + +// Context contains contextual information for actors +type Context interface { + infoPart + basePart + messagePart + senderPart + receiverPart + spawnerPart + stopperPart + extensionPart +} + +type ExtensionContext interface { + extensionPart +} + +type SenderContext interface { + infoPart + senderPart + messagePart +} + +type ReceiverContext interface { + infoPart + receiverPart + messagePart + extensionPart +} + +type SpawnerContext interface { + infoPart + spawnerPart +} + +type extensionPart interface { + Get(id ctxext.ContextExtensionID) ctxext.ContextExtension + Set(ext ctxext.ContextExtension) +} + +type infoPart interface { + // Parent returns the PID for the current actors parent + Parent() *PID + + // Self returns the PID for the current actor + Self() *PID + + // Actor returns the actor associated with this context + Actor() Actor + + ActorSystem() *ActorSystem +} + +type basePart interface { + // ReceiveTimeout returns the current timeout + ReceiveTimeout() time.Duration + + // Children returns a slice of the actors children + Children() []*PID + + // Respond sends a response to the current `Sender` + // If the Sender is nil, the actor will panic + Respond(response interface{}) + + // Stash stashes the current message on a stack for reprocessing when the actor restarts + Stash() + + // Watch registers the actor as a monitor for the specified PID + Watch(pid *PID) + + // Unwatch unregisters the actor as a monitor for the specified PID + Unwatch(pid *PID) + + // SetReceiveTimeout sets the inactivity timeout, after which a ReceiveTimeout message will be sent to the actor. + // A duration of less than 1ms will disable the inactivity timer. + // + // If a message is received before the duration d, the timer will be reset. If the message conforms to + // the NotInfluenceReceiveTimeout interface, the timer will not be reset + SetReceiveTimeout(d time.Duration) + + CancelReceiveTimeout() + + // Forward forwards current message to the given PID + Forward(pid *PID) + + ReenterAfter(f *Future, continuation func(res interface{}, err error)) +} + +type messagePart interface { + // Message returns the current message to be processed + Message() interface{} + + // MessageHeader returns the meta information for the currently processed message + MessageHeader() ReadonlyMessageHeader +} + +type senderPart interface { + // Sender returns the PID of actor that sent currently processed message + Sender() *PID + + // Send sends a message to the given PID + Send(pid *PID, message interface{}) + + // Request sends a message to the given PID + Request(pid *PID, message interface{}) + + // RequestWithCustomSender sends a message to the given PID and also provides a Sender PID + RequestWithCustomSender(pid *PID, message interface{}, sender *PID) + + // RequestFuture sends a message to a given PID and returns a Future + RequestFuture(pid *PID, message interface{}, timeout time.Duration) *Future +} + +type receiverPart interface { + Receive(envelope *MessageEnvelope) +} + +type spawnerPart interface { + // Spawn starts a new child actor based on props and named with a unique id + Spawn(props *Props) *PID + + // SpawnPrefix starts a new child actor based on props and named using a prefix followed by a unique id + SpawnPrefix(props *Props, prefix string) *PID + + // SpawnNamed starts a new child actor based on props and named using the specified name + // + // ErrNameExists will be returned if id already exists + // + // Please do not use name sharing same pattern with system actors, for example "YourPrefix$1", "Remote$1", "future$1" + SpawnNamed(props *Props, id string) (*PID, error) +} + +type stopperPart interface { + // Stop will stop actor immediately regardless of existing user messages in mailbox. + Stop(pid *PID) + + // StopFuture will stop actor immediately regardless of existing user messages in mailbox, and return its future. + StopFuture(pid *PID) *Future + + // Poison will tell actor to stop after processing current user messages in mailbox. + Poison(pid *PID) + + // PoisonFuture will tell actor to stop after processing current user messages in mailbox, and return its future. + PoisonFuture(pid *PID) *Future +} diff --git a/actor/context_example_setReceiveTimeout_test.go b/actor/context_example_setReceiveTimeout_test.go new file mode 100644 index 0000000000000000000000000000000000000000..6cb27798d26a3ab7797c08d1afc34b2e4c948652 --- /dev/null +++ b/actor/context_example_setReceiveTimeout_test.go @@ -0,0 +1,44 @@ +package actor_test + +import ( + "fmt" + "sync" + "time" + + "gitee.com/simplexyz/simpleactor-go/actor" +) + +type setReceiveTimeoutActor struct { + *sync.WaitGroup +} + +// Receive is the default message handler when an actor is started +func (f *setReceiveTimeoutActor) Receive(context actor.Context) { + switch context.Message().(type) { + case *actor.Started: + // when the actor starts, set the receive timeout to 10 milliseconds. + // + // the system will send an *actor.ReceiveTimeout message every time the timeout + // expires until SetReceiveTimeout is called again with a duration < 1 ms] + context.SetReceiveTimeout(10 * time.Millisecond) + case *actor.ReceiveTimeout: + fmt.Println("timed out") + f.Done() + } +} + +// SetReceiveTimeout allows an actor to be notified repeatedly if it does not receive any messages +// for a specified duration +func ExampleContext_setReceiveTimeout() { + wg := &sync.WaitGroup{} + wg.Add(1) + + pid := system.Root.Spawn(actor.PropsFromProducer(func() actor.Actor { return &setReceiveTimeoutActor{wg} })) + defer func() { + _ = system.Root.StopFuture(pid).Wait() + }() + + wg.Wait() // wait for the ReceiveTimeout message + + // Output: timed out +} diff --git a/actor/deadletter.go b/actor/deadletter.go new file mode 100644 index 0000000000000000000000000000000000000000..63e79c570a79470993487cd02fb78d476cde951d --- /dev/null +++ b/actor/deadletter.go @@ -0,0 +1,106 @@ +package actor + +import ( + "context" + "fmt" + "strings" + + "gitee.com/simplexyz/simpleactor-go/log" + "gitee.com/simplexyz/simpleactor-go/metrics" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/metric" +) + +type deadLetterProcess struct { + actorSystem *ActorSystem +} + +var _ Process = &deadLetterProcess{} + +func NewDeadLetter(actorSystem *ActorSystem) *deadLetterProcess { + dp := &deadLetterProcess{ + actorSystem: actorSystem, + } + + shouldThrottle := NewThrottle(actorSystem.Config.DeadLetterThrottleCount, actorSystem.Config.DeadLetterThrottleInterval, func(i int32) { + plog.Info("[DeadLetter]", log.Int64("throttled", int64(i))) + }) + + actorSystem.ProcessRegistry.Add(dp, "deadletter") + _ = actorSystem.EventStream.Subscribe(func(msg interface{}) { + if deadLetter, ok := msg.(*DeadLetterEvent); ok { + + // send back a response instead of timeout. + if deadLetter.Sender != nil { + actorSystem.Root.Send(deadLetter.Sender, &DeadLetterResponse{}) + } + + // bail out if sender is set and deadletter request logging is false + if !actorSystem.Config.DeadLetterRequestLogging && deadLetter.Sender != nil { + return + } + + if _, isIgnoreDeadLetter := deadLetter.Message.(IgnoreDeadLetterLogging); !isIgnoreDeadLetter { + if shouldThrottle() == Open { + plog.Debug("[DeadLetter]", log.Stringer("pid", deadLetter.PID), log.TypeOf("msg", deadLetter.Message), log.Stringer("sender", deadLetter.Sender)) + } + } + } + }) + + // this subscriber may not be deactivated. + // it ensures that Watch commands that reach a stopped actor gets a Terminated message back. + // This can happen if one actor tries to Watch a PID, while another thread sends a Stop message. + actorSystem.EventStream.Subscribe(func(msg interface{}) { + if deadLetter, ok := msg.(*DeadLetterEvent); ok { + if m, ok := deadLetter.Message.(*Watch); ok { + // we know that this is a local actor since we get it on our own event stream, thus the address is not terminated + m.Watcher.sendSystemMessage(actorSystem, &Terminated{ + Who: deadLetter.PID, + Why: TerminatedReason_NotFound, + }) + } + } + }) + + return dp +} + +// A DeadLetterEvent is published via event.Publish when a message is sent to a nonexistent PID +type DeadLetterEvent struct { + PID *PID // The invalid process, to which the message was sent + Message interface{} // The message that could not be delivered + Sender *PID // the process that sent the Message +} + +func (dp *deadLetterProcess) SendUserMessage(pid *PID, message interface{}) { + metricsSystem, ok := dp.actorSystem.Extensions.Get(extensionId).(*Metrics) + if ok && metricsSystem.enabled { + ctx := context.Background() + if instruments := metricsSystem.metrics.Get(metrics.InternalActorMetrics); instruments != nil { + labels := []attribute.KeyValue{ + attribute.String("address", dp.actorSystem.Address()), + attribute.String("messagetype", strings.Replace(fmt.Sprintf("%T", message), "*", "", 1)), + } + + instruments.DeadLetterCount.Add(ctx, 1, metric.WithAttributes(labels...)) + } + } + _, msg, sender := UnwrapEnvelope(message) + dp.actorSystem.EventStream.Publish(&DeadLetterEvent{ + PID: pid, + Message: msg, + Sender: sender, + }) +} + +func (dp *deadLetterProcess) SendSystemMessage(pid *PID, message interface{}) { + dp.actorSystem.EventStream.Publish(&DeadLetterEvent{ + PID: pid, + Message: message, + }) +} + +func (dp *deadLetterProcess) Stop(pid *PID) { + dp.SendSystemMessage(pid, stopMessage) +} diff --git a/actor/deadletter_test.go b/actor/deadletter_test.go new file mode 100644 index 0000000000000000000000000000000000000000..31347e41b609b13add1e4a1e93ef1e23f9bd87f0 --- /dev/null +++ b/actor/deadletter_test.go @@ -0,0 +1,37 @@ +package actor + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestDeadLetterAfterStop(t *testing.T) { + a := rootContext.Spawn(PropsFromProducer(NewBlackHoleActor)) + done := false + sub := system.EventStream.Subscribe(func(msg interface{}) { + if deadLetter, ok := msg.(*DeadLetterEvent); ok { + if deadLetter.PID == a { + done = true + } + } + }) + defer system.EventStream.Unsubscribe(sub) + + _ = rootContext.StopFuture(a).Wait() + + rootContext.Send(a, "hello") + + assert.True(t, done) +} + +func TestDeadLetterWatchRespondsWithTerminate(t *testing.T) { + // create an actor + pid := rootContext.Spawn(PropsFromProducer(NewBlackHoleActor)) + // stop id + _ = rootContext.StopFuture(pid).Wait() + f := NewFuture(system, testTimeout) + // send a watch message, from our future + pid.sendSystemMessage(system, &Watch{Watcher: f.PID()}) + assertFutureSuccess(f, t) +} diff --git a/actor/directive.go b/actor/directive.go new file mode 100644 index 0000000000000000000000000000000000000000..d2ae5d11e66d7d4931ad3504851f9be344fbdd7a --- /dev/null +++ b/actor/directive.go @@ -0,0 +1,20 @@ +package actor + +// Directive is an enum for supervision actions +type Directive int + +// Directive determines how a supervisor should handle a faulting actor +const ( + // ResumeDirective instructs the supervisor to resume the actor and continue processing messages + ResumeDirective Directive = iota + + // RestartDirective instructs the supervisor to discard the actor, replacing it with a new instance, + // before processing additional messages + RestartDirective + + // StopDirective instructs the supervisor to stop the actor + StopDirective + + // EscalateDirective instructs the supervisor to escalate handling of the failure to the actor'pids parent supervisor + EscalateDirective +) diff --git a/actor/directive_string.go b/actor/directive_string.go new file mode 100644 index 0000000000000000000000000000000000000000..b7c2c0cc1e68e0772c7c457fd1aae9d02d18a8a3 --- /dev/null +++ b/actor/directive_string.go @@ -0,0 +1,18 @@ +// Code generated by "stringer -type=Directive"; DO NOT EDIT + +package actor + +import "fmt" + +//goland:noinspection GoSnakeCaseUsage +const _Directive_name = "ResumeDirectiveRestartDirectiveStopDirectiveEscalateDirective" + +//goland:noinspection GoSnakeCaseUsage +var _Directive_index = [...]uint8{0, 15, 31, 44, 61} + +func (i Directive) String() string { + if i < 0 || i >= Directive(len(_Directive_index)-1) { + return fmt.Sprintf("Directive(%d)", i) + } + return _Directive_name[_Directive_index[i]:_Directive_index[i+1]] +} diff --git a/actor/dispatcher.go b/actor/dispatcher.go new file mode 100644 index 0000000000000000000000000000000000000000..b856db1498d8251fcb298645580b095ef737916c --- /dev/null +++ b/actor/dispatcher.go @@ -0,0 +1,38 @@ +package actor + +type Dispatcher interface { + Schedule(fn func()) + Throughput() int +} + +type goroutineDispatcher int + +var _ Dispatcher = goroutineDispatcher(0) + +func (goroutineDispatcher) Schedule(fn func()) { + go fn() +} + +func (d goroutineDispatcher) Throughput() int { + return int(d) +} + +func NewDefaultDispatcher(throughput int) Dispatcher { + return goroutineDispatcher(throughput) +} + +type synchronizedDispatcher int + +var _ Dispatcher = synchronizedDispatcher(0) + +func (synchronizedDispatcher) Schedule(fn func()) { + fn() +} + +func (d synchronizedDispatcher) Throughput() int { + return int(d) +} + +func NewSynchronizedDispatcher(throughput int) Dispatcher { + return synchronizedDispatcher(throughput) +} diff --git a/actor/doc.go b/actor/doc.go new file mode 100644 index 0000000000000000000000000000000000000000..c70ec420c3dc9fe3676f6b003cbefb2c9f998624 --- /dev/null +++ b/actor/doc.go @@ -0,0 +1,70 @@ +/* +Package actor declares the types used to represent actors in the Actor Model. + +The actors model provide a high level abstraction for writing concurrent and distributed systems. This approach +simplifies the burden imposed on engineers, such as explicit locks and concurrent access to shared state, as actors +receive messages synchronously. + +The following quote from Wikipedia distills the definition of an actor down to its essence + + In response to a message that it receives, an actor can: make local decisions, create more actors, + send more messages, and determine how to respond to the next message received. + +# Creating Actors + +Props provide the building blocks for declaring how actors should be created. The following example defines an actor +using a function literal to process messages: + + var props Props = actor.PropsFromFunc(func(c Context) { + // process messages + }) + +Alternatively, a type which conforms to the Actor interface, by defining a single Receive method, can be used. + + type MyActor struct {} + + func (a *MyActor) Receive(c Context) { + // process messages + } + + var props Props = actor.PropsFromProducer(func() Actor { return &MyActor{} }) + +Spawn and SpawnNamed use the given props to create a running instances of an actor. Once spawned, the actor is +ready to process incoming messages. To spawn an actor with a unique name, use + + pid := context.Spawn(props) + +The result of calling Spawn is a unique PID or process identifier. + +Each time an actor is spawned, a new mailbox is created and associated with the PID. Messages are sent to the mailbox +and then forwarded to the actor to process. + +# Processing Messages + +An actor processes messages via its Receive handler. The signature of this function is: + + Receive(c actor.Context) + +The actor system guarantees that this method is called synchronously, therefore there is no requirement to protect +shared state inside calls to this function. + +# Communicating With Actors + +A PID is the primary interface for sending messages to actors. Context.Send is used to send an asynchronous +message to the actor associated with the PID: + + context.Aend(pid, "Hello World") + +Depending on the requirements, communication between actors can take place synchronously or asynchronously. Regardless +of the circumstances, actors always communicate via a PID. + +When sending a message using PID.Request or PID.RequestFuture, the actor which receives the message will respond +using the Context.Sender method, which returns the PID of of the sender. + +For synchronous communication, an actor will use a Future and wait for the result before continuing. To send a message +to an actor and wait for a response, use the RequestFuture method, which returns a Future: + + f := actor.RequestFuture(pid,"Hello", 50 * time.Millisecond) + res, err := f.Result() // waits for pid to reply +*/ +package actor diff --git a/actor/eventstream_process.go b/actor/eventstream_process.go new file mode 100644 index 0000000000000000000000000000000000000000..4a7c4c3826adce8e4a9f7bd2fce265c773e9afd2 --- /dev/null +++ b/actor/eventstream_process.go @@ -0,0 +1,24 @@ +package actor + +type EventStreamProcess struct { + system *ActorSystem +} + +var _ Process = &EventStreamProcess{} + +func NewEventStreamProcess(actorSystem *ActorSystem) *EventStreamProcess { + return &EventStreamProcess{system: actorSystem} +} + +func (e *EventStreamProcess) SendUserMessage(_ *PID, message interface{}) { + _, msg, _ := UnwrapEnvelope(message) + e.system.EventStream.Publish(msg) +} + +func (e *EventStreamProcess) SendSystemMessage(_ *PID, _ interface{}) { + // pass +} + +func (e *EventStreamProcess) Stop(_ *PID) { + // pass +} diff --git a/actor/eventstream_process_test.go b/actor/eventstream_process_test.go new file mode 100644 index 0000000000000000000000000000000000000000..0b871910d85ccec4c5b429f365e2d06517a6ebc4 --- /dev/null +++ b/actor/eventstream_process_test.go @@ -0,0 +1,38 @@ +package actor + +import ( + "testing" +) + +type EsTestMsg struct{} + +func TestSendsMessagesToEventStream(t *testing.T) { + testCases := []struct { + name string + message interface{} + }{ + {name: "plain", message: &EsTestMsg{}}, + {name: "envelope", message: WrapEnvelope(&EsTestMsg{})}, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + system := NewActorSystem() + + gotMessageChan := make(chan struct{}, 1) + + subscription := system.EventStream.Subscribe(func(evt interface{}) { + if _, ok := evt.(*EsTestMsg); ok { + gotMessageChan <- struct{}{} + } + }) + defer system.EventStream.Unsubscribe(subscription) + + pid := system.NewLocalPID("eventstream") + + system.Root.Send(pid, testCase.message) + + <-gotMessageChan + }) + } +} diff --git a/actor/future.go b/actor/future.go new file mode 100644 index 0000000000000000000000000000000000000000..a3c55bff0d7e98133f302a8a8616a4cd93c15be5 --- /dev/null +++ b/actor/future.go @@ -0,0 +1,228 @@ +package actor + +import ( + "context" + "errors" + "sync" + "sync/atomic" + "time" + "unsafe" + + "gitee.com/simplexyz/simpleactor-go/log" + "gitee.com/simplexyz/simpleactor-go/metrics" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/metric" +) + +// ErrTimeout is the error used when a future times out before receiving a result. +var ErrTimeout = errors.New("future: timeout") + +// ErrDeadLetter is meaning you request to a unreachable PID. +var ErrDeadLetter = errors.New("future: dead letter") + +// NewFuture creates and returns a new actor.Future with a timeout of duration d. +func NewFuture(actorSystem *ActorSystem, d time.Duration) *Future { + ref := &futureProcess{Future{actorSystem: actorSystem, cond: sync.NewCond(&sync.Mutex{})}} + id := actorSystem.ProcessRegistry.NextId() + + pid, ok := actorSystem.ProcessRegistry.Add(ref, "future"+id) + if !ok { + plog.Error("failed to register future process", log.Stringer("pid", pid)) + } + + sysMetrics, ok := actorSystem.Extensions.Get(extensionId).(*Metrics) + if ok && sysMetrics.enabled { + if instruments := sysMetrics.metrics.Get(metrics.InternalActorMetrics); instruments != nil { + ctx := context.Background() + labels := []attribute.KeyValue{ + attribute.String("address", ref.actorSystem.Address()), + } + + instruments.FuturesStartedCount.Add(ctx, 1, metric.WithAttributes(labels...)) + } + } + + ref.pid = pid + + if d >= 0 { + tp := time.AfterFunc(d, func() { + ref.cond.L.Lock() + if ref.done { + ref.cond.L.Unlock() + + return + } + ref.err = ErrTimeout + ref.cond.L.Unlock() + ref.Stop(pid) + }) + atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&ref.t)), unsafe.Pointer(tp)) + } + + return &ref.Future +} + +type Future struct { + actorSystem *ActorSystem + pid *PID + cond *sync.Cond + // protected by cond + done bool + result interface{} + err error + t *time.Timer + pipes []*PID + completions []func(res interface{}, err error) +} + +// PID to the backing actor for the Future result. +func (f *Future) PID() *PID { + return f.pid +} + +// PipeTo forwards the result or error of the future to the specified pids. +func (f *Future) PipeTo(pids ...*PID) { + f.cond.L.Lock() + f.pipes = append(f.pipes, pids...) + // for an already completed future, force push the result to targets. + if f.done { + f.sendToPipes() + } + f.cond.L.Unlock() +} + +func (f *Future) sendToPipes() { + if f.pipes == nil { + return + } + + var m interface{} + if f.err != nil { + m = f.err + } else { + m = f.result + } + + for _, pid := range f.pipes { + pid.sendUserMessage(f.actorSystem, m) + } + + f.pipes = nil +} + +func (f *Future) wait() { + f.cond.L.Lock() + for !f.done { + f.cond.Wait() + } + f.cond.L.Unlock() +} + +// Result waits for the future to resolve. +func (f *Future) Result() (interface{}, error) { + f.wait() + + return f.result, f.err +} + +func (f *Future) Wait() error { + f.wait() + + return f.err +} + +func (f *Future) continueWith(continuation func(res interface{}, err error)) { + f.cond.L.Lock() + defer f.cond.L.Unlock() // use defer as the continuation co + // uld blow up + if f.done { + continuation(f.result, f.err) + } else { + f.completions = append(f.completions, continuation) + } +} + +// futureProcess is a struct carrying a response PID and a channel where the response is placed. +type futureProcess struct { + Future +} + +var _ Process = &futureProcess{} + +func (ref *futureProcess) SendUserMessage(pid *PID, message interface{}) { + defer ref.instrument() + + _, msg, _ := UnwrapEnvelope(message) + + if _, ok := msg.(*DeadLetterResponse); ok { + ref.result = nil + ref.err = ErrDeadLetter + } else { + ref.result = msg + } + + ref.Stop(pid) +} + +func (ref *futureProcess) SendSystemMessage(pid *PID, message interface{}) { + defer ref.instrument() + ref.result = message + ref.Stop(pid) +} + +func (ref *futureProcess) instrument() { + sysMetrics, ok := ref.actorSystem.Extensions.Get(extensionId).(*Metrics) + if ok && sysMetrics.enabled { + ctx := context.Background() + labels := []attribute.KeyValue{ + attribute.String("address", ref.actorSystem.Address()), + } + + instruments := sysMetrics.metrics.Get(metrics.InternalActorMetrics) + if instruments != nil { + if ref.err == nil { + instruments.FuturesCompletedCount.Add(ctx, 1, metric.WithAttributes(labels...)) + } else { + instruments.FuturesTimedOutCount.Add(ctx, 1, metric.WithAttributes(labels...)) + } + } + } +} + +func (ref *futureProcess) Stop(pid *PID) { + ref.cond.L.Lock() + if ref.done { + ref.cond.L.Unlock() + + return + } + + ref.done = true + tp := (*time.Timer)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&ref.t)))) + + if tp != nil { + tp.Stop() + } + + ref.actorSystem.ProcessRegistry.Remove(pid) + + ref.sendToPipes() + ref.runCompletions() + ref.cond.L.Unlock() + ref.cond.Signal() +} + +// TODO: we could replace "pipes" with this +// instead of pushing PIDs to pipes, we could push wrapper funcs that tells the pid +// as a completion, that would unify the model. +func (f *Future) runCompletions() { + if f.completions == nil { + return + } + + for _, c := range f.completions { + c(f.result, f.err) + } + + f.completions = nil +} diff --git a/actor/future_example_test.go b/actor/future_example_test.go new file mode 100644 index 0000000000000000000000000000000000000000..c512e9fa573b3d2a744f919ac23e69812d16a102 --- /dev/null +++ b/actor/future_example_test.go @@ -0,0 +1,34 @@ +package actor_test + +import ( + "fmt" + "sync" + "time" + + "gitee.com/simplexyz/simpleactor-go/actor" +) + +var system = actor.NewActorSystem() + +func ExampleFuture_PipeTo() { + var wg sync.WaitGroup + wg.Add(1) + + // test actor that will be the target of the future PipeTo + pid := system.Root.Spawn(actor.PropsFromFunc(func(ctx actor.Context) { + // check if the message is a string and therefore + // the "hello world" message piped from the future + if m, ok := ctx.Message().(string); ok { + fmt.Println(m) + wg.Done() + } + })) + + f := actor.NewFuture(system, 50*time.Millisecond) + f.PipeTo(pid) + // resolve the future and pipe to waiting actor + system.Root.Send(f.PID(), "hello world") + wg.Wait() + + // Output: hello world +} diff --git a/actor/future_test.go b/actor/future_test.go new file mode 100644 index 0000000000000000000000000000000000000000..bf860a1aff712f7fbedbda4b36407ff33a2c5846 --- /dev/null +++ b/actor/future_test.go @@ -0,0 +1,126 @@ +package actor + +import ( + "testing" + "time" + + "gitee.com/simplexyz/simpleactor-go/log" + "github.com/stretchr/testify/assert" +) + +func TestFuture_PipeTo_Message(t *testing.T) { + a1, p1 := spawnMockProcess("a1") + a2, p2 := spawnMockProcess("a2") + a3, p3 := spawnMockProcess("a3") + defer func() { + removeMockProcess(a1) + removeMockProcess(a2) + removeMockProcess(a3) + }() + + f := NewFuture(system, 1*time.Second) + + p1.On("SendUserMessage", a1, "hello") + p2.On("SendUserMessage", a2, "hello") + p3.On("SendUserMessage", a3, "hello") + + f.PipeTo(a1) + f.PipeTo(a2) + f.PipeTo(a3) + + ref, _ := system.ProcessRegistry.Get(f.pid) + assert.IsType(t, &futureProcess{}, ref) + fp, _ := ref.(*futureProcess) + + fp.SendUserMessage(f.pid, "hello") + p1.AssertExpectations(t) + p2.AssertExpectations(t) + p3.AssertExpectations(t) + assert.Empty(t, fp.pipes, "pipes were not cleared") +} + +func TestFuture_PipeTo_TimeoutSendsError(t *testing.T) { + a1, p1 := spawnMockProcess("a1") + a2, p2 := spawnMockProcess("a2") + a3, p3 := spawnMockProcess("a3") + defer func() { + removeMockProcess(a1) + removeMockProcess(a2) + removeMockProcess(a3) + }() + + p1.On("SendUserMessage", a1, ErrTimeout) + p2.On("SendUserMessage", a2, ErrTimeout) + p3.On("SendUserMessage", a3, ErrTimeout) + + f := NewFuture(system, 10*time.Millisecond) + ref, _ := system.ProcessRegistry.Get(f.pid) + + f.PipeTo(a1) + f.PipeTo(a2) + f.PipeTo(a3) + + err := f.Wait() + assert.Error(t, err) + + assert.IsType(t, &futureProcess{}, ref) + fp, _ := ref.(*futureProcess) + + p1.AssertExpectations(t) + p2.AssertExpectations(t) + p3.AssertExpectations(t) + assert.Empty(t, fp.pipes, "pipes were not cleared") +} + +func TestNewFuture_TimeoutNoRace(t *testing.T) { + plog.SetLevel(log.OffLevel) + future := NewFuture(system, 1*time.Microsecond) + a := rootContext.Spawn(PropsFromFunc(func(context Context) { + switch context.Message().(type) { + case *Started: + context.Send(future.PID(), EchoResponse{}) + } + })) + _ = rootContext.StopFuture(a).Wait() + _, _ = future.Result() +} + +func assertFutureSuccess(future *Future, t *testing.T) interface{} { + res, err := future.Result() + assert.NoError(t, err, "timed out") + return res +} + +func TestFuture_Result_DeadLetterResponse(t *testing.T) { + a := assert.New(t) + + plog.SetLevel(log.OffLevel) + + future := NewFuture(system, 1*time.Second) + rootContext.Send(future.PID(), &DeadLetterResponse{}) + resp, err := future.Result() + a.Equal(ErrDeadLetter, err) + a.Nil(resp) +} + +func TestFuture_Result_Timeout(t *testing.T) { + a := assert.New(t) + + plog.SetLevel(log.OffLevel) + + future := NewFuture(system, 1*time.Second) + resp, err := future.Result() + a.Equal(ErrTimeout, err) + a.Nil(resp) +} + +func TestFuture_Result_Success(t *testing.T) { + a := assert.New(t) + + plog.SetLevel(log.OffLevel) + + future := NewFuture(system, 1*time.Second) + rootContext.Send(future.PID(), EchoResponse{}) + resp := assertFutureSuccess(future, t) + a.Equal(EchoResponse{}, resp) +} diff --git a/actor/guardian.go b/actor/guardian.go new file mode 100644 index 0000000000000000000000000000000000000000..c730f2e5d8f82ebfadcb5cc913be53e10271cb5b --- /dev/null +++ b/actor/guardian.go @@ -0,0 +1,94 @@ +package actor + +import ( + "errors" + "sync" + + "gitee.com/simplexyz/simpleactor-go/log" +) + +type guardiansValue struct { + actorSystem *ActorSystem + guardians *sync.Map +} + +func NewGuardians(actorSystem *ActorSystem) *guardiansValue { + return &guardiansValue{ + actorSystem: actorSystem, + guardians: &sync.Map{}, + } +} + +func (gs *guardiansValue) getGuardianPid(s SupervisorStrategy) *PID { + if g, ok := gs.guardians.Load(s); ok { + return g.(*guardianProcess).pid + } + g := gs.newGuardian(s) + gs.guardians.Store(s, g) + return g.pid +} + +// newGuardian creates and returns a new actor.guardianProcess with a timeout of duration d +func (gs *guardiansValue) newGuardian(s SupervisorStrategy) *guardianProcess { + ref := &guardianProcess{ + strategy: s, + guardians: gs, + } + id := gs.actorSystem.ProcessRegistry.NextId() + + pid, ok := gs.actorSystem.ProcessRegistry.Add(ref, "guardian"+id) + if !ok { + plog.Error("failed to register guardian process", log.Stringer("pid", pid)) + } + + ref.pid = pid + return ref +} + +type guardianProcess struct { + guardians *guardiansValue + pid *PID + strategy SupervisorStrategy +} + +var _ Process = &guardianProcess{} + +func (g *guardianProcess) SendUserMessage(_ *PID, _ interface{}) { + panic(errors.New("guardian actor cannot receive any user messages")) +} + +func (g *guardianProcess) SendSystemMessage(_ *PID, message interface{}) { + if msg, ok := message.(*Failure); ok { + g.strategy.HandleFailure(g.guardians.actorSystem, g, msg.Who, msg.RestartStats, msg.Reason, msg.Message) + } +} + +func (g *guardianProcess) Stop(_ *PID) { + // Ignore +} + +func (g *guardianProcess) Children() []*PID { + panic(errors.New("guardian does not hold its children PIDs")) +} + +func (g *guardianProcess) EscalateFailure(_ interface{}, _ interface{}) { + panic(errors.New("guardian cannot escalate failure")) +} + +func (g *guardianProcess) RestartChildren(pids ...*PID) { + for _, pid := range pids { + pid.sendSystemMessage(g.guardians.actorSystem, restartMessage) + } +} + +func (g *guardianProcess) StopChildren(pids ...*PID) { + for _, pid := range pids { + pid.sendSystemMessage(g.guardians.actorSystem, stopMessage) + } +} + +func (g *guardianProcess) ResumeChildren(pids ...*PID) { + for _, pid := range pids { + pid.sendSystemMessage(g.guardians.actorSystem, resumeMailboxMessage) + } +} diff --git a/actor/interaction_test.go b/actor/interaction_test.go new file mode 100644 index 0000000000000000000000000000000000000000..49a2d5bf0b59a8fdf39da38511906ba016a6a6d0 --- /dev/null +++ b/actor/interaction_test.go @@ -0,0 +1,54 @@ +package actor + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +type ( + DummyMessage struct{} + BlackHoleActor struct{} +) + +var testTimeout = 1 * time.Second + +func (state *BlackHoleActor) Receive(Context) {} + +func NewBlackHoleActor() Actor { + return &BlackHoleActor{} +} + +func TestSpawnProducesProcess(t *testing.T) { + actor := rootContext.Spawn(PropsFromProducer(NewBlackHoleActor)) + defer rootContext.Stop(actor) + assert.NotNil(t, actor) +} + +type EchoRequest struct{} + +type EchoResponse struct{} + +type EchoActor struct{} + +func NewEchoActor() Actor { + return &EchoActor{} +} + +func (*EchoActor) Receive(context Context) { + switch context.Message().(type) { + case EchoRequest: + context.Respond(EchoResponse{}) + } +} + +func TestActorCanReplyToMessage(t *testing.T) { + pid := rootContext.Spawn(PropsFromProducer(NewEchoActor)) + defer rootContext.Stop(pid) + err := rootContext.RequestFuture(pid, EchoRequest{}, testTimeout).Wait() + if err != nil { + assert.Fail(t, "timed out") + return + } +} diff --git a/actor/lifecycle_test.go b/actor/lifecycle_test.go new file mode 100644 index 0000000000000000000000000000000000000000..4ef36cb941e38cf7faa1f4bd806d6d942df8c116 --- /dev/null +++ b/actor/lifecycle_test.go @@ -0,0 +1,72 @@ +package actor + +import ( + "testing" +) + +type ( + dummyRequest struct{} + dummyResponse struct{} +) + +func TestActorCanReplyOnStarting(t *testing.T) { + future := NewFuture(system, testTimeout) + a := rootContext.Spawn(PropsFromFunc(func(context Context) { + switch context.Message().(type) { + case *Started: + context.Send(future.PID(), dummyResponse{}) + } + })) + _ = rootContext.StopFuture(a).Wait() + assertFutureSuccess(future, t) +} + +func TestActorCanReplyOnStopping(t *testing.T) { + future := NewFuture(system, testTimeout) + a := rootContext.Spawn(PropsFromFunc(func(context Context) { + switch context.Message().(type) { + case *Stopping: + context.Send(future.PID(), dummyResponse{}) + } + })) + _ = rootContext.StopFuture(a).Wait() + assertFutureSuccess(future, t) +} + +func TestActorReceivesStartedMessage(t *testing.T) { + future := NewFuture(system, testTimeout) + _ = rootContext.Spawn(PropsFromFunc(func(context Context) { + switch context.Message().(type) { + case *Started: + context.Send(future.PID(), dummyResponse{}) + } + })) + _ = future.Wait() + assertFutureSuccess(future, t) +} + +func TestActorReceivesRestartingMessage(t *testing.T) { + future := NewFuture(system, testTimeout) + a := rootContext.Spawn(PropsFromFunc(func(context Context) { + switch context.Message().(type) { + case *dummyRequest: + panic("fail") + case *Restarting: + context.Send(future.PID(), dummyResponse{}) + } + })) + rootContext.Send(a, &dummyRequest{}) + assertFutureSuccess(future, t) +} + +func TestActorReceivesStoppingMessage(t *testing.T) { + future := NewFuture(system, testTimeout) + a := rootContext.Spawn(PropsFromFunc(func(context Context) { + switch context.Message().(type) { + case *Stopping: + context.Send(future.PID(), dummyResponse{}) + } + })) + _ = rootContext.StopFuture(a).Wait() + assertFutureSuccess(future, t) +} diff --git a/actor/log.go b/actor/log.go new file mode 100644 index 0000000000000000000000000000000000000000..6114c20afce43bf28a074d1e0953a50fbb9cb811 --- /dev/null +++ b/actor/log.go @@ -0,0 +1,14 @@ +package actor + +import ( + "gitee.com/simplexyz/simpleactor-go/log" +) + +var plog = log.New(log.DebugLevel, "[ACTOR]") + +// SetLogLevel sets the log level for the logger. +// +// SetLogLevel is safe to call concurrently +func SetLogLevel(level log.Level) { + plog.SetLevel(level) +} diff --git a/actor/mailbox.go b/actor/mailbox.go new file mode 100644 index 0000000000000000000000000000000000000000..5e5d936bf3b87d999b9d08570345b2484d51497a --- /dev/null +++ b/actor/mailbox.go @@ -0,0 +1,198 @@ +package actor + +import ( + "runtime" + "sync/atomic" + + "gitee.com/simplexyz/simpleactor-go/internal/queue/mpsc" + "gitee.com/simplexyz/simpleactor-go/log" +) + +// MailboxMiddleware is an interface for intercepting messages and events in the mailbox +type MailboxMiddleware interface { + MailboxStarted() + MessagePosted(message interface{}) + MessageReceived(message interface{}) + MailboxEmpty() +} + +// MessageInvoker is the interface used by a mailbox to forward messages for processing +type MessageInvoker interface { + InvokeSystemMessage(interface{}) + InvokeUserMessage(interface{}) + EscalateFailure(reason interface{}, message interface{}) +} + +// Mailbox interface is used to enqueue messages to the mailbox +type Mailbox interface { + PostUserMessage(message interface{}) + PostSystemMessage(message interface{}) + RegisterHandlers(invoker MessageInvoker, dispatcher Dispatcher) + Start() + UserMessageCount() int +} + +// MailboxProducer is a function which creates a new mailbox +type MailboxProducer func() Mailbox + +const ( + idle int32 = iota + running +) + +type defaultMailbox struct { + userMailbox queue + systemMailbox *mpsc.Queue + schedulerStatus int32 + userMessages int32 + sysMessages int32 + suspended int32 + invoker MessageInvoker + dispatcher Dispatcher + middlewares []MailboxMiddleware +} + +func (m *defaultMailbox) PostUserMessage(message interface{}) { + // is it a raw batch message? + if batch, ok := message.(MessageBatch); ok { + messages := batch.GetMessages() + + for _, msg := range messages { + m.PostUserMessage(msg) + } + } + + // is it an envelope batch message? + // FIXME: check if this is still needed, maybe MessageEnvelope can only exist as a pointer + if env, ok := message.(MessageEnvelope); ok { + if batch, ok := env.Message.(MessageBatch); ok { + messages := batch.GetMessages() + + for _, msg := range messages { + m.PostUserMessage(msg) + } + } + } + if env, ok := message.(*MessageEnvelope); ok { + if batch, ok := env.Message.(MessageBatch); ok { + messages := batch.GetMessages() + + for _, msg := range messages { + m.PostUserMessage(msg) + } + } + } + + // normal messages + for _, ms := range m.middlewares { + ms.MessagePosted(message) + } + m.userMailbox.Push(message) + atomic.AddInt32(&m.userMessages, 1) + m.schedule() +} + +func (m *defaultMailbox) PostSystemMessage(message interface{}) { + for _, ms := range m.middlewares { + ms.MessagePosted(message) + } + m.systemMailbox.Push(message) + atomic.AddInt32(&m.sysMessages, 1) + m.schedule() +} + +func (m *defaultMailbox) RegisterHandlers(invoker MessageInvoker, dispatcher Dispatcher) { + m.invoker = invoker + m.dispatcher = dispatcher +} + +func (m *defaultMailbox) schedule() { + if atomic.CompareAndSwapInt32(&m.schedulerStatus, idle, running) { + m.dispatcher.Schedule(m.processMessages) + } +} + +func (m *defaultMailbox) processMessages() { +process: + m.run() + + // set mailbox to idle + atomic.StoreInt32(&m.schedulerStatus, idle) + sys := atomic.LoadInt32(&m.sysMessages) + user := atomic.LoadInt32(&m.userMessages) + // check if there are still messages to process (sent after the message loop ended) + if sys > 0 || (atomic.LoadInt32(&m.suspended) == 0 && user > 0) { + // try setting the mailbox back to running + if atomic.CompareAndSwapInt32(&m.schedulerStatus, idle, running) { + // fmt.Printf("looping %v %v %v\n", sys, user, m.suspended) + goto process + } + } + + for _, ms := range m.middlewares { + ms.MailboxEmpty() + } +} + +func (m *defaultMailbox) run() { + var msg interface{} + + defer func() { + if r := recover(); r != nil { + plog.Info("[ACTOR] Recovering", log.Object("actor", m.invoker), log.Object("reason", r), log.Stack()) + m.invoker.EscalateFailure(r, msg) + } + }() + + i, t := 0, m.dispatcher.Throughput() + for { + if i > t { + i = 0 + runtime.Gosched() + } + + i++ + + // keep processing system messages until queue is empty + if msg = m.systemMailbox.Pop(); msg != nil { + atomic.AddInt32(&m.sysMessages, -1) + switch msg.(type) { + case *SuspendMailbox: + atomic.StoreInt32(&m.suspended, 1) + case *ResumeMailbox: + atomic.StoreInt32(&m.suspended, 0) + default: + m.invoker.InvokeSystemMessage(msg) + } + for _, ms := range m.middlewares { + ms.MessageReceived(msg) + } + continue + } + + // didn't process a system message, so break until we are resumed + if atomic.LoadInt32(&m.suspended) == 1 { + return + } + + if msg = m.userMailbox.Pop(); msg != nil { + atomic.AddInt32(&m.userMessages, -1) + m.invoker.InvokeUserMessage(msg) + for _, ms := range m.middlewares { + ms.MessageReceived(msg) + } + } else { + return + } + } +} + +func (m *defaultMailbox) Start() { + for _, ms := range m.middlewares { + ms.MailboxStarted() + } +} + +func (m *defaultMailbox) UserMessageCount() int { + return int(atomic.LoadInt32(&m.userMessages)) +} diff --git a/actor/mailbox_test.go b/actor/mailbox_test.go new file mode 100644 index 0000000000000000000000000000000000000000..5780ca2218dec5a7b692d579916970f2486e22cf --- /dev/null +++ b/actor/mailbox_test.go @@ -0,0 +1,154 @@ +package actor + +import ( + "fmt" + "log" + "math/rand" + "sync" + "testing" + "time" + + rbqueue "github.com/Workiva/go-datastructures/queue" + + "github.com/stretchr/testify/assert" +) + +type invoker struct { + count int + max int + wg *sync.WaitGroup +} + +func (i *invoker) InvokeSystemMessage(interface{}) { + i.count++ + if i.count == i.max { + i.wg.Done() + } + if i.count > i.max { + log.Println("Unexpected data..") + } +} + +func (i *invoker) InvokeUserMessage(interface{}) { + i.count++ + if i.count == i.max { + i.wg.Done() + } + if i.count > i.max { + log.Println("Unexpected data..") + } +} + +func (*invoker) EscalateFailure(_ interface{}, _ interface{}) {} + +func TestUnboundedLockfreeMailboxUsermessageConsistency(t *testing.T) { + max := 1000000 + c := 100 + var wg sync.WaitGroup + wg.Add(1) + p := UnboundedLockfree() + mi := &invoker{ + max: max, + wg: &wg, + } + q := p() + q.RegisterHandlers(mi, NewDefaultDispatcher(300)) + + for j := 0; j < c; j++ { + cmax := max / c + go func(j int) { + for i := 0; i < cmax; i++ { + if rand.Intn(10) == 0 { + time.Sleep(time.Duration(rand.Intn(1000))) + } + q.PostUserMessage(fmt.Sprintf("%v %v", j, i)) + } + }(j) + } + wg.Wait() + time.Sleep(1 * time.Second) +} + +type sysDummy struct { + value string +} + +func (*sysDummy) SystemMessage() { +} + +func TestUnboundedLockfreeMailboxSysMessageConsistency(t *testing.T) { + max := 1000000 + c := 100 + var wg sync.WaitGroup + wg.Add(1) + p := UnboundedLockfree() + mi := &invoker{ + max: max, + wg: &wg, + } + q := p() + q.RegisterHandlers(mi, NewDefaultDispatcher(300)) + + for j := 0; j < c; j++ { + cmax := max / c + go func(j int) { + for i := 0; i < cmax; i++ { + if rand.Intn(10) == 0 { + time.Sleep(time.Duration(rand.Intn(100))) + } + q.PostSystemMessage( + &sysDummy{ + value: fmt.Sprintf("%v %v", j, i), + }) + } + }(j) + } + wg.Wait() + time.Sleep(1 * time.Second) +} + +func TestBoundedMailbox(t *testing.T) { + size := 3 + m := boundedMailboxQueue{ + userMailbox: rbqueue.NewRingBuffer(uint64(size)), + dropping: false, + } + m.Push("1") + m.Push("2") + m.Push("3") + assert.Equal(t, "1", m.Pop()) +} + +func TestBoundedDroppingMailbox(t *testing.T) { + size := 3 + m := boundedMailboxQueue{ + userMailbox: rbqueue.NewRingBuffer(uint64(size)), + dropping: true, + } + m.Push("1") + m.Push("2") + m.Push("3") + m.Push("4") + assert.Equal(t, "2", m.Pop()) +} + +func TestMailboxUserMessageCount(t *testing.T) { + max := 10 + c := 10 + var wg sync.WaitGroup + wg.Add(1) + p := UnboundedLockfree() + mi := &invoker{ + max: max, + wg: &wg, + } + q := p() + q.RegisterHandlers(mi, NewDefaultDispatcher(300)) + + for j := 0; j < c; j++ { + q.PostUserMessage(fmt.Sprintf("%v", j)) + } + assert.Equal(t, c, q.UserMessageCount()) + wg.Wait() + time.Sleep(100 * time.Millisecond) +} diff --git a/actor/message.go b/actor/message.go new file mode 100644 index 0000000000000000000000000000000000000000..718d0a63986034d10b921d07e1eec6f4067ec7d5 --- /dev/null +++ b/actor/message.go @@ -0,0 +1,25 @@ +package actor + +// The Producer type is a function that creates a new actor +type Producer func() Actor + +// Actor is the interface that defines the Receive method. +// +// Receive is sent messages to be processed from the mailbox associated with the instance of the actor +type Actor interface { + Receive(c Context) +} + +// The ReceiveFunc type is an adapter to allow the use of ordinary functions as actors to process messages +type ReceiveFunc func(c Context) + +// Receive calls f(c) +func (f ReceiveFunc) Receive(c Context) { + f(c) +} + +type ReceiverFunc func(c ReceiverContext, envelope *MessageEnvelope) + +type SenderFunc func(c SenderContext, target *PID, envelope *MessageEnvelope) + +type ContextDecoratorFunc func(ctx Context) Context diff --git a/actor/message_batch.go b/actor/message_batch.go new file mode 100644 index 0000000000000000000000000000000000000000..500ab6263d987c30bb68a6c928b6a5863cb19ae5 --- /dev/null +++ b/actor/message_batch.go @@ -0,0 +1,5 @@ +package actor + +type MessageBatch interface { + GetMessages() []interface{} +} diff --git a/actor/message_batch_test.go b/actor/message_batch_test.go new file mode 100644 index 0000000000000000000000000000000000000000..239b97abde5779eff1524c5d7940a8c2e2247d21 --- /dev/null +++ b/actor/message_batch_test.go @@ -0,0 +1,45 @@ +package actor + +import ( + "sync" + "testing" +) + +type dummyMessageBatch struct { + messages []interface{} +} + +func (d dummyMessageBatch) GetMessages() []interface{} { + return d.messages +} + +func TestActorReceivesEachMessageInAMessageBatch(t *testing.T) { + // each message in the batch + seenMessagesWg := sync.WaitGroup{} + seenMessagesWg.Add(10) + + // the batch message itself + seenBatchMessageWg := sync.WaitGroup{} + seenBatchMessageWg.Add(1) + + pid := rootContext.Spawn(PropsFromFunc(func(ctx Context) { + if _, ok := ctx.Message().(*DummyMessage); ok { + seenMessagesWg.Done() + } + + if _, ok := ctx.Message().(*dummyMessageBatch); ok { + seenBatchMessageWg.Done() + } + })) + + batch := &dummyMessageBatch{messages: make([]interface{}, 10)} + + for i := 0; i < 10; i++ { + batch.messages[i] = &DummyMessage{} + } + + rootContext.Send(pid, batch) + + seenMessagesWg.Wait() + seenBatchMessageWg.Wait() +} diff --git a/actor/message_envelope.go b/actor/message_envelope.go new file mode 100644 index 0000000000000000000000000000000000000000..a143d5e3de864d9181f5a7cdf7f058695bee8451 --- /dev/null +++ b/actor/message_envelope.go @@ -0,0 +1,95 @@ +package actor + +type messageHeader map[string]string + +func (header messageHeader) Get(key string) string { + return header[key] +} + +func (header messageHeader) Set(key string, value string) { + header[key] = value +} + +func (header messageHeader) Keys() []string { + keys := make([]string, 0, len(header)) + for k := range header { + keys = append(keys, k) + } + return keys +} + +func (header messageHeader) Length() int { + return len(header) +} + +func (header messageHeader) ToMap() map[string]string { + mp := make(map[string]string) + for k, v := range header { + mp[k] = v + } + return mp +} + +type ReadonlyMessageHeader interface { + Get(key string) string + Keys() []string + Length() int + ToMap() map[string]string +} + +type MessageEnvelope struct { + Header messageHeader + Message interface{} + Sender *PID +} + +func (envelope *MessageEnvelope) GetHeader(key string) string { + if envelope.Header == nil { + return "" + } + return envelope.Header.Get(key) +} + +func (envelope *MessageEnvelope) SetHeader(key string, value string) { + if envelope.Header == nil { + envelope.Header = make(map[string]string) + } + envelope.Header.Set(key, value) +} + +var EmptyMessageHeader = make(messageHeader) + +func WrapEnvelope(message interface{}) *MessageEnvelope { + if e, ok := message.(*MessageEnvelope); ok { + return e + } + return &MessageEnvelope{nil, message, nil} +} + +func UnwrapEnvelope(message interface{}) (ReadonlyMessageHeader, interface{}, *PID) { + if env, ok := message.(*MessageEnvelope); ok { + return env.Header, env.Message, env.Sender + } + return nil, message, nil +} + +func UnwrapEnvelopeHeader(message interface{}) ReadonlyMessageHeader { + if env, ok := message.(*MessageEnvelope); ok { + return env.Header + } + return nil +} + +func UnwrapEnvelopeMessage(message interface{}) interface{} { + if env, ok := message.(*MessageEnvelope); ok { + return env.Message + } + return message +} + +func UnwrapEnvelopeSender(message interface{}) *PID { + if env, ok := message.(*MessageEnvelope); ok { + return env.Sender + } + return nil +} diff --git a/actor/message_envelope_test.go b/actor/message_envelope_test.go new file mode 100644 index 0000000000000000000000000000000000000000..d981aced8ac29e89ac0a795b83e77b18052e680a --- /dev/null +++ b/actor/message_envelope_test.go @@ -0,0 +1,28 @@ +package actor + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestNormalMessageGivesEmptyMessageHeaders(t *testing.T) { + t.Parallel() + + props := PropsFromFunc(func(ctx Context) { + if _, ok := ctx.Message().(string); ok { + l := len(ctx.MessageHeader().Keys()) + ctx.Respond(l) + } + }) + a := rootContext.Spawn(props) + + defer func() { + _ = rootContext.StopFuture(a).Wait() + }() + + f := rootContext.RequestFuture(a, "hello", testTimeout) + + res, _ := assertFutureSuccess(f, t).(int) + assert.Equal(t, 0, res) +} diff --git a/actor/messages.go b/actor/messages.go new file mode 100644 index 0000000000000000000000000000000000000000..73e78ae56077c5217edcd756c69ea8e47fcb2720 --- /dev/null +++ b/actor/messages.go @@ -0,0 +1,108 @@ +package actor + +// ResumeMailbox is message sent by the actor system to resume mailbox processing. +// +// This will not be forwarded to the Receive method +type ResumeMailbox struct{} + +// SuspendMailbox is message sent by the actor system to suspend mailbox processing. +// +// This will not be forwarded to the Receive method +type SuspendMailbox struct{} + +type MailboxMessage interface { + MailboxMessage() +} + +func (*SuspendMailbox) MailboxMessage() {} +func (*ResumeMailbox) MailboxMessage() {} + +// InfrastructureMessage is a marker for all built in Proto.Actor messages +type InfrastructureMessage interface { + InfrastructureMessage() +} + +// IgnoreDeadLetterLogging messages are not logged in deadletter log +type IgnoreDeadLetterLogging interface { + IgnoreDeadLetterLogging() +} + +// An AutoReceiveMessage is a special kind of user message that will be handled in some way automatically by the actor +type AutoReceiveMessage interface { + AutoReceiveMessage() +} + +// NotInfluenceReceiveTimeout messages will not reset the ReceiveTimeout timer of an actor that receives the message +type NotInfluenceReceiveTimeout interface { + NotInfluenceReceiveTimeout() +} + +// A SystemMessage message is reserved for specific lifecycle messages used by the actor system +type SystemMessage interface { + SystemMessage() +} + +// A ReceiveTimeout message is sent to an actor after the Context.ReceiveTimeout duration has expired +type ReceiveTimeout struct{} + +// A Restarting message is sent to an actor when the actor is being restarted by the system due to a failure +type Restarting struct{} + +// A Stopping message is sent to an actor prior to the actor being stopped +type Stopping struct{} + +// A Stopped message is sent to the actor once it has been stopped. A stopped actor will receive no further messages +type Stopped struct{} + +// A Started message is sent to an actor once it has been started and ready to begin receiving messages. +type Started struct{} + +// Restart is message sent by the actor system to control the lifecycle of an actor +type Restart struct{} + +// Failure message is sent to an actor parent when an exception is thrown by one of its methods +type Failure struct { + Who *PID + Reason interface{} + RestartStats *RestartStatistics + Message interface{} +} + +type continuation struct { + message interface{} + f func() +} + +func (*Touch) GetAutoResponse(ctx Context) interface{} { + return &Touched{ + Who: ctx.Self(), + } +} + +func (*Restarting) AutoReceiveMessage() {} +func (*Stopping) AutoReceiveMessage() {} +func (*Stopped) AutoReceiveMessage() {} +func (*PoisonPill) AutoReceiveMessage() {} + +func (*Started) SystemMessage() {} +func (*Stop) SystemMessage() {} +func (*Watch) SystemMessage() {} +func (*Unwatch) SystemMessage() {} +func (*Terminated) SystemMessage() {} +func (*Failure) SystemMessage() {} +func (*Restart) SystemMessage() {} +func (*continuation) SystemMessage() {} + +var ( + restartingMessage AutoReceiveMessage = &Restarting{} + stoppingMessage AutoReceiveMessage = &Stopping{} + stoppedMessage AutoReceiveMessage = &Stopped{} + poisonPillMessage AutoReceiveMessage = &PoisonPill{} + receiveTimeoutMessage interface{} = &ReceiveTimeout{} + restartMessage SystemMessage = &Restart{} + startedMessage SystemMessage = &Started{} + stopMessage SystemMessage = &Stop{} + resumeMailboxMessage MailboxMessage = &ResumeMailbox{} + suspendMailboxMessage MailboxMessage = &SuspendMailbox{} + _ AutoRespond = &Touch{} +) diff --git a/actor/metrics.go b/actor/metrics.go new file mode 100644 index 0000000000000000000000000000000000000000..9c82693e7e94994d49bcdec82dd01d65cdf700ab --- /dev/null +++ b/actor/metrics.go @@ -0,0 +1,65 @@ +// Copyright (C) 2017 - 2022 Asynkron.se + +package actor + +import ( + "fmt" + "strings" + + "gitee.com/simplexyz/simpleactor-go/log" + + "gitee.com/simplexyz/simpleactor-go/extensions" + "gitee.com/simplexyz/simpleactor-go/metrics" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/metric" +) + +var extensionId = extensions.NextExtensionID() + +type Metrics struct { + metrics *metrics.ProtoMetrics + enabled bool +} + +var _ extensions.Extension = &Metrics{} + +func (m *Metrics) Enabled() bool { + return m.enabled +} + +func (m *Metrics) ExtensionID() extensions.ExtensionID { + return extensionId +} + +func NewMetrics(provider metric.MeterProvider) *Metrics { + if provider == nil { + return &Metrics{} + } + + return &Metrics{ + metrics: metrics.NewProtoMetrics(provider), + enabled: true, + } +} + +func (m *Metrics) PrepareMailboxLengthGauge() { + meter := otel.Meter(metrics.LibName) + gauge, err := meter.Int64ObservableGauge("protoactor_actor_mailbox_length", + metric.WithDescription("Actor's Mailbox Length"), + metric.WithUnit("1")) + if err != nil { + err = fmt.Errorf("failed to create ActorMailBoxLength instrument, %w", err) + plog.Error(err.Error(), log.Error(err)) + } + m.metrics.Instruments().SetActorMailboxLengthGauge(gauge) +} + +func (m *Metrics) CommonLabels(ctx Context) []attribute.KeyValue { + labels := []attribute.KeyValue{ + attribute.String("address", ctx.ActorSystem().Address()), + attribute.String("actortype", strings.Replace(fmt.Sprintf("%T", ctx.Actor()), "*", "", 1)), + } + + return labels +} diff --git a/actor/middleware/logging.go b/actor/middleware/logging.go new file mode 100644 index 0000000000000000000000000000000000000000..932c543cb18ec0580dfb297668d05f8214477a18 --- /dev/null +++ b/actor/middleware/logging.go @@ -0,0 +1,19 @@ +package middleware + +import ( + "log" + "reflect" + + "gitee.com/simplexyz/simpleactor-go/actor" +) + +// Logger is message middleware which logs messages before continuing to the next middleware. +func Logger(next actor.ReceiverFunc) actor.ReceiverFunc { + fn := func(c actor.ReceiverContext, env *actor.MessageEnvelope) { + message := env.Message + log.Printf("%v got %v %+v", c.Self(), reflect.TypeOf(message), message) + next(c, env) + } + + return fn +} diff --git a/actor/middleware/opentracing/activespan.go b/actor/middleware/opentracing/activespan.go new file mode 100644 index 0000000000000000000000000000000000000000..cad67da994bf40e49d01d84e02ecb7d9f87edf9e --- /dev/null +++ b/actor/middleware/opentracing/activespan.go @@ -0,0 +1,40 @@ +package opentracing + +import ( + "fmt" + "sync" + + "gitee.com/simplexyz/simpleactor-go/actor" + "github.com/opentracing/opentracing-go" +) + +var activeSpan = sync.Map{} + +func getActiveSpan(pid *actor.PID) opentracing.Span { + value, ok := activeSpan.Load(pid) + if !ok { + return nil + } + + span, _ := value.(opentracing.Span) + + return span +} + +func clearActiveSpan(pid *actor.PID) { + activeSpan.Delete(pid) +} + +func setActiveSpan(pid *actor.PID, span opentracing.Span) { + activeSpan.Store(pid, span) +} + +func GetActiveSpan(context actor.Context) opentracing.Span { + span := getActiveSpan(context.Self()) + if span == nil { + // TODO: Fix finding the real span always or handle no-span better on receiving side + span = opentracing.StartSpan(fmt.Sprintf("%T/%T", context.Actor(), context.Message())) + } + + return span +} diff --git a/actor/middleware/opentracing/envelope.go b/actor/middleware/opentracing/envelope.go new file mode 100644 index 0000000000000000000000000000000000000000..1373fa5f5ccc4591c344f85513ba60496c12cf2b --- /dev/null +++ b/actor/middleware/opentracing/envelope.go @@ -0,0 +1,37 @@ +package opentracing + +import ( + "gitee.com/simplexyz/simpleactor-go/actor" + "github.com/opentracing/opentracing-go" +) + +type messageHeaderReader struct { + ReadOnlyMessageHeader actor.ReadonlyMessageHeader +} + +func (reader *messageHeaderReader) ForeachKey(handler func(key, val string) error) error { + if reader.ReadOnlyMessageHeader == nil { + return nil + } + + for _, key := range reader.ReadOnlyMessageHeader.Keys() { + err := handler(key, reader.ReadOnlyMessageHeader.Get(key)) + if err != nil { + return err + } + } + + return nil +} + +var _ opentracing.TextMapReader = &messageHeaderReader{} + +type messageEnvelopeWriter struct { + MessageEnvelope *actor.MessageEnvelope +} + +func (writer *messageEnvelopeWriter) Set(key, val string) { + writer.MessageEnvelope.SetHeader(key, val) +} + +var _ opentracing.TextMapWriter = &messageEnvelopeWriter{} diff --git a/actor/middleware/opentracing/logger.go b/actor/middleware/opentracing/logger.go new file mode 100644 index 0000000000000000000000000000000000000000..59d1bdfeb4b4a03d79345e577c46335c9f9d5804 --- /dev/null +++ b/actor/middleware/opentracing/logger.go @@ -0,0 +1,5 @@ +package opentracing + +import "gitee.com/simplexyz/simpleactor-go/log" + +var logger = log.New(log.ErrorLevel, "[TRACING]") diff --git a/actor/middleware/opentracing/middlewarepropagation.go b/actor/middleware/opentracing/middlewarepropagation.go new file mode 100644 index 0000000000000000000000000000000000000000..fd622484d3412dd29d3e313d61ac6c3ddf3050dd --- /dev/null +++ b/actor/middleware/opentracing/middlewarepropagation.go @@ -0,0 +1,15 @@ +package opentracing + +import ( + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/actor/middleware/propagator" +) + +func TracingMiddleware() actor.SpawnMiddleware { + return propagator.New(). + WithItselfForwarded(). + WithSpawnMiddleware(SpawnMiddleware()). + WithSenderMiddleware(SenderMiddleware()). + WithReceiverMiddleware(ReceiverMiddleware()). + SpawnMiddleware +} diff --git a/actor/middleware/opentracing/parentspan.go b/actor/middleware/opentracing/parentspan.go new file mode 100644 index 0000000000000000000000000000000000000000..95f55b9f71ab248e051643e5e6f57de6827b7e85 --- /dev/null +++ b/actor/middleware/opentracing/parentspan.go @@ -0,0 +1,27 @@ +package opentracing + +import ( + "sync" + + "gitee.com/simplexyz/simpleactor-go/actor" + "github.com/opentracing/opentracing-go" +) + +var parentSpans = sync.Map{} + +func getAndClearParentSpan(pid *actor.PID) opentracing.Span { + value, ok := parentSpans.Load(pid) + if !ok { + return nil + } + + parentSpans.Delete(pid) + + span, _ := value.(opentracing.Span) + + return span +} + +func setParentSpan(pid *actor.PID, span opentracing.Span) { + parentSpans.Store(pid, span) +} diff --git a/actor/middleware/opentracing/receivermiddleware.go b/actor/middleware/opentracing/receivermiddleware.go new file mode 100644 index 0000000000000000000000000000000000000000..786cf223bcbcc3bb2221a2ffeb5b810348060a46 --- /dev/null +++ b/actor/middleware/opentracing/receivermiddleware.go @@ -0,0 +1,82 @@ +package opentracing + +import ( + "fmt" + + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/log" + "github.com/opentracing/opentracing-go" +) + +func ReceiverMiddleware() actor.ReceiverMiddleware { + return func(next actor.ReceiverFunc) actor.ReceiverFunc { + return func(c actor.ReceiverContext, envelope *actor.MessageEnvelope) { + spanContext, err := opentracing.GlobalTracer().Extract(opentracing.TextMap, opentracing.TextMapReader(&messageHeaderReader{ReadOnlyMessageHeader: envelope.Header})) + if err == opentracing.ErrSpanContextNotFound { + logger.Debug("INBOUND No spanContext found", log.Stringer("PID", c.Self()), log.Error(err)) + // next(c) + } else if err != nil { + logger.Debug("INBOUND Error", log.Stringer("PID", c.Self()), log.Error(err)) + next(c, envelope) + return + } + var span opentracing.Span + switch envelope.Message.(type) { + case *actor.Started: + parentSpan := getAndClearParentSpan(c.Self()) + if parentSpan != nil { + span = opentracing.StartSpan(fmt.Sprintf("%T/%T", c.Actor(), envelope.Message), opentracing.ChildOf(parentSpan.Context())) + logger.Debug("INBOUND Found parent span", log.Stringer("PID", c.Self()), log.TypeOf("ActorType", c.Actor()), log.TypeOf("MessageType", envelope.Message)) + } else { + logger.Debug("INBOUND No parent span", log.Stringer("PID", c.Self()), log.TypeOf("ActorType", c.Actor()), log.TypeOf("MessageType", envelope.Message)) + } + case *actor.Stopping: + var parentSpan opentracing.Span + if c.Parent() != nil { + parentSpan = getStoppingSpan(c.Parent()) + } + if parentSpan != nil { + span = opentracing.StartSpan(fmt.Sprintf("%T/stopping", c.Actor()), opentracing.ChildOf(parentSpan.Context())) + } else { + span = opentracing.StartSpan(fmt.Sprintf("%T/stopping", c.Actor())) + } + setStoppingSpan(c.Self(), span) + span.SetTag("ActorPID", c.Self()) + span.SetTag("ActorType", fmt.Sprintf("%T", c.Actor())) + span.SetTag("MessageType", fmt.Sprintf("%T", envelope.Message)) + stoppingHandlingSpan := opentracing.StartSpan("stopping-handling", opentracing.ChildOf(span.Context())) + next(c, envelope) + stoppingHandlingSpan.Finish() + return + case *actor.Stopped: + span = getAndClearStoppingSpan(c.Self()) + next(c, envelope) + if span != nil { + span.Finish() + } + return + } + if span == nil && spanContext == nil { + logger.Debug("INBOUND No spanContext. Starting new span", log.Stringer("PID", c.Self()), log.TypeOf("ActorType", c.Actor()), log.TypeOf("MessageType", envelope.Message)) + span = opentracing.StartSpan(fmt.Sprintf("%T/%T", c.Actor(), envelope.Message)) + } + if span == nil { + logger.Debug("INBOUND Starting span from parent", log.Stringer("PID", c.Self()), log.TypeOf("ActorType", c.Actor()), log.TypeOf("MessageType", envelope.Message)) + span = opentracing.StartSpan(fmt.Sprintf("%T/%T", c.Actor(), envelope.Message), opentracing.ChildOf(spanContext)) + } + + setActiveSpan(c.Self(), span) + span.SetTag("ActorPID", c.Self()) + span.SetTag("ActorType", fmt.Sprintf("%T", c.Actor())) + span.SetTag("MessageType", fmt.Sprintf("%T", envelope.Message)) + + defer func() { + logger.Debug("INBOUND Finishing span", log.Stringer("PID", c.Self()), log.TypeOf("ActorType", c.Actor()), log.TypeOf("MessageType", envelope.Message)) + span.Finish() + clearActiveSpan(c.Self()) + }() + + next(c, envelope) + } + } +} diff --git a/actor/middleware/opentracing/sendermiddleware.go b/actor/middleware/opentracing/sendermiddleware.go new file mode 100644 index 0000000000000000000000000000000000000000..f462c8e619eb3393c70ffd03359d2145aec15b9f --- /dev/null +++ b/actor/middleware/opentracing/sendermiddleware.go @@ -0,0 +1,31 @@ +package opentracing + +import ( + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/log" + "github.com/opentracing/opentracing-go" +) + +func SenderMiddleware() actor.SenderMiddleware { + return func(next actor.SenderFunc) actor.SenderFunc { + return func(c actor.SenderContext, target *actor.PID, envelope *actor.MessageEnvelope) { + span := getActiveSpan(c.Self()) + + if span == nil { + logger.Debug("OUTBOUND No active span", log.Stringer("PID", c.Self()), log.TypeOf("ActorType", c.Actor()), log.TypeOf("MessageType", envelope.Message)) + next(c, target, envelope) + return + } + + err := opentracing.GlobalTracer().Inject(span.Context(), opentracing.TextMap, opentracing.TextMapWriter(&messageEnvelopeWriter{MessageEnvelope: envelope})) + if err != nil { + logger.Debug("OUTBOUND Error injecting", log.Stringer("PID", c.Self()), log.TypeOf("ActorType", c.Actor()), log.TypeOf("MessageType", envelope.Message)) + next(c, target, envelope) + return + } + + logger.Debug("OUTBOUND Successfully injected", log.Stringer("PID", c.Self()), log.TypeOf("ActorType", c.Actor()), log.TypeOf("MessageType", envelope.Message)) + next(c, target, envelope) + } + } +} diff --git a/actor/middleware/opentracing/spawnmiddleware.go b/actor/middleware/opentracing/spawnmiddleware.go new file mode 100644 index 0000000000000000000000000000000000000000..a127c905ddfe979593c94d3ad16a68b2303f8090 --- /dev/null +++ b/actor/middleware/opentracing/spawnmiddleware.go @@ -0,0 +1,33 @@ +package opentracing + +import ( + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/log" + olog "github.com/opentracing/opentracing-go/log" +) + +func SpawnMiddleware() actor.SpawnMiddleware { + return func(next actor.SpawnFunc) actor.SpawnFunc { + return func(actorSystem *actor.ActorSystem, id string, props *actor.Props, parentContext actor.SpawnerContext) (pid *actor.PID, e error) { + self := parentContext.Self() + pid, err := next(actorSystem, id, props, parentContext) + if err != nil { + logger.Debug("SPAWN got error trying to spawn", log.Stringer("PID", self), log.TypeOf("ActorType", parentContext.Actor()), log.Error(err)) + return pid, err + } + if self != nil { + span := getActiveSpan(self) + if span != nil { + setParentSpan(pid, span) + span.LogFields(olog.String("SpawnPID", pid.String())) + logger.Debug("SPAWN found active span", log.Stringer("PID", self), log.TypeOf("ActorType", parentContext.Actor()), log.Stringer("SpawnedPID", pid)) + } else { + logger.Debug("SPAWN no active span on parent", log.Stringer("PID", self), log.TypeOf("ActorType", parentContext.Actor()), log.Stringer("SpawnedPID", pid)) + } + } else { + logger.Debug("SPAWN no parent pid", log.Stringer("SpawnedPID", pid)) + } + return pid, err + } + } +} diff --git a/actor/middleware/opentracing/stoppingspan.go b/actor/middleware/opentracing/stoppingspan.go new file mode 100644 index 0000000000000000000000000000000000000000..b01dd9224d75dfaf8b8cc18c41e3b39c02639c24 --- /dev/null +++ b/actor/middleware/opentracing/stoppingspan.go @@ -0,0 +1,31 @@ +package opentracing + +import ( + "sync" + + "gitee.com/simplexyz/simpleactor-go/actor" + "github.com/opentracing/opentracing-go" +) + +var stoppingSpans = sync.Map{} + +func getAndClearStoppingSpan(pid *actor.PID) opentracing.Span { + value, ok := stoppingSpans.Load(pid) + if !ok { + return nil + } + stoppingSpans.Delete(pid) + return value.(opentracing.Span) +} + +func getStoppingSpan(pid *actor.PID) opentracing.Span { + value, ok := stoppingSpans.Load(pid) + if !ok { + return nil + } + return value.(opentracing.Span) +} + +func setStoppingSpan(pid *actor.PID, span opentracing.Span) { + stoppingSpans.Store(pid, span) +} diff --git a/actor/middleware/opentracing/tracing_test.go b/actor/middleware/opentracing/tracing_test.go new file mode 100644 index 0000000000000000000000000000000000000000..4fb5188ef1f75595af21f7edf9965402c8d18fea --- /dev/null +++ b/actor/middleware/opentracing/tracing_test.go @@ -0,0 +1 @@ +package opentracing diff --git a/actor/middleware/propagator/middlewarepropagation.go b/actor/middleware/propagator/middlewarepropagation.go new file mode 100644 index 0000000000000000000000000000000000000000..58e30fba303d8cb852e8e27a09c625f5dd136b19 --- /dev/null +++ b/actor/middleware/propagator/middlewarepropagation.go @@ -0,0 +1,59 @@ +package propagator + +import ( + "gitee.com/simplexyz/simpleactor-go/actor" +) + +type MiddlewarePropagator struct { + spawnMiddleware []actor.SpawnMiddleware + senderMiddleware []actor.SenderMiddleware + receiverMiddleware []actor.ReceiverMiddleware + contextDecorators []actor.ContextDecorator +} + +func New() *MiddlewarePropagator { + return &MiddlewarePropagator{} +} + +func (propagator *MiddlewarePropagator) WithItselfForwarded() *MiddlewarePropagator { + return propagator.WithSpawnMiddleware(propagator.SpawnMiddleware) +} + +func (propagator *MiddlewarePropagator) WithSpawnMiddleware(middleware ...actor.SpawnMiddleware) *MiddlewarePropagator { + propagator.spawnMiddleware = append(propagator.spawnMiddleware, middleware...) + return propagator +} + +func (propagator *MiddlewarePropagator) WithSenderMiddleware(middleware ...actor.SenderMiddleware) *MiddlewarePropagator { + propagator.senderMiddleware = append(propagator.senderMiddleware, middleware...) + return propagator +} + +func (propagator *MiddlewarePropagator) WithReceiverMiddleware(middleware ...actor.ReceiverMiddleware) *MiddlewarePropagator { + propagator.receiverMiddleware = append(propagator.receiverMiddleware, middleware...) + return propagator +} + +func (propagator *MiddlewarePropagator) WithContextDecorator(decorators ...actor.ContextDecorator) *MiddlewarePropagator { + propagator.contextDecorators = append(propagator.contextDecorators, decorators...) + return propagator +} + +func (propagator *MiddlewarePropagator) SpawnMiddleware(next actor.SpawnFunc) actor.SpawnFunc { + return func(actorSystem *actor.ActorSystem, id string, props *actor.Props, parentContext actor.SpawnerContext) (pid *actor.PID, e error) { + if propagator.spawnMiddleware != nil { + props = props.Configure(actor.WithSpawnMiddleware(propagator.spawnMiddleware...)) + } + if propagator.senderMiddleware != nil { + props = props.Configure(actor.WithSenderMiddleware(propagator.senderMiddleware...)) + } + if propagator.receiverMiddleware != nil { + props = props.Configure(actor.WithReceiverMiddleware(propagator.receiverMiddleware...)) + } + if propagator.contextDecorators != nil { + props = props.Configure(actor.WithContextDecorator(propagator.contextDecorators...)) + } + pid, err := next(actorSystem, id, props, parentContext) + return pid, err + } +} diff --git a/actor/middleware/propagator/middlewarepropagation_test.go b/actor/middleware/propagator/middlewarepropagation_test.go new file mode 100644 index 0000000000000000000000000000000000000000..89a141a275f21387977866077c4d228368354bca --- /dev/null +++ b/actor/middleware/propagator/middlewarepropagation_test.go @@ -0,0 +1,45 @@ +package propagator + +import ( + "sync" + "testing" + + "gitee.com/simplexyz/simpleactor-go/actor" + "github.com/stretchr/testify/assert" +) + +func TestPropagator(t *testing.T) { + mutex := &sync.Mutex{} + spawningCounter := 0 + system := actor.NewActorSystem() + + propagator := New(). + WithItselfForwarded(). + WithSpawnMiddleware(func(next actor.SpawnFunc) actor.SpawnFunc { + return func(actorSystem *actor.ActorSystem, id string, props *actor.Props, parentContext actor.SpawnerContext) (pid *actor.PID, e error) { + mutex.Lock() + spawningCounter++ + mutex.Unlock() + return next(actorSystem, id, props, parentContext) + } + }) + + var start func(input int) *actor.Props + start = func(input int) *actor.Props { + return actor.PropsFromFunc(func(c actor.Context) { + switch c.Message().(type) { + case *actor.Started: + if input > 0 { + c.Spawn(start(input - 1)) + } + } + }) + } + + rootContext := actor.NewRootContext(system, nil).WithSpawnMiddleware(propagator.SpawnMiddleware) + root := rootContext.Spawn(start(5)) + + _ = rootContext.StopFuture(root).Wait() + + assert.Equal(t, spawningCounter, 5) +} diff --git a/actor/middleware/protozip/inbound_middleware.go b/actor/middleware/protozip/inbound_middleware.go new file mode 100644 index 0000000000000000000000000000000000000000..3c45d39a2790c45002be61274caa3f72248351ff --- /dev/null +++ b/actor/middleware/protozip/inbound_middleware.go @@ -0,0 +1 @@ +package protozip diff --git a/actor/middleware/protozip/outbound_middleware.go b/actor/middleware/protozip/outbound_middleware.go new file mode 100644 index 0000000000000000000000000000000000000000..2a260749bd9eb4254d9fb94db1b427beefbcba43 --- /dev/null +++ b/actor/middleware/protozip/outbound_middleware.go @@ -0,0 +1,17 @@ +package protozip + +import ( + "gitee.com/simplexyz/simpleactor-go/actor" +) + +func ZipkinTracer(next actor.SenderFunc) actor.SenderFunc { + return func(ctx actor.SenderContext, target *actor.PID, envelope *actor.MessageEnvelope) { + header := ctx.MessageHeader() + + envelope.SetHeader("trace-id", header.Get("trace-id")) + envelope.SetHeader("span-id", header.Get("child-id")) + envelope.SetHeader("child-id", "123random") + + next(ctx, target, envelope) + } +} diff --git a/actor/middleware_chain.go b/actor/middleware_chain.go new file mode 100644 index 0000000000000000000000000000000000000000..26aa585ca49a90588d0e7e59f1bfb3fbb58d1442 --- /dev/null +++ b/actor/middleware_chain.go @@ -0,0 +1,53 @@ +package actor + +func makeReceiverMiddlewareChain(receiverMiddleware []ReceiverMiddleware, lastReceiver ReceiverFunc) ReceiverFunc { + if len(receiverMiddleware) == 0 { + return nil + } + + h := receiverMiddleware[len(receiverMiddleware)-1](lastReceiver) + for i := len(receiverMiddleware) - 2; i >= 0; i-- { + h = receiverMiddleware[i](h) + } + + return h +} + +func makeSenderMiddlewareChain(senderMiddleware []SenderMiddleware, lastSender SenderFunc) SenderFunc { + if len(senderMiddleware) == 0 { + return nil + } + + h := senderMiddleware[len(senderMiddleware)-1](lastSender) + for i := len(senderMiddleware) - 2; i >= 0; i-- { + h = senderMiddleware[i](h) + } + + return h +} + +func makeContextDecoratorChain(decorator []ContextDecorator, lastDecorator ContextDecoratorFunc) ContextDecoratorFunc { + if len(decorator) == 0 { + return nil + } + + h := decorator[len(decorator)-1](lastDecorator) + for i := len(decorator) - 2; i >= 0; i-- { + h = decorator[i](h) + } + + return h +} + +func makeSpawnMiddlewareChain(spawnMiddleware []SpawnMiddleware, lastSpawn SpawnFunc) SpawnFunc { + if len(spawnMiddleware) == 0 { + return nil + } + + h := spawnMiddleware[len(spawnMiddleware)-1](lastSpawn) + for i := len(spawnMiddleware) - 2; i >= 0; i-- { + h = spawnMiddleware[i](h) + } + + return h +} diff --git a/actor/middleware_chain_test.go b/actor/middleware_chain_test.go new file mode 100644 index 0000000000000000000000000000000000000000..e0c5fe994e2102e5e91f6b78f883c870f85f7e9f --- /dev/null +++ b/actor/middleware_chain_test.go @@ -0,0 +1,46 @@ +package actor + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func middleware(called *int) ReceiverMiddleware { + return func(next ReceiverFunc) ReceiverFunc { + fn := func(ctx ReceiverContext, env *MessageEnvelope) { + env.Message = env.Message.(int) + 1 + *called = env.Message.(int) + + next(ctx, env) + } + return fn + } +} + +func TestMakeReceiverMiddleware_CallsInCorrectOrder(t *testing.T) { + var c [3]int + + r := []ReceiverMiddleware{ + middleware(&c[0]), + middleware(&c[1]), + middleware(&c[2]), + } + + mc := &mockContext{} + + env := &MessageEnvelope{ + Message: 0, + } + + chain := makeReceiverMiddlewareChain(r, func(receiver ReceiverContext, env *MessageEnvelope) {}) + chain(mc, env) + + assert.Equal(t, 1, c[0]) + assert.Equal(t, 2, c[1]) + assert.Equal(t, 3, c[2]) +} + +func TestMakeInboundMiddleware_ReturnsNil(t *testing.T) { + assert.Nil(t, makeReceiverMiddlewareChain([]ReceiverMiddleware{}, func(_ ReceiverContext, _ *MessageEnvelope) {})) +} diff --git a/actor/pid.go b/actor/pid.go new file mode 100644 index 0000000000000000000000000000000000000000..347515438d142addfe9868973e73a32ed7ff79bc --- /dev/null +++ b/actor/pid.go @@ -0,0 +1,72 @@ +package actor + +import ( + "sync/atomic" + "unsafe" +) + +/* +ensure the generated pid file contains the p *Process +TODO: make some sed command to inject this somehow + +type PID struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Address string `protobuf:"bytes,1,opt,name=Address,proto3" json:"Address,omitempty"` + Id string `protobuf:"bytes,2,opt,name=Id,proto3" json:"Id,omitempty"` + RequestId uint32 `protobuf:"varint,3,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` + + //manually added + p *Process +} +*/ + +//goland:noinspection GoReceiverNames +func (pid *PID) ref(actorSystem *ActorSystem) Process { + p := (*Process)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&pid.p)))) + if p != nil { + if l, ok := (*p).(*ActorProcess); ok && atomic.LoadInt32(&l.dead) == 1 { + atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&pid.p)), nil) + } else { + return *p + } + } + + ref, exists := actorSystem.ProcessRegistry.Get(pid) + if exists { + atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&pid.p)), unsafe.Pointer(&ref)) + } + + return ref +} + +// sendUserMessage sends a messages asynchronously to the PID. +// +//goland:noinspection GoReceiverNames +func (pid *PID) sendUserMessage(actorSystem *ActorSystem, message interface{}) { + pid.ref(actorSystem).SendUserMessage(pid, message) +} + +//goland:noinspection GoReceiverNames. +func (pid *PID) sendSystemMessage(actorSystem *ActorSystem, message interface{}) { + pid.ref(actorSystem).SendSystemMessage(pid, message) +} + +//goland:noinspection GoReceiverNames. +func (pid *PID) Equal(other *PID) bool { + if pid != nil && other == nil { + return false + } + + return pid.ID == other.ID && pid.Address == other.Address && pid.RequestID == other.RequestID +} + +// NewPID returns a new instance of the PID struct. +func NewPID(address, id string) *PID { + return &PID{ + Address: address, + ID: id, + } +} diff --git a/actor/pid.rpc.go b/actor/pid.rpc.go new file mode 100644 index 0000000000000000000000000000000000000000..80e33ac7393cf40be9f1e1cd2a24e3bac2279143 --- /dev/null +++ b/actor/pid.rpc.go @@ -0,0 +1,62 @@ +package actor + +import "sync" + +const PH = 1 + +func (m *PID) ResetEx() { + if m == nil { + return + } + m.Address = "" + m.ID = "" + m.p = nil +} + +func (m *PID) Clone() *PID { + if m == nil { + return nil + } + return &PID{ + Address: m.Address, + ID: m.ID, + p: m.p, + } +} + +func Clone_PID_Slice(dest []*PID, src []*PID) []*PID { + if len(src) > 0 { + dest = make([]*PID, len(src)) + for i, e := range src { + if e != nil { + dest[i] = e.Clone() + } + } + } else { + //dest = []*PID{} + dest = nil + } + return dest +} + +var g_PID_Pool = sync.Pool{} + +func Get_PID() *PID { + m, ok := g_PID_Pool.Get().(*PID) + if !ok { + m = NewPID("", "") + } else { + if m == nil { + m = NewPID("", "") + } else { + m.ResetEx() + } + } + return m +} + +func Put_PID(i interface{}) { + if m, ok := i.(*PID); ok && m != nil { + g_PID_Pool.Put(i) + } +} diff --git a/actor/pid_test.go b/actor/pid_test.go new file mode 100644 index 0000000000000000000000000000000000000000..3d8ca54826ed0c02efc72be376743653de9934ed --- /dev/null +++ b/actor/pid_test.go @@ -0,0 +1,40 @@ +package actor + +import ( + "reflect" + "testing" + + "github.com/stretchr/testify/assert" +) + +type ShortLivingActor struct{} + +func (sl *ShortLivingActor) Receive(Context) { +} + +func TestStopFuture(t *testing.T) { + plog.Debug("hello world") + + ID := "UniqueID" + { + props := PropsFromProducer(func() Actor { return &ShortLivingActor{} }) + a, _ := rootContext.SpawnNamed(props, ID) + + fut := rootContext.StopFuture(a) + + res, errR := fut.Result() + if errR != nil { + assert.Fail(t, "Failed to wait stop actor %pids", errR) + return + } + + _, ok := res.(*Terminated) + if !ok { + assert.Fail(t, "Cannot cast %pids", reflect.TypeOf(res)) + return + } + + _, found := system.ProcessRegistry.Get(a) + assert.False(t, found) + } +} diff --git a/actor/pidset.go b/actor/pidset.go new file mode 100644 index 0000000000000000000000000000000000000000..7fb399f3fcfd0050772c1d18ee3652b2ef00a68c --- /dev/null +++ b/actor/pidset.go @@ -0,0 +1,112 @@ +package actor + +type PIDSet struct { + pids []*PID + lookup map[pidKey]int +} + +// pidKey is used as a key in the lookup map to avoid allocations. +type pidKey struct { + address string + id string +} + +func (p *PIDSet) key(pid *PID) pidKey { + return pidKey{address: pid.Address, id: pid.ID} +} + +// NewPIDSet returns a new PIDSet with the given pids. +func NewPIDSet(pids ...*PID) *PIDSet { + p := &PIDSet{} + for _, pid := range pids { + p.Add(pid) + } + return p +} + +func (p *PIDSet) ensureInit() { + if p.lookup == nil { + p.lookup = make(map[pidKey]int) + } +} + +func (p *PIDSet) indexOf(v *PID) int { + if idx, ok := p.lookup[p.key(v)]; ok { + return idx + } + + return -1 +} + +func (p *PIDSet) Contains(v *PID) bool { + _, ok := p.lookup[p.key(v)] + return ok +} + +// Add adds the element v to the set. +func (p *PIDSet) Add(v *PID) { + p.ensureInit() + if p.Contains(v) { + return + } + + p.pids = append(p.pids, v) + p.lookup[p.key(v)] = len(p.pids) - 1 +} + +// Remove removes v from the set and returns true if them element existed. +func (p *PIDSet) Remove(v *PID) bool { + p.ensureInit() + i := p.indexOf(v) + if i == -1 { + return false + } + + delete(p.lookup, p.key(v)) + if i < len(p.pids)-1 { + lastPID := p.pids[len(p.pids)-1] + + p.pids[i] = lastPID + p.lookup[p.key(lastPID)] = i + } + + p.pids = p.pids[:len(p.pids)-1] + + return true +} + +// Len returns the number of elements in the set. +func (p *PIDSet) Len() int { + return len(p.pids) +} + +// Clear removes all the elements in the set. +func (p *PIDSet) Clear() { + p.pids = p.pids[:0] + p.lookup = make(map[pidKey]int) +} + +// Empty reports whether the set is empty. +func (p *PIDSet) Empty() bool { + return p.Len() == 0 +} + +// Values returns all the elements of the set as a slice. +func (p *PIDSet) Values() []*PID { + return p.pids +} + +// ForEach invokes f for every element of the set. +func (p *PIDSet) ForEach(f func(i int, pid *PID)) { + for i, pid := range p.pids { + f(i, pid) + } +} + +func (p *PIDSet) Get(index int) *PID { + return p.pids[index] +} + +func (p *PIDSet) Clone() *PIDSet { + return NewPIDSet(p.pids...) +} diff --git a/actor/pidset_test.go b/actor/pidset_test.go new file mode 100644 index 0000000000000000000000000000000000000000..c4f70c497afd26411d8d865a00273e49cc4f8693 --- /dev/null +++ b/actor/pidset_test.go @@ -0,0 +1,164 @@ +package actor + +import ( + "math/rand" + "strconv" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestPIDSet_Empty(t *testing.T) { + var s PIDSet + assert.True(t, s.Empty()) +} + +func TestPIDSet_Clear(t *testing.T) { + var s PIDSet + s.Add(NewPID(localAddress, "p1")) + s.Add(NewPID(localAddress, "p2")) + s.Add(NewPID(localAddress, "p3")) + assert.Equal(t, 3, s.Len()) + s.Clear() + assert.True(t, s.Empty()) + assert.Len(t, s.pids, 0) +} + +func TestPIDSet_Remove(t *testing.T) { + var s PIDSet + s.Add(NewPID(localAddress, "p1")) + s.Add(NewPID(localAddress, "p2")) + s.Add(NewPID(localAddress, "p3")) + assert.Equal(t, 3, s.Len()) + + s.Remove(NewPID(localAddress, "p3")) + assert.Equal(t, 2, s.Len()) + assert.False(t, s.Contains(NewPID(localAddress, "p3"))) +} + +func TestPIDSet_AddSmall(t *testing.T) { + s := NewPIDSet() + p1 := NewPID(localAddress, "p1") + s.Add(p1) + assert.False(t, s.Empty()) + p1 = NewPID(localAddress, "p1") + s.Add(p1) + assert.Equal(t, 1, s.Len()) +} + +func TestPIDSet_Values(t *testing.T) { + var s PIDSet + s.Add(NewPID(localAddress, "p1")) + s.Add(NewPID(localAddress, "p2")) + s.Add(NewPID(localAddress, "p3")) + assert.False(t, s.Empty()) + + r := s.Values() + assert.Len(t, r, 3) +} + +func TestPIDSet_AddMap(t *testing.T) { + s := NewPIDSet() + p1 := NewPID(localAddress, "p1") + s.Add(p1) + assert.False(t, s.Empty()) + p1 = NewPID(localAddress, "p1") + s.Add(p1) + assert.Equal(t, 1, s.Len()) +} + +var pids []*PID + +func init() { + for i := 0; i < 100000; i++ { + pids = append(pids, NewPID(localAddress, "p"+strconv.Itoa(i))) + } +} + +func BenchmarkPIDSet_Add(b *testing.B) { + cases := []struct { + l int + }{ + {l: 1}, + {l: 5}, + {l: 20}, + {l: 500}, + } + + for _, tc := range cases { + b.Run("len "+strconv.Itoa(tc.l), func(b *testing.B) { + pidSetAdd(b, pids[:tc.l]) + }) + } +} + +func pidSetAdd(b *testing.B, data []*PID) { + for i := 0; i < b.N; i++ { + var s PIDSet + for j := 0; j < len(data); j++ { + s.Add(data[j]) + } + } +} + +func BenchmarkPIDSet_AddRemove(b *testing.B) { + cases := []struct { + l int + }{ + {l: 1}, + {l: 5}, + {l: 20}, + {l: 500}, + } + + for _, tc := range cases { + b.Run("len "+strconv.Itoa(tc.l), func(b *testing.B) { + pidSetAddRemove(b, pids[:tc.l]) + }) + } +} + +func pidSetAddRemove(b *testing.B, data []*PID) { + for i := 0; i < b.N; i++ { + var s PIDSet + for j := 0; j < len(data); j++ { + s.Add(data[j]) + } + for j := 0; j < len(data); j++ { + s.Remove(data[j]) + } + } +} + +func BenchmarkPIDSet(b *testing.B) { + cases := []struct { + l int + }{ + {l: 1}, + {l: 5}, + {l: 20}, + {l: 500}, + {l: 1000}, + {l: 10000}, + {l: 100000}, + } + + for _, tc := range cases { + b.Run("len "+strconv.Itoa(tc.l), func(b *testing.B) { + b.StopTimer() + var s PIDSet + for i := 0; i < tc.l; i++ { + s.Add(pids[i]) + } + b.StartTimer() + + for i := 0; i < b.N; i++ { + pid := pids[rand.Intn(len(pids))] + + s.Add(pid) + + s.Remove(s.Get(rand.Intn(s.Len()))) + } + }) + } +} diff --git a/actor/priority_queue.go b/actor/priority_queue.go new file mode 100644 index 0000000000000000000000000000000000000000..c8404f0592ee69c4e0277ec2e1fef60097dda510 --- /dev/null +++ b/actor/priority_queue.go @@ -0,0 +1,70 @@ +package actor + +// A priority queue is a sort of meta-queue that uses a queue per priority level. +// The underlying queues can be anything that implements the queue interface. +// +// Messages that implement the PriorityMessage interface (i.e. have a GetPriority +// method) will be consumed in priority order first, queue order second. So if a +// higher priority message arrives, it will jump to the front of the queue from +// the consumer's perspective. +// +// There are 8 priority levels (0-7) because having too many levels impacts +// performance. And 8 priority levels ought to be enough for anybody. ;) +// This means your GetPriority method should return int8s between 0 and 7. If any +// return values are higher or lower, they will be reset to 7 or 0, respectively. +// +// The default priority level is 4 for messages that don't implement PriorityMessage. +// If you want your message processed sooner than un-prioritized messages, have its +// GetPriority method return a larger int8 value. +// Likewise, if you'd like to de-prioritize your message, have its GetPriority method +// return an int8 less than 4. + +const ( + priorityLevels = 8 + DefaultPriority = int8(priorityLevels / 2) +) + +type PriorityMessage interface { + GetPriority() int8 +} + +type priorityQueue struct { + priorityQueues []queue +} + +func NewPriorityQueue(queueProducer func() queue) *priorityQueue { + q := &priorityQueue{ + priorityQueues: make([]queue, priorityLevels), + } + + for p := 0; p < priorityLevels; p++ { + q.priorityQueues[p] = queueProducer() + } + + return q +} + +func (q *priorityQueue) Push(item interface{}) { + itemPriority := DefaultPriority + + if priorityItem, ok := item.(PriorityMessage); ok { + itemPriority = priorityItem.GetPriority() + if itemPriority < 0 { + itemPriority = 0 + } + if itemPriority > priorityLevels-1 { + itemPriority = priorityLevels - 1 + } + } + + q.priorityQueues[itemPriority].Push(item) +} + +func (q *priorityQueue) Pop() interface{} { + for p := priorityLevels - 1; p >= 0; p-- { + if item := q.priorityQueues[p].Pop(); item != nil { + return item + } + } + return nil +} diff --git a/actor/priority_queue_test.go b/actor/priority_queue_test.go new file mode 100644 index 0000000000000000000000000000000000000000..6919c581c6e1e9635c1d4b4687931acf6c971bdd --- /dev/null +++ b/actor/priority_queue_test.go @@ -0,0 +1,193 @@ +package actor + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "gitee.com/simplexyz/simpleactor-go/internal/queue/goring" + "gitee.com/simplexyz/simpleactor-go/internal/queue/mpsc" +) + +type Message interface { + GetMessage() string +} + +type TestPriorityMessage struct { + message string + priority int8 +} + +type TestMessage struct { + message string +} + +func (tpm *TestPriorityMessage) GetPriority() int8 { + return tpm.priority +} + +func (tpm *TestPriorityMessage) GetMessage() string { + return tpm.message +} + +func (tm *TestMessage) GetMessage() string { + return tm.message +} + +func newTestGoringPriorityQueue() *priorityQueue { + return NewPriorityQueue(func() queue { + return &unboundedMailboxQueue{ + userMailbox: goring.New(1), + } + }) +} + +func newTestMpscPriorityQueue() *priorityQueue { + return NewPriorityQueue(func() queue { + return mpsc.New() + }) +} + +func TestPushPopGoring(t *testing.T) { + q := newTestGoringPriorityQueue() + q.Push("hello") + res := q.Pop() + assert.Equal(t, "hello", res) +} + +func TestPushPopGoringPriority(t *testing.T) { + q := newTestGoringPriorityQueue() + + // pushes + + for i := 0; i < 2; i++ { + q.Push(&TestPriorityMessage{ + message: "7 hello", + priority: 7, + }) + } + + for i := 0; i < 2; i++ { + q.Push(&TestPriorityMessage{ + message: "5 hello", + priority: 5, + }) + } + + for i := 0; i < 2; i++ { + q.Push(&TestPriorityMessage{ + message: "0 hello", + priority: 0, + }) + } + + for i := 0; i < 2; i++ { + q.Push(&TestPriorityMessage{ + message: "6 hello", + priority: 6, + }) + } + + for i := 0; i < 2; i++ { + q.Push(&TestMessage{message: "hello"}) + } + + // pops in priority order + + for i := 0; i < 2; i++ { + res := q.Pop() + assert.Equal(t, "7 hello", res.(Message).GetMessage()) + } + + for i := 0; i < 2; i++ { + res := q.Pop() + assert.Equal(t, "6 hello", res.(Message).GetMessage()) + } + + for i := 0; i < 2; i++ { + res := q.Pop() + assert.Equal(t, "5 hello", res.(Message).GetMessage()) + } + + for i := 0; i < 2; i++ { + res := q.Pop() + assert.Equal(t, "hello", res.(Message).GetMessage()) + } + + for i := 0; i < 2; i++ { + res := q.Pop() + assert.Equal(t, "0 hello", res.(Message).GetMessage()) + } +} + +func TestPushPopMpsc(t *testing.T) { + q := newTestMpscPriorityQueue() + q.Push("hello") + res := q.Pop() + assert.Equal(t, "hello", res) +} + +func TestPushPopMpscPriority(t *testing.T) { + q := newTestMpscPriorityQueue() + + // pushes + + for i := 0; i < 2; i++ { + q.Push(&TestPriorityMessage{ + message: "7 hello", + priority: 7, + }) + } + + for i := 0; i < 2; i++ { + q.Push(&TestPriorityMessage{ + message: "5 hello", + priority: 5, + }) + } + + for i := 0; i < 2; i++ { + q.Push(&TestPriorityMessage{ + message: "0 hello", + priority: 0, + }) + } + + for i := 0; i < 2; i++ { + q.Push(&TestPriorityMessage{ + message: "6 hello", + priority: 6, + }) + } + + for i := 0; i < 2; i++ { + q.Push(&TestMessage{message: "hello"}) + } + + // pops in priority order + + for i := 0; i < 2; i++ { + res := q.Pop() + assert.Equal(t, "7 hello", res.(Message).GetMessage()) + } + + for i := 0; i < 2; i++ { + res := q.Pop() + assert.Equal(t, "6 hello", res.(Message).GetMessage()) + } + + for i := 0; i < 2; i++ { + res := q.Pop() + assert.Equal(t, "5 hello", res.(Message).GetMessage()) + } + + for i := 0; i < 2; i++ { + res := q.Pop() + assert.Equal(t, "hello", res.(Message).GetMessage()) + } + + for i := 0; i < 2; i++ { + res := q.Pop() + assert.Equal(t, "0 hello", res.(Message).GetMessage()) + } +} diff --git a/actor/process.go b/actor/process.go new file mode 100644 index 0000000000000000000000000000000000000000..2ea0611076fe5aca42b1ab396657d557141504a7 --- /dev/null +++ b/actor/process.go @@ -0,0 +1,8 @@ +package actor + +// A Process is an interface that defines the base contract for interaction of actors +type Process interface { + SendUserMessage(pid *PID, message interface{}) + SendSystemMessage(pid *PID, message interface{}) + Stop(pid *PID) +} diff --git a/actor/process_registry.go b/actor/process_registry.go new file mode 100644 index 0000000000000000000000000000000000000000..e96ef4bf3723495fbd853cd821f894b55b8d406f --- /dev/null +++ b/actor/process_registry.go @@ -0,0 +1,141 @@ +package actor + +import ( + "sync/atomic" + + murmur32 "github.com/twmb/murmur3" + + cmap "github.com/orcaman/concurrent-map" +) + +type ProcessRegistryValue struct { + SequenceID uint64 + ActorSystem *ActorSystem + Address string + LocalPIDs *SliceMap + RemoteHandlers []AddressResolver +} + +type SliceMap struct { + LocalPIDs []cmap.ConcurrentMap +} + +func newSliceMap() *SliceMap { + sm := &SliceMap{} + sm.LocalPIDs = make([]cmap.ConcurrentMap, 1024) + + for i := 0; i < len(sm.LocalPIDs); i++ { + sm.LocalPIDs[i] = cmap.New() + } + + return sm +} + +func (s *SliceMap) GetBucket(key string) cmap.ConcurrentMap { + hash := murmur32.Sum32([]byte(key)) + index := int(hash) % len(s.LocalPIDs) + + return s.LocalPIDs[index] +} + +const ( + localAddress = "nonhost" +) + +func NewProcessRegistry(actorSystem *ActorSystem) *ProcessRegistryValue { + return &ProcessRegistryValue{ + ActorSystem: actorSystem, + Address: localAddress, + LocalPIDs: newSliceMap(), + } +} + +// An AddressResolver is used to resolve remote actors +type AddressResolver func(*PID) (Process, bool) + +func (pr *ProcessRegistryValue) RegisterAddressResolver(handler AddressResolver) { + pr.RemoteHandlers = append(pr.RemoteHandlers, handler) +} + +const ( + digits = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ~+" +) + +func uint64ToId(u uint64) string { + var buf [13]byte + i := 13 + // base is power of 2: use shifts and masks instead of / and % + for u >= 64 { + i-- + buf[i] = digits[uintptr(u)&0x3f] + u >>= 6 + } + // u < base + i-- + buf[i] = digits[uintptr(u)] + i-- + buf[i] = '$' + + return string(buf[i:]) +} + +func (pr *ProcessRegistryValue) NextId() string { + counter := atomic.AddUint64(&pr.SequenceID, 1) + + return uint64ToId(counter) +} + +func (pr *ProcessRegistryValue) Add(process Process, id string) (*PID, bool) { + bucket := pr.LocalPIDs.GetBucket(id) + + return &PID{ + Address: pr.Address, + ID: id, + }, bucket.SetIfAbsent(id, process) +} + +func (pr *ProcessRegistryValue) Remove(pid *PID) { + bucket := pr.LocalPIDs.GetBucket(pid.ID) + + ref, _ := bucket.Pop(pid.ID) + if l, ok := ref.(*ActorProcess); ok { + atomic.StoreInt32(&l.dead, 1) + } +} + +func (pr *ProcessRegistryValue) Get(pid *PID) (Process, bool) { + if pid == nil { + return pr.ActorSystem.DeadLetter, false + } + + if pid.Address != localAddress && pid.Address != pr.Address { + for _, handler := range pr.RemoteHandlers { + ref, ok := handler(pid) + if ok { + return ref, true + } + } + + return pr.ActorSystem.DeadLetter, false + } + + bucket := pr.LocalPIDs.GetBucket(pid.ID) + ref, ok := bucket.Get(pid.ID) + + if !ok { + return pr.ActorSystem.DeadLetter, false + } + + return ref.(Process), true +} + +func (pr *ProcessRegistryValue) GetLocal(id string) (Process, bool) { + bucket := pr.LocalPIDs.GetBucket(id) + ref, ok := bucket.Get(id) + + if !ok { + return pr.ActorSystem.DeadLetter, false + } + + return ref.(Process), true +} diff --git a/actor/process_registry_test.go b/actor/process_registry_test.go new file mode 100644 index 0000000000000000000000000000000000000000..14676c484d83d7de46c9de58ed7fdd1d1bc533e9 --- /dev/null +++ b/actor/process_registry_test.go @@ -0,0 +1,46 @@ +package actor + +import ( + "strconv" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestUint64ToId(t *testing.T) { + cases := []struct { + i uint64 + e string + }{ + {0xfedcba9876543210, "$fXsKFxSl38g"}, + {0x0, "$0"}, + {0x1, "$1"}, + {0xf, "$f"}, + {0x1041041041041041, "$11111111111"}, + } + for _, tc := range cases { + t.Run(tc.e, func(t *testing.T) { + s := uint64ToId(tc.i) + assert.Equal(t, tc.e, s) + }) + } +} + +var ss string + +func BenchmarkUint64ToId(b *testing.B) { + var s string + for i := 0; i < b.N; i++ { + s = uint64ToId(uint64(i) << 5) + } + ss = s +} + +func BenchmarkUint64ToString2(b *testing.B) { + var s string + var buf [12]byte + for i := 0; i < b.N; i++ { + s = string(strconv.AppendUint(buf[:], uint64(i)<<5, 36)) + } + ss = s +} diff --git a/actor/props.go b/actor/props.go new file mode 100644 index 0000000000000000000000000000000000000000..3f11fc7b3b14a908fa453ea6428133849ec2619f --- /dev/null +++ b/actor/props.go @@ -0,0 +1,155 @@ +package actor + +import ( + "context" + "errors" + "fmt" + + "gitee.com/simplexyz/simpleactor-go/log" + "gitee.com/simplexyz/simpleactor-go/metrics" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/metric" +) + +type ( + SpawnFunc func(actorSystem *ActorSystem, id string, props *Props, parentContext SpawnerContext) (*PID, error) + ReceiverMiddleware func(next ReceiverFunc) ReceiverFunc + SenderMiddleware func(next SenderFunc) SenderFunc + ContextDecorator func(next ContextDecoratorFunc) ContextDecoratorFunc + SpawnMiddleware func(next SpawnFunc) SpawnFunc +) + +// Default values. +var ( + defaultDispatcher = NewDefaultDispatcher(300) + defaultMailboxProducer = Unbounded() + defaultSpawner = func(actorSystem *ActorSystem, id string, props *Props, parentContext SpawnerContext) (*PID, error) { + ctx := newActorContext(actorSystem, props, parentContext.Self()) + mb := props.produceMailbox() + + // prepare the mailbox number counter + if ctx.actorSystem.Config.MetricsProvider != nil { + sysMetrics, ok := ctx.actorSystem.Extensions.Get(extensionId).(*Metrics) + if ok && sysMetrics.enabled { + if instruments := sysMetrics.metrics.Get(metrics.InternalActorMetrics); instruments != nil { + sysMetrics.PrepareMailboxLengthGauge() + meter := otel.Meter(metrics.LibName) + + if _, err := meter.RegisterCallback(func(_ context.Context, o metric.Observer) error { + o.ObserveInt64(instruments.ActorMailboxLength, int64(mb.UserMessageCount()), metric.WithAttributes(sysMetrics.CommonLabels(ctx)...)) + return nil + }); err != nil { + err = fmt.Errorf("failed to instrument Actor Mailbox, %w", err) + plog.Error(err.Error(), log.Error(err)) + } + } + } + } + + dp := props.getDispatcher() + proc := NewActorProcess(mb) + pid, absent := actorSystem.ProcessRegistry.Add(proc, id) + if !absent { + return pid, ErrNameExists + } + ctx.self = pid + + initialize(props, ctx) + + mb.RegisterHandlers(ctx, dp) + mb.PostSystemMessage(startedMessage) + mb.Start() + + return pid, nil + } + defaultContextDecorator = func(ctx Context) Context { + return ctx + } +) + +func initialize(props *Props, ctx *actorContext) { + if props.onInit == nil { + return + } + + for _, init := range props.onInit { + init(ctx) + } +} + +// DefaultSpawner this is a hacking way to allow Proto.Router access default spawner func. +var DefaultSpawner SpawnFunc = defaultSpawner + +// ErrNameExists is the error used when an existing name is used for spawning an actor. +var ErrNameExists = errors.New("spawn: name exists") + +// Props represents configuration to define how an actor should be created. +type Props struct { + spawner SpawnFunc + producer Producer + mailboxProducer MailboxProducer + guardianStrategy SupervisorStrategy + supervisionStrategy SupervisorStrategy + dispatcher Dispatcher + receiverMiddleware []ReceiverMiddleware + senderMiddleware []SenderMiddleware + spawnMiddleware []SpawnMiddleware + receiverMiddlewareChain ReceiverFunc + senderMiddlewareChain SenderFunc + spawnMiddlewareChain SpawnFunc + contextDecorator []ContextDecorator + contextDecoratorChain ContextDecoratorFunc + onInit []func(ctx Context) +} + +func (props *Props) getSpawner() SpawnFunc { + if props.spawner == nil { + return defaultSpawner + } + + return props.spawner +} + +func (props *Props) getDispatcher() Dispatcher { + if props.dispatcher == nil { + return defaultDispatcher + } + + return props.dispatcher +} + +func (props *Props) getSupervisor() SupervisorStrategy { + if props.supervisionStrategy == nil { + return defaultSupervisionStrategy + } + + return props.supervisionStrategy +} + +func (props *Props) getContextDecoratorChain() ContextDecoratorFunc { + if props.contextDecoratorChain == nil { + return defaultContextDecorator + } + + return props.contextDecoratorChain +} + +func (props *Props) produceMailbox() Mailbox { + if props.mailboxProducer == nil { + return defaultMailboxProducer() + } + + return props.mailboxProducer() +} + +func (props *Props) spawn(actorSystem *ActorSystem, name string, parentContext SpawnerContext) (*PID, error) { + return props.getSpawner()(actorSystem, name, props, parentContext) +} + +func (props *Props) Configure(opts ...PropsOption) *Props { + for _, opt := range opts { + opt(props) + } + + return props +} diff --git a/actor/props_opts.go b/actor/props_opts.go new file mode 100644 index 0000000000000000000000000000000000000000..542b4988252831a89e2a7c4d1bdd61167a54dd70 --- /dev/null +++ b/actor/props_opts.go @@ -0,0 +1,135 @@ +package actor + +type PropsOption func(props *Props) + +func WithOnInit(init ...func(ctx Context)) PropsOption { + return func(props *Props) { + props.onInit = append(props.onInit, init...) + } +} + +func WithProducer(p Producer) PropsOption { + return func(props *Props) { + props.producer = p + } +} + +func WithDispatcher(dispatcher Dispatcher) PropsOption { + return func(props *Props) { + props.dispatcher = dispatcher + } +} + +func WithMailbox(mailbox MailboxProducer) PropsOption { + return func(props *Props) { + props.mailboxProducer = mailbox + } +} + +func WithContextDecorator(contextDecorator ...ContextDecorator) PropsOption { + return func(props *Props) { + props.contextDecorator = append(props.contextDecorator, contextDecorator...) + + props.contextDecoratorChain = makeContextDecoratorChain(props.contextDecorator, func(ctx Context) Context { + return ctx + }) + } +} + +func WithGuardian(guardian SupervisorStrategy) PropsOption { + return func(props *Props) { + props.guardianStrategy = guardian + } +} + +func WithSupervisor(supervisor SupervisorStrategy) PropsOption { + return func(props *Props) { + props.supervisionStrategy = supervisor + } +} + +func WithReceiverMiddleware(middleware ...ReceiverMiddleware) PropsOption { + return func(props *Props) { + props.receiverMiddleware = append(props.receiverMiddleware, middleware...) + + // Construct the receiver middleware chain with the final receiver at the end + props.receiverMiddlewareChain = makeReceiverMiddlewareChain(props.receiverMiddleware, func(ctx ReceiverContext, envelope *MessageEnvelope) { + ctx.Receive(envelope) + }) + } +} + +func WithSenderMiddleware(middleware ...SenderMiddleware) PropsOption { + return func(props *Props) { + props.senderMiddleware = append(props.senderMiddleware, middleware...) + + // Construct the sender middleware chain with the final sender at the end + props.senderMiddlewareChain = makeSenderMiddlewareChain(props.senderMiddleware, func(sender SenderContext, target *PID, envelope *MessageEnvelope) { + target.sendUserMessage(sender.ActorSystem(), envelope) + }) + } +} + +func WithSpawnFunc(spawn SpawnFunc) PropsOption { + return func(props *Props) { + props.spawner = spawn + } +} + +func WithFunc(f ReceiveFunc) PropsOption { + return func(props *Props) { + props.producer = func() Actor { return f } + } +} + +func WithSpawnMiddleware(middleware ...SpawnMiddleware) PropsOption { + return func(props *Props) { + props.spawnMiddleware = append(props.spawnMiddleware, middleware...) + + // Construct the spawner middleware chain with the final spawner at the end + props.spawnMiddlewareChain = makeSpawnMiddlewareChain(props.spawnMiddleware, func(actorSystem *ActorSystem, id string, props *Props, parentContext SpawnerContext) (pid *PID, e error) { + if props.spawner == nil { + return defaultSpawner(actorSystem, id, props, parentContext) + } + + return props.spawner(actorSystem, id, props, parentContext) + }) + } +} + +// PropsFromProducer creates a props with the given actor producer assigned. +func PropsFromProducer(producer Producer, opts ...PropsOption) *Props { + p := &Props{ + producer: producer, + contextDecorator: make([]ContextDecorator, 0), + } + p.Configure(opts...) + + return p +} + +// PropsFromFunc creates a props with the given receive func assigned as the actor producer. +func PropsFromFunc(f ReceiveFunc, opts ...PropsOption) *Props { + p := PropsFromProducer(func() Actor { return f }, opts...) + + return p +} + +func (props *Props) Clone(opts ...PropsOption) *Props { + cp := PropsFromProducer(props.producer, + WithDispatcher(props.dispatcher), + WithMailbox(props.mailboxProducer), + WithContextDecorator(props.contextDecorator...), + WithGuardian(props.guardianStrategy), + WithSupervisor(props.supervisionStrategy), + WithReceiverMiddleware(props.receiverMiddleware...), + WithSenderMiddleware(props.senderMiddleware...), + WithSpawnFunc(props.spawner), + WithSpawnMiddleware(props.spawnMiddleware...), + WithOnInit(props.onInit...), + ) + + cp.Configure(opts...) + + return cp +} diff --git a/actor/props_test.go b/actor/props_test.go new file mode 100644 index 0000000000000000000000000000000000000000..5083be6a5f9f2d54f9500072060725225218769f --- /dev/null +++ b/actor/props_test.go @@ -0,0 +1,11 @@ +package actor + +import "testing" + +func TestProps_Clone(t *testing.T) { + p := PropsFromFunc(func(c Context) {}, WithOnInit(func(c Context) {})) + p2 := p.Clone() + if p == p2 { + t.Error("Clone should return a new instance") + } +} diff --git a/actor/queue.go b/actor/queue.go new file mode 100644 index 0000000000000000000000000000000000000000..fdfa616502c708a3fe8f962f7ae1d17afe7f5248 --- /dev/null +++ b/actor/queue.go @@ -0,0 +1,6 @@ +package actor + +type queue interface { + Push(interface{}) + Pop() interface{} +} diff --git a/actor/root_context.go b/actor/root_context.go new file mode 100644 index 0000000000000000000000000000000000000000..aae7ba718a59091a30fc8bf3c1f344821ee99c11 --- /dev/null +++ b/actor/root_context.go @@ -0,0 +1,221 @@ +package actor + +import ( + "time" +) + +type RootContext struct { + actorSystem *ActorSystem + senderMiddleware SenderFunc + spawnMiddleware SpawnFunc + headers messageHeader + guardianStrategy SupervisorStrategy +} + +var ( + _ SenderContext = &RootContext{} + _ SpawnerContext = &RootContext{} + _ stopperPart = &RootContext{} +) + +func NewRootContext(actorSystem *ActorSystem, header map[string]string, middleware ...SenderMiddleware) *RootContext { + if header == nil { + header = make(map[string]string) + } + + return &RootContext{ + actorSystem: actorSystem, + senderMiddleware: makeSenderMiddlewareChain(middleware, func(_ SenderContext, target *PID, envelope *MessageEnvelope) { + target.sendUserMessage(actorSystem, envelope) + }), + headers: header, + } +} + +func (rc RootContext) Copy() *RootContext { + return &rc +} + +func (rc *RootContext) ActorSystem() *ActorSystem { + return rc.actorSystem +} + +func (rc *RootContext) WithHeaders(headers map[string]string) *RootContext { + rc.headers = headers + + return rc +} + +func (rc *RootContext) WithSenderMiddleware(middleware ...SenderMiddleware) *RootContext { + rc.senderMiddleware = makeSenderMiddlewareChain(middleware, func(_ SenderContext, target *PID, envelope *MessageEnvelope) { + target.sendUserMessage(rc.actorSystem, envelope) + }) + + return rc +} + +func (rc *RootContext) WithSpawnMiddleware(middleware ...SpawnMiddleware) *RootContext { + rc.spawnMiddleware = makeSpawnMiddlewareChain(middleware, func(actorSystem *ActorSystem, id string, props *Props, parentContext SpawnerContext) (pid *PID, e error) { + return props.spawn(actorSystem, id, rc) + }) + + return rc +} + +func (rc *RootContext) WithGuardian(guardian SupervisorStrategy) *RootContext { + rc.guardianStrategy = guardian + + return rc +} + +// +// Interface: info +// + +func (rc *RootContext) Parent() *PID { + return nil +} + +func (rc *RootContext) Self() *PID { + if rc.guardianStrategy != nil { + return rc.actorSystem.Guardians.getGuardianPid(rc.guardianStrategy) + } + + return nil +} + +func (rc *RootContext) Sender() *PID { + return nil +} + +func (rc *RootContext) Actor() Actor { + return nil +} + +// +// Interface: sender +// + +func (rc *RootContext) Message() interface{} { + return nil +} + +func (rc *RootContext) MessageHeader() ReadonlyMessageHeader { + return rc.headers +} + +func (rc *RootContext) Send(pid *PID, message interface{}) { + rc.sendUserMessage(pid, message) +} + +func (rc *RootContext) Request(pid *PID, message interface{}) { + rc.sendUserMessage(pid, message) +} + +func (rc *RootContext) RequestWithCustomSender(pid *PID, message interface{}, sender *PID) { + env := &MessageEnvelope{ + Header: nil, + Message: message, + Sender: sender, + } + rc.sendUserMessage(pid, env) +} + +// RequestFuture sends a message to a given PID and returns a Future. +func (rc *RootContext) RequestFuture(pid *PID, message interface{}, timeout time.Duration) *Future { + future := NewFuture(rc.actorSystem, timeout) + env := &MessageEnvelope{ + Header: nil, + Message: message, + Sender: future.PID(), + } + rc.sendUserMessage(pid, env) + + return future +} + +func (rc *RootContext) sendUserMessage(pid *PID, message interface{}) { + if rc.senderMiddleware != nil { + // Request based middleware + rc.senderMiddleware(rc, pid, WrapEnvelope(message)) + } else { + // tell based middleware + pid.sendUserMessage(rc.actorSystem, message) + } +} + +// +// Interface: spawner +// + +// Spawn starts a new actor based on props and named with a unique id. +func (rc *RootContext) Spawn(props *Props) *PID { + pid, err := rc.SpawnNamed(props, rc.actorSystem.ProcessRegistry.NextId()) + if err != nil { + panic(err) + } + + return pid +} + +// SpawnPrefix starts a new actor based on props and named using a prefix followed by a unique id. +func (rc *RootContext) SpawnPrefix(props *Props, prefix string) *PID { + pid, err := rc.SpawnNamed(props, prefix+rc.actorSystem.ProcessRegistry.NextId()) + if err != nil { + panic(err) + } + + return pid +} + +// SpawnNamed starts a new actor based on props and named using the specified name +// +// # ErrNameExists will be returned if id already exists +// +// Please do not use name sharing same pattern with system actors, for example "YourPrefix$1", "Remote$1", "future$1". +func (rc *RootContext) SpawnNamed(props *Props, name string) (*PID, error) { + rootContext := rc + if props.guardianStrategy != nil { + rootContext = rc.Copy().WithGuardian(props.guardianStrategy) + } + + if rootContext.spawnMiddleware != nil { + return rc.spawnMiddleware(rc.actorSystem, name, props, rootContext) + } + + return props.spawn(rc.actorSystem, name, rootContext) +} + +// +// Interface: StopperContext +// + +// Stop will stop actor immediately regardless of existing user messages in mailbox. +func (rc *RootContext) Stop(pid *PID) { + pid.ref(rc.actorSystem).Stop(pid) +} + +// StopFuture will stop actor immediately regardless of existing user messages in mailbox, and return its future. +func (rc *RootContext) StopFuture(pid *PID) *Future { + future := NewFuture(rc.actorSystem, 10*time.Second) + + pid.sendSystemMessage(rc.actorSystem, &Watch{Watcher: future.pid}) + rc.Stop(pid) + + return future +} + +// Poison will tell actor to stop after processing current user messages in mailbox. +func (rc *RootContext) Poison(pid *PID) { + pid.sendUserMessage(rc.actorSystem, poisonPillMessage) +} + +// PoisonFuture will tell actor to stop after processing current user messages in mailbox, and return its future. +func (rc *RootContext) PoisonFuture(pid *PID) *Future { + future := NewFuture(rc.actorSystem, 10*time.Second) + + pid.sendSystemMessage(rc.actorSystem, &Watch{Watcher: future.pid}) + rc.Poison(pid) + + return future +} diff --git a/actor/spawn_example_test.go b/actor/spawn_example_test.go new file mode 100644 index 0000000000000000000000000000000000000000..9d1d880abad4a0f7ae870cab48212f393fab9c99 --- /dev/null +++ b/actor/spawn_example_test.go @@ -0,0 +1,33 @@ +package actor_test + +import ( + "fmt" + "sync" + + "gitee.com/simplexyz/simpleactor-go/actor" +) + +// Spawn creates instances of actors, similar to 'new' or 'make' but for actors. +func ExampleRootContext_Spawn() { + var wg sync.WaitGroup + wg.Add(1) + + // create root context + // define the actor props. + // props define the creation process of an actor + props := actor.PropsFromFunc(func(ctx actor.Context) { + // check if the message is a *actor.Started message + // this is the first message all actors get + // actor.Started is received async and can be used + // to initialize your actors initial state + if _, ok := ctx.Message().(*actor.Started); ok { + fmt.Println("hello world") + wg.Done() + } + }) + + // spawn the actor based on the props + system.Root.Spawn(props) + wg.Wait() + // Output: hello world +} diff --git a/actor/spawn_named_example_test.go b/actor/spawn_named_example_test.go new file mode 100644 index 0000000000000000000000000000000000000000..c45d92cdcdd2c831d4818e85cedd71af31d910e2 --- /dev/null +++ b/actor/spawn_named_example_test.go @@ -0,0 +1,39 @@ +package actor_test + +import ( + "fmt" + "log" + "sync" + + "gitee.com/simplexyz/simpleactor-go/actor" +) + +// Spawn creates instances of actors, similar to 'new' or 'make' but for actors. +func ExampleRootContext_SpawnNamed() { + var wg sync.WaitGroup + wg.Add(1) + + // create root context + context := system.Root + + // define the actor props. + // props define the creation process of an actor + props := actor.PropsFromFunc(func(ctx actor.Context) { + // check if the message is a *actor.Started message + // this is the first message all actors get + // actor.Started is received async and can be used + // to initialize your actors initial state + if _, ok := ctx.Message().(*actor.Started); ok { + fmt.Println("hello world") + wg.Done() + } + }) + + // spawn the actor based on the props + _, err := context.SpawnNamed(props, "my-actor") + if err != nil { + log.Fatal("The actor name is already in use") + } + wg.Wait() + // Output: hello world +} diff --git a/actor/spawn_test.go b/actor/spawn_test.go new file mode 100644 index 0000000000000000000000000000000000000000..b10f460d7bc5e7c9ebeaf5fc7842fa72646927fe --- /dev/null +++ b/actor/spawn_test.go @@ -0,0 +1,59 @@ +package actor + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +type Increment struct{} + +type GorgeousActor struct { + Counter +} + +type Counter struct { + value int +} + +func (counter *Counter) Increment() { + counter.value = counter.value + 1 +} + +func (a *GorgeousActor) Receive(context Context) { + switch context.Message().(type) { + case *Started: + case Increment: + a.Increment() + context.Respond(a.value) + } +} + +func TestLookupById(t *testing.T) { + ID := "UniqueID" + { + props := PropsFromProducer(func() Actor { return &GorgeousActor{Counter: Counter{value: 0}} }) + pid, _ := rootContext.SpawnNamed(props, ID) + defer rootContext.Stop(pid) + + result := rootContext.RequestFuture(pid, Increment{}, testTimeout) + value, err := result.Result() + if err != nil { + assert.Fail(t, "timed out") + return + } + assert.IsType(t, 0, value) + assert.Equal(t, 1, value.(int)) + } + { + props := PropsFromProducer(func() Actor { return &GorgeousActor{Counter: Counter{value: 0}} }) + pid, _ := rootContext.SpawnNamed(props, ID) + result := rootContext.RequestFuture(pid, Increment{}, testTimeout) + value, err := result.Result() + if err != nil { + assert.Fail(t, "timed out") + return + } + assert.Equal(t, 2, value.(int)) + } +} diff --git a/actor/strategy_all_for_one.go b/actor/strategy_all_for_one.go new file mode 100644 index 0000000000000000000000000000000000000000..2f4b58b0b3a2e9553c62f198c293b319d2fd6ed7 --- /dev/null +++ b/actor/strategy_all_for_one.go @@ -0,0 +1,70 @@ +package actor + +import "time" + +// NewAllForOneStrategy returns a new SupervisorStrategy which applies the given fault Directive from the decider to the +// failing child and all its children. +// +// This strategy is appropriate when the children have a strong dependency, such that and any single one failing would +// place them all into a potentially invalid state. +func NewAllForOneStrategy(maxNrOfRetries int, withinDuration time.Duration, decider DeciderFunc) SupervisorStrategy { + return &allForOneStrategy{ + maxNrOfRetries: maxNrOfRetries, + withinDuration: withinDuration, + decider: decider, + } +} + +type allForOneStrategy struct { + maxNrOfRetries int + withinDuration time.Duration + decider DeciderFunc +} + +var _ SupervisorStrategy = &allForOneStrategy{} + +func (strategy *allForOneStrategy) HandleFailure(actorSystem *ActorSystem, supervisor Supervisor, child *PID, rs *RestartStatistics, reason interface{}, message interface{}) { + directive := strategy.decider(reason) + switch directive { + case ResumeDirective: + // resume the failing child + logFailure(actorSystem, child, reason, directive) + supervisor.ResumeChildren(child) + case RestartDirective: + children := supervisor.Children() + // try restart the all the children + if strategy.shouldStop(rs) { + logFailure(actorSystem, child, reason, StopDirective) + supervisor.StopChildren(children...) + } else { + logFailure(actorSystem, child, reason, RestartDirective) + supervisor.RestartChildren(children...) + } + case StopDirective: + children := supervisor.Children() + // stop all the children, no need to involve the crs + logFailure(actorSystem, child, reason, directive) + supervisor.StopChildren(children...) + case EscalateDirective: + // send failure to parent + // supervisor mailbox + // do not log here, log in the parent handling the error + supervisor.EscalateFailure(reason, message) + } +} + +func (strategy *allForOneStrategy) shouldStop(rs *RestartStatistics) bool { + // supervisor says this child may not restart + if strategy.maxNrOfRetries == 0 { + return true + } + + rs.Fail() + + if rs.NumberOfFailures(strategy.withinDuration) > strategy.maxNrOfRetries { + rs.Reset() + return true + } + + return false +} diff --git a/actor/strategy_exponential_backoff.go b/actor/strategy_exponential_backoff.go new file mode 100644 index 0000000000000000000000000000000000000000..b7029388898208aa52390bd699126a1845576227 --- /dev/null +++ b/actor/strategy_exponential_backoff.go @@ -0,0 +1,44 @@ +package actor + +import ( + "math/rand" + "time" +) + +// NewExponentialBackoffStrategy creates a new Supervisor strategy that restarts a faulting child using an exponential +// back off algorithm: +// +// delay = +func NewExponentialBackoffStrategy(backoffWindow time.Duration, initialBackoff time.Duration) SupervisorStrategy { + return &exponentialBackoffStrategy{ + backoffWindow: backoffWindow, + initialBackoff: initialBackoff, + } +} + +type exponentialBackoffStrategy struct { + backoffWindow time.Duration + initialBackoff time.Duration +} + +var _ SupervisorStrategy = &exponentialBackoffStrategy{} + +func (strategy *exponentialBackoffStrategy) HandleFailure(actorSystem *ActorSystem, supervisor Supervisor, child *PID, rs *RestartStatistics, reason interface{}, _ interface{}) { + strategy.setFailureCount(rs) + + backoff := rs.FailureCount() * int(strategy.initialBackoff.Nanoseconds()) + noise := rand.Intn(500) + dur := time.Duration(backoff + noise) + time.AfterFunc(dur, func() { + logFailure(actorSystem, child, reason, RestartDirective) + supervisor.RestartChildren(child) + }) +} + +func (strategy *exponentialBackoffStrategy) setFailureCount(rs *RestartStatistics) { + if rs.NumberOfFailures(strategy.backoffWindow) == 0 { + rs.Reset() + } + + rs.Fail() +} diff --git a/actor/strategy_exponential_backoff_test.go b/actor/strategy_exponential_backoff_test.go new file mode 100644 index 0000000000000000000000000000000000000000..bb4d68fda5c9b6587cdf55d6d3e38e55c6e3dc9c --- /dev/null +++ b/actor/strategy_exponential_backoff_test.go @@ -0,0 +1,56 @@ +package actor + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func TestExponentialBackoffStrategy_setFailureCount(t *testing.T) { + cases := []struct { + n string + ft time.Duration + fc int + expected int + }{ + {n: "failure outside window; increment count", ft: 11 * time.Second, fc: 10, expected: 1}, + {n: "failure inside window; increment count", ft: 9 * time.Second, fc: 10, expected: 11}, + } + + for _, tc := range cases { + t.Run(tc.n, func(t *testing.T) { + s := &exponentialBackoffStrategy{backoffWindow: 10 * time.Second} + rs := &RestartStatistics{[]time.Time{}} + for i := 0; i < tc.fc; i++ { + rs.failureTimes = append(rs.failureTimes, time.Now().Add(-tc.ft)) + } + + s.setFailureCount(rs) + assert.Equal(t, tc.expected, rs.FailureCount()) + }) + } +} + +func TestExponentialBackoffStrategy_IncrementsFailureCount(t *testing.T) { + rs := NewRestartStatistics() + s := &exponentialBackoffStrategy{backoffWindow: 10 * time.Second} + + s.setFailureCount(rs) + s.setFailureCount(rs) + s.setFailureCount(rs) + + assert.Equal(t, 3, rs.FailureCount()) +} + +func TestExponentialBackoffStrategy_ResetsFailureCount(t *testing.T) { + rs := NewRestartStatistics() + for i := 0; i < 10; i++ { + rs.failureTimes = append(rs.failureTimes, time.Now().Add(-11*time.Second)) + } + s := &exponentialBackoffStrategy{backoffWindow: 10 * time.Second, initialBackoff: 1 * time.Second} + + s.setFailureCount(rs) + + assert.Equal(t, 1, rs.FailureCount()) +} diff --git a/actor/strategy_one_for_one.go b/actor/strategy_one_for_one.go new file mode 100644 index 0000000000000000000000000000000000000000..45d7749102191019dd8de45084eee1d591e4374a --- /dev/null +++ b/actor/strategy_one_for_one.go @@ -0,0 +1,68 @@ +package actor + +import "time" + +// NewOneForOneStrategy returns a new Supervisor strategy which applies the fault Directive from the decider +// to the failing child process. +// +// This strategy is applicable if it is safe to handle a single child in isolation from its peers or dependents +func NewOneForOneStrategy(maxNrOfRetries int, withinDuration time.Duration, decider DeciderFunc) SupervisorStrategy { + return &oneForOneStrategy{ + maxNrOfRetries: maxNrOfRetries, + withinDuration: withinDuration, + decider: decider, + } +} + +type oneForOneStrategy struct { + maxNrOfRetries int + withinDuration time.Duration + decider DeciderFunc +} + +var _ SupervisorStrategy = &oneForOneStrategy{} + +func (strategy *oneForOneStrategy) HandleFailure(actorSystem *ActorSystem, supervisor Supervisor, child *PID, rs *RestartStatistics, reason interface{}, message interface{}) { + directive := strategy.decider(reason) + + switch directive { + case ResumeDirective: + // resume the failing child + logFailure(actorSystem, child, reason, directive) + supervisor.ResumeChildren(child) + case RestartDirective: + // try restart the failing child + if strategy.shouldStop(rs) { + logFailure(actorSystem, child, reason, StopDirective) + supervisor.StopChildren(child) + } else { + logFailure(actorSystem, child, reason, RestartDirective) + supervisor.RestartChildren(child) + } + case StopDirective: + // stop the failing child, no need to involve the crs + logFailure(actorSystem, child, reason, directive) + supervisor.StopChildren(child) + case EscalateDirective: + // send failure to parent + // supervisor mailbox + // do not log here, log in the parent handling the error + supervisor.EscalateFailure(reason, message) + } +} + +func (strategy *oneForOneStrategy) shouldStop(rs *RestartStatistics) bool { + // supervisor says this child may not restart + if strategy.maxNrOfRetries == 0 { + return true + } + + rs.Fail() + + if rs.NumberOfFailures(strategy.withinDuration) > strategy.maxNrOfRetries { + rs.Reset() + return true + } + + return false +} diff --git a/actor/strategy_one_for_one_test.go b/actor/strategy_one_for_one_test.go new file mode 100644 index 0000000000000000000000000000000000000000..4ad0e718c192538f1e030f50af84ebd7060e4150 --- /dev/null +++ b/actor/strategy_one_for_one_test.go @@ -0,0 +1,83 @@ +package actor + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func TestOneForOneStrategy_requestRestartPermission(t *testing.T) { + cases := []struct { + n string + expectedResult bool + expectedCount int + s oneForOneStrategy + rs RestartStatistics + }{ + { + n: "no restart if max retries is 0", + + s: oneForOneStrategy{maxNrOfRetries: 0}, + rs: RestartStatistics{}, + + expectedResult: true, + expectedCount: 0, + }, + { + n: "restart when duration is 0", + + s: oneForOneStrategy{maxNrOfRetries: 1}, + rs: RestartStatistics{}, + + expectedResult: false, + expectedCount: 1, + }, + { + n: "no restart when duration is 0 and exceeds max retries", + + s: oneForOneStrategy{maxNrOfRetries: 1}, + rs: RestartStatistics{failureTimes: []time.Time{time.Now().Add(-1 * time.Second)}}, + + expectedResult: true, + expectedCount: 0, + }, + { + n: "restart when duration set and within window", + + s: oneForOneStrategy{maxNrOfRetries: 2, withinDuration: 10 * time.Second}, + rs: RestartStatistics{failureTimes: []time.Time{time.Now().Add(-5 * time.Second)}}, + + expectedResult: false, + expectedCount: 2, + }, + { + n: "no restart when duration set, within window and exceeds max retries", + + s: oneForOneStrategy{maxNrOfRetries: 1, withinDuration: 10 * time.Second}, + rs: RestartStatistics{failureTimes: []time.Time{time.Now().Add(-5 * time.Second), time.Now().Add(-5 * time.Second)}}, + + expectedResult: true, + expectedCount: 0, + }, + { + n: "restart and FailureCount reset when duration set and outside window", + + s: oneForOneStrategy{maxNrOfRetries: 1, withinDuration: 10 * time.Second}, + rs: RestartStatistics{failureTimes: []time.Time{time.Now().Add(-11 * time.Second), time.Now().Add(-11 * time.Second)}}, + + expectedResult: false, + expectedCount: 1, + }, + } + + for _, tc := range cases { + t.Run(tc.n, func(t *testing.T) { + s := tc.s + rs := tc.rs + actual := s.shouldStop(&rs) + assert.Equal(t, tc.expectedResult, actual) + assert.Equal(t, tc.expectedCount, rs.NumberOfFailures(s.withinDuration)) + }) + } +} diff --git a/actor/strategy_restarting.go b/actor/strategy_restarting.go new file mode 100644 index 0000000000000000000000000000000000000000..f947c3fb6aa946a3fc19d31c8be05db4d7b42e7d --- /dev/null +++ b/actor/strategy_restarting.go @@ -0,0 +1,15 @@ +package actor + +func NewRestartingStrategy() SupervisorStrategy { + return &restartingStrategy{} +} + +type restartingStrategy struct{} + +var _ SupervisorStrategy = &restartingStrategy{} + +func (strategy *restartingStrategy) HandleFailure(actorSystem *ActorSystem, supervisor Supervisor, child *PID, _ *RestartStatistics, reason interface{}, _ interface{}) { + // always restart + logFailure(actorSystem, child, reason, RestartDirective) + supervisor.RestartChildren(child) +} diff --git a/actor/supervision.go b/actor/supervision.go new file mode 100644 index 0000000000000000000000000000000000000000..a858c4a6e40a29f24ea085095ec61b5d6714bde3 --- /dev/null +++ b/actor/supervision.go @@ -0,0 +1,48 @@ +package actor + +import ( + "time" +) + +// DeciderFunc is a function which is called by a SupervisorStrategy +type DeciderFunc func(reason interface{}) Directive + +// SupervisorStrategy is an interface that decides how to handle failing child actors +type SupervisorStrategy interface { + HandleFailure(actorSystem *ActorSystem, supervisor Supervisor, child *PID, rs *RestartStatistics, reason interface{}, message interface{}) +} + +// Supervisor is an interface that is used by the SupervisorStrategy to manage child actor lifecycle +type Supervisor interface { + Children() []*PID + EscalateFailure(reason interface{}, message interface{}) + RestartChildren(pids ...*PID) + StopChildren(pids ...*PID) + ResumeChildren(pids ...*PID) +} + +func logFailure(actorSystem *ActorSystem, child *PID, reason interface{}, directive Directive) { + actorSystem.EventStream.Publish(&SupervisorEvent{ + Child: child, + Reason: reason, + Directive: directive, + }) +} + +// DefaultDecider is a decider that will always restart the failing child actor +func DefaultDecider(_ interface{}) Directive { + return RestartDirective +} + +var ( + defaultSupervisionStrategy = NewOneForOneStrategy(10, 10*time.Second, DefaultDecider) + restartingSupervisionStrategy = NewRestartingStrategy() +) + +func DefaultSupervisorStrategy() SupervisorStrategy { + return defaultSupervisionStrategy +} + +func RestartingSupervisorStrategy() SupervisorStrategy { + return restartingSupervisionStrategy +} diff --git a/actor/supervision_event.go b/actor/supervision_event.go new file mode 100644 index 0000000000000000000000000000000000000000..944b1fab35f77e9c9f929c8da21d569562eac66e --- /dev/null +++ b/actor/supervision_event.go @@ -0,0 +1,20 @@ +package actor + +import ( + "gitee.com/simplexyz/simpleactor-go/log" +) + +// SupervisorEvent is sent on the EventStream when a supervisor have applied a directive to a failing child actor +type SupervisorEvent struct { + Child *PID + Reason interface{} + Directive Directive +} + +func SubscribeSupervision(actorSystem *ActorSystem) { + _ = actorSystem.EventStream.Subscribe(func(evt interface{}) { + if supervisorEvent, ok := evt.(*SupervisorEvent); ok { + plog.Debug("[SUPERVISION]", log.Stringer("actor", supervisorEvent.Child), log.Stringer("directive", supervisorEvent.Directive), log.Object("reason", supervisorEvent.Reason)) + } + }) +} diff --git a/actor/supervision_event_test.go b/actor/supervision_event_test.go new file mode 100644 index 0000000000000000000000000000000000000000..a3108cac2cfb10d099708db73499ce0b1b5d2fee --- /dev/null +++ b/actor/supervision_event_test.go @@ -0,0 +1,60 @@ +package actor + +import ( + "sync" + "testing" + "time" +) + +type panicActor struct{} + +func (a *panicActor) Receive(ctx Context) { + switch ctx.Message().(type) { + case string: + panic("Boom!") + } +} + +func TestSupervisorEventHandleFromEventstream(t *testing.T) { + supervisors := []struct { + name string + strategy SupervisorStrategy + }{ + { + name: "all_for_one", + strategy: NewAllForOneStrategy(10, 10*time.Second, DefaultDecider), + }, + { + name: "exponential_backoff", + strategy: NewExponentialBackoffStrategy(10*time.Millisecond, 10*time.Millisecond), + }, + { + name: "one_for_one", + strategy: NewOneForOneStrategy(10, 10*time.Second, DefaultDecider), + }, + { + name: "restarting", + strategy: NewRestartingStrategy(), + }, + } + + for _, v := range supervisors { + t.Run(v.name, func(t *testing.T) { + wg := sync.WaitGroup{} + sid := system.EventStream.Subscribe(func(evt interface{}) { + if _, ok := evt.(*SupervisorEvent); ok { + wg.Done() + } + }) + defer system.EventStream.Unsubscribe(sid) + + props := PropsFromProducer(func() Actor { return &panicActor{} }, WithSupervisor(v.strategy)) + pid := rootContext.Spawn(props) + + wg.Add(1) + rootContext.Send(pid, "Fail!") + + wg.Wait() + }) + } +} diff --git a/actor/supervision_test.go b/actor/supervision_test.go new file mode 100644 index 0000000000000000000000000000000000000000..6ead7b0dee81dec4939442ad130dc551d8bf23f5 --- /dev/null +++ b/actor/supervision_test.go @@ -0,0 +1,102 @@ +package actor + +import ( + "reflect" + "sync" + "testing" + "time" +) + +type actorWithSupervisor struct { + wg *sync.WaitGroup +} + +func (a *actorWithSupervisor) Receive(ctx Context) { + switch ctx.Message().(type) { + case *Started: + child := ctx.Spawn(PropsFromProducer(func() Actor { return &failingChildActor{} })) + ctx.Send(child, "Fail!") + } +} + +func (a *actorWithSupervisor) HandleFailure(*ActorSystem, Supervisor, *PID, *RestartStatistics, interface{}, interface{}) { + a.wg.Done() +} + +type failingChildActor struct{} + +func (a *failingChildActor) Receive(ctx Context) { + switch ctx.Message().(type) { + case string: + panic("Oh noes!") + } +} + +func TestActorWithOwnSupervisorCanHandleFailure(t *testing.T) { + wg := &sync.WaitGroup{} + wg.Add(1) + props := PropsFromProducer(func() Actor { return &actorWithSupervisor{wg: wg} }) + rootContext.Spawn(props) + wg.Wait() +} + +func NewObserver() (func(ReceiverFunc) ReceiverFunc, *Expector) { + c := make(chan interface{}) + e := &Expector{C: c} + f := func(next ReceiverFunc) ReceiverFunc { + fn := func(context ReceiverContext, env *MessageEnvelope) { + message := env.Message + c <- message + next(context, env) + } + + return fn + } + return f, e +} + +type Expector struct { + C <-chan interface{} +} + +func (e *Expector) ExpectMsg(expected interface{}, t *testing.T) { + actual := <-e.C + if actual == expected { + } else { + + at := reflect.TypeOf(actual) + et := reflect.TypeOf(expected) + t.Errorf("Expected %v:%v, got %v:%v", et, expected, at, actual) + } +} + +func (e *Expector) ExpectNoMsg(t *testing.T) { + select { + case actual := <-e.C: + at := reflect.TypeOf(actual) + t.Errorf("Expected no message got %v:%v", at, actual) + case <-time.After(time.Second * 1): + // pass + } +} + +func TestActorStopsAfterXRestarts(t *testing.T) { + m, e := NewObserver() + props := PropsFromProducer(func() Actor { return &failingChildActor{} }, WithReceiverMiddleware(m)) + child := rootContext.Spawn(props) + fail := "fail!" + + e.ExpectMsg(startedMessage, t) + + // root supervisor allows 10 restarts + for i := 0; i < 10; i++ { + rootContext.Send(child, fail) + e.ExpectMsg(fail, t) + e.ExpectMsg(restartingMessage, t) + e.ExpectMsg(startedMessage, t) + } + rootContext.Send(child, fail) + e.ExpectMsg(fail, t) + // the 11th time should cause a termination + e.ExpectMsg(stoppingMessage, t) +} diff --git a/actor/throttler.go b/actor/throttler.go new file mode 100644 index 0000000000000000000000000000000000000000..0d0e9cf0a31a0222621a42e27b209e86376e1912 --- /dev/null +++ b/actor/throttler.go @@ -0,0 +1,55 @@ +package actor + +import ( + "sync/atomic" + "time" +) + +type ShouldThrottle func() Valve + +type Valve int32 + +const ( + Open Valve = iota + Closing + Closed +) + +// NewThrottle +// This has no guarantees that the throttle opens exactly after the period, since it is reset asynchronously +// Throughput has been prioritized over exact re-opening +// throttledCallBack, This will be called with the number of events what was throttled after the period +func NewThrottle(maxEventsInPeriod int32, period time.Duration, throttledCallBack func(int32)) ShouldThrottle { + currentEvents := int32(0) + + startTimer := func(duration time.Duration, back func(int32)) { + go func() { + // crete ticker to mimic sleep, we do not want to put the goroutine to sleep + // as it will schedule it out of the P making a syscall, we just want it to + // halt for the given period of time + ticker := time.NewTicker(duration) + defer ticker.Stop() + <-ticker.C // wait for the ticker to tick once + + timesCalled := atomic.SwapInt32(¤tEvents, 0) + if timesCalled > maxEventsInPeriod { + throttledCallBack(timesCalled - maxEventsInPeriod) + } + }() + } + + return func() Valve { + tries := atomic.AddInt32(¤tEvents, 1) + if tries == 1 { + startTimer(period, throttledCallBack) + } + + if tries == maxEventsInPeriod { + return Closing + } else if tries > maxEventsInPeriod { + return Closed + } else { + return Open + } + } +} diff --git a/actor/throttler_test.go b/actor/throttler_test.go new file mode 100644 index 0000000000000000000000000000000000000000..161613825af9d62ac3baef56b15a9d6b17c6a40a --- /dev/null +++ b/actor/throttler_test.go @@ -0,0 +1,43 @@ +package actor + +import ( + "sync" + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func TestThrottler(t *testing.T) { + wg := &sync.WaitGroup{} + wg.Add(1) + + // create a throttler that triggers after 10 invocations within 1 second + throttler := NewThrottle(10, 1*time.Second, func(i int32) { + wg.Done() + }) + + throttler() + v := throttler() + + assert.Equal(t, Open, v) + + for i := 0; i < 8; i++ { + v = throttler() + } + + // should be closing now when we have invoked 10 times + assert.Equal(t, Closing, v) + + // invoke once more + v = throttler() + // should bee closed, 11 invokes + assert.Equal(t, Closed, v) + + // wait for callback to be invoked + wg.Wait() + + // valve should be open now that time has elapsed + v = throttler() + assert.Equal(t, Open, v) +} diff --git a/actor/unbounded.go b/actor/unbounded.go new file mode 100644 index 0000000000000000000000000000000000000000..f91226442404b73321dec28566b774f4934c25e4 --- /dev/null +++ b/actor/unbounded.go @@ -0,0 +1,38 @@ +package actor + +import ( + "gitee.com/simplexyz/simpleactor-go/internal/queue/goring" + "gitee.com/simplexyz/simpleactor-go/internal/queue/mpsc" +) + +type unboundedMailboxQueue struct { + userMailbox *goring.Queue +} + +func (q *unboundedMailboxQueue) Push(m interface{}) { + q.userMailbox.Push(m) +} + +func (q *unboundedMailboxQueue) Pop() interface{} { + m, o := q.userMailbox.Pop() + if o { + return m + } + + return nil +} + +// Unbounded returns a producer which creates an unbounded mailbox +func Unbounded(mailboxStats ...MailboxMiddleware) MailboxProducer { + return func() Mailbox { + q := &unboundedMailboxQueue{ + userMailbox: goring.New(10), + } + + return &defaultMailbox{ + systemMailbox: mpsc.New(), + userMailbox: q, + middlewares: mailboxStats, + } + } +} diff --git a/actor/unbounded_lock_free.go b/actor/unbounded_lock_free.go new file mode 100644 index 0000000000000000000000000000000000000000..4b50b38d1bd71a59e43b0bfbf3a4fa0a5c8e38ad --- /dev/null +++ b/actor/unbounded_lock_free.go @@ -0,0 +1,17 @@ +package actor + +import ( + "gitee.com/simplexyz/simpleactor-go/internal/queue/mpsc" +) + +// UnboundedLockfree returns a producer which creates an unbounded, lock-free mailbox. +// This mailbox is cheaper to allocate, but has a slower throughput than the plain Unbounded mailbox. +func UnboundedLockfree(mailboxStats ...MailboxMiddleware) MailboxProducer { + return func() Mailbox { + return &defaultMailbox{ + userMailbox: mpsc.New(), + systemMailbox: mpsc.New(), + middlewares: mailboxStats, + } + } +} diff --git a/actor/unbounded_priority.go b/actor/unbounded_priority.go new file mode 100644 index 0000000000000000000000000000000000000000..886c43aad95d373e7470ad2450c02b887ba4ff71 --- /dev/null +++ b/actor/unbounded_priority.go @@ -0,0 +1,41 @@ +package actor + +import ( + "gitee.com/simplexyz/simpleactor-go/internal/queue/goring" + "gitee.com/simplexyz/simpleactor-go/internal/queue/mpsc" +) + +func NewPriorityGoringQueue() *priorityQueue { + return NewPriorityQueue(func() queue { + return &unboundedMailboxQueue{ + userMailbox: goring.New(10), + } + }) +} + +//goland:noinspection ALL +func UnboundedPriority(mailboxStats ...MailboxMiddleware) MailboxProducer { + return func() Mailbox { + return &defaultMailbox{ + systemMailbox: mpsc.New(), + userMailbox: NewPriorityGoringQueue(), + middlewares: mailboxStats, + } + } +} + +func NewPriorityMpscQueue() *priorityQueue { + return NewPriorityQueue(func() queue { + return mpsc.New() + }) +} + +func UnboundedPriorityMpsc(mailboxStats ...MailboxMiddleware) MailboxProducer { + return func() Mailbox { + return &defaultMailbox{ + systemMailbox: mpsc.New(), + userMailbox: NewPriorityMpscQueue(), + middlewares: mailboxStats, + } + } +} diff --git a/cluster/README.MD b/cluster/README.MD new file mode 100644 index 0000000000000000000000000000000000000000..543f658eb34e66fcc5b18c6a3e6e6567e9f41cd4 --- /dev/null +++ b/cluster/README.MD @@ -0,0 +1,134 @@ +# Proto.Actor Cluster - Virtual Actors (Alpha) + +## Massively distributed actors for GO + +Proto.Actor supports the classic actor model also found in Erlang and Akka.
+Our cluster support however uses a different approach, **Virtual Actor Model**. + +This is a model where each actor appears to *always exist*. +There is no lifecycle as in the classic actor model. +You get a reference to the actor by asking for it's ID. + +e.g. + +```go +hello := shared.GetHelloGrain("abc") +res := hello.SayHello(&shared.HelloRequest{Name: "Proto.Actor"}) +``` + +This will ask the cluster where the 'abc' actor is located. +If it does not yet exist, it will be created for you. + +See Microsoft Orleans for more info about the Virtual Actor Model: +[http://dotnet.github.io/orleans/](http://dotnet.github.io/orleans/) + +## How to + +## Protobuf IDL Definition + +Start by defining your messages and grain contracts. +You do this by using Protobuf IDL files. + +Here is the definition from the `/examples/cluster/shared` example + +```proto +syntax = "proto3"; +package shared; + +message HelloRequest { + string name = 1; +} + +message HelloResponse { + string message = 1; +} + +message AddRequest { + double a = 1; + double b = 2; +} + +message AddResponse { + double result = 1; +} + +service Hello { + rpc SayHello (HelloRequest) returns (HelloResponse) {} + rpc Add(AddRequest) returns (AddResponse) {} +} +``` + +Once you have this, you can generate your code using the protobuf `protoc` compiler. + +**Windows** + +```batch +#generate messages +protoc -I=. -I=%GOPATH%\src --gogoslick_out=. protos.proto +#generate grains +protoc -I=. -I=%GOPATH%\src --gorleans_out=. protos.proto +``` + +## Implementing + +Once the messages and contracts have been generated, you can start implementing your own business logic. +This is essentially a type which is powered by a Proto.Actor actor behind the scenes. + +```go +package shared + +// a Go struct implementing the Hello interface +type hello struct { +} + +func (*hello) SayHello(r *HelloRequest) *HelloResponse { + return &HelloResponse{Message: "hello " + r.Name} +} + +func (*hello) Add(r *AddRequest) *AddResponse { + return &AddResponse{Result: r.A + r.B} +} + +// Register what implementation Proto.Actor should use when +// creating actors for a certain grain type. +func init() { + // apply DI and setup logic + HelloFactory(func() Hello { return &hello{} }) +} +``` + +## Seed nodes + +```go +func main() { + cluster.Start("127.0.0.1:7711") + console.ReadLine() +} +``` + +## Member nodes + +```go +func main() { + cluster.Start("127.0.0.1:0", "127.0.0.1:7711") + + // get a reference to the virtual actor called "abc" of type Hello + hello := shared.GetHelloGrain("abc") + res := hello.SayHello(&shared.HelloRequest{Name: "Proto.Actor"}) + log.Printf("Message from grain %v", res.Message) +} +``` + +## FAQ + +### Can I use Proto.Actor Cluster in production? + +The Proto.Actor Cluster support is in alpha version, thus not production ready. + +### What about performance? + +Proto.Actor Remoting is able to pass 1 million+ messages per second on a standard dev machine. +This is the same infrastructure used in Proto.Actor cluster. +Proto.Actor Cluster however uses an RPC API, meaning it is Request/Response in nature. +If you wait for a response for each call, the throughput will ofcourse be a lot less. +Async Fire and forget for performance, Request/Response for simplicity. diff --git a/cluster/build.sh b/cluster/build.sh new file mode 100644 index 0000000000000000000000000000000000000000..3281957d9cbe9a83f8d18c2a0634b462a693131b --- /dev/null +++ b/cluster/build.sh @@ -0,0 +1,6 @@ +protoc -I=../actor --go_out=. --go_opt=paths=source_relative --proto_path=. cluster.proto +protoc -I=../actor --go_out=. --go_opt=paths=source_relative --proto_path=. gossip.proto +protoc -I=../actor --go_out=. --go_opt=paths=source_relative --proto_path=. grain.proto +protoc -I=../actor --go_out=. --go_opt=paths=source_relative --proto_path=. pubsub.proto +protoc -I=../actor --go_out=. --go_opt=paths=source_relative --proto_path=. pubsub_test.proto + diff --git a/cluster/cluster.go b/cluster/cluster.go new file mode 100644 index 0000000000000000000000000000000000000000..88c9e880dcc5c9b6f37812c80b05ea30d9e5f201 --- /dev/null +++ b/cluster/cluster.go @@ -0,0 +1,262 @@ +package cluster + +import ( + "time" + + "google.golang.org/protobuf/types/known/emptypb" + + "github.com/asynkron/gofun/set" + + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/extensions" + "gitee.com/simplexyz/simpleactor-go/log" + "gitee.com/simplexyz/simpleactor-go/remote" +) + +var extensionID = extensions.NextExtensionID() + +type Cluster struct { + ActorSystem *actor.ActorSystem + Config *Config + Gossip *Gossiper + PubSub *PubSub + Remote *remote.Remote + PidCache *PidCacheValue + MemberList *MemberList + IdentityLookup IdentityLookup + kinds map[string]*ActivatedKind + context Context +} + +var _ extensions.Extension = &Cluster{} + +func New(actorSystem *actor.ActorSystem, config *Config) *Cluster { + c := &Cluster{ + ActorSystem: actorSystem, + Config: config, + kinds: map[string]*ActivatedKind{}, + } + actorSystem.Extensions.Register(c) + + c.context = config.ClusterContextProducer(c) + c.PidCache = NewPidCache() + c.MemberList = NewMemberList(c) + c.subscribeToTopologyEvents() + + actorSystem.Extensions.Register(c) + + var err error + c.Gossip, err = newGossiper(c) + c.PubSub = NewPubSub(c) + + if err != nil { + panic(err) + } + + return c +} + +func (c *Cluster) subscribeToTopologyEvents() { + c.ActorSystem.EventStream.Subscribe(func(evt interface{}) { + if clusterTopology, ok := evt.(*ClusterTopology); ok { + for _, member := range clusterTopology.Left { + c.PidCache.RemoveByMember(member) + } + } + }) +} + +func (c *Cluster) ExtensionID() extensions.ExtensionID { + return extensionID +} + +//goland:noinspection GoUnusedExportedFunction +func GetCluster(actorSystem *actor.ActorSystem) *Cluster { + c := actorSystem.Extensions.Get(extensionID) + + return c.(*Cluster) +} + +func (c *Cluster) GetBlockedMembers() set.Set[string] { + return c.Remote.BlockList().BlockedMembers() +} + +func (c *Cluster) StartMember() { + cfg := c.Config + c.Remote = remote.NewRemote(c.ActorSystem, c.Config.RemoteConfig) + + c.initKinds() + + // TODO: make it possible to become a cluster even if remoting is already started + c.Remote.Start() + + address := c.ActorSystem.Address() + plog.Info("Starting Proto.Actor cluster member", log.String("id", c.ActorSystem.ID), log.String("address", address)) + + c.IdentityLookup = cfg.IdentityLookup + c.IdentityLookup.Setup(c, c.GetClusterKinds(), false) + + // TODO: Disable Gossip for now until API changes are done + // gossiper must be started whenever any topology events starts flowing + if err := c.Gossip.StartGossiping(); err != nil { + panic(err) + } + c.PubSub.Start() + c.MemberList.InitializeTopologyConsensus() + + if err := cfg.ClusterProvider.StartMember(c); err != nil { + panic(err) + } + + time.Sleep(1 * time.Second) +} + +func (c *Cluster) GetClusterKinds() []string { + keys := make([]string, 0, len(c.kinds)) + for k := range c.kinds { + keys = append(keys, k) + } + + return keys +} + +func (c *Cluster) StartClient() { + cfg := c.Config + c.Remote = remote.NewRemote(c.ActorSystem, c.Config.RemoteConfig) + + c.Remote.Start() + + address := c.ActorSystem.Address() + plog.Info("Starting Proto.Actor cluster-client", log.String("address", address)) + + c.IdentityLookup = cfg.IdentityLookup + c.IdentityLookup.Setup(c, c.GetClusterKinds(), true) + + if err := cfg.ClusterProvider.StartClient(c); err != nil { + panic(err) + } + c.PubSub.Start() +} + +func (c *Cluster) Shutdown(graceful bool) { + c.Gossip.SetState(GracefullyLeftKey, &emptypb.Empty{}) + c.ActorSystem.Shutdown() + if graceful { + _ = c.Config.ClusterProvider.Shutdown(graceful) + c.IdentityLookup.Shutdown() + // This is to wait ownership transferring complete. + time.Sleep(time.Millisecond * 2000) + c.MemberList.stopMemberList() + c.IdentityLookup.Shutdown() + c.Gossip.Shutdown() + } + + c.Remote.Shutdown(graceful) + + address := c.ActorSystem.Address() + plog.Info("Stopped Proto.Actor cluster", log.String("address", address)) +} + +func (c *Cluster) Get(identity string, kind string) *actor.PID { + return c.IdentityLookup.Get(NewClusterIdentity(identity, kind)) +} + +func (c *Cluster) Request(identity string, kind string, message interface{}) (interface{}, error) { + return c.context.Request(identity, kind, message) +} + +func (c *Cluster) GetClusterKind(kind string) *ActivatedKind { + k, ok := c.kinds[kind] + if !ok { + plog.Error("Invalid kind", log.String("kind", kind)) + + return nil + } + + return k +} + +func (c *Cluster) TryGetClusterKind(kind string) (*ActivatedKind, bool) { + k, ok := c.kinds[kind] + + return k, ok +} + +func (c *Cluster) initKinds() { + for name, kind := range c.Config.Kinds { + c.kinds[name] = kind.Build(c) + } + c.ensureTopicKindRegistered() +} + +// ensureTopicKindRegistered ensures that the topic kind is registered in the cluster +// if topic kind is not registered, it will be registered automatically +func (c *Cluster) ensureTopicKindRegistered() { + hasTopicKind := false + for name := range c.kinds { + if name == TopicActorKind { + hasTopicKind = true + break + } + } + if !hasTopicKind { + store := &EmptyKeyValueStore[*Subscribers]{} + + c.kinds[TopicActorKind] = NewKind(TopicActorKind, actor.PropsFromProducer(func() actor.Actor { + return NewTopicActor(store) + })).Build(c) + } +} + +// Call is a wrap of context.RequestFuture with retries. +func (c *Cluster) Call(name string, kind string, msg interface{}, opts ...GrainCallOption) (interface{}, error) { + callConfig := DefaultGrainCallConfig(c) + for _, o := range opts { + o(callConfig) + } + + _context := callConfig.Context + if _context == nil { + _context = c.ActorSystem.Root + } + + var lastError error + + for i := 0; i < callConfig.RetryCount; i++ { + pid := c.Get(name, kind) + + if pid == nil { + return nil, remote.ErrUnknownError + } + + timeout := callConfig.Timeout + _resp, err := _context.RequestFuture(pid, msg, timeout).Result() + if err != nil { + plog.Error("cluster.RequestFuture failed", log.Error(err), log.PID("pid", pid)) + lastError = err + + switch err { + case actor.ErrTimeout, remote.ErrTimeout: + callConfig.RetryAction(i) + + id := ClusterIdentity{Kind: kind, Identity: name} + c.PidCache.Remove(id.Identity, id.Kind) + + continue + case actor.ErrDeadLetter, remote.ErrDeadLetter: + callConfig.RetryAction(i) + + id := ClusterIdentity{Kind: kind, Identity: name} + c.PidCache.Remove(id.Identity, id.Kind) + + continue + default: + return nil, err + } + } + + return _resp, nil + } + + return nil, lastError +} diff --git a/cluster/cluster.pb.go b/cluster/cluster.pb.go new file mode 100644 index 0000000000000000000000000000000000000000..63117b92eb9845bf742335401aef51112bff8554 --- /dev/null +++ b/cluster/cluster.pb.go @@ -0,0 +1,1950 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.28.1 +// protoc v3.21.9 +// source: cluster.proto + +package cluster + +import ( + actor "gitee.com/simplexyz/simpleactor-go/actor" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type IdentityHandoverAck_State int32 + +const ( + IdentityHandoverAck_processed IdentityHandoverAck_State = 0 + IdentityHandoverAck_incorrect_topology IdentityHandoverAck_State = 1 +) + +// Enum value maps for IdentityHandoverAck_State. +var ( + IdentityHandoverAck_State_name = map[int32]string{ + 0: "processed", + 1: "incorrect_topology", + } + IdentityHandoverAck_State_value = map[string]int32{ + "processed": 0, + "incorrect_topology": 1, + } +) + +func (x IdentityHandoverAck_State) Enum() *IdentityHandoverAck_State { + p := new(IdentityHandoverAck_State) + *p = x + return p +} + +func (x IdentityHandoverAck_State) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (IdentityHandoverAck_State) Descriptor() protoreflect.EnumDescriptor { + return file_cluster_proto_enumTypes[0].Descriptor() +} + +func (IdentityHandoverAck_State) Type() protoreflect.EnumType { + return &file_cluster_proto_enumTypes[0] +} + +func (x IdentityHandoverAck_State) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use IdentityHandoverAck_State.Descriptor instead. +func (IdentityHandoverAck_State) EnumDescriptor() ([]byte, []int) { + return file_cluster_proto_rawDescGZIP(), []int{4, 0} +} + +// request response call from Identity actor sent to each member +// asking what activations they hold that belong to the requester +type IdentityHandoverRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + CurrentTopology *IdentityHandoverRequest_Topology `protobuf:"bytes,1,opt,name=current_topology,json=currentTopology,proto3" json:"current_topology,omitempty"` + Address string `protobuf:"bytes,2,opt,name=address,proto3" json:"address,omitempty"` + // If the requester passes a delta topology, only return activations which would not be assigned to the member + // in the previous topology. + DeltaTopology *IdentityHandoverRequest_Topology `protobuf:"bytes,3,opt,name=delta_topology,json=deltaTopology,proto3" json:"delta_topology,omitempty"` +} + +func (x *IdentityHandoverRequest) Reset() { + *x = IdentityHandoverRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_cluster_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *IdentityHandoverRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*IdentityHandoverRequest) ProtoMessage() {} + +func (x *IdentityHandoverRequest) ProtoReflect() protoreflect.Message { + mi := &file_cluster_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use IdentityHandoverRequest.ProtoReflect.Descriptor instead. +func (*IdentityHandoverRequest) Descriptor() ([]byte, []int) { + return file_cluster_proto_rawDescGZIP(), []int{0} +} + +func (x *IdentityHandoverRequest) GetCurrentTopology() *IdentityHandoverRequest_Topology { + if x != nil { + return x.CurrentTopology + } + return nil +} + +func (x *IdentityHandoverRequest) GetAddress() string { + if x != nil { + return x.Address + } + return "" +} + +func (x *IdentityHandoverRequest) GetDeltaTopology() *IdentityHandoverRequest_Topology { + if x != nil { + return x.DeltaTopology + } + return nil +} + +type IdentityHandover struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Actors []*Activation `protobuf:"bytes,1,rep,name=actors,proto3" json:"actors,omitempty"` + ChunkId int32 `protobuf:"varint,2,opt,name=chunk_id,json=chunkId,proto3" json:"chunk_id,omitempty"` + Final bool `protobuf:"varint,3,opt,name=final,proto3" json:"final,omitempty"` + TopologyHash uint64 `protobuf:"varint,4,opt,name=topology_hash,json=topologyHash,proto3" json:"topology_hash,omitempty"` + Skipped int32 `protobuf:"varint,5,opt,name=skipped,proto3" json:"skipped,omitempty"` // Total number of activations skipped + Sent int32 `protobuf:"varint,6,opt,name=sent,proto3" json:"sent,omitempty"` // Total number of activations sent +} + +func (x *IdentityHandover) Reset() { + *x = IdentityHandover{} + if protoimpl.UnsafeEnabled { + mi := &file_cluster_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *IdentityHandover) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*IdentityHandover) ProtoMessage() {} + +func (x *IdentityHandover) ProtoReflect() protoreflect.Message { + mi := &file_cluster_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use IdentityHandover.ProtoReflect.Descriptor instead. +func (*IdentityHandover) Descriptor() ([]byte, []int) { + return file_cluster_proto_rawDescGZIP(), []int{1} +} + +func (x *IdentityHandover) GetActors() []*Activation { + if x != nil { + return x.Actors + } + return nil +} + +func (x *IdentityHandover) GetChunkId() int32 { + if x != nil { + return x.ChunkId + } + return 0 +} + +func (x *IdentityHandover) GetFinal() bool { + if x != nil { + return x.Final + } + return false +} + +func (x *IdentityHandover) GetTopologyHash() uint64 { + if x != nil { + return x.TopologyHash + } + return 0 +} + +func (x *IdentityHandover) GetSkipped() int32 { + if x != nil { + return x.Skipped + } + return 0 +} + +func (x *IdentityHandover) GetSent() int32 { + if x != nil { + return x.Sent + } + return 0 +} + +type RemoteIdentityHandover struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Actors *PackedActivations `protobuf:"bytes,1,opt,name=actors,proto3" json:"actors,omitempty"` + ChunkId int32 `protobuf:"varint,2,opt,name=chunk_id,json=chunkId,proto3" json:"chunk_id,omitempty"` + Final bool `protobuf:"varint,3,opt,name=final,proto3" json:"final,omitempty"` + TopologyHash uint64 `protobuf:"varint,4,opt,name=topology_hash,json=topologyHash,proto3" json:"topology_hash,omitempty"` + Skipped int32 `protobuf:"varint,5,opt,name=skipped,proto3" json:"skipped,omitempty"` + Sent int32 `protobuf:"varint,6,opt,name=sent,proto3" json:"sent,omitempty"` +} + +func (x *RemoteIdentityHandover) Reset() { + *x = RemoteIdentityHandover{} + if protoimpl.UnsafeEnabled { + mi := &file_cluster_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RemoteIdentityHandover) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RemoteIdentityHandover) ProtoMessage() {} + +func (x *RemoteIdentityHandover) ProtoReflect() protoreflect.Message { + mi := &file_cluster_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RemoteIdentityHandover.ProtoReflect.Descriptor instead. +func (*RemoteIdentityHandover) Descriptor() ([]byte, []int) { + return file_cluster_proto_rawDescGZIP(), []int{2} +} + +func (x *RemoteIdentityHandover) GetActors() *PackedActivations { + if x != nil { + return x.Actors + } + return nil +} + +func (x *RemoteIdentityHandover) GetChunkId() int32 { + if x != nil { + return x.ChunkId + } + return 0 +} + +func (x *RemoteIdentityHandover) GetFinal() bool { + if x != nil { + return x.Final + } + return false +} + +func (x *RemoteIdentityHandover) GetTopologyHash() uint64 { + if x != nil { + return x.TopologyHash + } + return 0 +} + +func (x *RemoteIdentityHandover) GetSkipped() int32 { + if x != nil { + return x.Skipped + } + return 0 +} + +func (x *RemoteIdentityHandover) GetSent() int32 { + if x != nil { + return x.Sent + } + return 0 +} + +type PackedActivations struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` + Actors []*PackedActivations_Kind `protobuf:"bytes,2,rep,name=actors,proto3" json:"actors,omitempty"` +} + +func (x *PackedActivations) Reset() { + *x = PackedActivations{} + if protoimpl.UnsafeEnabled { + mi := &file_cluster_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PackedActivations) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PackedActivations) ProtoMessage() {} + +func (x *PackedActivations) ProtoReflect() protoreflect.Message { + mi := &file_cluster_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PackedActivations.ProtoReflect.Descriptor instead. +func (*PackedActivations) Descriptor() ([]byte, []int) { + return file_cluster_proto_rawDescGZIP(), []int{3} +} + +func (x *PackedActivations) GetAddress() string { + if x != nil { + return x.Address + } + return "" +} + +func (x *PackedActivations) GetActors() []*PackedActivations_Kind { + if x != nil { + return x.Actors + } + return nil +} + +type IdentityHandoverAck struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ChunkId int32 `protobuf:"varint,1,opt,name=chunk_id,json=chunkId,proto3" json:"chunk_id,omitempty"` + TopologyHash uint64 `protobuf:"varint,2,opt,name=topology_hash,json=topologyHash,proto3" json:"topology_hash,omitempty"` + ProcessingState IdentityHandoverAck_State `protobuf:"varint,3,opt,name=processing_state,json=processingState,proto3,enum=cluster.IdentityHandoverAck_State" json:"processing_state,omitempty"` +} + +func (x *IdentityHandoverAck) Reset() { + *x = IdentityHandoverAck{} + if protoimpl.UnsafeEnabled { + mi := &file_cluster_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *IdentityHandoverAck) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*IdentityHandoverAck) ProtoMessage() {} + +func (x *IdentityHandoverAck) ProtoReflect() protoreflect.Message { + mi := &file_cluster_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use IdentityHandoverAck.ProtoReflect.Descriptor instead. +func (*IdentityHandoverAck) Descriptor() ([]byte, []int) { + return file_cluster_proto_rawDescGZIP(), []int{4} +} + +func (x *IdentityHandoverAck) GetChunkId() int32 { + if x != nil { + return x.ChunkId + } + return 0 +} + +func (x *IdentityHandoverAck) GetTopologyHash() uint64 { + if x != nil { + return x.TopologyHash + } + return 0 +} + +func (x *IdentityHandoverAck) GetProcessingState() IdentityHandoverAck_State { + if x != nil { + return x.ProcessingState + } + return IdentityHandoverAck_processed +} + +type ClusterIdentity struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Identity string `protobuf:"bytes,1,opt,name=identity,proto3" json:"identity,omitempty"` + Kind string `protobuf:"bytes,2,opt,name=kind,proto3" json:"kind,omitempty"` +} + +func (x *ClusterIdentity) Reset() { + *x = ClusterIdentity{} + if protoimpl.UnsafeEnabled { + mi := &file_cluster_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ClusterIdentity) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ClusterIdentity) ProtoMessage() {} + +func (x *ClusterIdentity) ProtoReflect() protoreflect.Message { + mi := &file_cluster_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ClusterIdentity.ProtoReflect.Descriptor instead. +func (*ClusterIdentity) Descriptor() ([]byte, []int) { + return file_cluster_proto_rawDescGZIP(), []int{5} +} + +func (x *ClusterIdentity) GetIdentity() string { + if x != nil { + return x.Identity + } + return "" +} + +func (x *ClusterIdentity) GetKind() string { + if x != nil { + return x.Kind + } + return "" +} + +type Activation struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Pid *actor.PID `protobuf:"bytes,1,opt,name=pid,proto3" json:"pid,omitempty"` + ClusterIdentity *ClusterIdentity `protobuf:"bytes,2,opt,name=cluster_identity,json=clusterIdentity,proto3" json:"cluster_identity,omitempty"` +} + +func (x *Activation) Reset() { + *x = Activation{} + if protoimpl.UnsafeEnabled { + mi := &file_cluster_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Activation) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Activation) ProtoMessage() {} + +func (x *Activation) ProtoReflect() protoreflect.Message { + mi := &file_cluster_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Activation.ProtoReflect.Descriptor instead. +func (*Activation) Descriptor() ([]byte, []int) { + return file_cluster_proto_rawDescGZIP(), []int{6} +} + +func (x *Activation) GetPid() *actor.PID { + if x != nil { + return x.Pid + } + return nil +} + +func (x *Activation) GetClusterIdentity() *ClusterIdentity { + if x != nil { + return x.ClusterIdentity + } + return nil +} + +// Started terminating, not yet removed from IIdentityLookup +type ActivationTerminating struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Pid *actor.PID `protobuf:"bytes,1,opt,name=pid,proto3" json:"pid,omitempty"` + ClusterIdentity *ClusterIdentity `protobuf:"bytes,2,opt,name=cluster_identity,json=clusterIdentity,proto3" json:"cluster_identity,omitempty"` +} + +func (x *ActivationTerminating) Reset() { + *x = ActivationTerminating{} + if protoimpl.UnsafeEnabled { + mi := &file_cluster_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ActivationTerminating) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ActivationTerminating) ProtoMessage() {} + +func (x *ActivationTerminating) ProtoReflect() protoreflect.Message { + mi := &file_cluster_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ActivationTerminating.ProtoReflect.Descriptor instead. +func (*ActivationTerminating) Descriptor() ([]byte, []int) { + return file_cluster_proto_rawDescGZIP(), []int{7} +} + +func (x *ActivationTerminating) GetPid() *actor.PID { + if x != nil { + return x.Pid + } + return nil +} + +func (x *ActivationTerminating) GetClusterIdentity() *ClusterIdentity { + if x != nil { + return x.ClusterIdentity + } + return nil +} + +// Terminated, removed from lookup +type ActivationTerminated struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Pid *actor.PID `protobuf:"bytes,1,opt,name=pid,proto3" json:"pid,omitempty"` + ClusterIdentity *ClusterIdentity `protobuf:"bytes,2,opt,name=cluster_identity,json=clusterIdentity,proto3" json:"cluster_identity,omitempty"` +} + +func (x *ActivationTerminated) Reset() { + *x = ActivationTerminated{} + if protoimpl.UnsafeEnabled { + mi := &file_cluster_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ActivationTerminated) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ActivationTerminated) ProtoMessage() {} + +func (x *ActivationTerminated) ProtoReflect() protoreflect.Message { + mi := &file_cluster_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ActivationTerminated.ProtoReflect.Descriptor instead. +func (*ActivationTerminated) Descriptor() ([]byte, []int) { + return file_cluster_proto_rawDescGZIP(), []int{8} +} + +func (x *ActivationTerminated) GetPid() *actor.PID { + if x != nil { + return x.Pid + } + return nil +} + +func (x *ActivationTerminated) GetClusterIdentity() *ClusterIdentity { + if x != nil { + return x.ClusterIdentity + } + return nil +} + +type ActivationRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ClusterIdentity *ClusterIdentity `protobuf:"bytes,1,opt,name=cluster_identity,json=clusterIdentity,proto3" json:"cluster_identity,omitempty"` + RequestId string `protobuf:"bytes,2,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` + TopologyHash uint64 `protobuf:"varint,3,opt,name=topology_hash,json=topologyHash,proto3" json:"topology_hash,omitempty"` +} + +func (x *ActivationRequest) Reset() { + *x = ActivationRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_cluster_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ActivationRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ActivationRequest) ProtoMessage() {} + +func (x *ActivationRequest) ProtoReflect() protoreflect.Message { + mi := &file_cluster_proto_msgTypes[9] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ActivationRequest.ProtoReflect.Descriptor instead. +func (*ActivationRequest) Descriptor() ([]byte, []int) { + return file_cluster_proto_rawDescGZIP(), []int{9} +} + +func (x *ActivationRequest) GetClusterIdentity() *ClusterIdentity { + if x != nil { + return x.ClusterIdentity + } + return nil +} + +func (x *ActivationRequest) GetRequestId() string { + if x != nil { + return x.RequestId + } + return "" +} + +func (x *ActivationRequest) GetTopologyHash() uint64 { + if x != nil { + return x.TopologyHash + } + return 0 +} + +type ProxyActivationRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ClusterIdentity *ClusterIdentity `protobuf:"bytes,1,opt,name=cluster_identity,json=clusterIdentity,proto3" json:"cluster_identity,omitempty"` + ReplacedActivation *actor.PID `protobuf:"bytes,2,opt,name=replaced_activation,json=replacedActivation,proto3" json:"replaced_activation,omitempty"` +} + +func (x *ProxyActivationRequest) Reset() { + *x = ProxyActivationRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_cluster_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ProxyActivationRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProxyActivationRequest) ProtoMessage() {} + +func (x *ProxyActivationRequest) ProtoReflect() protoreflect.Message { + mi := &file_cluster_proto_msgTypes[10] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProxyActivationRequest.ProtoReflect.Descriptor instead. +func (*ProxyActivationRequest) Descriptor() ([]byte, []int) { + return file_cluster_proto_rawDescGZIP(), []int{10} +} + +func (x *ProxyActivationRequest) GetClusterIdentity() *ClusterIdentity { + if x != nil { + return x.ClusterIdentity + } + return nil +} + +func (x *ProxyActivationRequest) GetReplacedActivation() *actor.PID { + if x != nil { + return x.ReplacedActivation + } + return nil +} + +type ActivationResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Pid *actor.PID `protobuf:"bytes,1,opt,name=pid,proto3" json:"pid,omitempty"` + Failed bool `protobuf:"varint,2,opt,name=failed,proto3" json:"failed,omitempty"` + TopologyHash uint64 `protobuf:"varint,3,opt,name=topology_hash,json=topologyHash,proto3" json:"topology_hash,omitempty"` +} + +func (x *ActivationResponse) Reset() { + *x = ActivationResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_cluster_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ActivationResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ActivationResponse) ProtoMessage() {} + +func (x *ActivationResponse) ProtoReflect() protoreflect.Message { + mi := &file_cluster_proto_msgTypes[11] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ActivationResponse.ProtoReflect.Descriptor instead. +func (*ActivationResponse) Descriptor() ([]byte, []int) { + return file_cluster_proto_rawDescGZIP(), []int{11} +} + +func (x *ActivationResponse) GetPid() *actor.PID { + if x != nil { + return x.Pid + } + return nil +} + +func (x *ActivationResponse) GetFailed() bool { + if x != nil { + return x.Failed + } + return false +} + +func (x *ActivationResponse) GetTopologyHash() uint64 { + if x != nil { + return x.TopologyHash + } + return 0 +} + +type ReadyForRebalance struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + TopologyHash uint64 `protobuf:"varint,1,opt,name=topology_hash,json=topologyHash,proto3" json:"topology_hash,omitempty"` +} + +func (x *ReadyForRebalance) Reset() { + *x = ReadyForRebalance{} + if protoimpl.UnsafeEnabled { + mi := &file_cluster_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ReadyForRebalance) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ReadyForRebalance) ProtoMessage() {} + +func (x *ReadyForRebalance) ProtoReflect() protoreflect.Message { + mi := &file_cluster_proto_msgTypes[12] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ReadyForRebalance.ProtoReflect.Descriptor instead. +func (*ReadyForRebalance) Descriptor() ([]byte, []int) { + return file_cluster_proto_rawDescGZIP(), []int{12} +} + +func (x *ReadyForRebalance) GetTopologyHash() uint64 { + if x != nil { + return x.TopologyHash + } + return 0 +} + +type RebalanceCompleted struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + TopologyHash uint64 `protobuf:"varint,1,opt,name=topology_hash,json=topologyHash,proto3" json:"topology_hash,omitempty"` +} + +func (x *RebalanceCompleted) Reset() { + *x = RebalanceCompleted{} + if protoimpl.UnsafeEnabled { + mi := &file_cluster_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RebalanceCompleted) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RebalanceCompleted) ProtoMessage() {} + +func (x *RebalanceCompleted) ProtoReflect() protoreflect.Message { + mi := &file_cluster_proto_msgTypes[13] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RebalanceCompleted.ProtoReflect.Descriptor instead. +func (*RebalanceCompleted) Descriptor() ([]byte, []int) { + return file_cluster_proto_rawDescGZIP(), []int{13} +} + +func (x *RebalanceCompleted) GetTopologyHash() uint64 { + if x != nil { + return x.TopologyHash + } + return 0 +} + +type Member struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Host string `protobuf:"bytes,1,opt,name=host,proto3" json:"host,omitempty"` + Port int32 `protobuf:"varint,2,opt,name=port,proto3" json:"port,omitempty"` + Id string `protobuf:"bytes,3,opt,name=id,proto3" json:"id,omitempty"` + Kinds []string `protobuf:"bytes,4,rep,name=kinds,proto3" json:"kinds,omitempty"` +} + +func (x *Member) Reset() { + *x = Member{} + if protoimpl.UnsafeEnabled { + mi := &file_cluster_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Member) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Member) ProtoMessage() {} + +func (x *Member) ProtoReflect() protoreflect.Message { + mi := &file_cluster_proto_msgTypes[14] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Member.ProtoReflect.Descriptor instead. +func (*Member) Descriptor() ([]byte, []int) { + return file_cluster_proto_rawDescGZIP(), []int{14} +} + +func (x *Member) GetHost() string { + if x != nil { + return x.Host + } + return "" +} + +func (x *Member) GetPort() int32 { + if x != nil { + return x.Port + } + return 0 +} + +func (x *Member) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *Member) GetKinds() []string { + if x != nil { + return x.Kinds + } + return nil +} + +type ClusterTopology struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + TopologyHash uint64 `protobuf:"varint,1,opt,name=topology_hash,json=topologyHash,proto3" json:"topology_hash,omitempty"` + Members []*Member `protobuf:"bytes,2,rep,name=members,proto3" json:"members,omitempty"` + Joined []*Member `protobuf:"bytes,3,rep,name=joined,proto3" json:"joined,omitempty"` + Left []*Member `protobuf:"bytes,4,rep,name=left,proto3" json:"left,omitempty"` + Blocked []string `protobuf:"bytes,5,rep,name=blocked,proto3" json:"blocked,omitempty"` +} + +func (x *ClusterTopology) Reset() { + *x = ClusterTopology{} + if protoimpl.UnsafeEnabled { + mi := &file_cluster_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ClusterTopology) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ClusterTopology) ProtoMessage() {} + +func (x *ClusterTopology) ProtoReflect() protoreflect.Message { + mi := &file_cluster_proto_msgTypes[15] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ClusterTopology.ProtoReflect.Descriptor instead. +func (*ClusterTopology) Descriptor() ([]byte, []int) { + return file_cluster_proto_rawDescGZIP(), []int{15} +} + +func (x *ClusterTopology) GetTopologyHash() uint64 { + if x != nil { + return x.TopologyHash + } + return 0 +} + +func (x *ClusterTopology) GetMembers() []*Member { + if x != nil { + return x.Members + } + return nil +} + +func (x *ClusterTopology) GetJoined() []*Member { + if x != nil { + return x.Joined + } + return nil +} + +func (x *ClusterTopology) GetLeft() []*Member { + if x != nil { + return x.Left + } + return nil +} + +func (x *ClusterTopology) GetBlocked() []string { + if x != nil { + return x.Blocked + } + return nil +} + +type ClusterTopologyNotification struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + MemberId string `protobuf:"bytes,1,opt,name=member_id,json=memberId,proto3" json:"member_id,omitempty"` + TopologyHash uint32 `protobuf:"varint,2,opt,name=topology_hash,json=topologyHash,proto3" json:"topology_hash,omitempty"` + LeaderId string `protobuf:"bytes,3,opt,name=leader_id,json=leaderId,proto3" json:"leader_id,omitempty"` +} + +func (x *ClusterTopologyNotification) Reset() { + *x = ClusterTopologyNotification{} + if protoimpl.UnsafeEnabled { + mi := &file_cluster_proto_msgTypes[16] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ClusterTopologyNotification) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ClusterTopologyNotification) ProtoMessage() {} + +func (x *ClusterTopologyNotification) ProtoReflect() protoreflect.Message { + mi := &file_cluster_proto_msgTypes[16] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ClusterTopologyNotification.ProtoReflect.Descriptor instead. +func (*ClusterTopologyNotification) Descriptor() ([]byte, []int) { + return file_cluster_proto_rawDescGZIP(), []int{16} +} + +func (x *ClusterTopologyNotification) GetMemberId() string { + if x != nil { + return x.MemberId + } + return "" +} + +func (x *ClusterTopologyNotification) GetTopologyHash() uint32 { + if x != nil { + return x.TopologyHash + } + return 0 +} + +func (x *ClusterTopologyNotification) GetLeaderId() string { + if x != nil { + return x.LeaderId + } + return "" +} + +type MemberHeartbeat struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ActorStatistics *ActorStatistics `protobuf:"bytes,1,opt,name=actor_statistics,json=actorStatistics,proto3" json:"actor_statistics,omitempty"` +} + +func (x *MemberHeartbeat) Reset() { + *x = MemberHeartbeat{} + if protoimpl.UnsafeEnabled { + mi := &file_cluster_proto_msgTypes[17] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *MemberHeartbeat) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MemberHeartbeat) ProtoMessage() {} + +func (x *MemberHeartbeat) ProtoReflect() protoreflect.Message { + mi := &file_cluster_proto_msgTypes[17] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use MemberHeartbeat.ProtoReflect.Descriptor instead. +func (*MemberHeartbeat) Descriptor() ([]byte, []int) { + return file_cluster_proto_rawDescGZIP(), []int{17} +} + +func (x *MemberHeartbeat) GetActorStatistics() *ActorStatistics { + if x != nil { + return x.ActorStatistics + } + return nil +} + +type ActorStatistics struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ActorCount map[string]int64 `protobuf:"bytes,1,rep,name=actor_count,json=actorCount,proto3" json:"actor_count,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"` +} + +func (x *ActorStatistics) Reset() { + *x = ActorStatistics{} + if protoimpl.UnsafeEnabled { + mi := &file_cluster_proto_msgTypes[18] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ActorStatistics) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ActorStatistics) ProtoMessage() {} + +func (x *ActorStatistics) ProtoReflect() protoreflect.Message { + mi := &file_cluster_proto_msgTypes[18] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ActorStatistics.ProtoReflect.Descriptor instead. +func (*ActorStatistics) Descriptor() ([]byte, []int) { + return file_cluster_proto_rawDescGZIP(), []int{18} +} + +func (x *ActorStatistics) GetActorCount() map[string]int64 { + if x != nil { + return x.ActorCount + } + return nil +} + +type IdentityHandoverRequest_Topology struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + TopologyHash uint64 `protobuf:"varint,1,opt,name=topology_hash,json=topologyHash,proto3" json:"topology_hash,omitempty"` + Members []*Member `protobuf:"bytes,3,rep,name=members,proto3" json:"members,omitempty"` +} + +func (x *IdentityHandoverRequest_Topology) Reset() { + *x = IdentityHandoverRequest_Topology{} + if protoimpl.UnsafeEnabled { + mi := &file_cluster_proto_msgTypes[19] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *IdentityHandoverRequest_Topology) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*IdentityHandoverRequest_Topology) ProtoMessage() {} + +func (x *IdentityHandoverRequest_Topology) ProtoReflect() protoreflect.Message { + mi := &file_cluster_proto_msgTypes[19] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use IdentityHandoverRequest_Topology.ProtoReflect.Descriptor instead. +func (*IdentityHandoverRequest_Topology) Descriptor() ([]byte, []int) { + return file_cluster_proto_rawDescGZIP(), []int{0, 0} +} + +func (x *IdentityHandoverRequest_Topology) GetTopologyHash() uint64 { + if x != nil { + return x.TopologyHash + } + return 0 +} + +func (x *IdentityHandoverRequest_Topology) GetMembers() []*Member { + if x != nil { + return x.Members + } + return nil +} + +type PackedActivations_Kind struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Activations []*PackedActivations_Activation `protobuf:"bytes,2,rep,name=activations,proto3" json:"activations,omitempty"` +} + +func (x *PackedActivations_Kind) Reset() { + *x = PackedActivations_Kind{} + if protoimpl.UnsafeEnabled { + mi := &file_cluster_proto_msgTypes[20] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PackedActivations_Kind) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PackedActivations_Kind) ProtoMessage() {} + +func (x *PackedActivations_Kind) ProtoReflect() protoreflect.Message { + mi := &file_cluster_proto_msgTypes[20] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PackedActivations_Kind.ProtoReflect.Descriptor instead. +func (*PackedActivations_Kind) Descriptor() ([]byte, []int) { + return file_cluster_proto_rawDescGZIP(), []int{3, 0} +} + +func (x *PackedActivations_Kind) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *PackedActivations_Kind) GetActivations() []*PackedActivations_Activation { + if x != nil { + return x.Activations + } + return nil +} + +type PackedActivations_Activation struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Identity string `protobuf:"bytes,1,opt,name=identity,proto3" json:"identity,omitempty"` + ActivationId string `protobuf:"bytes,2,opt,name=activation_id,json=activationId,proto3" json:"activation_id,omitempty"` +} + +func (x *PackedActivations_Activation) Reset() { + *x = PackedActivations_Activation{} + if protoimpl.UnsafeEnabled { + mi := &file_cluster_proto_msgTypes[21] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PackedActivations_Activation) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PackedActivations_Activation) ProtoMessage() {} + +func (x *PackedActivations_Activation) ProtoReflect() protoreflect.Message { + mi := &file_cluster_proto_msgTypes[21] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PackedActivations_Activation.ProtoReflect.Descriptor instead. +func (*PackedActivations_Activation) Descriptor() ([]byte, []int) { + return file_cluster_proto_rawDescGZIP(), []int{3, 1} +} + +func (x *PackedActivations_Activation) GetIdentity() string { + if x != nil { + return x.Identity + } + return "" +} + +func (x *PackedActivations_Activation) GetActivationId() string { + if x != nil { + return x.ActivationId + } + return "" +} + +var File_cluster_proto protoreflect.FileDescriptor + +var file_cluster_proto_rawDesc = []byte{ + 0x0a, 0x0d, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, + 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x1a, 0x0b, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xb7, 0x02, 0x0a, 0x17, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, + 0x74, 0x79, 0x48, 0x61, 0x6e, 0x64, 0x6f, 0x76, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x54, 0x0a, 0x10, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x6f, 0x70, + 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x63, 0x6c, + 0x75, 0x73, 0x74, 0x65, 0x72, 0x2e, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x48, 0x61, + 0x6e, 0x64, 0x6f, 0x76, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x54, 0x6f, + 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x52, 0x0f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x54, + 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x12, 0x50, 0x0a, 0x0e, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x74, 0x6f, 0x70, 0x6f, 0x6c, + 0x6f, 0x67, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x63, 0x6c, 0x75, 0x73, + 0x74, 0x65, 0x72, 0x2e, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x48, 0x61, 0x6e, 0x64, + 0x6f, 0x76, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x54, 0x6f, 0x70, 0x6f, + 0x6c, 0x6f, 0x67, 0x79, 0x52, 0x0d, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x54, 0x6f, 0x70, 0x6f, 0x6c, + 0x6f, 0x67, 0x79, 0x1a, 0x5a, 0x0a, 0x08, 0x54, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x12, + 0x23, 0x0a, 0x0d, 0x74, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x5f, 0x68, 0x61, 0x73, 0x68, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x74, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, + 0x48, 0x61, 0x73, 0x68, 0x12, 0x29, 0x0a, 0x07, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x18, + 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x2e, + 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x07, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x22, + 0xc3, 0x01, 0x0a, 0x10, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x48, 0x61, 0x6e, 0x64, + 0x6f, 0x76, 0x65, 0x72, 0x12, 0x2b, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x2e, 0x41, + 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x61, 0x63, 0x74, 0x6f, 0x72, + 0x73, 0x12, 0x19, 0x0a, 0x08, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x07, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, + 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, 0x69, 0x6e, + 0x61, 0x6c, 0x12, 0x23, 0x0a, 0x0d, 0x74, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x5f, 0x68, + 0x61, 0x73, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x74, 0x6f, 0x70, 0x6f, 0x6c, + 0x6f, 0x67, 0x79, 0x48, 0x61, 0x73, 0x68, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x6b, 0x69, 0x70, 0x70, + 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x73, 0x6b, 0x69, 0x70, 0x70, 0x65, + 0x64, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x65, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, + 0x04, 0x73, 0x65, 0x6e, 0x74, 0x22, 0xd0, 0x01, 0x0a, 0x16, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, + 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x48, 0x61, 0x6e, 0x64, 0x6f, 0x76, 0x65, 0x72, + 0x12, 0x32, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1a, 0x2e, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x2e, 0x50, 0x61, 0x63, 0x6b, 0x65, + 0x64, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x06, 0x61, 0x63, + 0x74, 0x6f, 0x72, 0x73, 0x12, 0x19, 0x0a, 0x08, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x5f, 0x69, 0x64, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x49, 0x64, 0x12, + 0x14, 0x0a, 0x05, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, + 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x12, 0x23, 0x0a, 0x0d, 0x74, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, + 0x79, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x74, 0x6f, + 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x48, 0x61, 0x73, 0x68, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x6b, + 0x69, 0x70, 0x70, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x73, 0x6b, 0x69, + 0x70, 0x70, 0x65, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x65, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x04, 0x73, 0x65, 0x6e, 0x74, 0x22, 0x9a, 0x02, 0x0a, 0x11, 0x50, 0x61, 0x63, + 0x6b, 0x65, 0x64, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x18, + 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x37, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x6f, + 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x63, 0x6c, 0x75, 0x73, 0x74, + 0x65, 0x72, 0x2e, 0x50, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x4b, 0x69, 0x6e, 0x64, 0x52, 0x06, 0x61, 0x63, 0x74, 0x6f, 0x72, + 0x73, 0x1a, 0x63, 0x0a, 0x04, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x47, 0x0a, + 0x0b, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x2e, 0x50, 0x61, 0x63, + 0x6b, 0x65, 0x64, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x41, + 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x61, 0x63, 0x74, 0x69, 0x76, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x4d, 0x0a, 0x0a, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, + 0x12, 0x23, 0x0a, 0x0d, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, + 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x22, 0xd4, 0x01, 0x0a, 0x13, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, + 0x74, 0x79, 0x48, 0x61, 0x6e, 0x64, 0x6f, 0x76, 0x65, 0x72, 0x41, 0x63, 0x6b, 0x12, 0x19, 0x0a, + 0x08, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, + 0x07, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x74, 0x6f, 0x70, 0x6f, + 0x6c, 0x6f, 0x67, 0x79, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x0c, 0x74, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x48, 0x61, 0x73, 0x68, 0x12, 0x4d, 0x0a, + 0x10, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x74, 0x61, 0x74, + 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x22, 0x2e, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, + 0x72, 0x2e, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x48, 0x61, 0x6e, 0x64, 0x6f, 0x76, + 0x65, 0x72, 0x41, 0x63, 0x6b, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x0f, 0x70, 0x72, 0x6f, + 0x63, 0x65, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x65, 0x22, 0x2e, 0x0a, 0x05, + 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x0d, 0x0a, 0x09, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, + 0x65, 0x64, 0x10, 0x00, 0x12, 0x16, 0x0a, 0x12, 0x69, 0x6e, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x63, + 0x74, 0x5f, 0x74, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x10, 0x01, 0x22, 0x41, 0x0a, 0x0f, + 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x12, + 0x1a, 0x0a, 0x08, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x08, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x6b, + 0x69, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x22, + 0x6f, 0x0a, 0x0a, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1c, 0x0a, + 0x03, 0x70, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x61, 0x63, 0x74, + 0x6f, 0x72, 0x2e, 0x50, 0x49, 0x44, 0x52, 0x03, 0x70, 0x69, 0x64, 0x12, 0x43, 0x0a, 0x10, 0x63, + 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x2e, + 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, + 0x0f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, + 0x22, 0x7a, 0x0a, 0x15, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x65, + 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x1c, 0x0a, 0x03, 0x70, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x2e, 0x50, + 0x49, 0x44, 0x52, 0x03, 0x70, 0x69, 0x64, 0x12, 0x43, 0x0a, 0x10, 0x63, 0x6c, 0x75, 0x73, 0x74, + 0x65, 0x72, 0x5f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x18, 0x2e, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x2e, 0x43, 0x6c, 0x75, 0x73, + 0x74, 0x65, 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, 0x0f, 0x63, 0x6c, 0x75, + 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x22, 0x79, 0x0a, 0x14, + 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, + 0x61, 0x74, 0x65, 0x64, 0x12, 0x1c, 0x0a, 0x03, 0x70, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x0a, 0x2e, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x2e, 0x50, 0x49, 0x44, 0x52, 0x03, 0x70, + 0x69, 0x64, 0x12, 0x43, 0x0a, 0x10, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, + 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x63, + 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, + 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, 0x0f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, + 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x22, 0x9c, 0x01, 0x0a, 0x11, 0x41, 0x63, 0x74, 0x69, + 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x43, 0x0a, + 0x10, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, + 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, + 0x72, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, + 0x79, 0x52, 0x0f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, + 0x74, 0x79, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, + 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x74, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x5f, 0x68, 0x61, + 0x73, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x74, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, + 0x67, 0x79, 0x48, 0x61, 0x73, 0x68, 0x22, 0x9a, 0x01, 0x0a, 0x16, 0x50, 0x72, 0x6f, 0x78, 0x79, + 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x43, 0x0a, 0x10, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x65, + 0x6e, 0x74, 0x69, 0x74, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x63, 0x6c, + 0x75, 0x73, 0x74, 0x65, 0x72, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x65, + 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, 0x0f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, + 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x12, 0x3b, 0x0a, 0x13, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, + 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x2e, 0x50, 0x49, 0x44, 0x52, + 0x12, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x64, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x22, 0x6f, 0x0a, 0x12, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x03, 0x70, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x2e, 0x50, + 0x49, 0x44, 0x52, 0x03, 0x70, 0x69, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x66, 0x61, 0x69, 0x6c, 0x65, + 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x12, + 0x23, 0x0a, 0x0d, 0x74, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x5f, 0x68, 0x61, 0x73, 0x68, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x74, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, + 0x48, 0x61, 0x73, 0x68, 0x22, 0x38, 0x0a, 0x11, 0x52, 0x65, 0x61, 0x64, 0x79, 0x46, 0x6f, 0x72, + 0x52, 0x65, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x74, 0x6f, 0x70, + 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x0c, 0x74, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x48, 0x61, 0x73, 0x68, 0x22, 0x39, + 0x0a, 0x12, 0x52, 0x65, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6c, + 0x65, 0x74, 0x65, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x74, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, + 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x74, 0x6f, 0x70, + 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x48, 0x61, 0x73, 0x68, 0x22, 0x56, 0x0a, 0x06, 0x4d, 0x65, 0x6d, + 0x62, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, + 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x6b, + 0x69, 0x6e, 0x64, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x6b, 0x69, 0x6e, 0x64, + 0x73, 0x22, 0xc9, 0x01, 0x0a, 0x0f, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x54, 0x6f, 0x70, + 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x12, 0x23, 0x0a, 0x0d, 0x74, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, + 0x79, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x74, 0x6f, + 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x48, 0x61, 0x73, 0x68, 0x12, 0x29, 0x0a, 0x07, 0x6d, 0x65, + 0x6d, 0x62, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x63, 0x6c, + 0x75, 0x73, 0x74, 0x65, 0x72, 0x2e, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x07, 0x6d, 0x65, + 0x6d, 0x62, 0x65, 0x72, 0x73, 0x12, 0x27, 0x0a, 0x06, 0x6a, 0x6f, 0x69, 0x6e, 0x65, 0x64, 0x18, + 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x2e, + 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x06, 0x6a, 0x6f, 0x69, 0x6e, 0x65, 0x64, 0x12, 0x23, + 0x0a, 0x04, 0x6c, 0x65, 0x66, 0x74, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x63, + 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x2e, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x04, 0x6c, + 0x65, 0x66, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x18, 0x05, + 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x22, 0x7c, 0x0a, + 0x1b, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x54, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, + 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1b, 0x0a, 0x09, + 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x74, 0x6f, 0x70, + 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x0c, 0x74, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x48, 0x61, 0x73, 0x68, 0x12, 0x1b, + 0x0a, 0x09, 0x6c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x08, 0x6c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x49, 0x64, 0x22, 0x56, 0x0a, 0x0f, 0x4d, + 0x65, 0x6d, 0x62, 0x65, 0x72, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x12, 0x43, + 0x0a, 0x10, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x69, 0x73, 0x74, 0x69, + 0x63, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x63, 0x6c, 0x75, 0x73, 0x74, + 0x65, 0x72, 0x2e, 0x41, 0x63, 0x74, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x69, 0x73, 0x74, 0x69, + 0x63, 0x73, 0x52, 0x0f, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x69, 0x73, 0x74, + 0x69, 0x63, 0x73, 0x22, 0x9b, 0x01, 0x0a, 0x0f, 0x41, 0x63, 0x74, 0x6f, 0x72, 0x53, 0x74, 0x61, + 0x74, 0x69, 0x73, 0x74, 0x69, 0x63, 0x73, 0x12, 0x49, 0x0a, 0x0b, 0x61, 0x63, 0x74, 0x6f, 0x72, + 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x63, + 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x2e, 0x41, 0x63, 0x74, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, + 0x69, 0x73, 0x74, 0x69, 0x63, 0x73, 0x2e, 0x41, 0x63, 0x74, 0x6f, 0x72, 0x43, 0x6f, 0x75, 0x6e, + 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x43, 0x6f, 0x75, + 0x6e, 0x74, 0x1a, 0x3d, 0x0a, 0x0f, 0x41, 0x63, 0x74, 0x6f, 0x72, 0x43, 0x6f, 0x75, 0x6e, 0x74, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, + 0x01, 0x42, 0x2c, 0x5a, 0x2a, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x61, 0x73, 0x79, 0x6e, 0x6b, 0x72, 0x6f, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x61, + 0x63, 0x74, 0x6f, 0x72, 0x2d, 0x67, 0x6f, 0x2f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_cluster_proto_rawDescOnce sync.Once + file_cluster_proto_rawDescData = file_cluster_proto_rawDesc +) + +func file_cluster_proto_rawDescGZIP() []byte { + file_cluster_proto_rawDescOnce.Do(func() { + file_cluster_proto_rawDescData = protoimpl.X.CompressGZIP(file_cluster_proto_rawDescData) + }) + return file_cluster_proto_rawDescData +} + +var file_cluster_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_cluster_proto_msgTypes = make([]protoimpl.MessageInfo, 23) +var file_cluster_proto_goTypes = []interface{}{ + (IdentityHandoverAck_State)(0), // 0: cluster.IdentityHandoverAck.State + (*IdentityHandoverRequest)(nil), // 1: cluster.IdentityHandoverRequest + (*IdentityHandover)(nil), // 2: cluster.IdentityHandover + (*RemoteIdentityHandover)(nil), // 3: cluster.RemoteIdentityHandover + (*PackedActivations)(nil), // 4: cluster.PackedActivations + (*IdentityHandoverAck)(nil), // 5: cluster.IdentityHandoverAck + (*ClusterIdentity)(nil), // 6: cluster.ClusterIdentity + (*Activation)(nil), // 7: cluster.Activation + (*ActivationTerminating)(nil), // 8: cluster.ActivationTerminating + (*ActivationTerminated)(nil), // 9: cluster.ActivationTerminated + (*ActivationRequest)(nil), // 10: cluster.ActivationRequest + (*ProxyActivationRequest)(nil), // 11: cluster.ProxyActivationRequest + (*ActivationResponse)(nil), // 12: cluster.ActivationResponse + (*ReadyForRebalance)(nil), // 13: cluster.ReadyForRebalance + (*RebalanceCompleted)(nil), // 14: cluster.RebalanceCompleted + (*Member)(nil), // 15: cluster.Member + (*ClusterTopology)(nil), // 16: cluster.ClusterTopology + (*ClusterTopologyNotification)(nil), // 17: cluster.ClusterTopologyNotification + (*MemberHeartbeat)(nil), // 18: cluster.MemberHeartbeat + (*ActorStatistics)(nil), // 19: cluster.ActorStatistics + (*IdentityHandoverRequest_Topology)(nil), // 20: cluster.IdentityHandoverRequest.Topology + (*PackedActivations_Kind)(nil), // 21: cluster.PackedActivations.Kind + (*PackedActivations_Activation)(nil), // 22: cluster.PackedActivations.Activation + nil, // 23: cluster.ActorStatistics.ActorCountEntry + (*actor.PID)(nil), // 24: actor.PID +} +var file_cluster_proto_depIdxs = []int32{ + 20, // 0: cluster.IdentityHandoverRequest.current_topology:type_name -> cluster.IdentityHandoverRequest.Topology + 20, // 1: cluster.IdentityHandoverRequest.delta_topology:type_name -> cluster.IdentityHandoverRequest.Topology + 7, // 2: cluster.IdentityHandover.actors:type_name -> cluster.Activation + 4, // 3: cluster.RemoteIdentityHandover.actors:type_name -> cluster.PackedActivations + 21, // 4: cluster.PackedActivations.actors:type_name -> cluster.PackedActivations.Kind + 0, // 5: cluster.IdentityHandoverAck.processing_state:type_name -> cluster.IdentityHandoverAck.State + 24, // 6: cluster.Activation.pid:type_name -> actor.PID + 6, // 7: cluster.Activation.cluster_identity:type_name -> cluster.ClusterIdentity + 24, // 8: cluster.ActivationTerminating.pid:type_name -> actor.PID + 6, // 9: cluster.ActivationTerminating.cluster_identity:type_name -> cluster.ClusterIdentity + 24, // 10: cluster.ActivationTerminated.pid:type_name -> actor.PID + 6, // 11: cluster.ActivationTerminated.cluster_identity:type_name -> cluster.ClusterIdentity + 6, // 12: cluster.ActivationRequest.cluster_identity:type_name -> cluster.ClusterIdentity + 6, // 13: cluster.ProxyActivationRequest.cluster_identity:type_name -> cluster.ClusterIdentity + 24, // 14: cluster.ProxyActivationRequest.replaced_activation:type_name -> actor.PID + 24, // 15: cluster.ActivationResponse.pid:type_name -> actor.PID + 15, // 16: cluster.ClusterTopology.members:type_name -> cluster.Member + 15, // 17: cluster.ClusterTopology.joined:type_name -> cluster.Member + 15, // 18: cluster.ClusterTopology.left:type_name -> cluster.Member + 19, // 19: cluster.MemberHeartbeat.actor_statistics:type_name -> cluster.ActorStatistics + 23, // 20: cluster.ActorStatistics.actor_count:type_name -> cluster.ActorStatistics.ActorCountEntry + 15, // 21: cluster.IdentityHandoverRequest.Topology.members:type_name -> cluster.Member + 22, // 22: cluster.PackedActivations.Kind.activations:type_name -> cluster.PackedActivations.Activation + 23, // [23:23] is the sub-list for method output_type + 23, // [23:23] is the sub-list for method input_type + 23, // [23:23] is the sub-list for extension type_name + 23, // [23:23] is the sub-list for extension extendee + 0, // [0:23] is the sub-list for field type_name +} + +func init() { file_cluster_proto_init() } +func file_cluster_proto_init() { + if File_cluster_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_cluster_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*IdentityHandoverRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_cluster_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*IdentityHandover); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_cluster_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RemoteIdentityHandover); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_cluster_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PackedActivations); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_cluster_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*IdentityHandoverAck); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_cluster_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ClusterIdentity); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_cluster_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Activation); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_cluster_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ActivationTerminating); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_cluster_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ActivationTerminated); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_cluster_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ActivationRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_cluster_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ProxyActivationRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_cluster_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ActivationResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_cluster_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ReadyForRebalance); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_cluster_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RebalanceCompleted); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_cluster_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Member); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_cluster_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ClusterTopology); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_cluster_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ClusterTopologyNotification); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_cluster_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*MemberHeartbeat); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_cluster_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ActorStatistics); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_cluster_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*IdentityHandoverRequest_Topology); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_cluster_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PackedActivations_Kind); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_cluster_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PackedActivations_Activation); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_cluster_proto_rawDesc, + NumEnums: 1, + NumMessages: 23, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_cluster_proto_goTypes, + DependencyIndexes: file_cluster_proto_depIdxs, + EnumInfos: file_cluster_proto_enumTypes, + MessageInfos: file_cluster_proto_msgTypes, + }.Build() + File_cluster_proto = out.File + file_cluster_proto_rawDesc = nil + file_cluster_proto_goTypes = nil + file_cluster_proto_depIdxs = nil +} diff --git a/cluster/cluster.proto b/cluster/cluster.proto new file mode 100644 index 0000000000000000000000000000000000000000..2a76da9c2e19313bfe12416ff001e57ef870d6de --- /dev/null +++ b/cluster/cluster.proto @@ -0,0 +1,142 @@ +syntax = "proto3"; +package cluster; +option go_package = "/gitee.com/simplexyz/simpleactor-go/cluster"; +import "actor.proto"; + +//request response call from Identity actor sent to each member +//asking what activations they hold that belong to the requester +message IdentityHandoverRequest { + Topology current_topology = 1; + string address = 2; + // If the requester passes a delta topology, only return activations which would not be assigned to the member + // in the previous topology. + Topology delta_topology = 3; + message Topology{ + uint64 topology_hash = 1; + repeated Member members = 3; + } +} + +message IdentityHandover { + repeated Activation actors = 1; + int32 chunk_id = 2; + bool final = 3; + uint64 topology_hash = 4; + int32 skipped = 5; // Total number of activations skipped + int32 sent = 6; // Total number of activations sent +} + +message RemoteIdentityHandover { + PackedActivations actors = 1; + int32 chunk_id = 2; + bool final = 3; + uint64 topology_hash = 4; + int32 skipped = 5; + int32 sent = 6; +} + +message PackedActivations{ + string address = 1; + repeated Kind actors = 2; + message Kind{ + string name = 1; + repeated Activation activations = 2; + } + message Activation{ + string identity = 1; + string activation_id = 2; + } +} + +message IdentityHandoverAck { + int32 chunk_id = 1; + uint64 topology_hash = 2; + State processing_state = 3; + enum State { + processed = 0; + incorrect_topology = 1; + } +} + +message ClusterIdentity{ + string identity = 1; + string kind = 2; +} + +message Activation { + actor.PID pid = 1; + ClusterIdentity cluster_identity = 2; +} + +// Started terminating, not yet removed from IIdentityLookup +message ActivationTerminating { + actor.PID pid = 1; + ClusterIdentity cluster_identity = 2; +} + +// Terminated, removed from lookup +message ActivationTerminated { + actor.PID pid = 1; + ClusterIdentity cluster_identity = 2; +} + +message ActivationRequest { + ClusterIdentity cluster_identity = 1; + string request_id = 2; + uint64 topology_hash = 3; +} + +message ProxyActivationRequest { + ClusterIdentity cluster_identity = 1; + actor.PID replaced_activation = 2; +} + +message ActivationResponse { + actor.PID pid = 1; + bool failed = 2; + uint64 topology_hash = 3; +} + +message ReadyForRebalance { + uint64 topology_hash = 1; +} +message RebalanceCompleted { + uint64 topology_hash = 1; +} + +message Member { + string host = 1; + int32 port = 2; + string id = 3; + repeated string kinds = 4; +} + +message ClusterTopology { + uint64 topology_hash = 1; + repeated Member members = 2; + repeated Member joined = 3; + repeated Member left = 4; + repeated string blocked = 5; +} + +message ClusterTopologyNotification { + string member_id = 1; + uint32 topology_hash = 2; + string leader_id = 3; +} + +message MemberHeartbeat { + ActorStatistics actor_statistics = 1; +} + +message ActorStatistics { + map actor_count = 1; +} + + + + +//keys to implement initially +//topology - value is repeated members, this can replace ClusterTopologyNotification, as it would be the same, but better +//heartbeat - value is unit, or sender timestamp? +//leader - value is leader member id \ No newline at end of file diff --git a/cluster/cluster_config_context.go b/cluster/cluster_config_context.go new file mode 100644 index 0000000000000000000000000000000000000000..4075bba58f6e97758988b2ddc4a5d137226287e5 --- /dev/null +++ b/cluster/cluster_config_context.go @@ -0,0 +1,44 @@ +// Copyright (C) 2017 - 2022 Asynkron.se + +package cluster + +import ( + "fmt" + "time" + + "gitee.com/simplexyz/simpleactor-go/actor" +) + +const ( + defaultActorRequestTimeout = 5 * time.Second + defaultRequestsLogThrottlePeriod = 2 * time.Second + defaultMaxNumberOfEvetsInRequestLogThrottledPeriod int = 3 +) + +// ClusterContextConfig is used to configure cluster context parameters +type ClusterContextConfig struct { + ActorRequestTimeout time.Duration + RequestsLogThrottlePeriod time.Duration + MaxNumberOfEventsInRequestLogThrottledPeriod int + RetryAction func(int) int + requestLogThrottle actor.ShouldThrottle +} + +// NewDefaultClusterContextConfig creates a mew ClusterContextConfig with default +// values and returns a pointer to its memory address +func NewDefaultClusterContextConfig() *ClusterContextConfig { + config := ClusterContextConfig{ + ActorRequestTimeout: defaultActorRequestTimeout, + RequestsLogThrottlePeriod: defaultRequestsLogThrottlePeriod, + MaxNumberOfEventsInRequestLogThrottledPeriod: defaultMaxNumberOfEvetsInRequestLogThrottledPeriod, + RetryAction: defaultRetryAction, + requestLogThrottle: actor.NewThrottle( + int32(defaultMaxNumberOfEvetsInRequestLogThrottledPeriod), + defaultRequestsLogThrottlePeriod, + func(i int32) { + plog.Info(fmt.Sprintf("Throttled %d Request logs", i)) + }, + ), + } + return &config +} diff --git a/cluster/cluster_identity.go b/cluster/cluster_identity.go new file mode 100644 index 0000000000000000000000000000000000000000..efff49571990e534d0da233d959f9b34cd2b5a14 --- /dev/null +++ b/cluster/cluster_identity.go @@ -0,0 +1,36 @@ +package cluster + +import ( + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/ctxext" +) + +func (ci *ClusterIdentity) AsKey() string { + return ci.Kind + "/" + ci.Identity +} + +var ciExtensionId = ctxext.NextContextExtensionID() + +// remove +func (ci *ClusterIdentity) ToShortString() string { + return ci.Kind + "/" + ci.Identity +} + +func NewClusterIdentity(identity string, kind string) *ClusterIdentity { + return &ClusterIdentity{ + Identity: identity, + Kind: kind, + } +} + +func (ci *ClusterIdentity) ExtensionID() ctxext.ContextExtensionID { + return ciExtensionId +} + +func GetClusterIdentity(ctx actor.ExtensionContext) *ClusterIdentity { + return ctx.Get(ciExtensionId).(*ClusterIdentity) +} + +func SetClusterIdentity(ctx actor.ExtensionContext, ci *ClusterIdentity) { + ctx.Set(ci) +} diff --git a/cluster/cluster_provider.go b/cluster/cluster_provider.go new file mode 100644 index 0000000000000000000000000000000000000000..f717f73a84a48ae0522e8506c7b636ce47ac15a9 --- /dev/null +++ b/cluster/cluster_provider.go @@ -0,0 +1,12 @@ +package cluster + +//type ClusterState struct { +// BannedMembers []string `json:"blockedMembers"` +//} + +type ClusterProvider interface { + StartMember(cluster *Cluster) error + StartClient(cluster *Cluster) error + Shutdown(graceful bool) error + // UpdateClusterState(state ClusterState) error +} diff --git a/cluster/cluster_test.go b/cluster/cluster_test.go new file mode 100644 index 0000000000000000000000000000000000000000..e2fb3996a5ba03fa3c19e1cb2da3eee580bc119e --- /dev/null +++ b/cluster/cluster_test.go @@ -0,0 +1,192 @@ +package cluster + +import ( + "fmt" + "sync" + "testing" + "time" + + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/remote" + "github.com/stretchr/testify/assert" +) + +// inmemoryProvider use for test +type inmemoryProvider struct { + cluster *Cluster + members map[string]*Member + self *Member +} + +func newInmemoryProvider() *inmemoryProvider { + return &inmemoryProvider{members: map[string]*Member{}} +} + +func (p *inmemoryProvider) init(c *Cluster) error { + name := c.Config.Name + host, port, err := c.ActorSystem.GetHostPort() + if err != nil { + return err + } + p.cluster = c + p.self = &Member{ + Host: host, + Port: int32(port), + Id: fmt.Sprintf("%s@%s:%d", name, host, port), + Kinds: c.GetClusterKinds(), + } + + return nil +} + +func (p *inmemoryProvider) publishClusterTopologyEvent() { + var members Members + for _, m := range p.members { + members = append(members, m) + } + + res := members + + p.cluster.MemberList.UpdateClusterTopology(res) + // p.cluster.ActorSystem.EventStream.Publish(res) +} + +func (p *inmemoryProvider) StartMember(c *Cluster) error { + err := p.init(c) + if err != nil { + return err + } + p.members[p.self.Id] = p.self + p.publishClusterTopologyEvent() + return nil +} + +func (p *inmemoryProvider) StartClient(c *Cluster) error { + err := p.init(c) + if err != nil { + return err + } + p.publishClusterTopologyEvent() + return nil +} + +func (p *inmemoryProvider) Shutdown(graceful bool) error { + delete(p.members, p.self.Id) + + return nil +} + +type fakeIdentityLookup struct { + m sync.Map +} + +func (l *fakeIdentityLookup) Get(identity *ClusterIdentity) *actor.PID { + if val, ok := l.m.Load(identity.Identity); ok { + return val.(*actor.PID) + } else { + // pid := actor.NewPID("127.0.0.1", fmt.Sprintf("%s/%s", identity.Kind, identity.Identity)) + // l.m.Store(identity.Identity, pid) + // return pid + } + return nil +} + +func (l *fakeIdentityLookup) RemovePid(identity *ClusterIdentity, pid *actor.PID) { + if existPid := l.Get(identity); existPid.Equal(pid) { + l.m.Delete(identity.Identity) + } +} + +func (lu *fakeIdentityLookup) Setup(cluster *Cluster, kinds []string, isClient bool) { +} + +func (lu *fakeIdentityLookup) Shutdown() { +} + +func newClusterForTest(name string, cp ClusterProvider, opts ...ConfigOption) *Cluster { + system := actor.NewActorSystem() + lookup := fakeIdentityLookup{} + cfg := Configure(name, cp, &lookup, remote.Configure("127.0.0.1", 0), opts...) + c := New(system, cfg) + + c.MemberList = NewMemberList(c) + c.Config.RequestTimeoutTime = 1 * time.Second + c.Remote = remote.NewRemote(system, c.Config.RemoteConfig) + return c +} + +func TestCluster_Call(t *testing.T) { + t.Skipf("Maintaining") + assert := assert.New(t) + + members := Members{ + { + Id: "1", + Host: "nonhost", + Port: -1, + Kinds: []string{"kind"}, + }, + } + c := newClusterForTest("mycluster", nil) + c.MemberList.UpdateClusterTopology(members) + t.Run("invalid kind", func(t *testing.T) { + msg := struct{}{} + resp, err := c.Request("name", "nonkind", &msg) + assert.Equal(remote.ErrUnAvailable, err) + assert.Nil(resp) + }) + + // FIXME: testcase + // t.Run("timeout", func(t *testing.T) { + // msg := struct{}{} + // callopts := NewGrainCallOptions(c).WithRetry(2).WithRequestTimeout(1 * time.Second) + // resp, err := c.Call("name", "kind", &msg, callopts) + // assert.Equalf(Remote.ErrUnknownError, err, "%v", err) + // assert.Nil(resp) + // }) + + testProps := actor.PropsFromFunc( + func(context actor.Context) { + switch msg := context.Message().(type) { + case *struct{ Code int }: + msg.Code++ + context.Respond(msg) + } + }) + pid := c.ActorSystem.Root.Spawn(testProps) + assert.NotNil(pid) + c.PidCache.Set("name", "kind", pid) + t.Run("normal", func(t *testing.T) { + msg := struct{ Code int }{9527} + resp, err := c.Request("name", "kind", &msg) + assert.NoError(err) + assert.Equal(&struct{ Code int }{9528}, resp) + }) + // t.Fatalf("need more testcases for cluster.Call") +} + +func TestCluster_Get(t *testing.T) { + t.Skipf("Maintaining") + cp := newInmemoryProvider() + kind := NewKind("kind", actor.PropsFromFunc(func(ctx actor.Context) { + switch msg := ctx.Message().(type) { + case *actor.Started: + _ = msg + } + })) + c := newClusterForTest("mycluster", cp, WithKinds(kind)) + c.StartMember() + cp.publishClusterTopologyEvent() + t.Run("invalid kind", func(t *testing.T) { + assert := assert.New(t) + assert.Equal(1, c.MemberList.Length()) + pid := c.Get("name", "nonkind") + assert.Nil(pid) + }) + + t.Run("ok", func(t *testing.T) { + assert := assert.New(t) + pid := c.Get("name", "kind") + assert.NotNil(pid) + }) +} diff --git a/cluster/cluster_test_tool/build.sh b/cluster/cluster_test_tool/build.sh new file mode 100644 index 0000000000000000000000000000000000000000..144b2ffeddacabce2d67dfefd68d53e91c41c21f --- /dev/null +++ b/cluster/cluster_test_tool/build.sh @@ -0,0 +1,2 @@ +protoc -I=../../actor --go_out=. --go_opt=paths=source_relative --proto_path=. pubsub_cluster.proto + diff --git a/cluster/cluster_test_tool/cluster_fixture.go b/cluster/cluster_test_tool/cluster_fixture.go new file mode 100644 index 0000000000000000000000000000000000000000..e390aca37ae65bfcc85968cc146e92bc5dd3d5d7 --- /dev/null +++ b/cluster/cluster_test_tool/cluster_fixture.go @@ -0,0 +1,224 @@ +package cluster_test_tool + +import ( + "context" + "time" + + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/cluster" + "gitee.com/simplexyz/simpleactor-go/cluster/clusterproviders/test" + "gitee.com/simplexyz/simpleactor-go/cluster/identitylookup/disthash" + "gitee.com/simplexyz/simpleactor-go/log" + "gitee.com/simplexyz/simpleactor-go/remote" + "github.com/google/uuid" + "golang.org/x/sync/errgroup" +) + +type ClusterFixture interface { + GetMembers() []*cluster.Cluster + GetClusterSize() int + SpawnNode() *cluster.Cluster + RemoveNode(node *cluster.Cluster, graceful bool) + ShutDown() +} + +type ClusterFixtureConfig struct { + GetClusterKinds func() []*cluster.Kind + GetClusterProvider func() cluster.ClusterProvider + Configure func(*cluster.Config) *cluster.Config + GetIdentityLookup func(clusterName string) cluster.IdentityLookup + OnDeposing func() +} + +type ClusterFixtureOption func(*ClusterFixtureConfig) + +// WithGetClusterKinds sets the cluster kinds for the cluster fixture +func WithGetClusterKinds(getKinds func() []*cluster.Kind) ClusterFixtureOption { + return func(c *ClusterFixtureConfig) { + c.GetClusterKinds = getKinds + } +} + +// WithClusterConfigure sets the cluster configure function for the cluster fixture +func WithClusterConfigure(configure func(*cluster.Config) *cluster.Config) ClusterFixtureOption { + return func(c *ClusterFixtureConfig) { + c.Configure = configure + } +} + +// WithGetClusterProvider sets the cluster provider for the cluster fixture +func WithGetClusterProvider(getProvider func() cluster.ClusterProvider) ClusterFixtureOption { + return func(c *ClusterFixtureConfig) { + c.GetClusterProvider = getProvider + } +} + +// WithGetIdentityLookup sets the identity lookup function for the cluster fixture +func WithGetIdentityLookup(identityLookup func(clusterName string) cluster.IdentityLookup) ClusterFixtureOption { + return func(c *ClusterFixtureConfig) { + c.GetIdentityLookup = identityLookup + } +} + +// WithOnDeposing sets the on deposing function for the cluster fixture +func WithOnDeposing(onDeposing func()) ClusterFixtureOption { + return func(c *ClusterFixtureConfig) { + c.OnDeposing = onDeposing + } +} + +const InvalidIdentity string = "invalid" + +type BaseClusterFixture struct { + clusterName string + clusterSize int + config *ClusterFixtureConfig + members []*cluster.Cluster +} + +func NewBaseClusterFixture(clusterSize int, opts ...ClusterFixtureOption) *BaseClusterFixture { + config := &ClusterFixtureConfig{ + GetClusterKinds: func() []*cluster.Kind { return make([]*cluster.Kind, 0) }, + GetClusterProvider: func() cluster.ClusterProvider { return test.NewTestProvider(test.NewInMemAgent()) }, + Configure: func(c *cluster.Config) *cluster.Config { return c }, + GetIdentityLookup: func(clusterName string) cluster.IdentityLookup { return disthash.New() }, + OnDeposing: func() {}, + } + for _, opt := range opts { + opt(config) + } + + fixTure := &BaseClusterFixture{ + clusterSize: clusterSize, + clusterName: "test-cluster-" + uuid.NewString()[0:6], + config: config, + members: make([]*cluster.Cluster, 0), + } + return fixTure +} + +// Initialize initializes the cluster fixture +func (b *BaseClusterFixture) Initialize() { + nodes := b.spawnClusterNodes() + b.members = append(b.members, nodes...) +} + +func (b *BaseClusterFixture) GetMembers() []*cluster.Cluster { + return b.members +} + +func (b *BaseClusterFixture) GetClusterSize() int { + return b.clusterSize +} + +func (b *BaseClusterFixture) SpawnNode() *cluster.Cluster { + node := b.spawnClusterMember() + b.members = append(b.members, node) + return node +} + +func (b *BaseClusterFixture) RemoveNode(node *cluster.Cluster, graceful bool) { + has := false + for i, member := range b.members { + if member == node { + has = true + b.members = append(b.members[:i], b.members[i+1:]...) + member.Shutdown(graceful) + break + } + } + if !has { + plog.Error("node not found", log.Object("node", node)) + } +} + +func (b *BaseClusterFixture) ShutDown() { + b.config.OnDeposing() + b.waitForMembersToShutdown() + b.members = b.members[:0] +} + +// spawnClusterNodes spawns a number of cluster nodes +func (b *BaseClusterFixture) spawnClusterNodes() []*cluster.Cluster { + nodes := make([]*cluster.Cluster, 0, b.clusterSize) + for i := 0; i < b.clusterSize; i++ { + nodes = append(nodes, b.spawnClusterMember()) + } + + bgCtx := context.Background() + timeoutCtx, cancel := context.WithTimeout(bgCtx, time.Second*10) + defer cancel() + group := new(errgroup.Group) + for _, node := range nodes { + tmpNode := node + group.Go(func() error { + done := make(chan struct{}) + go func() { + tmpNode.MemberList.TopologyConsensus(timeoutCtx) + close(done) + }() + + select { + case <-timeoutCtx.Done(): + return timeoutCtx.Err() + case <-done: + return nil + } + }) + } + err := group.Wait() + if err != nil { + panic("Failed to reach consensus") + } + + return nodes +} + +// spawnClusterMember spawns a cluster members +func (b *BaseClusterFixture) spawnClusterMember() *cluster.Cluster { + config := cluster.Configure(b.clusterName, b.config.GetClusterProvider(), b.config.GetIdentityLookup(b.clusterName), + remote.Configure("localhost", 0), + cluster.WithKinds(b.config.GetClusterKinds()...), + ) + config = b.config.Configure(config) + + system := actor.NewActorSystem() + + c := cluster.New(system, config) + c.StartMember() + return c +} + +// waitForMembersToShutdown waits for the members to shutdown +func (b *BaseClusterFixture) waitForMembersToShutdown() { + for _, member := range b.members { + plog.Info("Preparing shutdown for cluster member", log.String("member", member.ActorSystem.ID)) + } + + group := new(errgroup.Group) + timeoutCtx, cancel := context.WithTimeout(context.Background(), time.Second*1000) + defer cancel() + + for _, member := range b.members { + member := member + group.Go(func() error { + done := make(chan struct{}) + go func() { + plog.Info("Shutting down cluster member", log.String("member", member.ActorSystem.ID)) + member.Shutdown(true) + close(done) + }() + + select { + case <-timeoutCtx.Done(): + return timeoutCtx.Err() + case <-done: + return nil + } + }) + } + err := group.Wait() + if err != nil { + panic(err) + } +} diff --git a/cluster/cluster_test_tool/in_memory_cluster_fixture.go b/cluster/cluster_test_tool/in_memory_cluster_fixture.go new file mode 100644 index 0000000000000000000000000000000000000000..7485be278a6d4cfdc7a92d51b5c8112d7552d6ac --- /dev/null +++ b/cluster/cluster_test_tool/in_memory_cluster_fixture.go @@ -0,0 +1,19 @@ +package cluster_test_tool + +import ( + "gitee.com/simplexyz/simpleactor-go/cluster" + "gitee.com/simplexyz/simpleactor-go/cluster/clusterproviders/test" +) + +// NewBaseInMemoryClusterFixture creates a new in memory cluster fixture +func NewBaseInMemoryClusterFixture(clusterSize int, opts ...ClusterFixtureOption) *BaseClusterFixture { + inMemAgent := test.NewInMemAgent() + baseInMemoryOpts := []ClusterFixtureOption{ + WithGetClusterProvider(func() cluster.ClusterProvider { + return test.NewTestProvider(inMemAgent) + }), + } + baseInMemoryOpts = append(baseInMemoryOpts, opts...) + + return NewBaseClusterFixture(clusterSize, baseInMemoryOpts...) +} diff --git a/cluster/cluster_test_tool/log.go b/cluster/cluster_test_tool/log.go new file mode 100644 index 0000000000000000000000000000000000000000..acd7e572b6097eb79390563d8c0541d9ca1cfc7a --- /dev/null +++ b/cluster/cluster_test_tool/log.go @@ -0,0 +1,11 @@ +package cluster_test_tool + +import "gitee.com/simplexyz/simpleactor-go/log" + +var plog = log.New(log.DebugLevel, "[CLUSTER TEST]") + +// SetLogLevel sets the log level for the logger +// SetLogLevel is safe to be called concurrently +func SetLogLevel(level log.Level) { + plog.SetLevel(level) +} diff --git a/cluster/cluster_test_tool/pubsub_cluster.pb.go b/cluster/cluster_test_tool/pubsub_cluster.pb.go new file mode 100644 index 0000000000000000000000000000000000000000..b91d781b7ac900f70398ede8b3b3ee61db2fac1d --- /dev/null +++ b/cluster/cluster_test_tool/pubsub_cluster.pb.go @@ -0,0 +1,198 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.28.1 +// protoc v3.21.9 +// source: pubsub_cluster.proto + +package cluster_test_tool + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type DataPublished struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Data int32 `protobuf:"varint,1,opt,name=data,proto3" json:"data,omitempty"` +} + +func (x *DataPublished) Reset() { + *x = DataPublished{} + if protoimpl.UnsafeEnabled { + mi := &file_pubsub_cluster_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DataPublished) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DataPublished) ProtoMessage() {} + +func (x *DataPublished) ProtoReflect() protoreflect.Message { + mi := &file_pubsub_cluster_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DataPublished.ProtoReflect.Descriptor instead. +func (*DataPublished) Descriptor() ([]byte, []int) { + return file_pubsub_cluster_proto_rawDescGZIP(), []int{0} +} + +func (x *DataPublished) GetData() int32 { + if x != nil { + return x.Data + } + return 0 +} + +type Response struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *Response) Reset() { + *x = Response{} + if protoimpl.UnsafeEnabled { + mi := &file_pubsub_cluster_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Response) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Response) ProtoMessage() {} + +func (x *Response) ProtoReflect() protoreflect.Message { + mi := &file_pubsub_cluster_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Response.ProtoReflect.Descriptor instead. +func (*Response) Descriptor() ([]byte, []int) { + return file_pubsub_cluster_proto_rawDescGZIP(), []int{1} +} + +var File_pubsub_cluster_proto protoreflect.FileDescriptor + +var file_pubsub_cluster_proto_rawDesc = []byte{ + 0x0a, 0x14, 0x70, 0x75, 0x62, 0x73, 0x75, 0x62, 0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x11, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, + 0x74, 0x65, 0x73, 0x74, 0x5f, 0x74, 0x6f, 0x6f, 0x6c, 0x22, 0x23, 0x0a, 0x0d, 0x44, 0x61, 0x74, + 0x61, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, + 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x0a, + 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x3e, 0x5a, 0x3c, 0x2f, 0x67, + 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x73, 0x79, 0x6e, 0x6b, 0x72, + 0x6f, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x2d, 0x67, 0x6f, + 0x2f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x2f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, + 0x5f, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x74, 0x6f, 0x6f, 0x6c, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, +} + +var ( + file_pubsub_cluster_proto_rawDescOnce sync.Once + file_pubsub_cluster_proto_rawDescData = file_pubsub_cluster_proto_rawDesc +) + +func file_pubsub_cluster_proto_rawDescGZIP() []byte { + file_pubsub_cluster_proto_rawDescOnce.Do(func() { + file_pubsub_cluster_proto_rawDescData = protoimpl.X.CompressGZIP(file_pubsub_cluster_proto_rawDescData) + }) + return file_pubsub_cluster_proto_rawDescData +} + +var file_pubsub_cluster_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_pubsub_cluster_proto_goTypes = []interface{}{ + (*DataPublished)(nil), // 0: cluster_test_tool.DataPublished + (*Response)(nil), // 1: cluster_test_tool.Response +} +var file_pubsub_cluster_proto_depIdxs = []int32{ + 0, // [0:0] is the sub-list for method output_type + 0, // [0:0] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_pubsub_cluster_proto_init() } +func file_pubsub_cluster_proto_init() { + if File_pubsub_cluster_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_pubsub_cluster_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DataPublished); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pubsub_cluster_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Response); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_pubsub_cluster_proto_rawDesc, + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_pubsub_cluster_proto_goTypes, + DependencyIndexes: file_pubsub_cluster_proto_depIdxs, + MessageInfos: file_pubsub_cluster_proto_msgTypes, + }.Build() + File_pubsub_cluster_proto = out.File + file_pubsub_cluster_proto_rawDesc = nil + file_pubsub_cluster_proto_goTypes = nil + file_pubsub_cluster_proto_depIdxs = nil +} diff --git a/cluster/cluster_test_tool/pubsub_cluster.proto b/cluster/cluster_test_tool/pubsub_cluster.proto new file mode 100644 index 0000000000000000000000000000000000000000..73d59178ab080f3c274828de0a6164a11a592921 --- /dev/null +++ b/cluster/cluster_test_tool/pubsub_cluster.proto @@ -0,0 +1,9 @@ +syntax = "proto3"; +package cluster_test_tool; +option go_package = "/gitee.com/simplexyz/simpleactor-go/cluster/cluster_test_tool"; + +message DataPublished { + int32 data = 1; +} + +message Response {} diff --git a/cluster/cluster_test_tool/pubsub_cluster_fixture.go b/cluster/cluster_test_tool/pubsub_cluster_fixture.go new file mode 100644 index 0000000000000000000000000000000000000000..bea422779a9c27fd1c3007fb9ff2a1f5dd845561 --- /dev/null +++ b/cluster/cluster_test_tool/pubsub_cluster_fixture.go @@ -0,0 +1,236 @@ +package cluster_test_tool + +import ( + "errors" + "math/rand" + "strconv" + "sync" + "testing" + "time" + + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/cluster" + "github.com/stretchr/testify/assert" + "golang.org/x/net/context" +) + +const ( + PubSubSubscriberKind = "Subscriber" + PubSubTimeoutSubscriberKind = "TimeoutSubscriber" +) + +type PubSubClusterFixture struct { + *BaseClusterFixture + + useDefaultTopicRegistration bool + t testing.TB + + Deliveries []Delivery + DeliveriesLock *sync.RWMutex + + subscriberStore cluster.KeyValueStore[*cluster.Subscribers] +} + +func NewPubSubClusterFixture(t testing.TB, clusterSize int, useDefaultTopicRegistration bool, opts ...ClusterFixtureOption) *PubSubClusterFixture { + lock := &sync.RWMutex{} + store := NewInMemorySubscriberStore() + fixture := &PubSubClusterFixture{ + t: t, + useDefaultTopicRegistration: useDefaultTopicRegistration, + Deliveries: []Delivery{}, + DeliveriesLock: lock, + subscriberStore: store, + } + + pubSubOpts := []ClusterFixtureOption{ + WithGetClusterKinds(func() []*cluster.Kind { + kinds := []*cluster.Kind{ + cluster.NewKind(PubSubSubscriberKind, fixture.subscriberProps()), + cluster.NewKind(PubSubTimeoutSubscriberKind, fixture.timeoutSubscriberProps()), + } + if !fixture.useDefaultTopicRegistration { + kinds = append(kinds, cluster.NewKind(cluster.TopicActorKind, actor.PropsFromProducer(func() actor.Actor { + return cluster.NewTopicActor(store) + }))) + } + return kinds + }), + WithClusterConfigure(func(config *cluster.Config) *cluster.Config { + cluster.WithRequestTimeout(time.Second * 1)(config) + cluster.WithPubSubSubscriberTimeout(time.Second * 2)(config) + return config + }), + } + pubSubOpts = append(pubSubOpts, opts...) + + fixture.BaseClusterFixture = NewBaseInMemoryClusterFixture(clusterSize, pubSubOpts...) + return fixture +} + +func (p *PubSubClusterFixture) RandomMember() *cluster.Cluster { + members := p.BaseClusterFixture.GetMembers() + return members[rand.Intn(len(members))] +} + +// VerifyAllSubscribersGotAllTheData verifies that all subscribers got all the data +func (p *PubSubClusterFixture) VerifyAllSubscribersGotAllTheData(subscriberIds []string, numMessages int) { + WaitUntil(p.t, func() bool { + p.DeliveriesLock.RLock() + defer p.DeliveriesLock.RUnlock() + return len(p.Deliveries) == numMessages*len(subscriberIds) + }, "All messages should be delivered ", DefaultWaitTimeout*1000) + + p.DeliveriesLock.RLock() + defer p.DeliveriesLock.RUnlock() + + expected := make([]Delivery, 0, len(subscriberIds)) + for _, subscriberId := range subscriberIds { + for i := 0; i < numMessages; i++ { + expected = append(expected, Delivery{ + Identity: subscriberId, + Data: i, + }) + } + } + assert.ElementsMatch(p.t, expected, p.Deliveries) +} + +// SubscribeAllTo subscribes all the given subscribers to the given topic +func (p *PubSubClusterFixture) SubscribeAllTo(topic string, subscriberIds []string) { + for _, subscriberId := range subscriberIds { + p.SubscribeTo(topic, subscriberId, PubSubSubscriberKind) + } +} + +// UnSubscribeAllFrom unsubscribes all the given subscribers from the given topic +func (p *PubSubClusterFixture) UnSubscribeAllFrom(topic string, subscriberIds []string) { + for _, subscriberId := range subscriberIds { + p.UnSubscribeTo(topic, subscriberId, PubSubSubscriberKind) + } +} + +// SubscribeTo subscribes the given subscriber to the given topic +func (p *PubSubClusterFixture) SubscribeTo(topic, identity, kind string) { + c := p.RandomMember() + res, err := c.SubscribeByClusterIdentity(topic, cluster.NewClusterIdentity(identity, kind), cluster.WithTimeout(time.Second*5)) + assert.NoError(p.t, err, kind+"/"+identity+" should be able to subscribe to topic "+topic) + assert.NotNil(p.t, res, kind+"/"+identity+" subscribing should not time out on topic "+topic) +} + +// UnSubscribeTo unsubscribes the given subscriber from the given topic +func (p *PubSubClusterFixture) UnSubscribeTo(topic, identity, kind string) { + c := p.RandomMember() + res, err := c.UnsubscribeByClusterIdentity(topic, cluster.NewClusterIdentity(identity, kind), cluster.WithTimeout(time.Second*5)) + assert.NoError(p.t, err, kind+"/"+identity+" should be able to unsubscribe from topic "+topic) + assert.NotNil(p.t, res, kind+"/"+identity+" subscribing should not time out on topic "+topic) +} + +// PublishData publishes the given message to the given topic +func (p *PubSubClusterFixture) PublishData(topic string, data int) (*cluster.PublishResponse, error) { + c := p.RandomMember() + return c.Publisher().Publish(context.Background(), topic, &DataPublished{Data: int32(data)}, cluster.WithTimeout(time.Second*5)) +} + +// PublishDataBatch publishes the given messages to the given topic +func (p *PubSubClusterFixture) PublishDataBatch(topic string, data []int) (*cluster.PublishResponse, error) { + batches := make([]interface{}, 0) + for _, d := range data { + batches = append(batches, &DataPublished{Data: int32(d)}) + } + + c := p.RandomMember() + return c.Publisher().PublishBatch(context.Background(), topic, &cluster.PubSubBatch{Envelopes: batches}, cluster.WithTimeout(time.Second*5)) +} + +// SubscriberIds returns the subscriber ids +func (p *PubSubClusterFixture) SubscriberIds(prefix string, count int) []string { + ids := make([]string, 0, count) + for i := 0; i < count; i++ { + ids = append(ids, prefix+strconv.Itoa(i)) + } + return ids +} + +// GetSubscribersForTopic returns the subscribers for the given topic +func (p *PubSubClusterFixture) GetSubscribersForTopic(topic string) (*cluster.Subscribers, error) { + return p.subscriberStore.Get(context.Background(), topic) +} + +// ClearDeliveries clears the deliveries +func (p *PubSubClusterFixture) ClearDeliveries() { + p.DeliveriesLock.Lock() + defer p.DeliveriesLock.Unlock() + p.Deliveries = make([]Delivery, 0) +} + +// subscriberProps returns the props for the subscriber actor +func (p *PubSubClusterFixture) subscriberProps() *actor.Props { + return actor.PropsFromFunc(func(context actor.Context) { + if msg, ok := context.Message().(*DataPublished); ok { + identity := cluster.GetClusterIdentity(context) + + p.AppendDelivery(Delivery{ + Identity: identity.Identity, + Data: int(msg.Data), + }) + context.Respond(&Response{}) + } + }) +} + +// timeoutSubscriberProps returns the props for the subscriber actor +func (p *PubSubClusterFixture) timeoutSubscriberProps() *actor.Props { + return actor.PropsFromFunc(func(context actor.Context) { + if msg, ok := context.Message().(*DataPublished); ok { + time.Sleep(time.Second * 4) // 4 seconds is longer than the configured subscriber timeout + + identity := cluster.GetClusterIdentity(context) + p.AppendDelivery(Delivery{ + Identity: identity.Identity, + Data: int(msg.Data), + }) + context.Respond(&Response{}) + } + }) +} + +// AppendDelivery appends a delivery to the deliveries slice +func (p *PubSubClusterFixture) AppendDelivery(delivery Delivery) { + p.DeliveriesLock.Lock() + p.Deliveries = append(p.Deliveries, delivery) + p.DeliveriesLock.Unlock() +} + +type Delivery struct { + Identity string + Data int +} + +func NewInMemorySubscriberStore() *InMemorySubscribersStore[*cluster.Subscribers] { + return &InMemorySubscribersStore[*cluster.Subscribers]{ + store: &sync.Map{}, + } +} + +type InMemorySubscribersStore[T any] struct { + store *sync.Map // map[string]T +} + +func (i *InMemorySubscribersStore[T]) Set(_ context.Context, key string, value T) error { + i.store.Store(key, value) + return nil +} + +func (i *InMemorySubscribersStore[T]) Get(_ context.Context, key string) (T, error) { + var r T + value, ok := i.store.Load(key) + if !ok { + return r, errors.New("not found") + } + return value.(T), nil +} + +func (i *InMemorySubscribersStore[T]) Clear(_ context.Context, key string) error { + i.store.Delete(key) + return nil +} diff --git a/cluster/cluster_test_tool/pubsub_default_registration_test.go b/cluster/cluster_test_tool/pubsub_default_registration_test.go new file mode 100644 index 0000000000000000000000000000000000000000..51b9ef281c256244a8f7e20420f58acf4df4781e --- /dev/null +++ b/cluster/cluster_test_tool/pubsub_default_registration_test.go @@ -0,0 +1,44 @@ +package cluster_test_tool + +import ( + "strconv" + "testing" + + "github.com/stretchr/testify/suite" +) + +type PubSubDefaultRegistrationTestSuite struct { + suite.Suite + fixture *PubSubClusterFixture +} + +func (suite *PubSubDefaultRegistrationTestSuite) SetupTest() { + suite.fixture = NewPubSubClusterFixture(suite.T(), 1, true) + suite.fixture.Initialize() +} + +func (suite *PubSubDefaultRegistrationTestSuite) TearDownTest() { + suite.fixture.ShutDown() +} + +func (suite *PubSubDefaultRegistrationTestSuite) TestPubSubWorksWithDefaultTopicRegistration() { + subscriberIds := suite.fixture.SubscriberIds("topic-default", 20) + const topic = "topic-default-registration" + const numMessage = 100 + + suite.fixture.SubscribeAllTo(topic, subscriberIds) + + for i := 0; i < numMessage; i++ { + data, err := suite.fixture.PublishData(topic, i) + suite.Assert().NoError(err, "message "+strconv.Itoa(i)+" should not has error") + suite.Assert().NotNil(data, "response "+strconv.Itoa(i)+" should not be nil") + } + + suite.fixture.VerifyAllSubscribersGotAllTheData(subscriberIds, numMessage) +} + +// In order for 'go test' to run this suite, we need to create +// a normal test function and pass our suite to suite.Run +func TestPubSubDefaultRegistrationTestSuite(t *testing.T) { + suite.Run(t, new(PubSubDefaultRegistrationTestSuite)) +} diff --git a/cluster/cluster_test_tool/pubsub_member_test.go b/cluster/cluster_test_tool/pubsub_member_test.go new file mode 100644 index 0000000000000000000000000000000000000000..84460512835485c79223bdf1665edefaf19c4e1e --- /dev/null +++ b/cluster/cluster_test_tool/pubsub_member_test.go @@ -0,0 +1,107 @@ +package cluster_test_tool + +import ( + "testing" + + "gitee.com/simplexyz/simpleactor-go/actor" + "github.com/stretchr/testify/suite" +) + +type PubSubMemberTestSuite struct { + suite.Suite + fixture *PubSubClusterFixture +} + +func (suite *PubSubMemberTestSuite) SetupTest() { + suite.fixture = NewPubSubClusterFixture(suite.T(), 3, false) + suite.fixture.Initialize() +} + +func (suite *PubSubMemberTestSuite) TestWhenMemberLeavesPidSubscribersGetRemovedFromTheSubscriberList() { + const topic = "leaving-member" + + props := actor.PropsFromFunc(func(context actor.Context) { + if msg, ok := context.Message().(*DataPublished); ok { + suite.fixture.AppendDelivery(Delivery{Identity: context.Self().String(), Data: int(msg.Data)}) + } + }) + // spawn on members + members := suite.fixture.GetMembers() + leavingMember := members[0] + leavingPid := leavingMember.ActorSystem.Root.Spawn(props) + stayingMember := members[len(members)-1] + stayingPid := stayingMember.ActorSystem.Root.Spawn(props) + + // subscribe by pids + _, err := leavingMember.SubscribeByPid(topic, leavingPid) + suite.Assert().NoError(err) + _, err = stayingMember.SubscribeByPid(topic, stayingPid) + suite.Assert().NoError(err) + + // to spice things up, also subscribe virtual actors + subscribeIds := suite.fixture.SubscriberIds("leaving", 20) + suite.fixture.SubscribeAllTo(topic, subscribeIds) + + // publish data + _, err = suite.fixture.PublishData(topic, 1) + suite.Assert().NoError(err) + + // everyone should have received the data + WaitUntil(suite.T(), func() bool { + suite.fixture.DeliveriesLock.RLock() + defer suite.fixture.DeliveriesLock.RUnlock() + return len(suite.fixture.Deliveries) == len(subscribeIds)+2 + }, "all subscribers should have received the data", DefaultWaitTimeout) + + suite.fixture.DeliveriesLock.RLock() + suite.Assert().Equal(len(subscribeIds)+2, len(suite.fixture.Deliveries)) + suite.fixture.DeliveriesLock.RUnlock() + + suite.fixture.RemoveNode(leavingMember, true) + + WaitUntil(suite.T(), func() bool { + blockedOnlyOne := true + for _, member := range suite.fixture.GetMembers() { + blockList := member.Remote.BlockList() + blockedOnlyOne = blockedOnlyOne && blockList.Len() == 1 + } + return blockedOnlyOne + }, "Member should leave cluster", DefaultWaitTimeout) + + suite.fixture.ClearDeliveries() + _, err = suite.fixture.PublishData(topic, 2) + suite.Assert().NoError(err) + + // the failure in delivery caused topic actor to remove subscribers from the member that left + // next publish should succeed and deliver to remaining subscribers + WaitUntil(suite.T(), func() bool { + suite.fixture.DeliveriesLock.RLock() + defer suite.fixture.DeliveriesLock.RUnlock() + return len(suite.fixture.Deliveries) == len(subscribeIds)+1 + }, "All subscribers apart the one that left should get the message", DefaultWaitTimeout) + + WaitUntil(suite.T(), func() bool { + subscribers, err := suite.fixture.GetSubscribersForTopic(topic) + suite.Assert().NoError(err) + + dontContainLeavingMember := true + for _, subscriber := range subscribers.Subscribers { + pid := subscriber.GetPid() + if pid != nil && pid.Address == leavingPid.Address && pid.Id == leavingPid.Id { + dontContainLeavingMember = false + break + } + } + return dontContainLeavingMember + }, "Subscriber that left should be removed from subscribers list", DefaultWaitTimeout) +} + +func (suite *PubSubMemberTestSuite) TearDownTest() { + suite.fixture.ShutDown() +} + +// In order for 'go test' to run this suite, we need to create +// a normal test function and pass our suite to suite.Run +func TestPubSubMemberTestSuite(t *testing.T) { + suite.Run(t, new(PubSubMemberTestSuite)) +} diff --git a/cluster/cluster_test_tool/pubsub_test.go b/cluster/cluster_test_tool/pubsub_test.go new file mode 100644 index 0000000000000000000000000000000000000000..09242d25af0846e4de90d8d35210807bf35de4a4 --- /dev/null +++ b/cluster/cluster_test_tool/pubsub_test.go @@ -0,0 +1,337 @@ +package cluster_test_tool + +import ( + "context" + "strconv" + "sync" + "sync/atomic" + "testing" + "time" + + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/cluster" + "github.com/stretchr/testify/suite" +) + +type PubSubTestSuite struct { + suite.Suite + fixture *PubSubClusterFixture +} + +func (suite *PubSubTestSuite) SetupTest() { + suite.fixture = NewPubSubClusterFixture(suite.T(), 2, false) + suite.fixture.Initialize() +} + +func (suite *PubSubTestSuite) TearDownTest() { + suite.fixture.ShutDown() +} + +func (suite *PubSubTestSuite) TestCanDeliverSingleMessages() { + subscriberIds := suite.fixture.SubscriberIds("single-test", 20) + const topic = "single-test-topic" + const numMessages = 100 + + suite.fixture.SubscribeAllTo(topic, subscriberIds) + + for i := 0; i < numMessages; i++ { + data, err := suite.fixture.PublishData(topic, i) + suite.Assert().NoError(err, "message "+strconv.Itoa(i)+" should not has error") + suite.Assert().NotNil(data, "response "+strconv.Itoa(i)+" should not be nil") + } + + suite.fixture.VerifyAllSubscribersGotAllTheData(subscriberIds, numMessages) +} + +func (suite *PubSubTestSuite) TestCanDeliverMessageBatches() { + subscriberIds := suite.fixture.SubscriberIds("batch-test", 20) + const topic = "batch-test-topic" + const numMessages = 100 + + suite.fixture.SubscribeAllTo(topic, subscriberIds) + + for i := 0; i < numMessages/10; i++ { + data := intRange(i*10, 10) + batch, err := suite.fixture.PublishDataBatch(topic, data) + suite.Assert().NoError(err, "message "+strconv.Itoa(i)+" should not has error") + suite.Assert().NotNil(batch, "response "+strconv.Itoa(i)+" should not be nil") + } + suite.fixture.VerifyAllSubscribersGotAllTheData(subscriberIds, numMessages) +} + +func (suite *PubSubTestSuite) TestUnsubscribedActorDoesNotReceiveMessages() { + const sub1 = "unsubscribe-test-1" + const sub2 = "unsubscribe-test-2" + const topic = "unsubscribe-test" + + suite.fixture.SubscribeTo(topic, sub1, PubSubSubscriberKind) + suite.fixture.SubscribeTo(topic, sub2, PubSubSubscriberKind) + + suite.fixture.UnSubscribeTo(topic, sub2, PubSubSubscriberKind) + + _, err := suite.fixture.PublishData(topic, 1) + suite.Assert().NoError(err, "PublishData should not has error") + + time.Sleep(time.Second * 1) // give time for the message "not to be delivered" to second subscriber + WaitUntil(suite.T(), func() bool { + suite.fixture.DeliveriesLock.RLock() + defer suite.fixture.DeliveriesLock.RUnlock() + return len(suite.fixture.Deliveries) == 1 + }, "only one delivery should happen because the other actor is unsubscribed", DefaultWaitTimeout) + + suite.fixture.DeliveriesLock.RLock() + defer suite.fixture.DeliveriesLock.RUnlock() + suite.Assert().Len(suite.fixture.Deliveries, 1, "only one delivery should happen because the other actor is unsubscribed") + suite.Assert().Equal(sub1, suite.fixture.Deliveries[0].Identity, "the other actor should be unsubscribed") +} + +func (suite *PubSubTestSuite) TestCanSubscribeWithPid() { + const topic = "pid-subscribe" + + var deliveredMessage *DataPublished + + props := actor.PropsFromFunc(func(context actor.Context) { + switch msg := context.Message().(type) { + case *DataPublished: + deliveredMessage = msg + } + }) + member := suite.fixture.GetMembers()[0] + pid := member.ActorSystem.Root.Spawn(props) + _, err := member.SubscribeByPid(topic, pid) + suite.Assert().NoError(err, "SubscribeByPid should not has error") + + _, err = suite.fixture.PublishData(topic, 1) + suite.Assert().NoError(err, "PublishData should not has error") + + WaitUntil(suite.T(), func() bool { + return deliveredMessage != nil + }, "message should be delivered", DefaultWaitTimeout) + suite.Assert().EqualValues(1, deliveredMessage.Data) +} + +func (suite *PubSubTestSuite) TestCanUnsubscribeWithPid() { + const topic = "pid-unsubscribe" + + var deliveryCount int32 = 0 + + props := actor.PropsFromFunc(func(context actor.Context) { + switch context.Message().(type) { + case *DataPublished: + atomic.AddInt32(&deliveryCount, 1) + } + }) + member := suite.fixture.GetMembers()[0] + pid := member.ActorSystem.Root.Spawn(props) + _, err := member.SubscribeByPid(topic, pid) + suite.Assert().NoError(err, "SubscribeByPid should not has error") + + _, err = member.UnsubscribeByPid(topic, pid) + suite.Assert().NoError(err, "UnsubscribeByPid should not has error") + + _, err = suite.fixture.PublishData(topic, 1) + suite.Assert().NoError(err, "PublishData should not has error") + + time.Sleep(time.Second * 1) // give time for the message "not to be delivered" to second subscriber + suite.Assert().EqualValues(0, deliveryCount, "message should not be delivered") +} + +func (suite *PubSubTestSuite) TestStoppedActorThatDidNotUnsubscribeDoesNotBlockPublishingToTopic() { + const topic = "missing-unsubscribe" + var deliveryCount int32 = 0 + + // this scenario is only relevant for regular actors, + // virtual actors always exist, so the msgs should never be deadlettered + props := actor.PropsFromFunc(func(context actor.Context) { + switch context.Message().(type) { + case *DataPublished: + atomic.AddInt32(&deliveryCount, 1) + } + }) + member := suite.fixture.GetMembers()[0] + pid1 := member.ActorSystem.Root.Spawn(props) + pid2 := member.ActorSystem.Root.Spawn(props) + + // spawn two actors and subscribe them to the topic + _, err := member.SubscribeByPid(topic, pid1) + suite.Assert().NoError(err, "SubscribeByPid1 should not has error") + _, err = member.SubscribeByPid(topic, pid2) + suite.Assert().NoError(err, "SubscribeByPid2 should not has error") + + // publish one message + _, err = suite.fixture.PublishData(topic, 1) + suite.Assert().NoError(err, "PublishData should not has error") + + WaitUntil(suite.T(), func() bool { + return atomic.LoadInt32(&deliveryCount) == 2 + }, "both messages should be delivered", DefaultWaitTimeout) + + // kill one of the actors + member.ActorSystem.Root.Stop(pid2) + + // publish again + _, err = suite.fixture.PublishData(topic, 2) + suite.Assert().NoError(err, "PublishData should not has error") + + WaitUntil(suite.T(), func() bool { + return atomic.LoadInt32(&deliveryCount) == 3 + }, "second publish should be delivered only to one of the actors", DefaultWaitTimeout) + + WaitUntil(suite.T(), func() bool { + subscribers, err := suite.fixture.GetSubscribersForTopic(topic) + suite.Assert().NoError(err, "GetSubscribersForTopic should not has error") + + hasPid2 := false + for _, subscriber := range subscribers.Subscribers { + if subscriber.GetPid() != nil && + subscriber.GetPid().Id == pid2.Id && + subscriber.GetPid().Address == pid2.Address { + hasPid2 = true + break + } + } + return !hasPid2 + }, "pid2 should be removed from subscriber store", DefaultWaitTimeout*1000) +} + +func (suite *PubSubTestSuite) TestSlowPidSubscriberThatTimesOutDoesNotPreventSubsequentPublishes() { + const topic = "slow-pid-subscriber" + var deliveryCount int32 = 0 + + // a slow subscriber that will timeout + props := actor.PropsFromFunc(func(context actor.Context) { + time.Sleep(time.Second * 4) + atomic.AddInt32(&deliveryCount, 1) + }) + + member := suite.fixture.RandomMember() + pid := member.ActorSystem.Root.Spawn(props) + _, err := member.SubscribeByPid(topic, pid) + suite.Assert().NoError(err, "SubscribeByPid should not has error") + + // publish one message + _, err = suite.fixture.PublishData(topic, 1) + suite.Assert().NoError(err, "PublishData should not has error") + + // next published message should also be delivered + _, err = suite.fixture.PublishData(topic, 1) + suite.Assert().NoError(err, "PublishData should not has error") + + WaitUntil(suite.T(), func() bool { + return atomic.LoadInt32(&deliveryCount) == 2 + }, "A timing out subscriber should not prevent subsequent publishes", time.Second*10) +} + +func (suite *PubSubTestSuite) TestSlowClusterIdentitySubscriberThatTimesOutDoesNotPreventSubsequentPublishes() { + const topic = "slow-ci-subscriber" + suite.fixture.SubscribeTo(topic, "slow-ci-1", PubSubTimeoutSubscriberKind) + + // publish one message + _, err := suite.fixture.PublishData(topic, 1) + suite.Assert().NoError(err, "PublishData1 should not has error") + + // next published message should also be delivered + _, err = suite.fixture.PublishData(topic, 1) + suite.Assert().NoError(err, "PublishData2 should not has error") + + WaitUntil(suite.T(), func() bool { + suite.fixture.DeliveriesLock.RLock() + defer suite.fixture.DeliveriesLock.RUnlock() + + return len(suite.fixture.Deliveries) == 2 + }, "A timing out subscriber should not prevent subsequent publishes", time.Second*10) +} + +func (suite *PubSubTestSuite) TestCanPublishMessagesViaBatchingProducer() { + subscriberIds := suite.fixture.SubscriberIds("batching-producer-test", 20) + const topic = "batching-producer" + const numMessages = 100 + + suite.fixture.SubscribeAllTo(topic, subscriberIds) + + producer := suite.fixture.GetMembers()[0].BatchingProducer(topic, cluster.WithBatchingProducerBatchSize(10)) + defer producer.Dispose() + + wg := sync.WaitGroup{} + for i := 0; i < numMessages; i++ { + wg.Add(1) + go func(i int) { + defer wg.Done() + _, err := producer.Produce(context.Background(), &DataPublished{Data: int32(i)}) + suite.Assert().NoError(err, "Produce should not has error") + }(i) + } + wg.Wait() + + suite.fixture.VerifyAllSubscribersGotAllTheData(subscriberIds, numMessages) +} + +func (suite *PubSubTestSuite) TestCanPublishMessagesViaBatchingProducerWithCustomQueue() { + subscriberIds := suite.fixture.SubscriberIds("batching-producer-test-with-chan", 20) + const topic = "batching-producer-with-chan" + const numMessages = 100 + + suite.fixture.SubscribeAllTo(topic, subscriberIds) + + producer := suite.fixture.GetMembers()[0].BatchingProducer(topic, cluster.WithBatchingProducerBatchSize(10), cluster.WithBatchingProducerMaxQueueSize(2000)) + defer producer.Dispose() + + wg := sync.WaitGroup{} + for i := 0; i < numMessages; i++ { + wg.Add(1) + go func(i int) { + defer wg.Done() + _, err := producer.Produce(context.Background(), &DataPublished{Data: int32(i)}) + suite.Assert().NoError(err, "Produce should not has error") + }(i) + } + wg.Wait() + + suite.fixture.VerifyAllSubscribersGotAllTheData(subscriberIds, numMessages) +} + +func (suite *PubSubTestSuite) TestWillExpireTopicActorAfterIdle() { + subscriberIds := suite.fixture.SubscriberIds("batching-producer-idl-test", 20) + const topic = "batching-producer" + const numMessages = 100 + + suite.fixture.SubscribeAllTo(topic, subscriberIds) + + firstCluster := suite.fixture.GetMembers()[0] + + producer := firstCluster.BatchingProducer(topic, cluster.WithBatchingProducerPublisherIdleTimeout(time.Second*2)) + defer producer.Dispose() + + wg := sync.WaitGroup{} + for i := 0; i < numMessages; i++ { + wg.Add(1) + go func(i int) { + defer wg.Done() + _, err := producer.Produce(context.Background(), &DataPublished{Data: int32(i)}) + suite.Assert().NoError(err, "Produce should not has error") + }(i) + } + wg.Wait() + + pid := firstCluster.Get(topic, cluster.TopicActorKind) + suite.Assert().NotNil(pid, "Topic actor should not be nil") + + time.Sleep(time.Second * 5) + + newPid := firstCluster.Get(topic, cluster.TopicActorKind) + suite.Assert().NotEqual(pid.String(), newPid.String(), "Topic actor should be recreated") +} + +// In order for 'go test' to run this suite, we need to create +// a normal test function and pass our suite to suite.Run +func TestPubSubTestSuite(t *testing.T) { + suite.Run(t, new(PubSubTestSuite)) +} + +func intRange(start int, count int) []int { + res := make([]int, count) + for i := 0; i < count; i++ { + res[i] = start + i + } + return res +} diff --git a/cluster/cluster_test_tool/wait_helper.go b/cluster/cluster_test_tool/wait_helper.go new file mode 100644 index 0000000000000000000000000000000000000000..058bcb98279ce7d87ba30c2f58255b09d953a392 --- /dev/null +++ b/cluster/cluster_test_tool/wait_helper.go @@ -0,0 +1,26 @@ +package cluster_test_tool + +import ( + "runtime/debug" + "testing" + "time" +) + +const DefaultWaitTimeout = time.Second * 5 + +func WaitUntil(t testing.TB, cond func() bool, errorMsg string, timeout time.Duration) { + after := time.After(timeout) + + for { + select { + case <-after: + t.Error(errorMsg) + debug.PrintStack() + default: + if cond() { + return + } + time.Sleep(100 * time.Millisecond) + } + } +} diff --git a/cluster/clusterproviders/automanaged/automanaged.go b/cluster/clusterproviders/automanaged/automanaged.go new file mode 100644 index 0000000000000000000000000000000000000000..02bdf35867cee8aa3ecdfeed9661d92ec4539cc2 --- /dev/null +++ b/cluster/clusterproviders/automanaged/automanaged.go @@ -0,0 +1,384 @@ +package automanaged + +import ( + "encoding/json" + "fmt" + "net" + "net/http" + "sync" + "time" + + "golang.org/x/net/context" + + "gitee.com/simplexyz/simpleactor-go/cluster" + "gitee.com/simplexyz/simpleactor-go/log" + "github.com/labstack/echo" + "golang.org/x/sync/errgroup" +) + +// TODO: needs to be attached to the provider instance +var ( + clusterTTLErrorMutex = new(sync.Mutex) + clusterMonitorErrorMutex = new(sync.Mutex) + shutdownMutex = new(sync.Mutex) + deregisteredMutex = new(sync.Mutex) + activeProviderMutex = new(sync.Mutex) + activeProviderRunningMutex = new(sync.Mutex) +) + +type AutoManagedProvider struct { + deregistered bool + shutdown bool + activeProvider *echo.Echo + activeProviderRunning bool + activeProviderTesting bool + httpClient *http.Client + monitoringStatus bool + clusterName string + address string + autoManagePort int + memberPort int + knownKinds []string + knownNodes []*NodeModel + hosts []string + refreshTTL time.Duration + clusterTTLError error + clusterMonitorError error + cluster *cluster.Cluster +} + +// New creates a AutoManagedProvider that connects locally +func New() *AutoManagedProvider { + return NewWithConfig( + 2*time.Second, + 6330, + "localhost:6330", + ) +} + +// NewWithConfig creates an Automanaged Provider that connects to an all the hosts +func NewWithConfig(refreshTTL time.Duration, autoManPort int, hosts ...string) *AutoManagedProvider { + transport := &http.Transport{ + Proxy: http.ProxyFromEnvironment, + DialContext: (&net.Dialer{ + Timeout: 5 * time.Second, + KeepAlive: 5 * time.Second, + }).DialContext, + MaxIdleConns: 10, + IdleConnTimeout: 90 * time.Second, + ExpectContinueTimeout: 1 * time.Second, + MaxConnsPerHost: 10, + } + + httpClient := &http.Client{ + Transport: transport, + Timeout: 2 * time.Second, + } + + p := &AutoManagedProvider{ + hosts: hosts, + httpClient: httpClient, + refreshTTL: refreshTTL, + autoManagePort: autoManPort, + activeProviderRunning: false, + monitoringStatus: false, + } + + return p +} + +// NewWithTesting creates a testable provider +func NewWithTesting(refreshTTL time.Duration, autoManPort int, activeProvider *echo.Echo, hosts ...string) *AutoManagedProvider { + p := NewWithConfig(refreshTTL, autoManPort, hosts...) + p.activeProviderTesting = true + p.activeProvider = activeProvider + return p +} + +func (p *AutoManagedProvider) init(cluster *cluster.Cluster) error { + host, port, err := cluster.ActorSystem.GetHostPort() + if err != nil { + return err + } + + p.clusterName = cluster.Config.Name + p.address = host + p.memberPort = port + p.knownKinds = cluster.GetClusterKinds() + p.deregistered = false + p.shutdown = false + p.cluster = cluster + return nil +} + +func (p *AutoManagedProvider) StartMember(cluster *cluster.Cluster) error { + if err := p.init(cluster); err != nil { + return err + } + p.UpdateTTL() + p.monitorMemberStatusChanges() + return nil +} + +func (p *AutoManagedProvider) StartClient(cluster *cluster.Cluster) error { + if err := p.init(cluster); err != nil { + return err + } + // p.UpdateTTL() + p.monitorMemberStatusChanges() + return nil +} + +// DeregisterMember set the shutdown to true preventing anymore TTL updates +func (p *AutoManagedProvider) DeregisterMember() error { + deregisteredMutex.Lock() + defer deregisteredMutex.Unlock() + + p.deregistered = true + return nil +} + +// Shutdown set the shutdown to true preventing anymore TTL updates +func (p *AutoManagedProvider) Shutdown(graceful bool) error { + shutdownMutex.Lock() + defer shutdownMutex.Unlock() + + p.shutdown = true + p.activeProvider.Close() + return nil +} + +// UpdateTTL sets up an endpoint to respond to other members +func (p *AutoManagedProvider) UpdateTTL() { + activeProviderRunningMutex.Lock() + running := p.activeProviderRunning + activeProviderRunningMutex.Unlock() + + if (p.isShutdown() || p.isDeregistered()) && running { + p.activeProvider.Close() + return + } + + if running { + return + } + + // it's not running, and it's not shutdown or de-registered + // it's also not a test (this should be refactored) + + if !p.activeProviderTesting { + p.activeProvider = echo.New() + p.activeProvider.HideBanner = true + p.activeProvider.GET("/_health", func(context echo.Context) error { + return context.JSON(http.StatusOK, p.getCurrentNode()) + }) + } + go func() { + activeProviderRunningMutex.Lock() + p.activeProviderRunning = true + activeProviderRunningMutex.Unlock() + + appURI := fmt.Sprintf("0.0.0.0:%d", p.autoManagePort) + plog.Error("Automanaged server stopping..!", log.Error(p.activeProvider.Start(appURI))) + + activeProviderRunningMutex.Lock() + p.activeProviderRunning = false + activeProviderRunningMutex.Unlock() + }() +} + +// MonitorMemberStatusChanges creates a go routine that continuously checks other members +func (p *AutoManagedProvider) monitorMemberStatusChanges() { + if !p.monitoringStatus { + go func() { + for !p.isShutdown() && !p.isDeregistered() { + p.monitorStatuses() + } + }() + } + p.monitoringStatus = true +} + +// GetHealthStatus returns an error if the cluster health status has problems +func (p *AutoManagedProvider) GetHealthStatus() error { + var err error + clusterTTLErrorMutex.Lock() + clusterMonitorErrorMutex.Lock() + defer clusterMonitorErrorMutex.Unlock() + defer clusterTTLErrorMutex.Unlock() + + if p.clusterTTLError != nil { + err = fmt.Errorf("TTL: %s", p.clusterTTLError.Error()) + } + + if p.clusterMonitorError != nil { + if err != nil { + err = fmt.Errorf("%s - Monitor: %s", err.Error(), p.clusterMonitorError.Error()) + } else { + err = fmt.Errorf("monitor: %s", p.clusterMonitorError.Error()) + } + } + + return err +} + +// +// Private methods +// + +// monitorStatuses checks for node changes in the cluster +func (p *AutoManagedProvider) monitorStatuses() { + clusterMonitorErrorMutex.Lock() + defer clusterMonitorErrorMutex.Unlock() + + autoManagedNodes, err := p.checkNodes() + if err != nil && len(autoManagedNodes) == 0 { + plog.Error("Failure reaching nodes", log.Error(err)) + p.clusterMonitorError = err + time.Sleep(p.refreshTTL) + return + } + // we should probably check if the cluster needs to be updated. + var members []*cluster.Member + var newNodes []*NodeModel + for _, node := range autoManagedNodes { + if node == nil || node.ClusterName != p.clusterName { + continue + } + ms := &cluster.Member{ + Id: node.ID, + Host: node.Address, + Port: int32(node.Port), + Kinds: node.Kinds, + } + members = append(members, ms) + newNodes = append(newNodes, node) + } + + p.knownNodes = newNodes + p.clusterMonitorError = nil + // publish the current cluster topology onto the event stream + p.cluster.MemberList.UpdateClusterTopology(members) + time.Sleep(p.refreshTTL) +} + +// checkNodes pings all the nodes and returns the new cluster topology +func (p *AutoManagedProvider) checkNodes() ([]*NodeModel, error) { + allNodes := make([]*NodeModel, len(p.hosts)) + g, _ := errgroup.WithContext(context.Background()) + + for indice, nodeHost := range p.hosts { + idx, el := indice, nodeHost // https://golang.org/doc/faq#closures_and_goroutines + + // Calling go funcs to execute the node check + g.Go(func() error { + url := fmt.Sprintf("http://%s/_health", el) + req, err := http.NewRequest("GET", url, nil) + if err != nil { + plog.Error("Couldn't request node health status", log.Error(err), log.String("autoManMemberUrl", url)) + return err + } + + resp, err := p.httpClient.Do(req) + if err != nil { + plog.Error("Bad connection to the node health status", log.Error(err), log.String("autoManMemberUrl", url)) + return err + } + + defer resp.Body.Close() // nolint: errcheck + + if resp.StatusCode != http.StatusOK { + err = fmt.Errorf("non 200 status returned: %d - from node: %s", resp.StatusCode, el) + plog.Error("Bad response from the node health status", log.Error(err), log.String("autoManMemberUrl", url)) + return err + } + + var node *NodeModel + err = json.NewDecoder(resp.Body).Decode(&node) + if err != nil { + err = fmt.Errorf("could not deserialize response: %v - from node: %s", resp, el) + plog.Error("Bad data from the node health status", log.Error(err), log.String("autoManMemberUrl", url)) + return err + } + + allNodes[idx] = node + return nil + }) + } + + // waits until all functions have returned + err := g.Wait() + var retNodes []*NodeModel + + // clear out the nil ones + for _, node := range allNodes { + if node != nil { + retNodes = append(retNodes, node) + } + } + + return retNodes, err +} + +func (p *AutoManagedProvider) deregisterService() { + deregisteredMutex.Lock() + defer deregisteredMutex.Unlock() + + p.deregistered = true +} + +func (p *AutoManagedProvider) startActiveProvider() { + activeProviderRunningMutex.Lock() + running := p.activeProviderRunning + activeProviderRunningMutex.Unlock() + + if !running { + if !p.activeProviderTesting { + p.activeProvider = echo.New() + p.activeProvider.HideBanner = true + p.activeProvider.GET("/_health", func(context echo.Context) error { + return context.JSON(http.StatusOK, p.getCurrentNode()) + }) + } + + appURI := fmt.Sprintf("0.0.0.0:%d", p.autoManagePort) + + go func() { + activeProviderRunningMutex.Lock() + p.activeProviderRunning = true + activeProviderRunningMutex.Unlock() + + plog.Error("Automanaged server stopping..!", log.Error(p.activeProvider.Start(appURI))) + + activeProviderRunningMutex.Lock() + p.activeProviderRunning = false + activeProviderRunningMutex.Unlock() + }() + } +} + +func (p *AutoManagedProvider) stopActiveProvider() { + p.activeProvider.Close() +} + +func (p *AutoManagedProvider) isShutdown() bool { + shutdownMutex.Lock() + defer shutdownMutex.Unlock() + return p.shutdown +} + +func (p *AutoManagedProvider) isDeregistered() bool { + deregisteredMutex.Lock() + defer deregisteredMutex.Unlock() + return p.deregistered +} + +func (p *AutoManagedProvider) isActiveProviderRunning() bool { + activeProviderRunningMutex.Lock() + defer activeProviderRunningMutex.Unlock() + return p.activeProviderRunning +} + +func (p *AutoManagedProvider) getCurrentNode() *NodeModel { + return NewNode(p.clusterName, p.cluster.ActorSystem.ID, p.address, p.memberPort, p.autoManagePort, p.knownKinds) +} diff --git a/cluster/clusterproviders/automanaged/automanaged_test.go b/cluster/clusterproviders/automanaged/automanaged_test.go new file mode 100644 index 0000000000000000000000000000000000000000..a729533473ec12ec39c0ddd020c1924f470ba56a --- /dev/null +++ b/cluster/clusterproviders/automanaged/automanaged_test.go @@ -0,0 +1 @@ +package automanaged diff --git a/cluster/clusterproviders/automanaged/log.go b/cluster/clusterproviders/automanaged/log.go new file mode 100644 index 0000000000000000000000000000000000000000..33cc82640c1ecbb89ae55f38508e446fbdccd7bd --- /dev/null +++ b/cluster/clusterproviders/automanaged/log.go @@ -0,0 +1,11 @@ +package automanaged + +import "gitee.com/simplexyz/simpleactor-go/log" + +var plog = log.New(log.DebugLevel, "[AUTOMANAGED]") + +// SetLogLevel sets the log level for the logger +// SetLogLevel is safe to be called concurrently +func SetLogLevel(level log.Level) { + plog.SetLevel(level) +} diff --git a/cluster/clusterproviders/automanaged/member_list_broadcast_test.go b/cluster/clusterproviders/automanaged/member_list_broadcast_test.go new file mode 100644 index 0000000000000000000000000000000000000000..4ecb342b0a465cff6e1ee135416ed327a67aae8e --- /dev/null +++ b/cluster/clusterproviders/automanaged/member_list_broadcast_test.go @@ -0,0 +1,75 @@ +package automanaged + +import ( + "testing" + "time" + + "gitee.com/simplexyz/simpleactor-go/cluster/identitylookup/disthash" + + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/cluster" + "gitee.com/simplexyz/simpleactor-go/remote" + "github.com/stretchr/testify/assert" +) + +func TestMemberList_Broadcast(t *testing.T) { + c := startNode() + defer c.Shutdown(true) + + var receivedEvent *cluster.GrainRequest + + for i := 1; i < 20; i++ { // retry several times as we don't know when the cluster will be ready + var ok bool + if receivedEvent, ok = trySendAndReceiveMessage(t, c, 0xBEEF); ok { + break + } + } + + assert.Equal(t, int32(0xBEEF), receivedEvent.MethodIndex) +} + +func startNode() *cluster.Cluster { + system := actor.NewActorSystem() + + provider := New() + config := remote.Configure("localhost", 0) + + lookup := disthash.New() + clusterConfig := cluster.Configure("my-cluster", provider, lookup, config) + cluster := cluster.New(system, clusterConfig) + + cluster.StartMember() + + return cluster +} + +func subscribe(c *cluster.Cluster) (events <-chan *cluster.GrainRequest, cancel func()) { + eventChan := make(chan *cluster.GrainRequest, 1) + + subscription := c.ActorSystem.EventStream.Subscribe(func(evt interface{}) { + if event, ok := evt.(*cluster.GrainRequest); ok { + eventChan <- event + } + }) + + return eventChan, func() { c.ActorSystem.EventStream.Unsubscribe(subscription) } +} + +func trySendAndReceiveMessage(t *testing.T, c *cluster.Cluster, methodIndex int32) (receivedEvent *cluster.GrainRequest, ok bool) { + events, cancel := subscribe(c) + + time.Sleep(500 * time.Millisecond) + + c.MemberList.BroadcastEvent(&cluster.GrainRequest{MethodIndex: methodIndex}, true) + + select { + case receivedEvent = <-events: + ok = true + case <-time.After(1 * time.Second): + t.Error("Timed out waiting for the event to arrive") + } + + cancel() + + return +} diff --git a/cluster/clusterproviders/automanaged/node_model.go b/cluster/clusterproviders/automanaged/node_model.go new file mode 100644 index 0000000000000000000000000000000000000000..83cf45a7217475e066d038954bd745a62c1a4a2d --- /dev/null +++ b/cluster/clusterproviders/automanaged/node_model.go @@ -0,0 +1,23 @@ +package automanaged + +// NodeModel represents a node in the cluster +type NodeModel struct { + ID string `json:"id"` + Address string `json:"address"` + AutoManagePort int `json:"auto_manage_port"` + Port int `json:"port"` + Kinds []string `json:"kinds"` + ClusterName string `json:"cluster_name"` +} + +// NewNode returns a new node for the cluster +func NewNode(clusterName string, id string, address string, port int, autoManPort int, kind []string) *NodeModel { + return &NodeModel{ + ID: id, + ClusterName: clusterName, + Address: address, + Port: port, + AutoManagePort: autoManPort, + Kinds: kind, + } +} diff --git a/cluster/clusterproviders/consul/consul_provider.go b/cluster/clusterproviders/consul/consul_provider.go new file mode 100644 index 0000000000000000000000000000000000000000..814fdfcc1e759383b68805da6757fb4692e7fff2 --- /dev/null +++ b/cluster/clusterproviders/consul/consul_provider.go @@ -0,0 +1,206 @@ +package consul + +import ( + "fmt" + "sync" + "time" + + "gitee.com/simplexyz/simpleactor-go/actor" + + "gitee.com/simplexyz/simpleactor-go/cluster" + "gitee.com/simplexyz/simpleactor-go/log" + "github.com/hashicorp/consul/api" +) + +var ProviderShuttingDownError = fmt.Errorf("consul cluster provider is shutting down") + +type Provider struct { + cluster *cluster.Cluster + deregistered bool + shutdown bool + id string + clusterName string + address string + port int + knownKinds []string + index uint64 // consul blocking index + client *api.Client + ttl time.Duration + refreshTTL time.Duration + updateTTLWaitGroup sync.WaitGroup + deregisterCritical time.Duration + blockingWaitTime time.Duration + clusterError error + pid *actor.PID + consulConfig *api.Config +} + +func New(opts ...Option) (*Provider, error) { + return NewWithConfig(&api.Config{}, opts...) +} + +func NewWithConfig(consulConfig *api.Config, opts ...Option) (*Provider, error) { + client, err := api.NewClient(consulConfig) + if err != nil { + return nil, err + } + p := &Provider{ + client: client, + ttl: 3 * time.Second, + refreshTTL: 1 * time.Second, + deregisterCritical: 60 * time.Second, + blockingWaitTime: 20 * time.Second, + consulConfig: consulConfig, + } + for _, opt := range opts { + opt(p) + } + return p, nil +} + +func (p *Provider) init(c *cluster.Cluster) error { + knownKinds := c.GetClusterKinds() + clusterName := c.Config.Name + memberId := c.ActorSystem.ID + + host, port, err := c.ActorSystem.GetHostPort() + if err != nil { + return err + } + + p.cluster = c + p.id = memberId + p.clusterName = clusterName + p.address = host + p.port = port + p.knownKinds = knownKinds + return nil +} + +func (p *Provider) StartMember(c *cluster.Cluster) error { + err := p.init(c) + if err != nil { + return err + } + + p.pid, err = c.ActorSystem.Root.SpawnNamed(actor.PropsFromProducer(func() actor.Actor { + return newProviderActor(p) + }), "consul-provider") + if err != nil { + plog.Error("Failed to start consul-provider actor", log.Error(err)) + return err + } + + return nil +} + +func (p *Provider) StartClient(c *cluster.Cluster) error { + if err := p.init(c); err != nil { + return err + } + p.blockingStatusChange() + p.monitorMemberStatusChanges() + return nil +} + +func (p *Provider) DeregisterMember() error { + err := p.deregisterService() + if err != nil { + fmt.Println(err) + return err + } + p.deregistered = true + return nil +} + +func (p *Provider) Shutdown(graceful bool) error { + if p.shutdown { + return nil + } + p.shutdown = true + if p.pid != nil { + if err := p.cluster.ActorSystem.Root.StopFuture(p.pid).Wait(); err != nil { + plog.Error("Failed to stop consul-provider actor", log.Error(err)) + } + p.pid = nil + } + + return nil +} + +func blockingUpdateTTL(p *Provider) error { + p.clusterError = p.client.Agent().UpdateTTL("service:"+p.id, "", api.HealthPassing) + return p.clusterError +} + +func (p *Provider) registerService() error { + s := &api.AgentServiceRegistration{ + ID: p.id, + Name: p.clusterName, + Tags: p.knownKinds, + Address: p.address, + Port: p.port, + Meta: map[string]string{ + "id": p.id, + }, + Check: &api.AgentServiceCheck{ + DeregisterCriticalServiceAfter: p.deregisterCritical.String(), + TTL: p.ttl.String(), + }, + } + return p.client.Agent().ServiceRegister(s) +} + +func (p *Provider) deregisterService() error { + return p.client.Agent().ServiceDeregister(p.id) +} + +// call this directly after registering the service +func (p *Provider) blockingStatusChange() { + p.notifyStatuses() +} + +func (p *Provider) notifyStatuses() { + statuses, meta, err := p.client.Health().Service(p.clusterName, "", false, &api.QueryOptions{ + WaitIndex: p.index, + WaitTime: p.blockingWaitTime, + }) + plog.Info("Consul health check") + + if err != nil { + plog.Error("notifyStatues", log.Error(err)) + return + } + p.index = meta.LastIndex + + var members []*cluster.Member + for _, v := range statuses { + if len(v.Checks) > 0 && v.Checks.AggregatedStatus() == api.HealthPassing { + memberId := v.Service.Meta["id"] + if memberId == "" { + memberId = fmt.Sprintf("%v@%v:%v", p.clusterName, v.Service.Address, v.Service.Port) + plog.Info("meta['id'] was empty, fixeds", log.String("id", memberId)) + } + members = append(members, &cluster.Member{ + Id: memberId, + Host: v.Service.Address, + Port: int32(v.Service.Port), + Kinds: v.Service.Tags, + }) + } + } + // the reason why we want this in a batch and not as individual messages is that + // if we have an atomic batch, we can calculate what nodes have left the cluster + // passing events one by one, we can't know if someone left or just haven't changed status for a long time + + // publish the current cluster topology onto the event stream + p.cluster.MemberList.UpdateClusterTopology(members) +} + +func (p *Provider) monitorMemberStatusChanges() { + go func() { + for !p.shutdown { + p.notifyStatuses() + } + }() +} diff --git a/cluster/clusterproviders/consul/consul_provider_test.go b/cluster/clusterproviders/consul/consul_provider_test.go new file mode 100644 index 0000000000000000000000000000000000000000..48d3ac3c1068674b97909f4b58e0074bb3064b53 --- /dev/null +++ b/cluster/clusterproviders/consul/consul_provider_test.go @@ -0,0 +1,180 @@ +package consul + +import ( + "fmt" + "net" + "strconv" + "testing" + "time" + + "gitee.com/simplexyz/simpleactor-go/cluster/identitylookup/disthash" + + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/cluster" + "gitee.com/simplexyz/simpleactor-go/remote" + "github.com/stretchr/testify/assert" +) + +func newClusterForTest(name string, addr string, cp cluster.ClusterProvider) *cluster.Cluster { + host, _port, err := net.SplitHostPort(addr) + if err != nil { + panic(err) + } + port, _ := strconv.Atoi(_port) + remoteConfig := remote.Configure(host, port) + lookup := disthash.New() + config := cluster.Configure(name, cp, lookup, remoteConfig) + // return cluster.NewForTest(system, config) + + system := actor.NewActorSystem() + c := cluster.New(system, config) + + // use for test without start remote + c.ActorSystem.ProcessRegistry.Address = addr + c.MemberList = cluster.NewMemberList(c) + c.Remote = remote.NewRemote(c.ActorSystem, c.Config.RemoteConfig) + return c +} + +func TestStartMember(t *testing.T) { + if testing.Short() { + return + } + a := assert.New(t) + + p, _ := New() + defer p.Shutdown(true) + + c := newClusterForTest("mycluster", "127.0.0.1:8000", p) + eventstream := c.ActorSystem.EventStream + ch := make(chan interface{}, 16) + eventstream.Subscribe(func(m interface{}) { + if _, ok := m.(*cluster.ClusterTopology); ok { + ch <- m + } + }) + + err := p.StartMember(c) + a.NoError(err) + + select { + case <-time.After(10 * time.Second): + a.FailNow("no member joined yet") + + case m := <-ch: + msg := m.(*cluster.ClusterTopology) + // member joined + members := []*cluster.Member{ + { + // Id: "mycluster@127.0.0.1:8000", + Id: fmt.Sprintf("%s", c.ActorSystem.ID), + Host: "127.0.0.1", + Port: 8000, + Kinds: []string{}, + }, + } + + expected := &cluster.ClusterTopology{ + Members: members, + Joined: members, + Left: []*cluster.Member{}, + TopologyHash: msg.TopologyHash, + } + a.Equal(expected, msg) + } +} + +func TestRegisterMultipleMembers(t *testing.T) { + if testing.Short() { + return + } + a := assert.New(t) + + members := []struct { + cluster string + host string + port int + }{ + {"mycluster2", "127.0.0.1", 8001}, + {"mycluster2", "127.0.0.1", 8002}, + {"mycluster2", "127.0.0.1", 8003}, + } + + p, _ := New() + defer p.Shutdown(true) + for _, member := range members { + addr := fmt.Sprintf("%s:%d", member.host, member.port) + _p, _ := New() + c := newClusterForTest(member.cluster, addr, _p) + err := p.StartMember(c) + a.NoError(err) + t.Cleanup(func() { + _p.Shutdown(true) + }) + } + + entries, _, err := p.client.Health().Service("mycluster2", "", true, nil) + a.NoError(err) + + found := false + for _, entry := range entries { + found = false + for _, member := range members { + if entry.Service.Port == member.port { + found = true + } + } + a.Truef(found, "Member port not found - ExtensionID:%v Address: %v:%v", + entry.Service.ID, entry.Service.Address, entry.Service.Port) + } +} + +func TestUpdateTTL_DoesNotReregisterAfterShutdown(t *testing.T) { + if testing.Short() { + return + } + a := assert.New(t) + + p, _ := New() + c := newClusterForTest("mycluster5", "127.0.0.1:8001", p) + + shutdownShouldHaveResolved := make(chan bool, 1) + + err := p.StartMember(c) + a.NoError(err) + + time.Sleep(time.Second) + found, _ := findService(t, p) + a.True(found, "service was not registered in consul") + + go func() { + // if after 5 seconds `Shutdown` did not resolve, assume that it will not resolve until `blockingUpdateTTL` resolves + time.Sleep(5 * time.Second) + shutdownShouldHaveResolved <- true + }() + + err = p.Shutdown(true) + a.NoError(err) + shutdownShouldHaveResolved <- true + + // since `UpdateTTL` runs in a separate goroutine we need to wait until it is actually finished before checking the member's clusterstatus + p.updateTTLWaitGroup.Wait() + found, status := findService(t, p) + a.Falsef(found, "service was still registered in consul after shutdown (service status: %s)", status) +} + +func findService(t *testing.T, p *Provider) (found bool, status string) { + service := p.cluster.Config.Name + port := p.cluster.Config.RemoteConfig.Port + entries, _, err := p.client.Health().Service(service, "", false, nil) + if err != nil { + t.Error(err) + } + + for _, entry := range entries { + if entry.Service.Port == port { + return true, entry.Checks.AggregatedStatus() + } + } + return false, "" +} diff --git a/cluster/clusterproviders/consul/log.go b/cluster/clusterproviders/consul/log.go new file mode 100644 index 0000000000000000000000000000000000000000..5a1e13828877fb13e38d5d83d756d5784459d946 --- /dev/null +++ b/cluster/clusterproviders/consul/log.go @@ -0,0 +1,11 @@ +package consul + +import "gitee.com/simplexyz/simpleactor-go/log" + +var plog = log.New(log.DebugLevel, "[CONSUL]") + +// SetLogLevel sets the log level for the logger +// SetLogLevel is safe to be called concurrently +func SetLogLevel(level log.Level) { + plog.SetLevel(level) +} diff --git a/cluster/clusterproviders/consul/options.go b/cluster/clusterproviders/consul/options.go new file mode 100644 index 0000000000000000000000000000000000000000..e5cec33299317ddc21cb0e10a9fcd3cda5c8be0b --- /dev/null +++ b/cluster/clusterproviders/consul/options.go @@ -0,0 +1,17 @@ +package consul + +import "time" + +type Option func(p *Provider) + +func WithTTL(ttl time.Duration) Option { + return func(p *Provider) { + p.ttl = ttl + } +} + +func WithRefreshTTL(refreshTTL time.Duration) Option { + return func(p *Provider) { + p.refreshTTL = refreshTTL + } +} diff --git a/cluster/clusterproviders/consul/provider_actor.go b/cluster/clusterproviders/consul/provider_actor.go new file mode 100644 index 0000000000000000000000000000000000000000..900c52bbd8dd39595bf97df70362c0a3cd35f57f --- /dev/null +++ b/cluster/clusterproviders/consul/provider_actor.go @@ -0,0 +1,133 @@ +package consul + +import ( + "fmt" + + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/cluster" + "gitee.com/simplexyz/simpleactor-go/log" + "gitee.com/simplexyz/simpleactor-go/scheduler" + "github.com/hashicorp/consul/api" + "github.com/hashicorp/consul/api/watch" +) + +type providerActor struct { + *Provider + actor.Behavior + refreshCanceller scheduler.CancelFunc +} + +type ( + RegisterService struct{} + UpdateTTL struct{} + MemberListUpdated struct { + members []*cluster.Member + index uint64 + } +) + +func (pa *providerActor) Receive(ctx actor.Context) { + pa.Behavior.Receive(ctx) +} + +func newProviderActor(provider *Provider) actor.Actor { + pa := &providerActor{ + Behavior: actor.NewBehavior(), + Provider: provider, + } + pa.Become(pa.init) + return pa +} + +func (pa *providerActor) init(ctx actor.Context) { + switch ctx.Message().(type) { + case *actor.Started: + ctx.Send(ctx.Self(), &RegisterService{}) + case *RegisterService: + if err := pa.registerService(); err != nil { + plog.Error("Failed to register service to consul, will retry", log.Error(err)) + ctx.Send(ctx.Self(), &RegisterService{}) + } else { + plog.Info("Registered service to consul") + refreshScheduler := scheduler.NewTimerScheduler(ctx) + pa.refreshCanceller = refreshScheduler.SendRepeatedly(0, pa.refreshTTL, ctx.Self(), &UpdateTTL{}) + if err := pa.startWatch(ctx); err == nil { + pa.Become(pa.running) + } + } + } +} + +func (pa *providerActor) running(ctx actor.Context) { + switch msg := ctx.Message().(type) { + case *UpdateTTL: + if err := blockingUpdateTTL(pa.Provider); err != nil { + plog.Warn("Failed to update TTL", log.Error(err)) + } + case *MemberListUpdated: + pa.cluster.MemberList.UpdateClusterTopology(msg.members) + case *actor.Stopping: + pa.refreshCanceller() + if err := pa.deregisterService(); err != nil { + plog.Error("Failed to deregister service from consul", log.Error(err)) + } else { + plog.Info("De-registered service from consul") + } + } +} + +func (pa *providerActor) startWatch(ctx actor.Context) error { + params := make(map[string]interface{}) + params["type"] = "service" + params["service"] = pa.clusterName + params["passingonly"] = false + plan, err := watch.Parse(params) + if err != nil { + plog.Error("Failed to parse consul watch definition", log.Error(err)) + return err + } + plan.Handler = func(index uint64, result interface{}) { + pa.processConsulUpdate(index, result, ctx) + } + + go func() { + if err = plan.RunWithConfig(pa.consulConfig.Address, pa.consulConfig); err != nil { + plog.Error("Failed to start consul watch", log.Error(err)) + panic(err) + } + }() + + return nil +} + +func (pa *providerActor) processConsulUpdate(index uint64, result interface{}, ctx actor.Context) { + serviceEntries, ok := result.([]*api.ServiceEntry) + if !ok { + plog.Warn("Didn't get expected data from consul watch") + return + } + var members []*cluster.Member + for _, v := range serviceEntries { + if len(v.Checks) > 0 && v.Checks.AggregatedStatus() == api.HealthPassing { + memberId := v.Service.Meta["id"] + if memberId == "" { + memberId = fmt.Sprintf("%v@%v:%v", pa.clusterName, v.Service.Address, v.Service.Port) + plog.Info("meta['id'] was empty, fixed", log.String("id", memberId)) + } + members = append(members, &cluster.Member{ + Id: memberId, + Host: v.Service.Address, + Port: int32(v.Service.Port), + Kinds: v.Service.Tags, + }) + } + } + + // delay the fist update until there is at least one member + if len(members) > 0 { + ctx.Send(ctx.Self(), &MemberListUpdated{ + members: members, + index: index, + }) + } +} diff --git a/cluster/clusterproviders/etcd/etcd_provider.go b/cluster/clusterproviders/etcd/etcd_provider.go new file mode 100644 index 0000000000000000000000000000000000000000..0e3f7cd6e498016738b761df918311d460ccadbc --- /dev/null +++ b/cluster/clusterproviders/etcd/etcd_provider.go @@ -0,0 +1,415 @@ +package etcd + +import ( + "context" + "fmt" + "net" + "strconv" + "strings" + "sync/atomic" + "time" + + "gitee.com/simplexyz/simpleactor-go/cluster" + "gitee.com/simplexyz/simpleactor-go/log" + clientv3 "go.etcd.io/etcd/client/v3" +) + +type Provider struct { + leaseID clientv3.LeaseID + cluster *cluster.Cluster + baseKey string + clusterName string + deregistered bool + shutdown bool + self *Node + members map[string]*Node // all, contains self. + clusterError error + client *clientv3.Client + cancelWatch func() + cancelWatchCh chan bool + keepAliveTTL time.Duration + retryInterval time.Duration + revision uint64 + // deregisterCritical time.Duration +} + +func New() (*Provider, error) { + return NewWithConfig("/protoactor", clientv3.Config{ + Endpoints: []string{"127.0.0.1:2379"}, + DialTimeout: time.Second * 5, + }) +} + +func NewWithConfig(baseKey string, cfg clientv3.Config) (*Provider, error) { + client, err := clientv3.New(cfg) + if err != nil { + return nil, err + } + p := &Provider{ + client: client, + keepAliveTTL: 3 * time.Second, + retryInterval: 1 * time.Second, + baseKey: baseKey, + members: map[string]*Node{}, + cancelWatchCh: make(chan bool), + } + return p, nil +} + +func (p *Provider) init(c *cluster.Cluster) error { + p.cluster = c + addr := p.cluster.ActorSystem.Address() + host, port, err := splitHostPort(addr) + if err != nil { + return err + } + + p.cluster = c + p.clusterName = p.cluster.Config.Name + memberID := p.cluster.ActorSystem.ID + knownKinds := c.GetClusterKinds() + nodeName := fmt.Sprintf("%v@%v", p.clusterName, memberID) + p.self = NewNode(nodeName, host, port, knownKinds) + p.self.SetMeta("id", p.getID()) + return nil +} + +func (p *Provider) StartMember(c *cluster.Cluster) error { + if err := p.init(c); err != nil { + return err + } + + // fetch memberlist + nodes, err := p.fetchNodes() + if err != nil { + return err + } + // initialize members + p.updateNodesWithSelf(nodes) + p.publishClusterTopologyEvent() + p.startWatching() + + // register self + if err := p.registerService(); err != nil { + return err + } + ctx := context.TODO() + p.startKeepAlive(ctx) + return nil +} + +func (p *Provider) StartClient(c *cluster.Cluster) error { + if err := p.init(c); err != nil { + return err + } + nodes, err := p.fetchNodes() + if err != nil { + return err + } + // initialize members + p.updateNodes(nodes) + p.publishClusterTopologyEvent() + p.startWatching() + return nil +} + +func (p *Provider) Shutdown(graceful bool) error { + p.shutdown = true + if !p.deregistered { + err := p.deregisterService() + if err != nil { + plog.Error("deregisterMember", log.Error(err)) + return err + } + p.deregistered = true + } + if p.cancelWatch != nil { + p.cancelWatch() + p.cancelWatch = nil + } + return nil +} + +func (p *Provider) keepAliveForever(ctx context.Context) error { + if p.self == nil { + return fmt.Errorf("keepalive must be after initialize") + } + + data, err := p.self.Serialize() + if err != nil { + return err + } + fullKey := p.getEtcdKey() + + var leaseId clientv3.LeaseID + leaseId, err = p.newLeaseID() + if err != nil { + return err + } + p.setLeaseID(leaseId) + + if leaseId <= 0 { + return fmt.Errorf("grant lease failed. leaseId=%d", leaseId) + } + _, err = p.client.Put(context.TODO(), fullKey, string(data), clientv3.WithLease(leaseId)) + if err != nil { + return err + } + kaRespCh, err := p.client.KeepAlive(context.TODO(), leaseId) + if err != nil { + return err + } + + for resp := range kaRespCh { + if resp == nil { + return fmt.Errorf("keep alive failed. resp=%s", resp.String()) + } + // plog.Infof("keep alive %s ttl=%d", p.getID(), resp.TTL) + if p.shutdown { + return nil + } + } + return nil +} + +func (p *Provider) startKeepAlive(ctx context.Context) { + go func() { + for !p.shutdown { + if err := ctx.Err(); err != nil { + plog.Info("Keepalive was stopped.", log.Error(err)) + return + } + + if err := p.keepAliveForever(ctx); err != nil { + plog.Info("Failure refreshing service TTL. ReTrying...", log.Duration("after", p.retryInterval), log.Error(err)) + } + time.Sleep(p.retryInterval) + } + }() +} + +func (p *Provider) getID() string { + return p.self.ID +} + +func (p *Provider) getEtcdKey() string { + return p.buildKey(p.clusterName, p.getID()) +} + +func (p *Provider) registerService() error { + data, err := p.self.Serialize() + if err != nil { + return err + } + fullKey := p.getEtcdKey() + if err != nil { + return err + } + leaseId := p.getLeaseID() + if leaseId <= 0 { + _leaseId, err := p.newLeaseID() + if err != nil { + return err + } + leaseId = _leaseId + p.setLeaseID(leaseId) + } + _, err = p.client.Put(context.TODO(), fullKey, string(data), clientv3.WithLease(leaseId)) + if err != nil { + return err + } + return nil +} + +func (p *Provider) deregisterService() error { + fullKey := p.getEtcdKey() + _, err := p.client.Delete(context.TODO(), fullKey) + return err +} + +func (p *Provider) handleWatchResponse(resp clientv3.WatchResponse) map[string]*Node { + changes := map[string]*Node{} + for _, ev := range resp.Events { + key := string(ev.Kv.Key) + nodeId, err := getNodeID(key, "/") + if err != nil { + plog.Error("Invalid member.", log.String("key", key)) + continue + } + + switch ev.Type { + case clientv3.EventTypePut: + node, err := NewNodeFromBytes(ev.Kv.Value) + if err != nil { + plog.Error("Invalid member.", log.String("key", key)) + continue + } + if p.self.Equal(node) { + plog.Debug("Skip self.", log.String("key", key)) + continue + } + if _, ok := p.members[nodeId]; ok { + plog.Debug("Update member.", log.String("key", key)) + } else { + plog.Debug("New member.", log.String("key", key)) + } + changes[nodeId] = node + case clientv3.EventTypeDelete: + node, ok := p.members[nodeId] + if !ok { + continue + } + plog.Debug("Delete member.", log.String("key", key)) + cloned := *node + cloned.SetAlive(false) + changes[nodeId] = &cloned + default: + plog.Error("Invalid etcd event.type.", log.String("key", key), + log.String("type", ev.Type.String())) + } + } + p.revision = uint64(resp.Header.GetRevision()) + return changes +} + +func (p *Provider) keepWatching(ctx context.Context) error { + clusterKey := p.buildKey(p.clusterName) + stream := p.client.Watch(ctx, clusterKey, clientv3.WithPrefix()) + return p._keepWatching(stream) +} + +func (p *Provider) _keepWatching(stream clientv3.WatchChan) error { + for resp := range stream { + if err := resp.Err(); err != nil { + plog.Error("Failure watching service.") + return err + } + if len(resp.Events) <= 0 { + plog.Error("Empty etcd.events.", log.Int("events", len(resp.Events))) + continue + } + nodesChanges := p.handleWatchResponse(resp) + p.updateNodesWithChanges(nodesChanges) + p.publishClusterTopologyEvent() + } + return nil +} + +func (p *Provider) startWatching() { + ctx := context.TODO() + ctx, cancel := context.WithCancel(ctx) + p.cancelWatch = cancel + go func() { + for !p.shutdown { + if err := p.keepWatching(ctx); err != nil { + plog.Error("Failed to keepWatching.", log.Error(err)) + p.clusterError = err + } + } + }() +} + +// GetHealthStatus returns an error if the cluster health status has problems +func (p *Provider) GetHealthStatus() error { + return p.clusterError +} + +func newContext(timeout time.Duration) (context.Context, context.CancelFunc) { + return context.WithTimeout(context.TODO(), timeout) +} + +func (p *Provider) buildKey(names ...string) string { + return strings.Join(append([]string{p.baseKey}, names...), "/") +} + +func (p *Provider) fetchNodes() ([]*Node, error) { + key := p.buildKey(p.clusterName) + resp, err := p.client.Get(context.TODO(), key, clientv3.WithPrefix()) + if err != nil { + return nil, err + } + var nodes []*Node + for _, v := range resp.Kvs { + n := Node{} + if err := n.Deserialize(v.Value); err != nil { + return nil, err + } + nodes = append(nodes, &n) + } + p.revision = uint64(resp.Header.GetRevision()) + // plog.Debug("fetch nodes", + // log.Uint64("raft term", resp.Header.GetRaftTerm()), + // log.Int64("revision", resp.Header.GetRevision())) + return nodes, nil +} + +func (p *Provider) updateNodes(members []*Node) { + for _, n := range members { + p.members[n.ID] = n + } +} + +func (p *Provider) updateNodesWithSelf(members []*Node) { + p.updateNodes(members) + p.members[p.self.ID] = p.self +} + +func (p *Provider) updateNodesWithChanges(changes map[string]*Node) { + for memberId, member := range changes { + p.members[memberId] = member + if !member.IsAlive() { + delete(p.members, memberId) + } + } +} + +func (p *Provider) createClusterTopologyEvent() []*cluster.Member { + res := make([]*cluster.Member, len(p.members)) + i := 0 + for _, m := range p.members { + res[i] = m.MemberStatus() + i++ + } + return res +} + +func (p *Provider) publishClusterTopologyEvent() { + res := p.createClusterTopologyEvent() + plog.Info("Update cluster.", log.Int("members", len(res))) + // for _, m := range res { + // plog.Info("\t", log.Object("member", m)) + // } + p.cluster.MemberList.UpdateClusterTopology(res) + // p.cluster.ActorSystem.EventStream.Publish(res) +} + +func (p *Provider) getLeaseID() clientv3.LeaseID { + return (clientv3.LeaseID)(atomic.LoadInt64((*int64)(&p.leaseID))) +} + +func (p *Provider) setLeaseID(leaseID clientv3.LeaseID) { + atomic.StoreInt64((*int64)(&p.leaseID), (int64)(leaseID)) +} + +func (p *Provider) newLeaseID() (clientv3.LeaseID, error) { + ttlSecs := int64(p.keepAliveTTL / time.Second) + resp, err := p.client.Grant(context.TODO(), ttlSecs) + if err != nil { + return 0, err + } + return resp.ID, nil +} + +func splitHostPort(addr string) (host string, port int, err error) { + if h, p, e := net.SplitHostPort(addr); e != nil { + if addr != "nonhost" { + err = e + } + host = "nonhost" + port = -1 + } else { + host = h + port, err = strconv.Atoi(p) + } + return +} diff --git a/cluster/clusterproviders/etcd/etcd_provider_test.go b/cluster/clusterproviders/etcd/etcd_provider_test.go new file mode 100644 index 0000000000000000000000000000000000000000..d2eda4a59cb35178bccf10c4563169b8ddf5a8c1 --- /dev/null +++ b/cluster/clusterproviders/etcd/etcd_provider_test.go @@ -0,0 +1,186 @@ +package etcd + +import ( + "fmt" + "net" + "strconv" + "testing" + "time" + + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/cluster" + "gitee.com/simplexyz/simpleactor-go/remote" + "github.com/stretchr/testify/assert" +) + +func newClusterForTest(name string, addr string, cp cluster.ClusterProvider) *cluster.Cluster { + host, _port, err := net.SplitHostPort(addr) + if err != nil { + panic(err) + } + port, _ := strconv.Atoi(_port) + remoteConfig := remote.Configure(host, port) + config := cluster.Configure(name, cp, nil, remoteConfig) + + system := actor.NewActorSystem() + c := cluster.New(system, config) + // use for test without start remote + c.ActorSystem.ProcessRegistry.Address = addr + c.MemberList = cluster.NewMemberList(c) + c.Remote = remote.NewRemote(c.ActorSystem, c.Config.RemoteConfig) + + return c +} + +func TestStartMember(t *testing.T) { + if testing.Short() { + return + } + + a := assert.New(t) + + p, err := New() + a.NoError(err) + defer p.Shutdown(true) + + c := newClusterForTest("test_etcd_provider", "127.0.0.1:8000", p) + eventstream := c.ActorSystem.EventStream + ch := make(chan interface{}, 16) + + eventstream.Subscribe(func(m interface{}) { + if _, ok := m.(*cluster.ClusterTopology); ok { + ch <- m + } + }) + + err = p.StartMember(c) + a.NoError(err) + + select { + case <-time.After(5 * time.Second): + a.FailNow("no member joined yet") + + case m := <-ch: + // member joined + msg, _ := m.(*cluster.ClusterTopology) + + members := []*cluster.Member{ + { + // Id: "test_etcd_provider@127.0.0.1:8000", + Id: fmt.Sprintf("test_etcd_provider@%s", c.ActorSystem.ID), + Host: "127.0.0.1", + Port: 8000, + Kinds: []string{}, + }, + } + + expected := &cluster.ClusterTopology{ + Members: members, + Joined: members, + Left: []*cluster.Member{}, + TopologyHash: msg.TopologyHash, + } + a.Equal(expected, msg) + + } +} + +func TestStartMember_Multiple(t *testing.T) { + if testing.Short() { + return + } + + a := assert.New(t) + members := []struct { + cluster string + host string + port int + }{ + {"mycluster2", "127.0.0.1", 8001}, + {"mycluster2", "127.0.0.1", 8002}, + {"mycluster2", "127.0.0.1", 8003}, + } + + p := make([]*Provider, len(members)) + + var err error + + t.Cleanup(func() { + for i := range p { + _ = p[i].Shutdown(true) + } + }) + + for i, member := range members { + addr := fmt.Sprintf("%s:%d", member.host, member.port) + p[i], err = New() + a.NoError(err) + + c := newClusterForTest(member.cluster, addr, p[i]) + err := p[i].StartMember(c) + a.NoError(err) + } + + isNodesEqual := func(nodes []*Node) bool { + for _, node := range nodes { + for _, member := range members { + if node.Host == member.host && node.Port == member.port { + return true + } + } + } + + return false + } + + for i := range p { + nodes, err := p[i].fetchNodes() + a.NoError(err) + a.Equal(len(members), len(nodes)) + flag := isNodesEqual(nodes) + a.Truef(flag, "Member not found - %+v", p[i].self) + } +} + +//func TestUpdateMemberState(t *testing.T) { +// if testing.Short() { +// return +// } +// assert := assert.New(t) +// +// p, _ := New() +// defer p.Shutdown(true) +// +// c := newClusterForTest("mycluster3", "127.0.0.1:8000", p) +// err := p.StartMember(c) +// assert.NoError(err) +// +// state := cluster.ClusterState{[]string{"yes"}} +// err = p.UpdateClusterState(state) +// assert.NoError(err) +//} +// +//func TestUpdateMemberState_DoesNotReregisterAfterShutdown(t *testing.T) { +// if testing.Short() { +// return +// } +// assert := assert.New(t) +// +// p, _ := New() +// c := newClusterForTest("mycluster4", "127.0.0.1:8001", p) +// err := p.StartMember(c) +// assert.NoError(err) +// t.Cleanup(func() { +// p.Shutdown(true) +// }) +// +// state := cluster.ClusterState{[]string{"yes"}} +// err = p.UpdateClusterState(state) +// assert.NoError(err) +// +// err = p.Shutdown(true) +// assert.NoError(err) +// +// err = p.UpdateClusterState(state) +// assert.Error(err) +//} diff --git a/cluster/clusterproviders/etcd/log.go b/cluster/clusterproviders/etcd/log.go new file mode 100644 index 0000000000000000000000000000000000000000..45da2460921b3441a9df7cac4f625740e98df2cb --- /dev/null +++ b/cluster/clusterproviders/etcd/log.go @@ -0,0 +1,11 @@ +package etcd + +import "gitee.com/simplexyz/simpleactor-go/log" + +var plog = log.New(log.DefaultLevel, "[CLUSTER] [ETCD]") + +// SetLogLevel sets the log level for the logger +// SetLogLevel is safe to be called concurrently +func SetLogLevel(level log.Level) { + plog.SetLevel(level) +} diff --git a/cluster/clusterproviders/etcd/node.go b/cluster/clusterproviders/etcd/node.go new file mode 100644 index 0000000000000000000000000000000000000000..da278ba03e07180eb32eb26e1e15fcb3261e6ca7 --- /dev/null +++ b/cluster/clusterproviders/etcd/node.go @@ -0,0 +1,107 @@ +package etcd + +import ( + "encoding/json" + + "gitee.com/simplexyz/simpleactor-go/cluster" +) + +type Node struct { + ID string `json:"id"` + Name string `json:"name"` + Host string `json:"host"` + Address string `json:"address"` + Port int `json:"port"` + Kinds []string `json:"kinds"` + Meta map[string]string `json:"-"` + Alive bool `json:"alive"` +} + +func NewNode(name, host string, port int, kinds []string) *Node { + return &Node{ + ID: name, + Name: name, + Address: host, + Host: host, + Port: port, + Kinds: kinds, + Meta: map[string]string{}, + Alive: true, + } +} + +func NewNodeFromBytes(data []byte) (*Node, error) { + n := Node{} + if err := json.Unmarshal(data, &n); err != nil { + return nil, err + } + return &n, nil +} + +func (n *Node) GetAddress() (host string, port int) { + host = n.Host + port = n.Port + if host == "" { + host = n.Address + } + return +} + +func (n *Node) Equal(other *Node) bool { + if n == nil || other == nil { + return false + } + if n == other { + return true + } + return n.ID == other.ID +} + +func (n *Node) GetMeta(name string) (string, bool) { + if n.Meta == nil { + return "", false + } + val, ok := n.Meta[name] + return val, ok +} + +func (n *Node) MemberStatus() *cluster.Member { + host, port := n.GetAddress() + kinds := n.Kinds + if kinds == nil { + kinds = []string{} + } + return &cluster.Member{ + Id: n.ID, + Host: host, + Port: int32(port), + Kinds: kinds, + } +} + +func (n *Node) SetMeta(name string, val string) { + if n.Meta == nil { + n.Meta = map[string]string{} + } + n.Meta[name] = val +} + +func (n *Node) Serialize() ([]byte, error) { + data, err := json.Marshal(n) + if err != nil { + return nil, err + } + return data, nil +} + +func (n *Node) Deserialize(data []byte) error { + return json.Unmarshal(data, n) +} + +func (n *Node) IsAlive() bool { + return n.Alive +} + +func (n *Node) SetAlive(alive bool) { + n.Alive = alive +} diff --git a/cluster/clusterproviders/etcd/utils.go b/cluster/clusterproviders/etcd/utils.go new file mode 100644 index 0000000000000000000000000000000000000000..0b8b14d2727866e5fd5d68a7d8e18b48786ccbf6 --- /dev/null +++ b/cluster/clusterproviders/etcd/utils.go @@ -0,0 +1,15 @@ +package etcd + +import ( + "fmt" + "strings" +) + +func getNodeID(key string, sep string) (string, error) { + tmpArr := strings.Split(key, sep) + if len(tmpArr) == 0 { + return "", fmt.Errorf("invalid key or sep") + } + lastIndex := len(tmpArr) - 1 + return tmpArr[lastIndex], nil +} diff --git a/cluster/clusterproviders/k8s/k8s_cluster_monitor.go b/cluster/clusterproviders/k8s/k8s_cluster_monitor.go new file mode 100644 index 0000000000000000000000000000000000000000..4ed0cca802a555c0edb5fefc96af928944d9317c --- /dev/null +++ b/cluster/clusterproviders/k8s/k8s_cluster_monitor.go @@ -0,0 +1,78 @@ +package k8s + +import ( + "time" + + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/log" + "gitee.com/simplexyz/simpleactor-go/scheduler" +) + +type k8sClusterMonitorActor struct { + *Provider + actor.Behavior + + refreshCanceller scheduler.CancelFunc +} + +func (kcm *k8sClusterMonitorActor) Receive(ctx actor.Context) { kcm.Behavior.Receive(ctx) } + +func (kcm *k8sClusterMonitorActor) init(ctx actor.Context) { + switch r := ctx.Message().(type) { + case *RegisterMember: + // make sure timeout is set to some meaningful value + timeout := getTimeout(ctx, kcm) + + if err := kcm.registerMember(timeout); err != nil { + plog.Error("Failed to register service to k8s, will retry", log.Error(err)) + ctx.Send(ctx.Self(), r) + return + } + plog.Info("Registered service to k8s") + case *DeregisterMember: + plog.Debug("Deregistering service from k8s") + timeout := getTimeout(ctx, kcm) + + if err := kcm.deregisterMember(timeout); err != nil { + plog.Error("Failed to deregister service from k8s, proceeding with shutdown", log.Error(err)) + } else { + plog.Info("Deregistered service from k8s") + } + ctx.Respond(&DeregisterMemberResponse{}) + case *StartWatchingCluster: + if err := kcm.startWatchingCluster(); err != nil { + plog.Error("Failed to start watching k8s cluster, will retry", log.Error(err)) + ctx.Send(ctx.Self(), r) + return + } + plog.Info("k8s cluster started to being watched") + case *StopWatchingCluster: + if kcm.cancelWatch != nil { + kcm.cancelWatch() + } + ctx.Respond(&StopWatchingClusterResponse{}) + } +} + +func getTimeout(ctx actor.Context, kcm *k8sClusterMonitorActor) time.Duration { + timeout := ctx.ReceiveTimeout() + if timeout.Microseconds() == 0 { + timeout = kcm.Provider.cluster.Config.RequestTimeoutTime + if timeout.Microseconds() == 0 { + timeout = time.Second * 5 // default to 5 seconds + } + } + + return timeout +} + +// creates and initializes a new k8sClusterMonitorActor in the heap and +// returns a reference to its memory address +func newClusterMonitor(provider *Provider) actor.Actor { + kcm := k8sClusterMonitorActor{ + Behavior: actor.NewBehavior(), + Provider: provider, + } + kcm.Become(kcm.init) + return &kcm +} diff --git a/cluster/clusterproviders/k8s/k8s_provider.go b/cluster/clusterproviders/k8s/k8s_provider.go new file mode 100644 index 0000000000000000000000000000000000000000..7cb6f6f4284a7edb8ffd9e4aa793c387118a3c0d --- /dev/null +++ b/cluster/clusterproviders/k8s/k8s_provider.go @@ -0,0 +1,429 @@ +package k8s + +import ( + "context" + "encoding/json" + "fmt" + "os" + "path/filepath" + "strconv" + "strings" + "time" + + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/cluster" + "gitee.com/simplexyz/simpleactor-go/log" + "github.com/google/uuid" + v1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/watch" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" +) + +var ProviderShuttingDownError = fmt.Errorf("kubernetes cluster provider is being shut down") + +// Convenience type to store cluster labels +type Labels map[string]string + +// This data structure provides of k8s as cluster provider for Proto.Actor +type Provider struct { + id string + cluster *cluster.Cluster + clusterName string + podName string + host string + address string + namespace string + knownKinds []string + clusterPods map[types.UID]*v1.Pod + port int + client *kubernetes.Clientset + clusterMonitor *actor.PID + deregistered bool + shutdown bool + cancelWatch context.CancelFunc +} + +// make sure our Provider complies with the ClusterProvider interface +var _ cluster.ClusterProvider = (*Provider)(nil) + +// New crates a new k8s Provider in the heap and return back a reference to its memory address +func New(opts ...Option) (*Provider, error) { + // create new default k8s config + config, err := rest.InClusterConfig() + if err != nil { + return nil, err + } + + return NewWithConfig(config, opts...) +} + +// NewWithConfig creates a new k8s Provider in the heap using the given configuration +// and options, it returns a reference to its memory address or an error +func NewWithConfig(config *rest.Config, opts ...Option) (*Provider, error) { + clientset, err := kubernetes.NewForConfig(config) + if err != nil { + return nil, err + } + + p := Provider{ + client: clientset, + } + + // process given options + for _, opt := range opts { + opt(&p) + } + return &p, nil +} + +// initializes the cluster provider +func (p *Provider) init(c *cluster.Cluster) error { + host, port, err := c.ActorSystem.GetHostPort() + if err != nil { + return err + } + + p.cluster = c + p.id = strings.Replace(uuid.New().String(), "-", "", -1) + p.knownKinds = c.GetClusterKinds() + p.clusterName = c.Config.Name + p.clusterPods = make(map[types.UID]*v1.Pod) + p.host = host + p.port = port + p.address = fmt.Sprintf("%s:%d", host, port) + return nil +} + +// StartMember registers the member in the cluster and start it +func (p *Provider) StartMember(c *cluster.Cluster) error { + if err := p.init(c); err != nil { + return err + } + + if err := p.startClusterMonitor(c); err != nil { + return err + } + + p.registerMemberAsync(c) + p.startWatchingClusterAsync(c) + + return nil +} + +// StartClient starts the k8s client and monitor watch +func (p *Provider) StartClient(c *cluster.Cluster) error { + if err := p.init(c); err != nil { + return err + } + + if err := p.startClusterMonitor(c); err != nil { + return err + } + + p.startWatchingClusterAsync(c) + return nil +} + +func (p *Provider) Shutdown(graceful bool) error { + if p.shutdown { + // we are already shut down or shutting down + return nil + } + + p.shutdown = true + + plog.Info("Shutting down k8s cluster provider") + if p.clusterMonitor != nil { + if err := p.cluster.ActorSystem.Root.RequestFuture(p.clusterMonitor, &DeregisterMember{}, 5*time.Second).Wait(); err != nil { + plog.Error("Failed to deregister member - cluster monitor did not respond, proceeding with shutdown", log.Error(err)) + } + + if err := p.cluster.ActorSystem.Root.RequestFuture(p.clusterMonitor, &StopWatchingCluster{}, 5*time.Second).Wait(); err != nil { + plog.Error("Failed to deregister member - cluster monitor did not respond, proceeding with shutdown", log.Error(err)) + } + + _ = p.cluster.ActorSystem.Root.StopFuture(p.clusterMonitor).Wait() + p.clusterMonitor = nil + } + + return nil +} + +// starts the cluster monitor in its own goroutine +func (p *Provider) startClusterMonitor(c *cluster.Cluster) error { + var err error + p.clusterMonitor, err = c.ActorSystem.Root.SpawnNamed(actor.PropsFromProducer(func() actor.Actor { + return newClusterMonitor(p) + }), "k8s-cluster-monitor") + + if err != nil { + plog.Error("Failed to start k8s-cluster-monitor actor", log.Error(err)) + return err + } + + p.podName, _ = os.Hostname() + return nil +} + +// registers itself as a member asynchronously using an actor +func (p *Provider) registerMemberAsync(c *cluster.Cluster) { + msg := RegisterMember{} + c.ActorSystem.Root.Send(p.clusterMonitor, &msg) +} + +// registers itself as a member in k8s cluster +func (p *Provider) registerMember(timeout time.Duration) error { + plog.Info(fmt.Sprintf("Registering service %s on %s", p.podName, p.address)) + + ctx, cancel := context.WithTimeout(context.Background(), timeout) + defer cancel() + + pod, err := p.client.CoreV1().Pods(p.retrieveNamespace()).Get(ctx, p.podName, metav1.GetOptions{}) + if err != nil { + return fmt.Errorf("unable to get own pod information for %s: %w", p.podName, err) + } + + plog.Info(fmt.Sprintf("Using Kubernetes namespace: %s\nUsing Kubernetes port: %d", pod.Namespace, p.port)) + + labels := Labels{ + LabelCluster: p.clusterName, + LabelPort: fmt.Sprintf("%d", p.port), + LabelMemberID: p.id, + } + + // add known kinds to labels + for _, kind := range p.knownKinds { + labelkey := fmt.Sprintf("%s-%s", LabelKind, kind) + labels[labelkey] = "true" + } + + // add existing labels back + for key, value := range pod.ObjectMeta.Labels { + labels[key] = value + } + pod.SetLabels(labels) + + return p.replacePodLabels(ctx, pod) +} + +func (p *Provider) startWatchingClusterAsync(c *cluster.Cluster) { + msg := StartWatchingCluster{p.clusterName} + c.ActorSystem.Root.Send(p.clusterMonitor, &msg) +} + +func (p *Provider) startWatchingCluster() error { + selector := fmt.Sprintf("%s=%s", LabelCluster, p.clusterName) + + plog.Debug(fmt.Sprintf("Starting to watch pods with %s", selector), log.String("selector", selector)) + + ctx, cancel := context.WithCancel(context.Background()) + p.cancelWatch = cancel + + // start a new goroutine to monitor the cluster events + go func() { + for { + select { + case <-ctx.Done(): + plog.Debug("Stopping watch on pods") + return + default: + if err := p.watchPods(ctx, selector); err != nil { + plog.Error("Error watching pods, will retry", log.Error(err)) + time.Sleep(5 * time.Second) + } + } + } + }() + + return nil +} + +func (p *Provider) watchPods(ctx context.Context, selector string) error { + watcher, err := p.client.CoreV1().Pods(p.retrieveNamespace()).Watch(context.Background(), metav1.ListOptions{LabelSelector: selector, Watch: true}) + if err != nil { + err = fmt.Errorf("unable to watch pods: %w", err) + plog.Error(err.Error(), log.Error(err)) + return err + } + + plog.Info("Pod watcher started") + + for { + select { + case <-ctx.Done(): + watcher.Stop() + return nil + case event, ok := <-watcher.ResultChan(): + if !ok { + return fmt.Errorf("pod watcher channel closed abruptly") + } + pod, ok := event.Object.(*v1.Pod) + if !ok { + err := fmt.Errorf("could not cast %#v[%T] into v1.Pod", event.Object, event.Object) + plog.Error(err.Error(), log.Error(err)) + continue + } + + p.processPodEvent(event, pod) + } + } +} + +func (p *Provider) processPodEvent(event watch.Event, pod *v1.Pod) { + plog.Debug("Watcher reported event for pod", log.Object("eventType", event.Type), log.String("podName", pod.ObjectMeta.Name)) + + podClusterName, hasClusterName := pod.ObjectMeta.Labels[LabelCluster] + if !hasClusterName { + plog.Info("The pod is not a cluster member", log.Object("podName", pod.ObjectMeta.Name)) + delete(p.clusterPods, pod.UID) // pod could have been in the cluster, but then it was deregistered + } else if podClusterName != p.clusterName { + plog.Info("The pod is a member of another cluster", log.Object("podName", pod.ObjectMeta.Name), log.String("otherCluster", podClusterName)) + return + } else { + switch event.Type { + case watch.Deleted: + delete(p.clusterPods, pod.UID) + case watch.Error: + err := apierrors.FromObject(event.Object) + plog.Error(err.Error(), log.Error(err)) + default: + p.clusterPods[pod.UID] = pod + } + } + + if plog.Level() == log.DebugLevel { + logCurrentPods(p.clusterPods) + } + + members := mapPodsToMembers(p.clusterPods) + + plog.Info("Topology received from Kubernetes", log.Object("members", members)) + p.cluster.MemberList.UpdateClusterTopology(members) +} + +func logCurrentPods(clusterPods map[types.UID]*v1.Pod) { + podNames := make([]string, 0, len(clusterPods)) + for _, pod := range clusterPods { + podNames = append(podNames, pod.ObjectMeta.Name) + } + plog.Debug("Detected cluster pods are now", log.Int("numberOfPods", len(clusterPods)), log.Object("podNames", podNames)) +} + +func mapPodsToMembers(clusterPods map[types.UID]*v1.Pod) []*cluster.Member { + members := make([]*cluster.Member, 0, len(clusterPods)) + for _, clusterPod := range clusterPods { + if clusterPod.Status.Phase == "Running" && len(clusterPod.Status.PodIPs) > 0 { + + var kinds []string + for key, value := range clusterPod.ObjectMeta.Labels { + if strings.HasPrefix(key, LabelKind) && value == "true" { + kinds = append(kinds, strings.Replace(key, fmt.Sprintf("%s-", LabelKind), "", 1)) + } + } + + host := clusterPod.Status.PodIP + port, err := strconv.Atoi(clusterPod.ObjectMeta.Labels[LabelPort]) + if err != nil { + err = fmt.Errorf("can not convert pod meta %s into integer: %w", LabelPort, err) + plog.Error(err.Error(), log.Error(err)) + continue + } + + mid := clusterPod.ObjectMeta.Labels[LabelMemberID] + alive := true + for _, status := range clusterPod.Status.ContainerStatuses { + if !status.Ready { + plog.Debug("Pod container is not ready", log.String("podName", clusterPod.ObjectMeta.Name), log.String("containerName", status.Name)) + alive = false + break + } + } + + if !alive { + continue + } + + plog.Debug("Pod is running and all containers are ready", log.String("podName", clusterPod.ObjectMeta.Name), log.Object("podIPs", clusterPod.Status.PodIPs), log.String("podPhase", string(clusterPod.Status.Phase))) + + members = append(members, &cluster.Member{ + Id: mid, + Host: host, + Port: int32(port), + Kinds: kinds, + }) + } else { + plog.Debug("Pod is not in Running state", log.String("podName", clusterPod.ObjectMeta.Name), log.Object("podIPs", clusterPod.Status.PodIPs), log.String("podPhase", string(clusterPod.Status.Phase))) + } + } + + return members +} + +// deregister itself as a member from a k8s cluster +func (p *Provider) deregisterMember(timeout time.Duration) error { + plog.Info(fmt.Sprintf("Deregistering service %s from %s", p.podName, p.address)) + + ctx, cancel := context.WithTimeout(context.Background(), timeout) + defer cancel() + + pod, err := p.client.CoreV1().Pods(p.retrieveNamespace()).Get(ctx, p.podName, metav1.GetOptions{}) + if err != nil { + return fmt.Errorf("unable to get own pod information for %s: %w", p.podName, err) + } + + labels := pod.GetLabels() + + for labelKey := range labels { + if strings.HasPrefix(labelKey, LabelPrefix) { + delete(labels, labelKey) + } + } + + pod.SetLabels(labels) + + return p.replacePodLabels(ctx, pod) +} + +// prepares a patching payload and sends it to kubernetes to replace labels +func (p *Provider) replacePodLabels(ctx context.Context, pod *v1.Pod) error { + plog.Debug("Setting pod labels to ", log.Object("labels", pod.GetLabels())) + + payload := []struct { + Op string `json:"op"` + Path string `json:"path"` + Value Labels `json:"value"` + }{ + { + Op: "replace", + Path: "/metadata/labels", + Value: pod.GetLabels(), + }, + } + + payloadData, err := json.Marshal(payload) + if err != nil { + return fmt.Errorf("unable to update pod labels, operation failed: %w", err) + } + + _, patcherr := p.client.CoreV1().Pods(pod.GetNamespace()).Patch(ctx, pod.GetName(), types.JSONPatchType, payloadData, metav1.PatchOptions{}) + return patcherr +} + +// get the namespace of the current pod +func (p *Provider) retrieveNamespace() string { + if (p.namespace) == "" { + filename := filepath.Join(string(filepath.Separator), "var", "run", "secrets", "kubernetes.io", "serviceaccount", "namespace") + content, err := os.ReadFile(filename) + if err != nil { + plog.Warn(fmt.Sprintf("Could not read %s contents defaulting to empty namespace: %s", filename, err.Error())) + return p.namespace + } + p.namespace = string(content) + } + + return p.namespace +} diff --git a/cluster/clusterproviders/k8s/k8s_provider_test.go b/cluster/clusterproviders/k8s/k8s_provider_test.go new file mode 100644 index 0000000000000000000000000000000000000000..226a34a0ff18a654f5b50002d42c35431c2331ec --- /dev/null +++ b/cluster/clusterproviders/k8s/k8s_provider_test.go @@ -0,0 +1,174 @@ +package k8s + +import ( + "context" + "fmt" + "net" + "os" + "strconv" + "testing" + "time" + + "gitee.com/simplexyz/simpleactor-go/cluster/identitylookup/disthash" + + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/cluster" + "gitee.com/simplexyz/simpleactor-go/remote" + "github.com/stretchr/testify/assert" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func newClusterForTest(name string, addr string, cp cluster.ClusterProvider, id cluster.IdentityLookup) *cluster.Cluster { + host, _port, err := net.SplitHostPort(addr) + if err != nil { + panic(err) + } + port, _ := strconv.Atoi(_port) + remoteConfig := remote.Configure(host, port) + config := cluster.Configure(name, cp, id, remoteConfig) + + system := actor.NewActorSystem() + c := cluster.New(system, config) + + // use for test without start remote + c.ActorSystem.ProcessRegistry.Address = addr + c.MemberList = cluster.NewMemberList(c) + c.Remote = remote.NewRemote(system, config.RemoteConfig) + return c +} + +func TestStartMember(t *testing.T) { + if testing.Short() { + return + } + if os.Getenv("KUBERNETES_SERVICE_HOST") == "" { + t.Skipf("Skipped k8s testcases") + } + assert := assert.New(t) + + p, _ := New() + p, newErr := New() + if newErr != nil { + panic(fmt.Errorf("could not create new cluster provider: %w", newErr)) + } + id := disthash.New() + defer p.Shutdown(true) + + c := newClusterForTest("k8scluster", "127.0.0.1:8000", p, id) + eventstream := c.ActorSystem.EventStream + ch := make(chan interface{}, 16) + eventstream.Subscribe(func(m interface{}) { + if _, ok := m.(*cluster.ClusterTopology); ok { + ch <- m + } + }) + + err := p.StartMember(c) + assert.NoError(err) + + select { + case <-time.After(10 * time.Second): + assert.FailNow("no member joined yet") + + case m := <-ch: + msg := m.(*cluster.ClusterTopology) + // member joined + members := []*cluster.Member{ + { + Id: "k8scluster@127.0.0.1:8000", + Host: "127.0.0.1", + Port: 8000, + Kinds: []string{}, + }, + } + + expected := &cluster.ClusterTopology{ + Members: members, + Joined: members, + } + assert.Equal(expected, msg) + } +} + +func TestRegisterMultipleMembers(t *testing.T) { + if testing.Short() { + return + } + if os.Getenv("KUBERNETES_SERVICE_HOST") == "" { + t.Skipf("Skipped k8s testcases") + } + assert := assert.New(t) + + members := []struct { + cluster string + host string + port int + }{ + {"k8scluster2", "127.0.0.1", 8001}, + {"k8scluster2", "127.0.0.1", 8002}, + {"k8scluster2", "127.0.0.1", 8003}, + } + + p, _ := New() + defer p.Shutdown(true) + for _, member := range members { + addr := fmt.Sprintf("%s:%d", member.host, member.port) + _p, _ := New() + _id := disthash.New() + c := newClusterForTest(member.cluster, addr, _p, _id) + err := p.StartMember(c) + assert.NoError(err) + t.Cleanup(func() { + _p.Shutdown(true) + }) + } + + ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) + defer cancel() + + pods, err := p.client.CoreV1().Pods(p.retrieveNamespace()).List(ctx, metav1.ListOptions{}) + assert.NoError(err) + assert.Equal(pods.Size(), len(members)) +} + +func TestUpdateMemberState(t *testing.T) { + if testing.Short() { + return + } + if os.Getenv("KUBERNETES_SERVICE_HOST") == "" { + t.Skipf("Skipped k8s testcases") + } + assert := assert.New(t) + + p, _ := New() + id := disthash.New() + defer p.Shutdown(true) + + c := newClusterForTest("k8scluster3", "127.0.0.1:8000", p, id) + err := p.StartMember(c) + assert.NoError(err) +} + +func TestUpdateMemberState_DoesNotReregisterAfterShutdown(t *testing.T) { + if testing.Short() { + return + } + if os.Getenv("KUBERNETES_SERVICE_HOST") == "" { + t.Skipf("Skipped k8s testcases") + } + assert := assert.New(t) + + p, _ := New() + id := disthash.New() + c := newClusterForTest("k8scluster4", "127.0.0.1:8001", p, id) + err := p.StartMember(c) + assert.NoError(err) + t.Cleanup(func() { + p.Shutdown(true) + }) + + err = p.Shutdown(true) + assert.NoError(err) + + assert.Equal(ProviderShuttingDownError, err) +} diff --git a/cluster/clusterproviders/k8s/labels.go b/cluster/clusterproviders/k8s/labels.go new file mode 100644 index 0000000000000000000000000000000000000000..10a3c341932979e1911a84b4d778cdacb13cf530 --- /dev/null +++ b/cluster/clusterproviders/k8s/labels.go @@ -0,0 +1,11 @@ +package k8s + +// Label keys that will be used to update the Pods metadata +const ( + LabelPrefix = "cluster.proto.actor/" + LabelPort = LabelPrefix + "port" + LabelKind = LabelPrefix + "kind" + LabelCluster = LabelPrefix + "cluster" + LabelStatusValue = LabelPrefix + "status-value" + LabelMemberID = LabelPrefix + "member-id" +) diff --git a/cluster/clusterproviders/k8s/log.go b/cluster/clusterproviders/k8s/log.go new file mode 100644 index 0000000000000000000000000000000000000000..500e947d0ce2bcb95ec88cef2f1e0a08bda0985c --- /dev/null +++ b/cluster/clusterproviders/k8s/log.go @@ -0,0 +1,11 @@ +package k8s + +import "gitee.com/simplexyz/simpleactor-go/log" + +var plog = log.New(log.DebugLevel, "[CLUSTER] [KUBERNETES]") + +// SetLogLevel sets the log level for the logger +// SetLogLevel is safe to be called concurrently +func SetLogLevel(level log.Level) { + plog.SetLevel(level) +} diff --git a/cluster/clusterproviders/k8s/messages.go b/cluster/clusterproviders/k8s/messages.go new file mode 100644 index 0000000000000000000000000000000000000000..95c0bb36a182f7feb0355a3a9cd9e140b5b4c17a --- /dev/null +++ b/cluster/clusterproviders/k8s/messages.go @@ -0,0 +1,21 @@ +package k8s + +// RegisterMember message used to register a new member in k8s +type RegisterMember struct{} + +// DeregisterMember Empty struct used to deregister a member from k8s +type DeregisterMember struct{} + +// DeregisterMemberResponse sent back from cluster monitor when deregistering completes or fails +type DeregisterMemberResponse struct{} + +// StartWatchingCluster message used to start watching a k8s cluster +type StartWatchingCluster struct { + ClusterName string +} + +// StopWatchingCluster message used to stop watching a k8s cluster +type StopWatchingCluster struct{} + +// StopWatchingClusterResponse sent back from cluster monitor when stop watching completes +type StopWatchingClusterResponse struct{} diff --git a/cluster/clusterproviders/k8s/options.go b/cluster/clusterproviders/k8s/options.go new file mode 100644 index 0000000000000000000000000000000000000000..c7708009c67aec829070f71dbd44cdf19fd087dd --- /dev/null +++ b/cluster/clusterproviders/k8s/options.go @@ -0,0 +1,4 @@ +package k8s + +// Convenience type to refer to Option callables +type Option func(p *Provider) diff --git a/cluster/clusterproviders/test/log.go b/cluster/clusterproviders/test/log.go new file mode 100644 index 0000000000000000000000000000000000000000..daf1626c27f4ab0363802a2165422efe611667aa --- /dev/null +++ b/cluster/clusterproviders/test/log.go @@ -0,0 +1,11 @@ +package test + +import "gitee.com/simplexyz/simpleactor-go/log" + +var plog = log.New(log.DebugLevel, "[TEST]") + +// SetLogLevel sets the log level for the logger +// SetLogLevel is safe to be called concurrently +func SetLogLevel(level log.Level) { + plog.SetLevel(level) +} diff --git a/cluster/clusterproviders/test/test_provider.go b/cluster/clusterproviders/test/test_provider.go new file mode 100644 index 0000000000000000000000000000000000000000..cf81319b5104f60ba71650cdc449f3b1b0c0ec52 --- /dev/null +++ b/cluster/clusterproviders/test/test_provider.go @@ -0,0 +1,223 @@ +package test + +import ( + "sync" + "time" + + "gitee.com/simplexyz/simpleactor-go/cluster" + "gitee.com/simplexyz/simpleactor-go/log" + "golang.org/x/exp/maps" +) + +type ProviderConfig struct { + // ServiceTtl is the time to live for services. Default: 3s + ServiceTtl time.Duration + // RefreshTtl is the time between refreshes of the service ttl. Default: 1s + RefreshTtl time.Duration + // DeregisterCritical is the time after which a service is deregistered if it is not refreshed. Default: 10s + DeregisterCritical time.Duration +} + +type ProviderOption func(config *ProviderConfig) + +// WithTestProviderServiceTtl sets the service ttl. Default: 3s +func WithTestProviderServiceTtl(serviceTtl time.Duration) ProviderOption { + return func(config *ProviderConfig) { + config.ServiceTtl = serviceTtl + } +} + +// WithTestProviderRefreshTtl sets the refresh ttl. Default: 1s +func WithTestProviderRefreshTtl(refreshTtl time.Duration) ProviderOption { + return func(config *ProviderConfig) { + config.RefreshTtl = refreshTtl + } +} + +// WithTestProviderDeregisterCritical sets the deregister critical. Default: 10s +func WithTestProviderDeregisterCritical(deregisterCritical time.Duration) ProviderOption { + return func(config *ProviderConfig) { + config.DeregisterCritical = deregisterCritical + } +} + +type Provider struct { + memberList *cluster.MemberList + config *ProviderConfig + + agent *InMemAgent + id string + ttlReportTicker *time.Ticker +} + +func NewTestProvider(agent *InMemAgent, options ...ProviderOption) *Provider { + config := &ProviderConfig{ + ServiceTtl: time.Second * 3, + RefreshTtl: time.Second, + DeregisterCritical: time.Second * 10, + } + for _, option := range options { + option(config) + } + return &Provider{ + config: config, + agent: agent, + } +} + +func (t *Provider) StartMember(c *cluster.Cluster) error { + plog.Debug("start cluster member") + t.memberList = c.MemberList + host, port, err := c.ActorSystem.GetHostPort() + if err != nil { + return err + } + kinds := c.GetClusterKinds() + t.id = c.ActorSystem.ID + t.startTtlReport() + t.agent.SubscribeStatusUpdate(t.notifyStatuses) + t.agent.RegisterService(NewAgentServiceStatus(t.id, host, port, kinds)) + return nil +} + +func (t *Provider) StartClient(cluster *cluster.Cluster) error { + t.memberList = cluster.MemberList + t.id = cluster.ActorSystem.ID + t.agent.SubscribeStatusUpdate(t.notifyStatuses) + t.agent.ForceUpdate() + return nil +} + +func (t *Provider) Shutdown(_ bool) error { + plog.Debug("Unregistering service", log.String("service", t.id)) + if t.ttlReportTicker != nil { + t.ttlReportTicker.Stop() + } + t.agent.DeregisterService(t.id) + return nil +} + +// notifyStatuses notifies the cluster that the service status has changed. +func (t *Provider) notifyStatuses() { + statuses := t.agent.GetStatusHealth() + + plog.Debug("TestAgent response", log.Object("statuses", statuses)) + members := make([]*cluster.Member, 0, len(statuses)) + for _, status := range statuses { + copiedKinds := make([]string, 0, len(status.Kinds)) + copiedKinds = append(copiedKinds, status.Kinds...) + + members = append(members, &cluster.Member{ + Id: status.ID, + Port: int32(status.Port), + Host: status.Host, + Kinds: copiedKinds, + }) + } + t.memberList.UpdateClusterTopology(members) +} + +// startTtlReport starts the ttl report loop. +func (t *Provider) startTtlReport() { + t.ttlReportTicker = time.NewTicker(t.config.RefreshTtl) + go func() { + for range t.ttlReportTicker.C { + t.agent.RefreshServiceTTL(t.id) + } + }() +} + +type InMemAgent struct { + services map[string]AgentServiceStatus + servicesLock *sync.RWMutex + + statusUpdateHandlers []func() + statusUpdateHandlersLock *sync.RWMutex +} + +func NewInMemAgent() *InMemAgent { + return &InMemAgent{ + services: make(map[string]AgentServiceStatus), + servicesLock: &sync.RWMutex{}, + statusUpdateHandlers: make([]func(), 0), + statusUpdateHandlersLock: &sync.RWMutex{}, + } +} + +// RegisterService registers a AgentServiceStatus with the agent. +func (m *InMemAgent) RegisterService(registration AgentServiceStatus) { + m.servicesLock.Lock() + m.services[registration.ID] = registration + m.servicesLock.Unlock() + + m.onStatusUpdate() +} + +// DeregisterService removes a service from the agent. +func (m *InMemAgent) DeregisterService(id string) { + m.servicesLock.Lock() + delete(m.services, id) + m.servicesLock.Unlock() + + m.onStatusUpdate() +} + +// RefreshServiceTTL updates the TTL of all services. +func (m *InMemAgent) RefreshServiceTTL(id string) { + m.servicesLock.Lock() + defer m.servicesLock.Unlock() + if service, ok := m.services[id]; ok { + service.TTL = time.Now() + m.services[id] = service + } +} + +// SubscribeStatusUpdate registers a handler that will be called when the service map changes. +func (m *InMemAgent) SubscribeStatusUpdate(handler func()) { + m.statusUpdateHandlersLock.Lock() + defer m.statusUpdateHandlersLock.Unlock() + m.statusUpdateHandlers = append(m.statusUpdateHandlers, handler) +} + +// GetStatusHealth returns the health of the service. +func (m *InMemAgent) GetStatusHealth() []AgentServiceStatus { + m.servicesLock.RLock() + defer m.servicesLock.RUnlock() + return maps.Values(m.services) +} + +// ForceUpdate is used to trigger a status update event. +func (m *InMemAgent) ForceUpdate() { + m.onStatusUpdate() +} + +func (m *InMemAgent) onStatusUpdate() { + m.statusUpdateHandlersLock.RLock() + defer m.statusUpdateHandlersLock.RUnlock() + for _, handler := range m.statusUpdateHandlers { + handler() + } +} + +type AgentServiceStatus struct { + ID string + TTL time.Time // last alive time + Host string + Port int + Kinds []string +} + +// NewAgentServiceStatus creates a new AgentServiceStatus. +func NewAgentServiceStatus(id string, host string, port int, kinds []string) AgentServiceStatus { + return AgentServiceStatus{ + ID: id, + TTL: time.Now(), + Host: host, + Port: port, + Kinds: kinds, + } +} + +func (a AgentServiceStatus) Alive() bool { + return time.Now().Sub(a.TTL) <= (time.Second * 5) +} diff --git a/cluster/clusterproviders/zk/config.go b/cluster/clusterproviders/zk/config.go new file mode 100644 index 0000000000000000000000000000000000000000..45e784cb87f555890e0d280e066356c4213785b6 --- /dev/null +++ b/cluster/clusterproviders/zk/config.go @@ -0,0 +1,87 @@ +package zk + +import ( + "time" +) + +const baseKey = `/protoactor` + +type Option func(*config) + +// WithAuth set zk auth +func WithAuth(scheme string, credential string) Option { + return func(o *config) { + o.Auth = authConfig{Scheme: scheme, Credential: credential} + } +} + +// WithBaseKey set actors base key +func WithBaseKey(key string) Option { + return func(o *config) { + if isStrBlank(key) { + o.BaseKey = baseKey + } else { + o.BaseKey = formatBaseKey(key) + } + } +} + +// WithSessionTimeout set zk session timeout +func WithSessionTimeout(tm time.Duration) Option { + return func(o *config) { + o.SessionTimeout = tm + } +} + +// WithRoleChangedListener triggered on self role changed +func WithRoleChangedListener(l RoleChangedListener) Option { + return func(o *config) { + o.RoleChanged = l + } +} + +func WithRoleChangedFunc(f OnRoleChangedFunc) Option { + return func(o *config) { + o.RoleChanged = f + } +} + +func withEndpoints(e []string) Option { + return func(o *config) { + o.Endpoints = e + } +} + +type authConfig struct { + Scheme string + Credential string +} + +func (za authConfig) isEmpty() bool { + return za.Scheme == "" && za.Credential == "" +} + +type RoleChangedListener interface { + OnRoleChanged(RoleType) +} + +type OnRoleChangedFunc func(RoleType) + +func (fn OnRoleChangedFunc) OnRoleChanged(rt RoleType) { + fn(rt) +} + +type config struct { + BaseKey string + Endpoints []string + SessionTimeout time.Duration + Auth authConfig + RoleChanged RoleChangedListener +} + +func defaultConfig() *config { + return &config{ + BaseKey: baseKey, + SessionTimeout: time.Second * 10, + } +} diff --git a/cluster/clusterproviders/zk/log.go b/cluster/clusterproviders/zk/log.go new file mode 100644 index 0000000000000000000000000000000000000000..fdeea87ee2b0d713b9b559ab1fe292ba9da9d8c0 --- /dev/null +++ b/cluster/clusterproviders/zk/log.go @@ -0,0 +1,11 @@ +package zk + +import "gitee.com/simplexyz/simpleactor-go/log" + +var plog = log.New(log.InfoLevel, "[CLU/ZK]") + +// SetLogLevel sets the log level for the logger +// SetLogLevel is safe to be called concurrently +func SetLogLevel(level log.Level) { + plog.SetLevel(level) +} diff --git a/cluster/clusterproviders/zk/misc_test.go b/cluster/clusterproviders/zk/misc_test.go new file mode 100644 index 0000000000000000000000000000000000000000..67627d57e03283d92faf3d53f4e9381d76d8d295 --- /dev/null +++ b/cluster/clusterproviders/zk/misc_test.go @@ -0,0 +1,103 @@ +package zk + +import ( + "strings" + "testing" + + "gitee.com/simplexyz/simpleactor-go/cluster" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/suite" +) + +func (suite *MiscTestSuite) TestIntToStr() { + suite.Equal("100", intToStr(100)) +} + +func (suite *MiscTestSuite) TestStrToInt() { + suite.Equal(100, strToInt("100")) + suite.Equal(0, strToInt("str0")) +} + +func (suite *MiscTestSuite) TestIsStrBlank() { + suite.True(isStrBlank("")) + suite.False(isStrBlank("e")) +} + +func (suite *MiscTestSuite) TestFormatBaseKey() { + suite.Equal("/", formatBaseKey("")) + suite.Equal("/a/b/c", formatBaseKey("a/b/c")) + suite.Equal("/", formatBaseKey("/")) + suite.Equal("/a", formatBaseKey("a/")) +} + +func (suite *MiscTestSuite) TestParseSeq() { + seq, err := parseSeq("/proto.actors/dev/my_cluster/_c_f4245284934bdaf384102b6fc233bd14-actor-0000000042") + suite.Nil(err) + suite.Equal(42, seq) +} + +func (suite *MiscTestSuite) TestStringContains() { + suite.True(stringContains([]string{"a", "b"}, "b")) + suite.False(stringContains([]string{"a", "b"}, "c")) +} + +func (suite *MiscTestSuite) TestMapString() { + orig := []string{"A", "B", "C"} + suite.ElementsMatch([]string{"a", "b", "c"}, mapString(orig, strings.ToLower)) + suite.ElementsMatch([]string{"A", "B", "C"}, orig) +} + +func (suite *MiscTestSuite) TestSafeRun() { + suite.NotPanics(func() { safeRun(func() { panic("don't worry, should panic here") }) }) +} + +func (suite *MiscTestSuite) TestNode() { + node := NewNode("pod1", "192.168.0.1", 7788, []string{"kind1", "kind2"}) + host, port := node.GetAddress() + suite.Equal("192.168.0.1", host) + suite.Equal(7788, port) + + suite.Equal("192.168.0.1:7788", node.GetAddressString()) + + suite.True(NewNode("pod1", "", 0, nil).Equal(node)) + + _, ok := node.GetMeta("key") + suite.False(ok) + + node.SetMeta(metaKeySeq, "100") + suite.Equal(100, node.GetSeq()) + + suite.Equal(&cluster.Member{ + Id: "pod1", + Host: "192.168.0.1", + Port: int32(7788), + Kinds: []string{"kind1", "kind2"}, + }, node.MemberStatus()) + + data, err := node.Serialize() + suite.Nil(err) + suite.Equal(`{"id":"pod1","name":"pod1","host":"192.168.0.1","address":"192.168.0.1","port":7788,"kinds":["kind1","kind2"],"alive":true}`, string(data)) + + node2 := &Node{} + err = node2.Deserialize([]byte(`{"id":"pod1","name":"pod1","host":"192.168.0.1","address":"192.168.0.1","port":7788,"kinds":["kind1","kind2"],"alive":true}`)) + suite.Nil(err) + node.Meta = nil + suite.Equal(node2, node) +} + +type MiscTestSuite struct { + suite.Suite + ctrl *gomock.Controller +} + +func (suite *MiscTestSuite) SetupTest() { + suite.ctrl = gomock.NewController(suite.T()) +} + +func (suite *MiscTestSuite) TearDownTest() { + suite.ctrl.Finish() +} + +func TestMiscTestSuite(t *testing.T) { + suite.Run(t, new(MiscTestSuite)) +} diff --git a/cluster/clusterproviders/zk/node.go b/cluster/clusterproviders/zk/node.go new file mode 100644 index 0000000000000000000000000000000000000000..e55846066258857791a2cdc1b6fbf7389749c563 --- /dev/null +++ b/cluster/clusterproviders/zk/node.go @@ -0,0 +1,109 @@ +package zk + +import ( + "encoding/json" + "fmt" + + "gitee.com/simplexyz/simpleactor-go/cluster" +) + +const ( + metaKeyID = "id" + metaKeySeq = "seq" +) + +type Node struct { + ID string `json:"id"` + Name string `json:"name"` + Host string `json:"host"` + Address string `json:"address"` + Port int `json:"port"` + Kinds []string `json:"kinds"` + Meta map[string]string `json:"-"` + Alive bool `json:"alive"` +} + +func NewNode(name, host string, port int, kinds []string) *Node { + return &Node{ + ID: name, + Name: name, + Address: host, + Host: host, + Port: port, + Kinds: kinds, + Meta: map[string]string{}, + Alive: true, + } +} + +func (n *Node) GetAddress() (host string, port int) { + host = n.Host + port = n.Port + if host == "" { + host = n.Address + } + return +} + +func (n *Node) GetAddressString() string { + h, p := n.GetAddress() + return fmt.Sprintf("%v:%v", h, p) +} + +func (n *Node) Equal(other *Node) bool { + if n == nil || other == nil { + return false + } + if n == other { + return true + } + return n.ID == other.ID +} + +func (n *Node) GetMeta(name string) (string, bool) { + if n.Meta == nil { + return "", false + } + val, ok := n.Meta[name] + return val, ok +} + +func (n *Node) GetSeq() int { + if seqStr, ok := n.GetMeta(metaKeySeq); ok { + return strToInt(seqStr) + } + return 0 +} + +func (n *Node) MemberStatus() *cluster.Member { + host, port := n.GetAddress() + kinds := n.Kinds + if kinds == nil { + kinds = []string{} + } + return &cluster.Member{ + Id: n.ID, + Host: host, + Port: int32(port), + Kinds: kinds, + } +} + +func (n *Node) SetMeta(name string, val string) { + if n.Meta == nil { + n.Meta = map[string]string{} + } + n.Meta[name] = val +} + +func (n *Node) Serialize() ([]byte, error) { + data, err := json.Marshal(n) + if err != nil { + return nil, err + } + return data, nil +} + +func (n *Node) Deserialize(data []byte) error { + return json.Unmarshal(data, n) +} diff --git a/cluster/clusterproviders/zk/singleton.go b/cluster/clusterproviders/zk/singleton.go new file mode 100644 index 0000000000000000000000000000000000000000..280898e89d1e5427169cd7b67be39a80dbbcd158 --- /dev/null +++ b/cluster/clusterproviders/zk/singleton.go @@ -0,0 +1,54 @@ +package zk + +import ( + "sync" + + "gitee.com/simplexyz/simpleactor-go/actor" +) + +type SingletonScheduler struct { + sync.Mutex + root *actor.RootContext + props []*actor.Props + pids []*actor.PID +} + +func NewSingletonScheduler(rc *actor.RootContext) *SingletonScheduler { + return &SingletonScheduler{root: rc} +} + +func (s *SingletonScheduler) FromFunc(f actor.ReceiveFunc) *SingletonScheduler { + s.Lock() + defer s.Unlock() + s.props = append(s.props, actor.PropsFromFunc(f)) + return s +} + +func (s *SingletonScheduler) FromProducer(f actor.Producer) *SingletonScheduler { + s.Lock() + defer s.Unlock() + s.props = append(s.props, actor.PropsFromProducer(f)) + return s +} + +func (s *SingletonScheduler) OnRoleChanged(rt RoleType) { + s.Lock() + defer s.Unlock() + if rt == Follower { + if len(s.pids) > 0 { + plog.Info("I am follower, poison singleton actors") + for _, pid := range s.pids { + s.root.Poison(pid) + } + s.pids = nil + } + } else if rt == Leader { + if len(s.props) > 0 { + plog.Info("I am leader now, start singleton actors") + s.pids = make([]*actor.PID, len(s.props)) + for i, p := range s.props { + s.pids[i] = s.root.Spawn(p) + } + } + } +} diff --git a/cluster/clusterproviders/zk/utils.go b/cluster/clusterproviders/zk/utils.go new file mode 100644 index 0000000000000000000000000000000000000000..158672b068d944e70ac2410479389fd3f7ad0b3c --- /dev/null +++ b/cluster/clusterproviders/zk/utils.go @@ -0,0 +1,83 @@ +package zk + +import ( + "fmt" + "runtime" + "strconv" + "strings" + + "gitee.com/simplexyz/simpleactor-go/log" +) + +func intToStr(i int) string { + return strconv.FormatInt(int64(i), 10) +} + +func strToInt(s string) int { + i, _ := strconv.ParseInt(s, 10, 64) + return int(i) +} + +func isStrBlank(s string) bool { return s == "" } + +func formatBaseKey(s string) string { + if !strings.HasPrefix(s, "/") { + s = "/" + s + } + if strings.HasSuffix(s, "/") && s != "/" { + s = strings.TrimSuffix(s, "/") + } + return s +} + +func parseSeq(path string) (int, error) { + parts := strings.Split(path, "-") + if len(parts) == 1 { + parts = strings.Split(path, "__") + } + return strconv.Atoi(parts[len(parts)-1]) +} + +func stringContains(list []string, str string) bool { + for _, v := range list { + if v == str { + return true + } + } + return false +} + +func mapString(list []string, fn func(string) string) []string { + l := make([]string, len(list)) + for i, str := range list { + l[i] = fn(str) + } + return l +} + +func safeRun(fn func()) { + defer func() { + if r := recover(); r != nil { + plog.Warn("OnRoleChanged.", log.Error(fmt.Errorf("%v\n%s", r, string(getRunTimeStack())))) + } + }() + fn() +} + +func getRunTimeStack() []byte { + const size = 64 << 10 + buf := make([]byte, size) + return buf[:runtime.Stack(buf, false)] +} + +func getParentDir(path string) string { + parent := path[:strings.LastIndex(path, "/")] + if parent == "" { + return "/" + } + return parent +} + +func joinPath(paths ...string) string { + return strings.Join(paths, "/") +} diff --git a/cluster/clusterproviders/zk/zk_conn.go b/cluster/clusterproviders/zk/zk_conn.go new file mode 100644 index 0000000000000000000000000000000000000000..e1148020e2fac2de2f10b140cdfa5ab48674f495 --- /dev/null +++ b/cluster/clusterproviders/zk/zk_conn.go @@ -0,0 +1,94 @@ +package zk + +import ( + "time" + + "github.com/go-zookeeper/zk" +) + +type zkConn interface { + AddAuth(scheme string, auth []byte) error + Exists(path string) (bool, *zk.Stat, error) + Create(path string, data []byte, flags int32, acl []zk.ACL) (string, error) + Delete(path string, version int32) error + Get(path string) ([]byte, *zk.Stat, error) + Children(path string) ([]string, *zk.Stat, error) + ChildrenW(path string) ([]string, *zk.Stat, <-chan zk.Event, error) + CreateProtectedEphemeralSequential(path string, data []byte, acl []zk.ACL) (string, error) + Close() +} + +func connectZk(servers []string, sessionTimeout time.Duration, opts ...zkConnOpt) (zkConn, error) { + opt := newZkOptions(opts...) + var conn *zk.Conn + var err error + if opt.ecb != nil { + conn, _, err = zk.Connect(servers, sessionTimeout, zk.WithEventCallback(opt.ecb)) + } else { + conn, _, err = zk.Connect(servers, sessionTimeout) + } + if err != nil { + return nil, err + } + return &zkConnImpl{conn: conn}, nil +} + +type zkConnImpl struct { + conn *zk.Conn +} + +func (impl *zkConnImpl) AddAuth(scheme string, auth []byte) error { + return impl.conn.AddAuth(scheme, auth) +} + +func (impl *zkConnImpl) Exists(path string) (bool, *zk.Stat, error) { + return impl.conn.Exists(path) +} + +func (impl *zkConnImpl) Create(path string, data []byte, flags int32, acl []zk.ACL) (string, error) { + return impl.conn.Create(path, data, flags, acl) +} + +func (impl *zkConnImpl) Delete(path string, version int32) error { + return impl.conn.Delete(path, version) +} + +func (impl *zkConnImpl) Get(path string) ([]byte, *zk.Stat, error) { + return impl.conn.Get(path) +} + +func (impl *zkConnImpl) Children(path string) ([]string, *zk.Stat, error) { + return impl.conn.Children(path) +} + +func (impl *zkConnImpl) ChildrenW(path string) ([]string, *zk.Stat, <-chan zk.Event, error) { + return impl.conn.ChildrenW(path) +} + +func (impl *zkConnImpl) CreateProtectedEphemeralSequential(path string, data []byte, acl []zk.ACL) (string, error) { + return impl.conn.CreateProtectedEphemeralSequential(path, data, acl) +} + +func (impl *zkConnImpl) Close() { + impl.conn.Close() +} + +type zkoption struct { + ecb zk.EventCallback +} + +func newZkOptions(opts ...zkConnOpt) *zkoption { + opt := &zkoption{} + for _, fn := range opts { + fn(opt) + } + return opt +} + +type zkConnOpt func(*zkoption) + +func WithEventCallback(cb zk.EventCallback) zkConnOpt { + return func(o *zkoption) { + o.ecb = cb + } +} diff --git a/cluster/clusterproviders/zk/zk_provider.go b/cluster/clusterproviders/zk/zk_provider.go new file mode 100644 index 0000000000000000000000000000000000000000..cc737cb2fb5eb4e4723799ecd810b71c6645265e --- /dev/null +++ b/cluster/clusterproviders/zk/zk_provider.go @@ -0,0 +1,523 @@ +package zk + +import ( + "context" + "fmt" + "net" + "strconv" + "strings" + "time" + + "gitee.com/simplexyz/simpleactor-go/cluster" + "gitee.com/simplexyz/simpleactor-go/log" + "github.com/go-zookeeper/zk" +) + +var _ cluster.ClusterProvider = new(Provider) + +type RoleType int + +const ( + Follower RoleType = iota + Leader +) + +func (r RoleType) String() string { + if r == Leader { + return "LEADER" + } + return "FOLLOWER" +} + +type Provider struct { + cluster *cluster.Cluster + baseKey string + clusterName string + clusterKey string + deregistered bool + shutdown bool + self *Node + members map[string]*Node // all, contains self. + clusterError error + conn zkConn + revision uint64 + fullpath string + roleChangedListener RoleChangedListener + role RoleType + roleChangedChan chan RoleType +} + +// New zk cluster provider with config +func New(endpoints []string, opts ...Option) (*Provider, error) { + zkCfg := defaultConfig() + withEndpoints(endpoints)(zkCfg) + for _, fn := range opts { + fn(zkCfg) + } + p := &Provider{ + cluster: &cluster.Cluster{}, + baseKey: zkCfg.BaseKey, + clusterKey: "", + clusterName: "", + deregistered: false, + shutdown: false, + self: &Node{}, + members: map[string]*Node{}, + revision: 0, + fullpath: "", + roleChangedListener: zkCfg.RoleChanged, + roleChangedChan: make(chan RoleType, 1), + role: Follower, + } + conn, err := connectZk(endpoints, zkCfg.SessionTimeout, WithEventCallback(p.onEvent)) + if err != nil { + plog.Error("connect zk fail", log.Error(err)) + return nil, err + } + if auth := zkCfg.Auth; !auth.isEmpty() { + if err = conn.AddAuth(auth.Scheme, []byte(auth.Credential)); err != nil { + plog.Error("auth failure.", log.String("scheme", auth.Scheme), log.String("cred", auth.Credential), log.Error(err)) + return nil, err + } + } + p.conn = conn + + return p, nil +} + +func (p *Provider) IsLeader() bool { + return p.role == Leader +} + +func (p *Provider) init(c *cluster.Cluster) error { + p.cluster = c + addr := p.cluster.ActorSystem.Address() + host, port, err := splitHostPort(addr) + if err != nil { + return err + } + + p.cluster = c + p.clusterName = p.cluster.Config.Name + p.clusterKey = joinPath(p.baseKey, p.clusterName) + knownKinds := c.GetClusterKinds() + nodeName := fmt.Sprintf("%v@%v:%v", p.clusterName, host, port) + p.self = NewNode(nodeName, host, port, knownKinds) + p.self.SetMeta(metaKeyID, p.getID()) + + if err = p.createClusterNode(p.clusterKey); err != nil { + return err + } + return nil +} + +func (p *Provider) StartMember(c *cluster.Cluster) error { + if err := p.init(c); err != nil { + plog.Error("init fail " + err.Error()) + return err + } + + p.startRoleChangedNotifyLoop() + + // register self + if err := p.registerService(); err != nil { + plog.Error("register service fail " + err.Error()) + return err + } + plog.Info("StartMember register service.", log.String("node", p.self.ID), log.String("seq", p.self.Meta[metaKeySeq])) + + // fetch member list + nodes, version, err := p.fetchNodes() + if err != nil { + plog.Error("fetch nodes fail " + err.Error()) + return err + } + // initialize members + p.updateNodesWithSelf(nodes, version) + p.publishClusterTopologyEvent() + p.updateLeadership(nodes) + p.startWatching(true) + + return nil +} + +func (p *Provider) StartClient(c *cluster.Cluster) error { + if err := p.init(c); err != nil { + return err + } + nodes, version, err := p.fetchNodes() + if err != nil { + return err + } + // initialize members + p.updateNodes(nodes, version) + p.publishClusterTopologyEvent() + p.startWatching(false) + + return nil +} + +func (p *Provider) Shutdown(graceful bool) error { + p.shutdown = true + if !p.deregistered { + p.updateLeadership(nil) + err := p.deregisterService() + if err != nil { + plog.Error("deregisterMember", log.Error(err)) + return err + } + p.deregistered = true + } + return nil +} + +func (p *Provider) getID() string { + return p.self.ID +} + +func (p *Provider) registerService() error { + data, err := p.self.Serialize() + if err != nil { + plog.Error("registerService Serialize fail.", log.Error(err)) + return err + } + + path, err := p.createEphemeralChildNode(data) + if err != nil { + plog.Error("createEphemeralChildNode fail.", log.String("node", p.clusterKey), log.Error(err)) + return err + } + p.fullpath = path + seq, _ := parseSeq(path) + p.self.SetMeta(metaKeySeq, intToStr(seq)) + plog.Info("RegisterService.", log.String("id", p.self.ID), log.Int("seq", seq)) + + return nil +} + +func (p *Provider) createClusterNode(dir string) error { + if dir == "/" { + return nil + } + exist, _, err := p.conn.Exists(dir) + if err != nil { + plog.Error("check exist of node fail", log.String("dir", dir), log.Error(err)) + return err + } + if exist { + return nil + } + if err = p.createClusterNode(getParentDir(dir)); err != nil { + return err + } + if _, err = p.conn.Create(dir, []byte{}, 0, zk.WorldACL(zk.PermAll)); err != nil { + plog.Error("create dir node fail", log.String("dir", dir), log.Error(err)) + return err + } + return nil +} + +func (p *Provider) deregisterService() error { + if p.fullpath != "" { + p.conn.Delete(p.fullpath, -1) + } + p.fullpath = "" + p.conn.Close() + return nil +} + +func (p *Provider) keepWatching(ctx context.Context, registerSelf bool) error { + evtChan, err := p.addWatcher(ctx, p.clusterKey) + if err != nil { + plog.Error("list children fail", log.String("node", p.clusterKey), log.Error(err)) + return err + } + + return p._keepWatching(registerSelf, evtChan) +} + +func (p *Provider) addWatcher(ctx context.Context, clusterKey string) (<-chan zk.Event, error) { + _, stat, evtChan, err := p.conn.ChildrenW(clusterKey) + if err != nil { + plog.Error("list children fail", log.String("node", clusterKey), log.Error(err)) + return nil, err + } + + plog.Info("KeepWatching cluster.", log.String("cluster", clusterKey), log.Int("children", int(stat.NumChildren))) + if !p.isChildrenChanged(ctx, stat) { + return evtChan, nil + } + + plog.Info("Chilren changed, wait 1 sec and watch again", log.Int("old_cversion", int(p.revision)), log.Int("new_revison", int(stat.Cversion))) + time.Sleep(1 * time.Second) + nodes, version, err := p.fetchNodes() + if err != nil { + return nil, err + } + // initialize members + p.updateNodes(nodes, version) + p.publishClusterTopologyEvent() + p.updateLeadership(nodes) + return p.addWatcher(ctx, clusterKey) +} + +func (p *Provider) isChildrenChanged(ctx context.Context, stat *zk.Stat) bool { + return stat.Cversion != int32(p.revision) +} + +func (p *Provider) _keepWatching(registerSelf bool, stream <-chan zk.Event) error { + event := <-stream + if err := event.Err; err != nil { + plog.Error("Failure watching service.", log.Error(err)) + if registerSelf && p.clusterNotContainsSelfPath() { + plog.Info("Register info lost, register self again") + p.registerService() + } + return err + } + nodes, version, err := p.fetchNodes() + if err != nil { + plog.Error("Failure fetch nodes when watching service.", log.Error(err)) + return err + } + if !p.containSelf(nodes) && registerSelf { + // i am lost, register self + if err = p.registerService(); err != nil { + return err + } + // reload nodes + nodes, version, err = p.fetchNodes() + if err != nil { + plog.Error("Failure fetch nodes when watching service.", log.Error(err)) + return err + } + } + p.updateNodes(nodes, version) + p.publishClusterTopologyEvent() + if registerSelf { + p.updateLeadership(nodes) + } + + return nil +} + +func (p *Provider) clusterNotContainsSelfPath() bool { + children, _, err := p.conn.Children(p.clusterKey) + return err == nil && !stringContains(mapString(children, func(s string) string { + return joinPath(p.clusterKey, s) + }), p.fullpath) +} + +func (p *Provider) containSelf(ns []*Node) bool { + for _, node := range ns { + if p.self != nil && node.ID == p.self.ID { + return true + } + } + return false +} + +func (p *Provider) startRoleChangedNotifyLoop() { + go func() { + for !p.shutdown { + role := <-p.roleChangedChan + if lis := p.roleChangedListener; lis != nil { + safeRun(func() { lis.OnRoleChanged(role) }) + } + } + }() +} + +func (p *Provider) updateLeadership(ns []*Node) { + role := Follower + if p.isLeaderOf(ns) { + role = Leader + } + if role != p.role { + plog.Info("Role changed.", log.String("from", p.role.String()), log.String("to", role.String())) + p.role = role + p.roleChangedChan <- role + } +} + +func (p *Provider) onEvent(evt zk.Event) { + plog.Debug("Zookeeper event.", log.String("type", evt.Type.String()), log.String("state", evt.State.String()), log.String("path", evt.Path)) + if evt.Type != zk.EventSession { + return + } + switch evt.State { + case zk.StateConnecting, zk.StateDisconnected, zk.StateExpired: + if p.role == Leader { + plog.Info("Role changed.", log.String("from", Leader.String()), log.String("to", Follower.String())) + p.role = Follower + p.roleChangedChan <- Follower + } + case zk.StateConnected, zk.StateHasSession: + } +} + +func (p *Provider) isLeaderOf(ns []*Node) bool { + if len(ns) == 1 && p.self != nil && ns[0].ID == p.self.ID { + return true + } + var minSeq int + for _, node := range ns { + if seq := node.GetSeq(); (seq > 0 && seq < minSeq) || minSeq == 0 { + minSeq = seq + } + } + for _, node := range ns { + if p.self != nil && node.ID == p.self.ID { + return minSeq > 0 && minSeq == p.self.GetSeq() + } + } + return false +} + +func (p *Provider) startWatching(registerSelf bool) { + ctx := context.TODO() + go func() { + for !p.shutdown { + if err := p.keepWatching(ctx, registerSelf); err != nil { + plog.Error("Failed to keepWatching.", log.Error(err)) + p.clusterError = err + } + } + }() +} + +// GetHealthStatus returns an error if the cluster health status has problems +func (p *Provider) GetHealthStatus() error { + return p.clusterError +} + +func (p *Provider) fetchNodes() ([]*Node, int32, error) { + children, stat, err := p.conn.Children(p.clusterKey) + if err != nil { + plog.Error("FetchNodes fail.", log.String("node", p.clusterKey), log.Error(err)) + return nil, 0, err + } + + var nodes []*Node + for _, short := range children { + long := joinPath(p.clusterKey, short) + value, _, err := p.conn.Get(long) + if err != nil { + plog.Error("FetchNodes fail.", log.String("node", long), log.Error(err)) + return nil, stat.Cversion, err + } + n := Node{Meta: make(map[string]string)} + if err := n.Deserialize(value); err != nil { + plog.Error("FetchNodes Deserialize fail.", log.String("node", long), log.String("val", string(value)), log.Error(err)) + return nil, stat.Cversion, err + } + seq, err := parseSeq(long) + if err != nil { + plog.Error("FetchNodes parse seq fail.", log.String("node", long), log.String("val", string(value)), log.Error(err)) + } else { + n.SetMeta(metaKeySeq, intToStr(seq)) + } + plog.Info("FetchNodes new node.", log.String("id", n.ID), log.String("path", long), log.Int("seq", seq)) + nodes = append(nodes, &n) + } + return p.uniqNodes(nodes), stat.Cversion, nil +} + +func (p *Provider) updateNodes(members []*Node, reversion int32) { + nm := make(map[string]*Node) + for _, n := range members { + nm[n.ID] = n + } + p.members = nm + p.revision = uint64(reversion) +} + +func (p *Provider) uniqNodes(nodes []*Node) []*Node { + nodeMap := make(map[string]*Node) + for _, node := range nodes { + if n, ok := nodeMap[node.GetAddressString()]; ok { + // keep node with higher version + if node.GetSeq() > n.GetSeq() { + nodeMap[node.GetAddressString()] = node + } + } else { + nodeMap[node.GetAddressString()] = node + } + } + + var out []*Node + for _, node := range nodeMap { + out = append(out, node) + } + return out +} + +func (p *Provider) updateNodesWithSelf(members []*Node, version int32) { + p.updateNodes(members, version) + p.members[p.self.ID] = p.self +} + +func (p *Provider) createClusterTopologyEvent() []*cluster.Member { + res := make([]*cluster.Member, len(p.members)) + i := 0 + for _, m := range p.members { + res[i] = m.MemberStatus() + i++ + } + return res +} + +func (p *Provider) publishClusterTopologyEvent() { + res := p.createClusterTopologyEvent() + plog.Info("Update cluster.", log.Int("members", len(res))) + p.cluster.MemberList.UpdateClusterTopology(res) +} + +func splitHostPort(addr string) (host string, port int, err error) { + if h, p, e := net.SplitHostPort(addr); e != nil { + if addr != "nonhost" { + err = e + } + host = "nonhost" + port = -1 + } else { + host = h + port, err = strconv.Atoi(p) + } + return +} + +func (pro *Provider) createEphemeralChildNode(data []byte) (string, error) { + acl := zk.WorldACL(zk.PermAll) + prefix := joinPath(pro.clusterKey, "actor-") + path := "" + var err error + for i := 0; i < 3; i++ { + path, err = pro.conn.CreateProtectedEphemeralSequential(prefix, data, acl) + if err == zk.ErrNoNode { + // Create parent node. + parts := strings.Split(pro.clusterKey, "/") + pth := "" + for _, p := range parts[1:] { + var exists bool + pth += "/" + p + exists, _, err = pro.conn.Exists(pth) + if err != nil { + return "", err + } + if exists == true { + continue + } + _, err = pro.conn.Create(pth, []byte{}, 0, acl) + if err != nil && err != zk.ErrNodeExists { + return "", err + } + } + } else if err == nil { + break + } else { + return "", err + } + } + return path, err +} diff --git a/cluster/clusterproviders/zk/zk_provider_test.go b/cluster/clusterproviders/zk/zk_provider_test.go new file mode 100644 index 0000000000000000000000000000000000000000..9d8adf53e18827551a0e73802526c71cb7ff6bd7 --- /dev/null +++ b/cluster/clusterproviders/zk/zk_provider_test.go @@ -0,0 +1,77 @@ +package zk + +import ( + "sync/atomic" + "testing" + "time" + + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/cluster" + "gitee.com/simplexyz/simpleactor-go/cluster/identitylookup/disthash" + "gitee.com/simplexyz/simpleactor-go/log" + "gitee.com/simplexyz/simpleactor-go/remote" + "github.com/stretchr/testify/suite" +) + +type ZookeeperTestSuite struct { + suite.Suite +} + +func (suite *ZookeeperTestSuite) SetupTest() { + plog.SetLevel(log.ErrorLevel) +} + +func (suite *ZookeeperTestSuite) TearDownTest() { +} + +func TestZookeeperTestSuite(t *testing.T) { + suite.Run(t, new(ZookeeperTestSuite)) +} + +type ClusterAndSystem struct { + Cluster *cluster.Cluster + System *actor.ActorSystem +} + +func (self *ClusterAndSystem) Shutdown() { + self.Cluster.Shutdown(true) +} + +func (suite *ZookeeperTestSuite) start(name string, opts ...cluster.ConfigOption) *ClusterAndSystem { + cp, _ := New([]string{`localhost:8000`}) + remoteConfig := remote.Configure("localhost", 0) + config := cluster.Configure(name, cp, disthash.New(), remoteConfig, opts...) + system := actor.NewActorSystem() + c := cluster.New(system, config) + c.StartMember() + return &ClusterAndSystem{Cluster: c, System: system} +} + +func (suite *ZookeeperTestSuite) TestEmptyExecute() { + name := `cluster0` + suite.start(name).Shutdown() +} + +func (suite *ZookeeperTestSuite) TestMultiNodes() { + var actorCount int32 + props := actor.PropsFromFunc(func(ctx actor.Context) { + switch ctx.Message().(type) { + case *actor.Started: + atomic.AddInt32(&actorCount, 1) + } + }) + helloKind := cluster.NewKind("hello", props) + + name := `cluster1` + c1 := suite.start(name, cluster.WithKinds(helloKind)) + defer c1.Shutdown() + c2 := suite.start(name, cluster.WithKinds(helloKind)) + defer c2.Shutdown() + c1.Cluster.Get(`a1`, `hello`) + c2.Cluster.Get(`a2`, `hello`) + for actorCount != 2 { + time.Sleep(time.Microsecond * 5) + } + suite.Assert().Equal(2, c1.Cluster.MemberList.Members().Len(), "Expected 2 members in the cluster") + suite.Assert().Equal(2, c2.Cluster.MemberList.Members().Len(), "Expected 2 members in the cluster") +} diff --git a/cluster/config.go b/cluster/config.go new file mode 100644 index 0000000000000000000000000000000000000000..c343f8a35c2a7b38e07d120233cf9ae94548d8e9 --- /dev/null +++ b/cluster/config.go @@ -0,0 +1,137 @@ +package cluster + +import ( + "fmt" + "time" + + "gitee.com/simplexyz/simpleactor-go/actor" + + "gitee.com/simplexyz/simpleactor-go/remote" +) + +type Config struct { + Name string + Address string + ClusterProvider ClusterProvider + IdentityLookup IdentityLookup + RemoteConfig *remote.Config + RequestTimeoutTime time.Duration + RequestsLogThrottlePeriod time.Duration + MaxNumberOfEventsInRequestLogThrottledPeriod int + ClusterContextProducer ContextProducer + MemberStrategyBuilder func(cluster *Cluster, kind string) MemberStrategy + Kinds map[string]*Kind + TimeoutTime time.Duration + GossipInterval time.Duration + GossipRequestTimeout time.Duration + GossipFanOut int + GossipMaxSend int + HeartbeatExpiration time.Duration // Gossip heartbeat timeout. If the member does not update its heartbeat within this period, it will be added to the BlockList + PubSubConfig *PubSubConfig +} + +func Configure(clusterName string, clusterProvider ClusterProvider, identityLookup IdentityLookup, remoteConfig *remote.Config, options ...ConfigOption) *Config { + config := &Config{ + Name: clusterName, + ClusterProvider: clusterProvider, + IdentityLookup: identityLookup, + RequestTimeoutTime: defaultActorRequestTimeout, + RequestsLogThrottlePeriod: defaultRequestsLogThrottlePeriod, + MemberStrategyBuilder: newDefaultMemberStrategy, + RemoteConfig: remoteConfig, + Kinds: make(map[string]*Kind), + ClusterContextProducer: newDefaultClusterContext, + MaxNumberOfEventsInRequestLogThrottledPeriod: defaultMaxNumberOfEvetsInRequestLogThrottledPeriod, + TimeoutTime: time.Second * 5, + GossipInterval: time.Millisecond * 300, + GossipRequestTimeout: time.Millisecond * 500, + GossipFanOut: 3, + GossipMaxSend: 50, + HeartbeatExpiration: time.Second * 20, + PubSubConfig: newPubSubConfig(), + } + + for _, option := range options { + option(config) + } + + return config +} + +// ToClusterContextConfig converts this cluster Config Context parameters +// into a valid ClusterContextConfig value and returns a pointer to its memory +func (c *Config) ToClusterContextConfig() *ClusterContextConfig { + clusterContextConfig := ClusterContextConfig{ + ActorRequestTimeout: c.RequestTimeoutTime, + RequestsLogThrottlePeriod: c.RequestsLogThrottlePeriod, + MaxNumberOfEventsInRequestLogThrottledPeriod: c.MaxNumberOfEventsInRequestLogThrottledPeriod, + RetryAction: defaultRetryAction, + requestLogThrottle: actor.NewThrottle( + int32(defaultMaxNumberOfEvetsInRequestLogThrottledPeriod), + defaultRequestsLogThrottlePeriod, + func(i int32) { + plog.Info(fmt.Sprintf("Throttled %d Request logs", i)) + }, + ), + } + return &clusterContextConfig +} + +func WithClusterIdentity(props *actor.Props, ci *ClusterIdentity) *actor.Props { + // inject the cluster identity into the actor context + p := props.Clone( + actor.WithOnInit(func(ctx actor.Context) { + SetClusterIdentity(ctx, ci) + })) + return p +} + +func withClusterReceiveMiddleware() actor.PropsOption { + return actor.WithReceiverMiddleware(func(next actor.ReceiverFunc) actor.ReceiverFunc { + return func(c actor.ReceiverContext, envelope *actor.MessageEnvelope) { + // the above code as a type switch + switch envelope.Message.(type) { + case *actor.Started: + handleStarted(c, next, envelope) + case *actor.Stopped: + handleStopped(c, next, envelope) + default: + next(c, envelope) + } + + return + } + }) +} + +func handleStopped(c actor.ReceiverContext, next actor.ReceiverFunc, envelope *actor.MessageEnvelope) { + /* + clusterKind.Dec(); + */ + cl := GetCluster(c.ActorSystem()) + identity := GetClusterIdentity(c) + + if identity != nil { + cl.ActorSystem.EventStream.Publish(&ActivationTerminating{ + Pid: c.Self(), + ClusterIdentity: identity, + }) + cl.PidCache.RemoveByValue(identity.Identity, identity.Kind, c.Self()) + } + + next(c, envelope) +} + +func handleStarted(c actor.ReceiverContext, next actor.ReceiverFunc, envelope *actor.MessageEnvelope) { + next(c, envelope) + cl := GetCluster(c.ActorSystem()) + identity := GetClusterIdentity(c) + + grainInit := &ClusterInit{ + Identity: identity, + Cluster: cl, + } + + ge := actor.WrapEnvelope(grainInit) + next(c, ge) +} diff --git a/cluster/config_opts.go b/cluster/config_opts.go new file mode 100644 index 0000000000000000000000000000000000000000..2b3d23b3025b1083866e4876c683ca4f11181991 --- /dev/null +++ b/cluster/config_opts.go @@ -0,0 +1,56 @@ +package cluster + +import "time" + +type ConfigOption func(config *Config) + +// WithRequestTimeout sets the request timeout. +func WithRequestTimeout(t time.Duration) ConfigOption { + return func(c *Config) { + c.RequestTimeoutTime = t + } +} + +// WithRequestsLogThrottlePeriod sets the requests log throttle period. +func WithRequestsLogThrottlePeriod(period time.Duration) ConfigOption { + return func(c *Config) { + c.RequestsLogThrottlePeriod = period + } +} + +// WithClusterContextProducer sets the cluster context producer. +func WithClusterContextProducer(producer ContextProducer) ConfigOption { + return func(c *Config) { + c.ClusterContextProducer = producer + } +} + +// WithMaxNumberOfEventsInRequestLogThrottlePeriod sets the max number of events in request log throttled period. +func WithMaxNumberOfEventsInRequestLogThrottlePeriod(maxNumber int) ConfigOption { + return func(c *Config) { + c.MaxNumberOfEventsInRequestLogThrottledPeriod = maxNumber + } +} + +func WithKinds(kinds ...*Kind) ConfigOption { + return func(c *Config) { + for _, kind := range kinds { + c.Kinds[kind.Kind] = kind + } + } +} + +// WithPubSubSubscriberTimeout sets a timeout used when delivering a message batch to a subscriber. +// Default is 5s. +func WithPubSubSubscriberTimeout(timeout time.Duration) ConfigOption { + return func(c *Config) { + c.PubSubConfig.SubscriberTimeout = timeout + } +} + +// WithHeartbeatExpiration sets the gossip heartbeat expiration. +func WithHeartbeatExpiration(t time.Duration) ConfigOption { + return func(c *Config) { + c.HeartbeatExpiration = t + } +} diff --git a/cluster/consensus.go b/cluster/consensus.go new file mode 100644 index 0000000000000000000000000000000000000000..26d96d61ae7256a0a553b71b4515c43b86d2aadb --- /dev/null +++ b/cluster/consensus.go @@ -0,0 +1,67 @@ +// Copyright (C) 2015-2022 Asynkron AB All rights reserved + +package cluster + +import ( + "context" + "sync" + + "github.com/google/uuid" +) + +// this data structure is used to host consensus check results, the +// contents are protected from data races as it embeds RWMutex. +type consensusResult struct { + sync.Mutex + + consensus bool + value interface{} +} + +type ConsensusHandler interface { + GetID() string + TryGetConsensus(context.Context) (interface{}, bool) +} + +type gossipConsensusHandler struct { + ID string + result *consensusResult +} + +func NewGossipConsensusHandler() *gossipConsensusHandler { + handler := gossipConsensusHandler{ + ID: uuid.New().String(), + result: &consensusResult{ + consensus: false, + value: nil, + }, + } + + return &handler +} + +func (hdl *gossipConsensusHandler) GetID() string { return hdl.ID } + +func (hdl *gossipConsensusHandler) TryGetConsensus(context.Context) (interface{}, bool) { + // wait until our result is available + hdl.result.Lock() + defer hdl.result.Unlock() + + return hdl.result.value, hdl.result.consensus +} + +func (hdl *gossipConsensusHandler) TrySetConsensus(consensus interface{}) { + hdl.result.Lock() + go func() { + defer hdl.result.Unlock() + + hdl.result.value = consensus + hdl.result.consensus = true + }() +} + +func (hdl *gossipConsensusHandler) TryResetConsensus() { + // this is a noop for now need to discuss the right + // approach for check waiting in Go as might be another + // way of expressing this +} diff --git a/cluster/consensus_check_builder.go b/cluster/consensus_check_builder.go new file mode 100644 index 0000000000000000000000000000000000000000..523ee3bd76bf595b8ae9dca67a4d1c7517129817 --- /dev/null +++ b/cluster/consensus_check_builder.go @@ -0,0 +1,228 @@ +// Copyright (C) 2015-2022 Asynkron AB All rights reserved + +package cluster + +import ( + "fmt" + "strings" + + "gitee.com/simplexyz/simpleactor-go/log" + "google.golang.org/protobuf/types/known/anypb" +) + +type ConsensusCheckDefinition interface { + Check() *ConsensusCheck + AffectedKeys() map[string]struct{} +} + +type consensusValue struct { + Key string + Value func(*anypb.Any) interface{} +} + +type consensusMemberValue struct { + memberID string + key string + value uint64 +} + +type ConsensusCheckBuilder struct { + getConsensusValues []*consensusValue + check ConsensusChecker +} + +func NewConsensusCheckBuilder(key string, getValue func(*anypb.Any) interface{}) *ConsensusCheckBuilder { + builder := ConsensusCheckBuilder{ + getConsensusValues: []*consensusValue{ + { + Key: key, + Value: getValue, + }, + }, + } + builder.check = builder.build() + return &builder +} + +// Build builds a new ConsensusHandler and ConsensusCheck values and returns pointers to them +func (ccb *ConsensusCheckBuilder) Build() (ConsensusHandler, *ConsensusCheck) { + handle := NewGossipConsensusHandler() + onConsensus := handle.TrySetConsensus + lostConsensus := handle.TryResetConsensus + + check := func() *ConsensusCheck { + hasConsensus := ccb.Check() + hadConsensus := false + + checkConsensus := func(state *GossipState, members map[string]empty) { + consensus, value := hasConsensus(state, members) + if consensus { + if hadConsensus { + return + } + + onConsensus(value) + hadConsensus = true + } else if hadConsensus { + lostConsensus() + hadConsensus = false + } + } + + consensusCheck := NewConsensusCheck(ccb.AffectedKeys(), checkConsensus) + return &consensusCheck + } + + return handle, check() +} + +func (ccb *ConsensusCheckBuilder) Check() ConsensusChecker { return ccb.check } + +func (ccb *ConsensusCheckBuilder) AffectedKeys() []string { + var keys []string + for _, value := range ccb.getConsensusValues { + keys = append(keys, value.Key) + } + return keys +} + +func (ccb *ConsensusCheckBuilder) MapToValue(valueTuple *consensusValue) func(string, *GossipMemberState) (string, string, uint64) { + // REVISIT: in .NET implementation the ConsensusCheckBuilder can be of any given T type + // so this method returns (string, string, T) in .NET, it just feels wrong to + // return an interface{} from here as so far only checkers for uint64 are + // being used but this is not acceptable, and we shall put this implementation + // on par with .NET version, so maybe with new go1.18 generics or making the + // ConsensusCheckBuilder struct to store an additional field of type empty + // interface to operate with internally and then provide of a custom callback + // from users of the data structure to convert back and forth ¯\_(ツ)_/¯ + key := valueTuple.Key + unpack := valueTuple.Value + + return func(member string, state *GossipMemberState) (string, string, uint64) { + var value uint64 + + gossipKey, ok := state.Values[key] + if !ok { + value = 0 + } else { + // REVISIT: the valueTuple is here supposedly to be able to convert + // the protobuf Any values contained by GossipMemberState + // into the right value, this is true in the .NET version + // as ConsensusCheckBuilder is defined as a generic type + // ConsensusCheckBuilder so the unpacker can unpack from + // Any into T, but we can not do that (for now) so we have + // to stick to unpack to the concrete uint64 type here + value = unpack(gossipKey.Value).(uint64) + } + return member, key, value + } +} + +func (ccb *ConsensusCheckBuilder) build() func(*GossipState, map[string]empty) (bool, interface{}) { + getValidMemberStates := func(state *GossipState, ids map[string]empty, result []map[string]*GossipMemberState) { + for member, memberState := range state.Members { + if _, ok := ids[member]; ok { + result = append(result, map[string]*GossipMemberState{ + member: memberState, + }) + } + } + } + + showLog := func(hasConsensus bool, topologyHash uint64, valueTuples []*consensusMemberValue) { + if plog.Level() == log.DebugLevel { + groups := map[string]int{} + for _, memberValue := range valueTuples { + key := fmt.Sprintf("%s:%d", memberValue.key, memberValue.value) + if _, ok := groups[key]; ok { + groups[key]++ + } else { + groups[key] = 1 + } + } + + for k, value := range groups { + suffix := strings.Split(k, ":")[0] + if value > 1 { + suffix = fmt.Sprintf("%s, %d nodes", k, value) + } + plog.Debug("consensus", log.Bool("consensus", hasConsensus), log.String("values", suffix)) + } + } + } + + if len(ccb.getConsensusValues) == 1 { + mapToValue := ccb.MapToValue(ccb.getConsensusValues[0]) + + return func(state *GossipState, ids map[string]empty) (bool, interface{}) { + var memberStates []map[string]*GossipMemberState + getValidMemberStates(state, ids, memberStates) + + if len(memberStates) < len(ids) { // Not all members have state... + return false, nil + } + + var valueTuples []*consensusMemberValue + for _, memberState := range memberStates { + for id, state := range memberState { + member, key, value := mapToValue(id, state) + valueTuples = append(valueTuples, &consensusMemberValue{member, key, value}) + } + } + + hasConsensus, topologyHash := ccb.HasConsensus(valueTuples) + showLog(hasConsensus, topologyHash, valueTuples) + + return hasConsensus, topologyHash + } + } + + return func(state *GossipState, ids map[string]empty) (bool, interface{}) { + var memberStates []map[string]*GossipMemberState + getValidMemberStates(state, ids, memberStates) + + if len(memberStates) < len(ids) { // Not all members have state... + return false, nil + } + + var valueTuples []*consensusMemberValue + for _, consensusValues := range ccb.getConsensusValues { + mapToValue := ccb.MapToValue(consensusValues) + for _, memberState := range memberStates { + for id, state := range memberState { + member, key, value := mapToValue(id, state) + valueTuples = append(valueTuples, &consensusMemberValue{member, key, value}) + } + } + } + + hasConsensus, topologyHash := ccb.HasConsensus(valueTuples) + showLog(hasConsensus, topologyHash, valueTuples) + + return hasConsensus, topologyHash + } +} + +func (ccb *ConsensusCheckBuilder) HasConsensus(memberValues []*consensusMemberValue) (bool, uint64) { + var hasConsensus bool + var topologyHash uint64 + + if len(memberValues) == 0 { + return hasConsensus, topologyHash + } + + first := memberValues[0] + for i, next := range memberValues { + if i == 0 { + continue + } + + if first.value != next.value { + return hasConsensus, topologyHash + } + } + + hasConsensus = true + topologyHash = first.value + return hasConsensus, topologyHash +} diff --git a/cluster/consensus_checks.go b/cluster/consensus_checks.go new file mode 100644 index 0000000000000000000000000000000000000000..f9e30d0fd1e7a2b843bc6b960a516c950553044c --- /dev/null +++ b/cluster/consensus_checks.go @@ -0,0 +1,127 @@ +// Copyright (C) 2015-2022 Asynkron AB All rights reserved + +package cluster + +type GossipUpdater func(*GossipState, map[string]empty) + +// data structure helpful to store consensus check information and behavior. +type ConsensusCheck struct { + affectedKeys []string + check GossipUpdater +} + +// creates a new ConsensusCheck value with the given data and return it back. +func NewConsensusCheck(affectedKeys []string, check GossipUpdater) ConsensusCheck { + consensusCheck := ConsensusCheck{ + affectedKeys: affectedKeys, + check: check, + } + + return consensusCheck +} + +// acts as a storage of pointers to ConsensusCheck stored by key. +type ConsensusChecks struct { + checks map[string]*ConsensusCheck + affectedKeysByStateKey map[string]map[string]empty +} + +// creates a new ConsensusChecks value and returns a pointer to it. +func NewConsensusChecks() *ConsensusChecks { + checks := ConsensusChecks{ + checks: make(map[string]*ConsensusCheck), + affectedKeysByStateKey: make(map[string]map[string]empty), + } + + return &checks +} + +// iterates over all the keys stored in the set (map[string]empty) found in +// the given key map and populates a slice of pointers to ConsensusCheck values +// that is returned as a set of ConsensusCheck updated by the given key. +func (cc *ConsensusChecks) GetByUpdatedKey(key string) []*ConsensusCheck { + var result []*ConsensusCheck + if _, ok := cc.affectedKeysByStateKey[key]; !ok { + return result + } + + for id := range cc.affectedKeysByStateKey[key] { + if ConsensusCheck, ok := cc.checks[id]; ok { + result = append(result, ConsensusCheck) + } + } + + return result +} + +// iterate over all the keys stored in the set (map[string]empty) found in +// the given key maps and populates a slice of pointers to ConsensusCheck values +// that is returned as a set of ConsensusCheck updated by the given keys +// with removed duplicates on it (as it is a "set"). +func (cc *ConsensusChecks) GetByUpdatedKeys(keys []string) []*ConsensusCheck { + var result []*ConsensusCheck + + temporaryIDs := make(map[string]empty) + + for _, key := range keys { + if _, ok := cc.affectedKeysByStateKey[key]; !ok { + continue + } + + for id := range cc.affectedKeysByStateKey[key] { + if ConsensusCheck, ok := cc.checks[id]; ok { + if _, ok := temporaryIDs[id]; !ok { + temporaryIDs[id] = empty{} + + result = append(result, ConsensusCheck) + } + } + } + } + + return result +} + +// adds a new pointer to a ConsensusCheck value in the storage +// and registers its affected by keys index. +func (cc *ConsensusChecks) Add(id string, check *ConsensusCheck) { + cc.checks[id] = check + cc.registerAffectedKeys(id, check.affectedKeys) +} + +// Remove removes the given ConsensusCheck identity from the storage and +// removes its affected by keys index if needed after cleaning. +func (cc *ConsensusChecks) Remove(id string) { + if _, ok := cc.affectedKeysByStateKey[id]; ok { + delete(cc.affectedKeysByStateKey, id) + cc.unregisterAffectedKeys(id) + } +} + +func (cc *ConsensusChecks) registerAffectedKeys(id string, keys []string) { + for _, key := range keys { + if _, ok := cc.affectedKeysByStateKey[key]; ok { + cc.affectedKeysByStateKey[key][id] = empty{} + } else { + cc.affectedKeysByStateKey[key] = map[string]empty{id: {}} + } + } +} + +func (cc *ConsensusChecks) unregisterAffectedKeys(id string) { + var keysToDelete []string + + for key, internal := range cc.affectedKeysByStateKey { + if _, ok := internal[id]; ok { + delete(cc.affectedKeysByStateKey[key], id) + + if len(cc.affectedKeysByStateKey[key]) == 0 { + keysToDelete = append(keysToDelete, key) + } + } + } + + for _, key := range keysToDelete { + delete(cc.affectedKeysByStateKey, key) + } +} diff --git a/cluster/context.go b/cluster/context.go new file mode 100644 index 0000000000000000000000000000000000000000..50010a72347bf1c4b2affa82b7b857f70c9b836d --- /dev/null +++ b/cluster/context.go @@ -0,0 +1,8 @@ +package cluster + +import "time" + +// Context is an interface any cluster context needs to implement +type Context interface { + Request(identity string, kind string, message interface{}, timeout ...time.Duration) (interface{}, error) +} diff --git a/cluster/default_context.go b/cluster/default_context.go new file mode 100644 index 0000000000000000000000000000000000000000..d8e3af452cfb138f90ea2019cdd9b5fe8071a72d --- /dev/null +++ b/cluster/default_context.go @@ -0,0 +1,120 @@ +// Copyright (C) 2017 - 2022 Asynkron.se + +package cluster + +import ( + "context" + "fmt" + "time" + + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/log" + "gitee.com/simplexyz/simpleactor-go/remote" +) + +// Defines a type to provide DefaultContext configurations / implementations. +type ContextProducer func(*Cluster) Context + +// Defines a default cluster context hashBytes structure. +type DefaultContext struct { + cluster *Cluster +} + +var _ Context = (*DefaultContext)(nil) + +// Creates a new DefaultContext value and returns +// a pointer to its memory address as a Context. +func newDefaultClusterContext(cluster *Cluster) Context { + clusterContext := DefaultContext{ + cluster: cluster, + } + + return &clusterContext +} + +func (dcc *DefaultContext) Request(identity, kind string, message interface{}, timeout ...time.Duration) (interface{}, error) { + var err error + + var resp interface{} + + var counter int + + // get the configuration from the composed Cluster value + cfg := dcc.cluster.Config.ToClusterContextConfig() + + start := time.Now() + + plog.Debug(fmt.Sprintf("Requesting %s:%s Message %#v", identity, kind, message)) + + // crate a new Timeout Context + ttl := cfg.ActorRequestTimeout + if len(timeout) > 0 { + ttl = timeout[0] + } + + ctx, cancel := context.WithTimeout(context.Background(), ttl) + defer cancel() + + _context := dcc.cluster.ActorSystem.Root +selectloop: + for { + select { + case <-ctx.Done(): + // TODO: handler throttling and messaging here + err = fmt.Errorf("request failed: %w", ctx.Err()) + + break selectloop + default: + pid := dcc.getCachedPid(identity, kind) + if pid == nil { + plog.Debug(fmt.Sprintf("Requesting %s:%s did not get PID from IdentityLookup", identity, kind)) + counter = cfg.RetryAction(counter) + + continue + } + + resp, err = _context.RequestFuture(pid, message, ttl).Result() + if err != nil { + plog.Error("cluster.RequestFuture failed", log.Error(err), log.PID("pid", pid)) + switch err { + case actor.ErrTimeout, remote.ErrTimeout, actor.ErrDeadLetter, remote.ErrDeadLetter: + counter = cfg.RetryAction(counter) + dcc.cluster.PidCache.Remove(identity, kind) + err = nil // reset our error variable as we can succeed still + + continue + default: + break selectloop + } + } + + // TODO: add metrics to increment retries + } + } + + totalTime := time.Since(start) + // TODO: add metrics ot set histogram for total request time + + if contextError := ctx.Err(); contextError != nil && cfg.requestLogThrottle() == actor.Open { + // context timeout exceeded, report and return + plog.Warn(fmt.Sprintf("Request retried but failed for %s:%s, elapsed %v", identity, kind, totalTime)) + } + + return resp, err +} + +// gets the cached PID for the given identity +// it can return nil if none is found. +func (dcc *DefaultContext) getCachedPid(identity, kind string) *actor.PID { + pid, _ := dcc.cluster.PidCache.Get(identity, kind) + + return pid +} + +// default retry action, it just sleeps incrementally. +func defaultRetryAction(i int) int { + i++ + time.Sleep(time.Duration(i * i * 50)) + + return i +} diff --git a/cluster/gossip.go b/cluster/gossip.go new file mode 100644 index 0000000000000000000000000000000000000000..bac7762b2cd31623a6f4783b5b5e990d8dbdb31a --- /dev/null +++ b/cluster/gossip.go @@ -0,0 +1,41 @@ +// Copyright (C) 2015-2022 Asynkton AB All rights reserved + +package cluster + +import ( + "google.golang.org/protobuf/proto" +) + +// customary type that defines a states sender callback. +type LocalStateSender func(memberStateDelta *MemberStateDelta, member *Member) + +// This interface must be implemented by any value that. +// wants to be used as a gossip state storage +type GossipStateStorer interface { + GetState(key string) map[string]*GossipKeyValue + SetState(key string, value proto.Message) +} + +// This interface must be implemented by any value that +// wants to add or remove consensus checkers +type GossipConsensusChecker interface { + AddConsensusCheck(id string, check *ConsensusCheck) + RemoveConsensusCheck(id string) +} + +// This interface must be implemented by any value that +// wants to react to cluster topology events +type GossipCore interface { + UpdateClusterTopology(topology *ClusterTopology) + ReceiveState(remoteState *GossipState) []*GossipUpdate + SendState(sendStateToMember LocalStateSender) + GetMemberStateDelta(targetMemberID string) *MemberStateDelta +} + +// The Gossip interface must be implemented by any value +// that pretends to participate with-in the Gossip protocol +type Gossip interface { + GossipStateStorer + GossipConsensusChecker + GossipCore +} diff --git a/cluster/gossip.pb.go b/cluster/gossip.pb.go new file mode 100644 index 0000000000000000000000000000000000000000..d1cdae2939e0aa381f5327e9b8cca5cf86de53dc --- /dev/null +++ b/cluster/gossip.pb.go @@ -0,0 +1,615 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.26.0 +// protoc v3.20.2 +// source: gossip.proto + +package cluster + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + anypb "google.golang.org/protobuf/types/known/anypb" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type GossipRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + MemberId string `protobuf:"bytes,2,opt,name=member_id,json=memberId,proto3" json:"member_id,omitempty"` + State *GossipState `protobuf:"bytes,1,opt,name=state,proto3" json:"state,omitempty"` +} + +func (x *GossipRequest) Reset() { + *x = GossipRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_gossip_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GossipRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GossipRequest) ProtoMessage() {} + +func (x *GossipRequest) ProtoReflect() protoreflect.Message { + mi := &file_gossip_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GossipRequest.ProtoReflect.Descriptor instead. +func (*GossipRequest) Descriptor() ([]byte, []int) { + return file_gossip_proto_rawDescGZIP(), []int{0} +} + +func (x *GossipRequest) GetMemberId() string { + if x != nil { + return x.MemberId + } + return "" +} + +func (x *GossipRequest) GetState() *GossipState { + if x != nil { + return x.State + } + return nil +} + +// Ack a gossip request +type GossipResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + State *GossipState `protobuf:"bytes,1,opt,name=state,proto3" json:"state,omitempty"` +} + +func (x *GossipResponse) Reset() { + *x = GossipResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_gossip_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GossipResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GossipResponse) ProtoMessage() {} + +func (x *GossipResponse) ProtoReflect() protoreflect.Message { + mi := &file_gossip_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GossipResponse.ProtoReflect.Descriptor instead. +func (*GossipResponse) Descriptor() ([]byte, []int) { + return file_gossip_proto_rawDescGZIP(), []int{1} +} + +func (x *GossipResponse) GetState() *GossipState { + if x != nil { + return x.State + } + return nil +} + +// two GossipState objects can be merged +// key + member_id gets it's own entry, if collision, highest version is selected +type GossipState struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Members map[string]*GossipState_GossipMemberState `protobuf:"bytes,1,rep,name=members,proto3" json:"members,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (x *GossipState) Reset() { + *x = GossipState{} + if protoimpl.UnsafeEnabled { + mi := &file_gossip_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GossipState) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GossipState) ProtoMessage() {} + +func (x *GossipState) ProtoReflect() protoreflect.Message { + mi := &file_gossip_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GossipState.ProtoReflect.Descriptor instead. +func (*GossipState) Descriptor() ([]byte, []int) { + return file_gossip_proto_rawDescGZIP(), []int{2} +} + +func (x *GossipState) GetMembers() map[string]*GossipState_GossipMemberState { + if x != nil { + return x.Members + } + return nil +} + +// a known key might be heartbeat. if we locally tag each entry with a local timestamp +// this means that we can measure if we have not received a new heartbeat from one member in some time +// even if we don't know the exact time the heartbeat was issued, due to clock differences. +// we still know when _we_ as in this node, got this data. +// and we can measure time from then til now. +// +// if we got a hear-beat from another node, and X seconds pass, we can assume it to be dead +type GossipKeyValue struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + SequenceNumber int64 `protobuf:"varint,2,opt,name=sequence_number,json=sequenceNumber,proto3" json:"sequence_number,omitempty"` //version is local to the owner member + Value *anypb.Any `protobuf:"bytes,4,opt,name=value,proto3" json:"value,omitempty"` //value is any format + LocalTimestampUnixMilliseconds int64 `protobuf:"varint,5,opt,name=local_timestamp_unix_milliseconds,json=localTimestampUnixMilliseconds,proto3" json:"local_timestamp_unix_milliseconds,omitempty"` +} + +func (x *GossipKeyValue) Reset() { + *x = GossipKeyValue{} + if protoimpl.UnsafeEnabled { + mi := &file_gossip_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GossipKeyValue) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GossipKeyValue) ProtoMessage() {} + +func (x *GossipKeyValue) ProtoReflect() protoreflect.Message { + mi := &file_gossip_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GossipKeyValue.ProtoReflect.Descriptor instead. +func (*GossipKeyValue) Descriptor() ([]byte, []int) { + return file_gossip_proto_rawDescGZIP(), []int{3} +} + +func (x *GossipKeyValue) GetSequenceNumber() int64 { + if x != nil { + return x.SequenceNumber + } + return 0 +} + +func (x *GossipKeyValue) GetValue() *anypb.Any { + if x != nil { + return x.Value + } + return nil +} + +func (x *GossipKeyValue) GetLocalTimestampUnixMilliseconds() int64 { + if x != nil { + return x.LocalTimestampUnixMilliseconds + } + return 0 +} + +// represents a value that can be sent in form of a delta change +// instead of a full value replace +type GossipDeltaValue struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Entries []*GossipDeltaValue_GossipDeltaEntry `protobuf:"bytes,1,rep,name=entries,proto3" json:"entries,omitempty"` +} + +func (x *GossipDeltaValue) Reset() { + *x = GossipDeltaValue{} + if protoimpl.UnsafeEnabled { + mi := &file_gossip_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GossipDeltaValue) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GossipDeltaValue) ProtoMessage() {} + +func (x *GossipDeltaValue) ProtoReflect() protoreflect.Message { + mi := &file_gossip_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GossipDeltaValue.ProtoReflect.Descriptor instead. +func (*GossipDeltaValue) Descriptor() ([]byte, []int) { + return file_gossip_proto_rawDescGZIP(), []int{4} +} + +func (x *GossipDeltaValue) GetEntries() []*GossipDeltaValue_GossipDeltaEntry { + if x != nil { + return x.Entries + } + return nil +} + +type GossipState_GossipMemberState struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Values map[string]*GossipKeyValue `protobuf:"bytes,1,rep,name=values,proto3" json:"values,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (x *GossipState_GossipMemberState) Reset() { + *x = GossipState_GossipMemberState{} + if protoimpl.UnsafeEnabled { + mi := &file_gossip_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GossipState_GossipMemberState) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GossipState_GossipMemberState) ProtoMessage() {} + +func (x *GossipState_GossipMemberState) ProtoReflect() protoreflect.Message { + mi := &file_gossip_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GossipState_GossipMemberState.ProtoReflect.Descriptor instead. +func (*GossipState_GossipMemberState) Descriptor() ([]byte, []int) { + return file_gossip_proto_rawDescGZIP(), []int{2, 0} +} + +func (x *GossipState_GossipMemberState) GetValues() map[string]*GossipKeyValue { + if x != nil { + return x.Values + } + return nil +} + +// these are the entries of a delta value +// this can be seen as an array with data, where each element in the array is tagged with a sequence number +type GossipDeltaValue_GossipDeltaEntry struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + SequenceNumber int64 `protobuf:"varint,1,opt,name=sequence_number,json=sequenceNumber,proto3" json:"sequence_number,omitempty"` + Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"` +} + +func (x *GossipDeltaValue_GossipDeltaEntry) Reset() { + *x = GossipDeltaValue_GossipDeltaEntry{} + if protoimpl.UnsafeEnabled { + mi := &file_gossip_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GossipDeltaValue_GossipDeltaEntry) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GossipDeltaValue_GossipDeltaEntry) ProtoMessage() {} + +func (x *GossipDeltaValue_GossipDeltaEntry) ProtoReflect() protoreflect.Message { + mi := &file_gossip_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GossipDeltaValue_GossipDeltaEntry.ProtoReflect.Descriptor instead. +func (*GossipDeltaValue_GossipDeltaEntry) Descriptor() ([]byte, []int) { + return file_gossip_proto_rawDescGZIP(), []int{4, 0} +} + +func (x *GossipDeltaValue_GossipDeltaEntry) GetSequenceNumber() int64 { + if x != nil { + return x.SequenceNumber + } + return 0 +} + +func (x *GossipDeltaValue_GossipDeltaEntry) GetData() []byte { + if x != nil { + return x.Data + } + return nil +} + +var File_gossip_proto protoreflect.FileDescriptor + +var file_gossip_proto_rawDesc = []byte{ + 0x0a, 0x0c, 0x67, 0x6f, 0x73, 0x73, 0x69, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x07, + 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x1a, 0x19, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x61, 0x6e, 0x79, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x22, 0x58, 0x0a, 0x0d, 0x47, 0x6f, 0x73, 0x73, 0x69, 0x70, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x5f, 0x69, 0x64, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x49, 0x64, + 0x12, 0x2a, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x14, 0x2e, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x2e, 0x47, 0x6f, 0x73, 0x73, 0x69, 0x70, + 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0x3c, 0x0a, 0x0e, + 0x47, 0x6f, 0x73, 0x73, 0x69, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, + 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, + 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x2e, 0x47, 0x6f, 0x73, 0x73, 0x69, 0x70, 0x53, 0x74, + 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0xe4, 0x02, 0x0a, 0x0b, 0x47, + 0x6f, 0x73, 0x73, 0x69, 0x70, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x3b, 0x0a, 0x07, 0x6d, 0x65, + 0x6d, 0x62, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x63, 0x6c, + 0x75, 0x73, 0x74, 0x65, 0x72, 0x2e, 0x47, 0x6f, 0x73, 0x73, 0x69, 0x70, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x2e, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, + 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x1a, 0xb3, 0x01, 0x0a, 0x11, 0x47, 0x6f, 0x73, 0x73, + 0x69, 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x4a, 0x0a, + 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x32, 0x2e, + 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x2e, 0x47, 0x6f, 0x73, 0x73, 0x69, 0x70, 0x53, 0x74, + 0x61, 0x74, 0x65, 0x2e, 0x47, 0x6f, 0x73, 0x73, 0x69, 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, + 0x53, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x52, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x1a, 0x52, 0x0a, 0x0b, 0x56, 0x61, 0x6c, + 0x75, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2d, 0x0a, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x63, 0x6c, 0x75, 0x73, + 0x74, 0x65, 0x72, 0x2e, 0x47, 0x6f, 0x73, 0x73, 0x69, 0x70, 0x4b, 0x65, 0x79, 0x56, 0x61, 0x6c, + 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x62, 0x0a, + 0x0c, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, + 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, + 0x3c, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, + 0x2e, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x2e, 0x47, 0x6f, 0x73, 0x73, 0x69, 0x70, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x2e, 0x47, 0x6f, 0x73, 0x73, 0x69, 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, + 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, + 0x01, 0x22, 0xb0, 0x01, 0x0a, 0x0e, 0x47, 0x6f, 0x73, 0x73, 0x69, 0x70, 0x4b, 0x65, 0x79, 0x56, + 0x61, 0x6c, 0x75, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, + 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0e, 0x73, + 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x2a, 0x0a, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, + 0x6e, 0x79, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x49, 0x0a, 0x21, 0x6c, 0x6f, 0x63, + 0x61, 0x6c, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x5f, 0x75, 0x6e, 0x69, + 0x78, 0x5f, 0x6d, 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x1e, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x54, 0x69, 0x6d, 0x65, 0x73, + 0x74, 0x61, 0x6d, 0x70, 0x55, 0x6e, 0x69, 0x78, 0x4d, 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x65, 0x63, + 0x6f, 0x6e, 0x64, 0x73, 0x22, 0xa9, 0x01, 0x0a, 0x10, 0x47, 0x6f, 0x73, 0x73, 0x69, 0x70, 0x44, + 0x65, 0x6c, 0x74, 0x61, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x44, 0x0a, 0x07, 0x65, 0x6e, 0x74, + 0x72, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6c, 0x75, + 0x73, 0x74, 0x65, 0x72, 0x2e, 0x47, 0x6f, 0x73, 0x73, 0x69, 0x70, 0x44, 0x65, 0x6c, 0x74, 0x61, + 0x56, 0x61, 0x6c, 0x75, 0x65, 0x2e, 0x47, 0x6f, 0x73, 0x73, 0x69, 0x70, 0x44, 0x65, 0x6c, 0x74, + 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x1a, + 0x4f, 0x0a, 0x10, 0x47, 0x6f, 0x73, 0x73, 0x69, 0x70, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x12, 0x27, 0x0a, 0x0f, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x5f, + 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0e, 0x73, 0x65, + 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, + 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, + 0x42, 0x2c, 0x5a, 0x2a, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x61, 0x73, 0x79, 0x6e, 0x6b, 0x72, 0x6f, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x61, 0x63, + 0x74, 0x6f, 0x72, 0x2d, 0x67, 0x6f, 0x2f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x62, 0x06, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_gossip_proto_rawDescOnce sync.Once + file_gossip_proto_rawDescData = file_gossip_proto_rawDesc +) + +func file_gossip_proto_rawDescGZIP() []byte { + file_gossip_proto_rawDescOnce.Do(func() { + file_gossip_proto_rawDescData = protoimpl.X.CompressGZIP(file_gossip_proto_rawDescData) + }) + return file_gossip_proto_rawDescData +} + +var file_gossip_proto_msgTypes = make([]protoimpl.MessageInfo, 9) +var file_gossip_proto_goTypes = []interface{}{ + (*GossipRequest)(nil), // 0: cluster.GossipRequest + (*GossipResponse)(nil), // 1: cluster.GossipResponse + (*GossipState)(nil), // 2: cluster.GossipState + (*GossipKeyValue)(nil), // 3: cluster.GossipKeyValue + (*GossipDeltaValue)(nil), // 4: cluster.GossipDeltaValue + (*GossipState_GossipMemberState)(nil), // 5: cluster.GossipState.GossipMemberState + nil, // 6: cluster.GossipState.MembersEntry + nil, // 7: cluster.GossipState.GossipMemberState.ValuesEntry + (*GossipDeltaValue_GossipDeltaEntry)(nil), // 8: cluster.GossipDeltaValue.GossipDeltaEntry + (*anypb.Any)(nil), // 9: google.protobuf.Any +} +var file_gossip_proto_depIdxs = []int32{ + 2, // 0: cluster.GossipRequest.state:type_name -> cluster.GossipState + 2, // 1: cluster.GossipResponse.state:type_name -> cluster.GossipState + 6, // 2: cluster.GossipState.members:type_name -> cluster.GossipState.MembersEntry + 9, // 3: cluster.GossipKeyValue.value:type_name -> google.protobuf.Any + 8, // 4: cluster.GossipDeltaValue.entries:type_name -> cluster.GossipDeltaValue.GossipDeltaEntry + 7, // 5: cluster.GossipState.GossipMemberState.values:type_name -> cluster.GossipState.GossipMemberState.ValuesEntry + 5, // 6: cluster.GossipState.MembersEntry.value:type_name -> cluster.GossipState.GossipMemberState + 3, // 7: cluster.GossipState.GossipMemberState.ValuesEntry.value:type_name -> cluster.GossipKeyValue + 8, // [8:8] is the sub-list for method output_type + 8, // [8:8] is the sub-list for method input_type + 8, // [8:8] is the sub-list for extension type_name + 8, // [8:8] is the sub-list for extension extendee + 0, // [0:8] is the sub-list for field type_name +} + +func init() { file_gossip_proto_init() } +func file_gossip_proto_init() { + if File_gossip_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_gossip_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GossipRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_gossip_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GossipResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_gossip_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GossipState); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_gossip_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GossipKeyValue); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_gossip_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GossipDeltaValue); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_gossip_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GossipState_GossipMemberState); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_gossip_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GossipDeltaValue_GossipDeltaEntry); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_gossip_proto_rawDesc, + NumEnums: 0, + NumMessages: 9, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_gossip_proto_goTypes, + DependencyIndexes: file_gossip_proto_depIdxs, + MessageInfos: file_gossip_proto_msgTypes, + }.Build() + File_gossip_proto = out.File + file_gossip_proto_rawDesc = nil + file_gossip_proto_goTypes = nil + file_gossip_proto_depIdxs = nil +} diff --git a/cluster/gossip.proto b/cluster/gossip.proto new file mode 100644 index 0000000000000000000000000000000000000000..21ae4323603f59f3b3ecafc22fbdc8a9a1d9b121 --- /dev/null +++ b/cluster/gossip.proto @@ -0,0 +1,55 @@ +syntax = "proto3"; +package cluster; +option go_package = "/gitee.com/simplexyz/simpleactor-go/cluster"; +import "google/protobuf/any.proto"; + + +message GossipRequest { + string member_id = 2; + GossipState state = 1; +} + +//Ack a gossip request +message GossipResponse { + GossipState state = 1; +} + +//two GossipState objects can be merged +//key + member_id gets it's own entry, if collision, highest version is selected +message GossipState { + message GossipMemberState { + map values = 1; + } + + map members = 1; +} + + + +//a known key might be heartbeat. if we locally tag each entry with a local timestamp +//this means that we can measure if we have not received a new heartbeat from one member in some time +//even if we don't know the exact time the heartbeat was issued, due to clock differences. +//we still know when _we_ as in this node, got this data. +//and we can measure time from then til now. +// +//if we got a hear-beat from another node, and X seconds pass, we can assume it to be dead +message GossipKeyValue { + int64 sequence_number = 2; //version is local to the owner member + google.protobuf.Any value = 4; //value is any format + int64 local_timestamp_unix_milliseconds = 5; +} + +//represents a value that can be sent in form of a delta change +//instead of a full value replace +message GossipDeltaValue +{ + //these are the entries of a delta value + //this can be seen as an array with data, where each element in the array is tagged with a sequence number + message GossipDeltaEntry + { + int64 sequence_number = 1; + bytes data = 2; + } + + repeated GossipDeltaEntry entries = 1; +} \ No newline at end of file diff --git a/cluster/gossip_actor.go b/cluster/gossip_actor.go new file mode 100644 index 0000000000000000000000000000000000000000..5df9df15ba7c4992fba2eb156f2279609674e960 --- /dev/null +++ b/cluster/gossip_actor.go @@ -0,0 +1,202 @@ +// Copyright (C) 2015-2022 Asynkron AB All rights reserved + +package cluster + +import ( + "time" + + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/log" + "github.com/asynkron/gofun/set" +) + +// convenience customary type to represent an empty value +// that takes no space in memory. +type empty struct{} + +// Actor used to send gossip messages around +type GossipActor struct { + gossipRequestTimeout time.Duration + gossip Gossip + + /// Message throttler + throttler actor.ShouldThrottle +} + +// Creates a new GossipActor and returns a pointer to its location in the heap +func NewGossipActor(requestTimeout time.Duration, myID string, getBlockedMembers func() set.Set[string], fanOut int, maxSend int) *GossipActor { + informer := newInformer(myID, getBlockedMembers, fanOut, maxSend) + gossipActor := GossipActor{ + gossipRequestTimeout: requestTimeout, + gossip: informer, + } + gossipActor.throttler = actor.NewThrottle(3, 60*time.Second, gossipActor.throttledLog) + + return &gossipActor +} + +// Receive method. +func (ga *GossipActor) Receive(ctx actor.Context) { + switch r := ctx.Message().(type) { + case *actor.Started: + // pass + case *SetGossipStateKey: + ga.onSetGossipStateKey(r, ctx) + case *GetGossipStateRequest: + ga.onGetGossipStateKey(r, ctx) + case *GossipRequest: + ga.onGossipRequest(r, ctx) + case *SendGossipStateRequest: + ga.onSendGossipState(ctx) + case *AddConsensusCheck: + ga.onAddConsensusCheck(r) + case *RemoveConsensusCheck: + ga.onRemoveConsensusCheck(r) + case *ClusterTopology: + ga.onClusterTopology(r) + case *GossipResponse: + plog.Error("GossipResponse should not be received by GossipActor") // it should be a response to a request + default: + plog.Warn("Gossip received unknown message request", log.Message(r), log.TypeOf("msg_type", r)) + } +} + +func (ga *GossipActor) onClusterTopology(topology *ClusterTopology) { + ga.gossip.UpdateClusterTopology(topology) +} + +func (ga *GossipActor) onAddConsensusCheck(r *AddConsensusCheck) { + ga.gossip.AddConsensusCheck(r.ID, r.Check) +} + +func (ga *GossipActor) onRemoveConsensusCheck(r *RemoveConsensusCheck) { + ga.gossip.RemoveConsensusCheck(r.ID) +} + +func (ga *GossipActor) onGetGossipStateKey(r *GetGossipStateRequest, ctx actor.Context) { + state := ga.gossip.GetState(r.Key) + res := NewGetGossipStateResponse(state) + ctx.Respond(&res) +} + +func (ga *GossipActor) onGossipRequest(r *GossipRequest, ctx actor.Context) { + if ga.throttler() == actor.Open { + plog.Debug("OnGossipRequest", log.PID("sender", ctx.Sender())) + } + ga.ReceiveState(r.State, ctx) + + if !GetCluster(ctx.ActorSystem()).MemberList.ContainsMemberID(r.MemberId) { + plog.Warn("Got gossip request from unknown member", log.String("MemberId", r.MemberId)) + + // nothing to send, do not provide sender or state payload + // ctx.Respond(&GossipResponse{State: &GossipState{Members: make(map[string]*GossipState_GossipMemberState)}}) + ctx.Respond(&GossipResponse{}) + + return + } + + memberState := ga.gossip.GetMemberStateDelta(r.MemberId) + if !memberState.HasState { + plog.Warn("Got gossip request from member, but no state was found", log.String("MemberId", r.MemberId)) + + // nothing to send, do not provide sender or state payload + ctx.Respond(&GossipResponse{}) + + return + } + + ctx.Respond(&GossipResponse{}) + return + + // turn off acking for now + + //msg := GossipResponse{ + // State: memberState.State, + //} + //future := ctx.RequestFuture(ctx.Sender(), &msg, GetCluster(ctx.ActorSystem()).Config.GossipRequestTimeout) + // + //ctx.ReenterAfter(future, func(res interface{}, err error) { + // if err != nil { + // plog.Warn("onGossipRequest failed", log.String("MemberId", r.MemberId), log.Error(err)) + // return + // } + // + // if _, ok := res.(*GossipResponseAck); ok { + // memberState.CommitOffsets() + // return + // } + // + // m, ok := res.(proto.Message) + // if !ok { + // plog.Warn("onGossipRequest failed", log.String("MemberId", r.MemberId), log.Error(err)) + // return + // } + // n := string(proto.MessageName(m).Name()) + // + // plog.Error("onGossipRequest received unknown response message", log.String("type", n), log.Message(r)) + //}) +} + +func (ga *GossipActor) onSetGossipStateKey(r *SetGossipStateKey, ctx actor.Context) { + key, message := r.Key, r.Value + ga.gossip.SetState(key, message) + + if ctx.Sender() != nil { + ctx.Respond(&SetGossipStateResponse{}) + } +} + +func (ga *GossipActor) onSendGossipState(ctx actor.Context) { + ga.gossip.SendState(func(memberState *MemberStateDelta, member *Member) { + ga.sendGossipForMember(member, memberState, ctx) + }) + ctx.Respond(&SendGossipStateResponse{}) +} + +func (ga *GossipActor) ReceiveState(remoteState *GossipState, ctx actor.Context) { + // stream our updates + updates := ga.gossip.ReceiveState(remoteState) + for _, update := range updates { + ctx.ActorSystem().EventStream.Publish(update) + } +} + +func (ga *GossipActor) sendGossipForMember(member *Member, memberStateDelta *MemberStateDelta, ctx actor.Context) { + pid := actor.NewPID(member.Address(), DefaultGossipActorName) + if ga.throttler() == actor.Open { + plog.Debug("Sending GossipRequest", log.String("MemberId", member.Id)) + } + + // a short timeout is massively important, we cannot afford hanging around waiting + // for timeout, blocking other gossips from getting through + + msg := GossipRequest{ + MemberId: member.Id, + State: memberStateDelta.State, + } + future := ctx.RequestFuture(pid, &msg, ga.gossipRequestTimeout) + + ctx.ReenterAfter(future, func(res interface{}, err error) { + if err != nil { + plog.Warn("sendGossipForMember failed", log.String("MemberId", member.Id), log.Error(err)) + return + } + + resp, ok := res.(*GossipResponse) + if !ok { + plog.Error("sendGossipForMember received unknown response message", log.TypeOf("messageType", res), log.Message(resp)) + + return + } + + memberStateDelta.CommitOffsets() + + if resp.State != nil { + ga.ReceiveState(resp.State, ctx) + } + }) +} + +func (ga *GossipActor) throttledLog(counter int32) { + plog.Debug("[Gossip] Sending GossipRequest", log.Int("throttled", int(counter))) +} diff --git a/cluster/gossip_state_management.go b/cluster/gossip_state_management.go new file mode 100644 index 0000000000000000000000000000000000000000..536036a3b4e7d0de1add72c91a1b5239f01904f3 --- /dev/null +++ b/cluster/gossip_state_management.go @@ -0,0 +1,122 @@ +package cluster + +import ( + "time" + + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/anypb" +) + +// convenience type alias +type GossipMemberState = GossipState_GossipMemberState + +func ensureEntryExists(memberState *GossipMemberState, key string) *GossipKeyValue { + value, ok := memberState.Values[key] + if ok { + return value + } + + value = &GossipKeyValue{} + memberState.Values[key] = value + + return value +} + +// returns back the GossipMemberState registered in the given GossipState +// under the given memberID key, if the key doesn't exists yet it is created +func ensureMemberStateExists(state *GossipState, memberID string) *GossipMemberState { + memberState, ok := state.Members[memberID] + if ok { + return memberState + } + + memberState = &GossipMemberState{Values: make(map[string]*GossipKeyValue)} + state.Members[memberID] = memberState + + return memberState +} + +// sets the given key with the given value in the given gossip state and returns sequenceNo + 1 +func setKey(state *GossipState, key string, value proto.Message, memberID string, sequenceNo int64) int64 { + // if entry does not exists, add it + memberState := ensureMemberStateExists(state, memberID) + entry := ensureEntryExists(memberState, key) + entry.LocalTimestampUnixMilliseconds = time.Now().UnixMilli() + + sequenceNo++ + entry.SequenceNumber = sequenceNo + + a, _ := anypb.New(value) + entry.Value = a + + return sequenceNo +} + +// merges the local and the incoming remote states into a new states slice and return it +func mergeState(localState *GossipState, remoteState *GossipState) ([]*GossipUpdate, *GossipState, map[string]empty) { + // make a copy of the localState (we do not want to modify localState just yet) + mergedState := &GossipState{Members: make(map[string]*GossipState_GossipMemberState)} + for id, member := range localState.Members { + mergedState.Members[id] = member + } + + var updates []*GossipUpdate + updatedKeys := make(map[string]empty) + + for memberID, remoteMemberState := range remoteState.Members { + if _, ok := mergedState.Members[memberID]; !ok { + mergedState.Members[memberID] = remoteMemberState + for key, entry := range remoteMemberState.Values { + update := GossipUpdate{ + MemberID: memberID, + Key: key, + Value: entry.Value, + SeqNumber: entry.SequenceNumber, + } + updates = append(updates, &update) + entry.LocalTimestampUnixMilliseconds = time.Now().UnixMilli() + updatedKeys[key] = empty{} + } + continue + } + + // this entry exists in both mergedState and remoteState, we should merge them + newMemberState := mergedState.Members[memberID] + for key, remoteValue := range remoteMemberState.Values { + // this entry does not exist in newMemberState, just copy all of it + if _, ok := newMemberState.Values[key]; !ok { + newMemberState.Values[key] = remoteValue + update := GossipUpdate{ + MemberID: memberID, + Key: key, + Value: remoteValue.Value, + SeqNumber: remoteValue.SequenceNumber, + } + updates = append(updates, &update) + remoteValue.LocalTimestampUnixMilliseconds = time.Now().UnixMilli() + updatedKeys[key] = empty{} + continue + } + + newValue := newMemberState.Values[key] + + // remote value is older, ignore + if remoteValue.SequenceNumber <= newValue.SequenceNumber { + continue + } + + // just replace the existing value + newMemberState.Values[key] = remoteValue + update := GossipUpdate{ + MemberID: memberID, + Key: key, + Value: remoteValue.Value, + SeqNumber: remoteValue.SequenceNumber, + } + updates = append(updates, &update) + remoteValue.LocalTimestampUnixMilliseconds = time.Now().UnixMilli() + updatedKeys[key] = empty{} + } + } + return updates, mergedState, updatedKeys +} diff --git a/cluster/gossiper.go b/cluster/gossiper.go new file mode 100644 index 0000000000000000000000000000000000000000..e6b83e10e5516be799edbce40bb58db461d1e126 --- /dev/null +++ b/cluster/gossiper.go @@ -0,0 +1,301 @@ +// Copyright (C) 2015-2022 Asynkron AB All rights reserved + +package cluster + +import ( + "errors" + "fmt" + "strings" + "time" + + "gitee.com/simplexyz/simpleactor-go/remote" + + "github.com/asynkron/gofun/set" + "google.golang.org/protobuf/proto" + + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/log" + "google.golang.org/protobuf/types/known/anypb" +) + +const DefaultGossipActorName string = "gossip" + +// GossipUpdate Used to update gossip data when a ClusterTopology event occurs +type GossipUpdate struct { + MemberID, Key string + Value *anypb.Any + SeqNumber int64 +} + +// ConsensusChecker Customary type used to provide consensus check callbacks of any type +// note: this is equivalent to (for future go v1.18): +// +// type ConsensusChecker[T] func(GossipState, map[string]empty) (bool, T) +type ConsensusChecker func(*GossipState, map[string]empty) (bool, interface{}) + +// The Gossiper data structure manages Gossip +type Gossiper struct { + // The Gossiper Actor Name, defaults to "gossip" + GossipActorName string + + // The Gossiper Cluster + cluster *Cluster + + // The actor PID + pid *actor.PID + + // Channel use to stop the gossip loop + close chan struct{} + + // Message throttler + throttler actor.ShouldThrottle +} + +// Creates a new Gossiper value and return it back +func newGossiper(cl *Cluster, opts ...Option) (*Gossiper, error) { + // create a new Gossiper value + gossiper := &Gossiper{ + GossipActorName: DefaultGossipActorName, + cluster: cl, + close: make(chan struct{}), + } + + // apply any given options + for _, opt := range opts { + opt(gossiper) + } + + return gossiper, nil +} + +func (g *Gossiper) GetState(key string) (map[string]*GossipKeyValue, error) { + plog.Debug(fmt.Sprintf("Gossiper getting state from %s", g.pid)) + + msg := NewGetGossipStateRequest(key) + timeout := g.cluster.Config.TimeoutTime + r, err := g.cluster.ActorSystem.Root.RequestFuture(g.pid, &msg, timeout).Result() + if err != nil { + switch err { + case actor.ErrTimeout: + plog.Error("Could not get a response from GossipActor: request timeout", log.Error(err), log.String("remote", g.pid.String())) + return nil, err + case actor.ErrDeadLetter: + plog.Error("remote no longer exists", log.Error(err), log.String("remote", g.pid.String())) + return nil, err + default: + plog.Error("Could not get a response from GossipActor", log.Error(err), log.String("remote", g.pid.String())) + return nil, err + } + } + + // try to cast the response to GetGossipStateResponse concrete value + response, ok := r.(*GetGossipStateResponse) + if !ok { + err := fmt.Errorf("could not promote %T interface to GetGossipStateResponse", r) + plog.Error("Could not get a response from GossipActor", log.Error(err), log.String("remote", g.pid.String())) + return nil, err + } + + return response.State, nil +} + +// SetState Sends fire and forget message to update member state +func (g *Gossiper) SetState(key string, value proto.Message) { + if g.throttler() == actor.Open { + plog.Debug(fmt.Sprintf("Gossiper setting state %s to %s", key, g.pid)) + } + + if g.pid == nil { + return + } + + msg := NewGossipStateKey(key, value) + g.cluster.ActorSystem.Root.Send(g.pid, &msg) +} + +// SetStateRequest Sends a Request (that blocks) to update member state +func (g *Gossiper) SetStateRequest(key string, value proto.Message) error { + if g.throttler() == actor.Open { + plog.Debug(fmt.Sprintf("Gossiper setting state %s to %s", key, g.pid)) + } + + if g.pid == nil { + return errors.New("gossiper Actor PID is nil") + } + + msg := NewGossipStateKey(key, value) + r, err := g.cluster.ActorSystem.Root.RequestFuture(g.pid, &msg, g.cluster.Config.TimeoutTime).Result() + if err != nil { + if err == actor.ErrTimeout { + plog.Error("Could not get a response from Gossiper Actor: request timeout", log.String("remote", g.pid.String())) + return err + } + plog.Error("Could not get a response from Gossiper Actor", log.Error(err), log.String("remote", g.pid.String())) + return err + } + + // try to cast the response to SetGossipStateResponse concrete value + _, ok := r.(*SetGossipStateResponse) + if !ok { + err := fmt.Errorf("could not promote %T interface to SetGossipStateResponse", r) + plog.Error("Could not get a response from Gossip Actor", log.Error(err), log.String("remote", g.pid.String())) + return err + } + return nil +} + +func (g *Gossiper) SendState() { + if g.pid == nil { + return + } + + r, err := g.cluster.ActorSystem.Root.RequestFuture(g.pid, &SendGossipStateRequest{}, 5*time.Second).Result() + if err != nil { + plog.Warn("Gossip could not send gossip request", log.PID("PID", g.pid), log.Error(err)) + return + } + + if _, ok := r.(*SendGossipStateResponse); !ok { + plog.Error("Gossip SendState received unknown response", log.Message(r)) + } +} + +// RegisterConsensusCheck Builds a consensus handler and a consensus checker, send the checker to the +// Gossip actor and returns the handler back to the caller +func (g *Gossiper) RegisterConsensusCheck(key string, getValue func(*anypb.Any) interface{}) ConsensusHandler { + definition := NewConsensusCheckBuilder(key, getValue) + consensusHandle, check := definition.Build() + request := NewAddConsensusCheck(consensusHandle.GetID(), check) + g.cluster.ActorSystem.Root.Send(g.pid, &request) + return consensusHandle +} + +func (g *Gossiper) StartGossiping() error { + var err error + g.pid, err = g.cluster.ActorSystem.Root.SpawnNamed(actor.PropsFromProducer(func() actor.Actor { + return NewGossipActor( + g.cluster.Config.GossipRequestTimeout, + g.cluster.ActorSystem.ID, + func() set.Set[string] { + return g.cluster.GetBlockedMembers() + }, + g.cluster.Config.GossipFanOut, + g.cluster.Config.GossipMaxSend, + ) + }), g.GossipActorName) + + if err != nil { + plog.Error("Failed to start gossip actor", log.Error(err)) + return err + } + + g.cluster.ActorSystem.EventStream.Subscribe(func(evt interface{}) { + if topology, ok := evt.(*ClusterTopology); ok { + g.cluster.ActorSystem.Root.Send(g.pid, topology) + } + }) + plog.Info("Started Cluster Gossip") + g.throttler = actor.NewThrottle(3, 60*time.Second, g.throttledLog) + go g.gossipLoop() + + return nil +} + +func (g *Gossiper) Shutdown() { + if g.pid == nil { + return + } + + plog.Info("Shutting down gossip") + + close(g.close) + + err := g.cluster.ActorSystem.Root.StopFuture(g.pid).Wait() + if err != nil { + plog.Error("failed to stop gossip actor", log.Error(err)) + } + + plog.Info("Shut down gossip") +} + +func (g *Gossiper) gossipLoop() { + plog.Info("Starting gossip loop") + + // create a ticker that will tick each GossipInterval milliseconds + // we do not use sleep as sleep puts the goroutine out of the scheduler + // P, and we do not want our Gs to be scheduled out from the running Ms + ticker := time.NewTicker(g.cluster.Config.GossipInterval) +breakLoop: + for !g.cluster.ActorSystem.IsStopped() { + select { + case <-g.close: + plog.Info("Stopping Gossip Loop") + break breakLoop + case <-ticker.C: + + g.blockExpiredHeartbeats() + g.blockGracefullyLeft() + + g.SetState(HearthbeatKey, &MemberHeartbeat{ + // todo collect the actor statistics + ActorStatistics: &ActorStatistics{}, + }) + g.SendState() + } + } +} + +// blockExpiredHeartbeats blocks members that have not sent a heartbeat for a long time +func (g *Gossiper) blockExpiredHeartbeats() { + if g.cluster.Config.GossipInterval == 0 { + return + } + t, err := g.GetState(HearthbeatKey) + if err != nil { + plog.Error("Could not get heartbeat state", log.Error(err)) + return + } + + blockList := remote.GetRemote(g.cluster.ActorSystem).BlockList() + + blocked := make([]string, 0) + + for k, v := range t { + if k != g.cluster.ActorSystem.ID && + !blockList.IsBlocked(k) && + time.Now().Sub(time.UnixMilli(v.LocalTimestampUnixMilliseconds)) > g.cluster.Config.HeartbeatExpiration { + blocked = append(blocked, k) + } + } + + if len(blocked) > 0 { + plog.Info("Blocking members due to expired heartbeat", log.String("members", strings.Join(blocked, ","))) + blockList.Block(blocked...) + } +} + +// blockGracefullyLeft blocking members due to gracefully leaving +func (g *Gossiper) blockGracefullyLeft() { + t, err := g.GetState(GracefullyLeftKey) + if err != nil { + plog.Error("Could not get gracefully left members", log.Error(err)) + return + } + + blockList := remote.GetRemote(g.cluster.ActorSystem).BlockList() + + gracefullyLeft := make([]string, 0) + for k := range t { + if !blockList.IsBlocked(k) && k != g.cluster.ActorSystem.ID { + gracefullyLeft = append(gracefullyLeft, k) + } + } + if len(gracefullyLeft) > 0 { + plog.Info("Blocking members due to gracefully leaving", log.String("members", strings.Join(gracefullyLeft, ","))) + blockList.Block(gracefullyLeft...) + } +} + +func (g *Gossiper) throttledLog(counter int32) { + plog.Debug(fmt.Sprintf("[Gossiper] Gossiper Setting State to %s", g.pid), log.Int("throttled", int(counter))) +} diff --git a/cluster/grain.go b/cluster/grain.go new file mode 100644 index 0000000000000000000000000000000000000000..3834fbde2a4303a7fe435f79d85752b28f9c2381 --- /dev/null +++ b/cluster/grain.go @@ -0,0 +1,65 @@ +package cluster + +import ( + "time" + + "gitee.com/simplexyz/simpleactor-go/actor" +) + +type GrainCallConfig struct { + RetryCount int + Timeout time.Duration + RetryAction func(n int) + Context actor.SenderContext +} + +type GrainCallOption func(config *GrainCallConfig) + +var defaultGrainCallOptions *GrainCallConfig + +func DefaultGrainCallConfig(cluster *Cluster) *GrainCallConfig { + if defaultGrainCallOptions == nil { + defaultGrainCallOptions = NewGrainCallOptions(cluster) + } + return defaultGrainCallOptions +} + +func NewGrainCallOptions(cluster *Cluster) *GrainCallConfig { + return &GrainCallConfig{ + RetryCount: 10, + Timeout: cluster.Config.RequestTimeoutTime, + RetryAction: func(i int) { + i++ + time.Sleep(time.Duration(i * i * 50)) + }, + } +} + +func WithTimeout(timeout time.Duration) GrainCallOption { + return func(config *GrainCallConfig) { + config.Timeout = timeout + } +} + +func WithRetry(count int) GrainCallOption { + return func(config *GrainCallConfig) { + config.RetryCount = count + } +} + +func WithRetryAction(act func(i int)) GrainCallOption { + return func(config *GrainCallConfig) { + config.RetryAction = act + } +} + +func WithContext(ctx actor.SenderContext) GrainCallOption { + return func(config *GrainCallConfig) { + config.Context = ctx + } +} + +type ClusterInit struct { + Identity *ClusterIdentity + Cluster *Cluster +} diff --git a/cluster/grain.pb.go b/cluster/grain.pb.go new file mode 100644 index 0000000000000000000000000000000000000000..d43494fe571fe603693bf1964c3a0d9f9ee4b0ec --- /dev/null +++ b/cluster/grain.pb.go @@ -0,0 +1,302 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc v3.19.1 +// source: grain.proto + +package cluster + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type GrainRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + MethodIndex int32 `protobuf:"varint,1,opt,name=method_index,json=methodIndex,proto3" json:"method_index,omitempty"` + MessageData []byte `protobuf:"bytes,2,opt,name=message_data,json=messageData,proto3" json:"message_data,omitempty"` + MessageTypeName string `protobuf:"bytes,3,opt,name=message_type_name,json=messageTypeName,proto3" json:"message_type_name,omitempty"` +} + +func (x *GrainRequest) Reset() { + *x = GrainRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_grain_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GrainRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GrainRequest) ProtoMessage() {} + +func (x *GrainRequest) ProtoReflect() protoreflect.Message { + mi := &file_grain_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GrainRequest.ProtoReflect.Descriptor instead. +func (*GrainRequest) Descriptor() ([]byte, []int) { + return file_grain_proto_rawDescGZIP(), []int{0} +} + +func (x *GrainRequest) GetMethodIndex() int32 { + if x != nil { + return x.MethodIndex + } + return 0 +} + +func (x *GrainRequest) GetMessageData() []byte { + if x != nil { + return x.MessageData + } + return nil +} + +func (x *GrainRequest) GetMessageTypeName() string { + if x != nil { + return x.MessageTypeName + } + return "" +} + +type GrainResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + MessageData []byte `protobuf:"bytes,1,opt,name=message_data,json=messageData,proto3" json:"message_data,omitempty"` + MessageTypeName string `protobuf:"bytes,2,opt,name=message_type_name,json=messageTypeName,proto3" json:"message_type_name,omitempty"` +} + +func (x *GrainResponse) Reset() { + *x = GrainResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_grain_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GrainResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GrainResponse) ProtoMessage() {} + +func (x *GrainResponse) ProtoReflect() protoreflect.Message { + mi := &file_grain_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GrainResponse.ProtoReflect.Descriptor instead. +func (*GrainResponse) Descriptor() ([]byte, []int) { + return file_grain_proto_rawDescGZIP(), []int{1} +} + +func (x *GrainResponse) GetMessageData() []byte { + if x != nil { + return x.MessageData + } + return nil +} + +func (x *GrainResponse) GetMessageTypeName() string { + if x != nil { + return x.MessageTypeName + } + return "" +} + +type GrainErrorResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Err string `protobuf:"bytes,1,opt,name=err,proto3" json:"err,omitempty"` +} + +func (x *GrainErrorResponse) Reset() { + *x = GrainErrorResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_grain_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GrainErrorResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GrainErrorResponse) ProtoMessage() {} + +func (x *GrainErrorResponse) ProtoReflect() protoreflect.Message { + mi := &file_grain_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GrainErrorResponse.ProtoReflect.Descriptor instead. +func (*GrainErrorResponse) Descriptor() ([]byte, []int) { + return file_grain_proto_rawDescGZIP(), []int{2} +} + +func (x *GrainErrorResponse) GetErr() string { + if x != nil { + return x.Err + } + return "" +} + +var File_grain_proto protoreflect.FileDescriptor + +var file_grain_proto_rawDesc = []byte{ + 0x0a, 0x0b, 0x67, 0x72, 0x61, 0x69, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x07, 0x63, + 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x22, 0x80, 0x01, 0x0a, 0x0c, 0x47, 0x72, 0x61, 0x69, 0x6e, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x6d, 0x65, 0x74, 0x68, 0x6f, + 0x64, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x6d, + 0x65, 0x74, 0x68, 0x6f, 0x64, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x21, 0x0a, 0x0c, 0x6d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x0b, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x44, 0x61, 0x74, 0x61, 0x12, 0x2a, 0x0a, + 0x11, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x54, 0x79, 0x70, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x5e, 0x0a, 0x0d, 0x47, 0x72, 0x61, + 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x6d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x0b, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x44, 0x61, 0x74, 0x61, 0x12, 0x2a, 0x0a, + 0x11, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x54, 0x79, 0x70, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x26, 0x0a, 0x12, 0x47, 0x72, 0x61, + 0x69, 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x10, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x65, 0x72, + 0x72, 0x42, 0x2c, 0x5a, 0x2a, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x61, 0x73, 0x79, 0x6e, 0x6b, 0x72, 0x6f, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x61, + 0x63, 0x74, 0x6f, 0x72, 0x2d, 0x67, 0x6f, 0x2f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_grain_proto_rawDescOnce sync.Once + file_grain_proto_rawDescData = file_grain_proto_rawDesc +) + +func file_grain_proto_rawDescGZIP() []byte { + file_grain_proto_rawDescOnce.Do(func() { + file_grain_proto_rawDescData = protoimpl.X.CompressGZIP(file_grain_proto_rawDescData) + }) + return file_grain_proto_rawDescData +} + +var file_grain_proto_msgTypes = make([]protoimpl.MessageInfo, 3) +var file_grain_proto_goTypes = []interface{}{ + (*GrainRequest)(nil), // 0: cluster.GrainRequest + (*GrainResponse)(nil), // 1: cluster.GrainResponse + (*GrainErrorResponse)(nil), // 2: cluster.GrainErrorResponse +} +var file_grain_proto_depIdxs = []int32{ + 0, // [0:0] is the sub-list for method output_type + 0, // [0:0] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_grain_proto_init() } +func file_grain_proto_init() { + if File_grain_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_grain_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GrainRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_grain_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GrainResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_grain_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GrainErrorResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_grain_proto_rawDesc, + NumEnums: 0, + NumMessages: 3, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_grain_proto_goTypes, + DependencyIndexes: file_grain_proto_depIdxs, + MessageInfos: file_grain_proto_msgTypes, + }.Build() + File_grain_proto = out.File + file_grain_proto_rawDesc = nil + file_grain_proto_goTypes = nil + file_grain_proto_depIdxs = nil +} diff --git a/cluster/grain.proto b/cluster/grain.proto new file mode 100644 index 0000000000000000000000000000000000000000..85ad4104c7121aae2a3f6cd7ae71ea62d6946166 --- /dev/null +++ b/cluster/grain.proto @@ -0,0 +1,18 @@ +syntax = "proto3"; +package cluster; +option go_package = "/gitee.com/simplexyz/simpleactor-go/cluster"; + +message GrainRequest { + int32 method_index = 1; + bytes message_data = 2; + string message_type_name = 3; +} + +message GrainResponse { + bytes message_data = 1; + string message_type_name = 2; +} + +message GrainErrorResponse { + string err = 1; +} diff --git a/cluster/grain_context.go b/cluster/grain_context.go new file mode 100644 index 0000000000000000000000000000000000000000..05200ecb9907b554addb86116c1f098990dff768 --- /dev/null +++ b/cluster/grain_context.go @@ -0,0 +1,41 @@ +package cluster + +import ( + "gitee.com/simplexyz/simpleactor-go/actor" +) + +type GrainContext interface { + actor.Context + + Identity() string + Kind() string + Cluster() *Cluster +} + +var _ actor.Context = GrainContext(&grainContextImpl{}) + +type grainContextImpl struct { + actor.Context + ci *ClusterIdentity + cluster *Cluster +} + +func (g grainContextImpl) Identity() string { + return g.ci.Identity +} + +func (g grainContextImpl) Kind() string { + return g.ci.Kind +} + +func (g grainContextImpl) Cluster() *Cluster { + return g.cluster +} + +func NewGrainContext(context actor.Context, identity *ClusterIdentity, cluster *Cluster) GrainContext { + return &grainContextImpl{ + Context: context, + ci: identity, + cluster: cluster, + } +} diff --git a/cluster/identity_lookup.go b/cluster/identity_lookup.go new file mode 100644 index 0000000000000000000000000000000000000000..6e1858340190f4f6b3035cbc8f8cea62b699e8a9 --- /dev/null +++ b/cluster/identity_lookup.go @@ -0,0 +1,89 @@ +package cluster + +import ( + "gitee.com/simplexyz/simpleactor-go/actor" +) + +// IdentityLookup contains +type IdentityLookup interface { + Get(clusterIdentity *ClusterIdentity) *actor.PID + + RemovePid(clusterIdentity *ClusterIdentity, pid *actor.PID) + + Setup(cluster *Cluster, kinds []string, isClient bool) + + Shutdown() +} + +// StorageLookup contains +type StorageLookup interface { + TryGetExistingActivation(clusterIdentity *ClusterIdentity) *StoredActivation + + TryAcquireLock(clusterIdentity *ClusterIdentity) *SpawnLock + + WaitForActivation(clusterIdentity *ClusterIdentity) *StoredActivation + + RemoveLock(spawnLock SpawnLock) + + StoreActivation(memberID string, spawnLock *SpawnLock, pid *actor.PID) + + RemoveActivation(pid *SpawnLock) + + RemoveMemberId(memberID string) +} + +// SpawnLock contains +type SpawnLock struct { + LockID string + ClusterIdentity *ClusterIdentity +} + +func newSpawnLock(lockID string, clusterIdentity *ClusterIdentity) *SpawnLock { + this := &SpawnLock{ + LockID: lockID, + ClusterIdentity: clusterIdentity, + } + + return this +} + +// StoredActivation contains +type StoredActivation struct { + Pid string + MemberID string +} + +func newStoredActivation(pid string, memberID string) *StoredActivation { + this := &StoredActivation{ + Pid: pid, + MemberID: memberID, + } + + return this +} + +// GetPid contains +type GetPid struct { + ClusterIdentity *ClusterIdentity +} + +func newGetPid(clusterIdentity *ClusterIdentity) *GetPid { + this := &GetPid{ + ClusterIdentity: clusterIdentity, + } + + return this +} + +// PidResult contains +type PidResult struct { + Pid *actor.PID +} + +func newPidResult(p *actor.PID) *PidResult { + this := &PidResult{ + Pid: p, + } + + return this +} diff --git a/cluster/identity_storage_lookup.go b/cluster/identity_storage_lookup.go new file mode 100644 index 0000000000000000000000000000000000000000..c234fd009c2ec280469400bc14b3f946fc3508b7 --- /dev/null +++ b/cluster/identity_storage_lookup.go @@ -0,0 +1,65 @@ +package cluster + +import ( + "time" + + "gitee.com/simplexyz/simpleactor-go/actor" +) + +const ( + placementActorName = "placement-activator" + pidClusterIdentityStartIndex = len(placementActorName) + 1 +) + +// IdentityStorageLookup contains +type IdentityStorageLookup struct { + Storage StorageLookup + cluster *Cluster + isClient bool + placementActor *actor.PID + system *actor.ActorSystem + router *actor.PID + memberID string +} + +func newIdentityStorageLookup(storage StorageLookup) *IdentityStorageLookup { + this := &IdentityStorageLookup{ + Storage: storage, + } + return this +} + +// RemoveMember from identity storage +func (i *IdentityStorageLookup) RemoveMember(memberID string) { + i.Storage.RemoveMemberId(memberID) +} + +// RemotePlacementActor returns the PID of the remote placement actor +func RemotePlacementActor(address string) *actor.PID { + return actor.NewPID(address, placementActorName) +} + +// +// Interface: IdentityLookup +// + +// Get returns a PID for a given ClusterIdentity +func (id *IdentityStorageLookup) Get(clusterIdentity *ClusterIdentity) *actor.PID { + msg := newGetPid(clusterIdentity) + timeout := 5 * time.Second + + res, _ := id.system.Root.RequestFuture(id.router, msg, timeout).Result() + response := res.(*actor.Future) + + return response.PID() +} + +func (id *IdentityStorageLookup) Setup(cluster *Cluster, kinds []string, isClient bool) { + id.cluster = cluster + id.system = cluster.ActorSystem + id.memberID = cluster.ActorSystem.ID + + // workerProps := actor.PropsFromProducer(func() actor.Actor { return newIdentityStorageWorker(identity) }) + + // routerProps := identity.system.Root.(workerProps, 50); +} diff --git a/cluster/identity_storage_worker.go b/cluster/identity_storage_worker.go new file mode 100644 index 0000000000000000000000000000000000000000..3ae1303673233689ffd82fd25b52e5034788ec5c --- /dev/null +++ b/cluster/identity_storage_worker.go @@ -0,0 +1,47 @@ +package cluster + +import ( + "log" + + "gitee.com/simplexyz/simpleactor-go/actor" +) + +type IdentityStorageWorker struct { + cluster *Cluster + lookup *IdentityStorageLookup + storage StorageLookup +} + +func newIdentityStorageWorker(storageLookup *IdentityStorageLookup) *IdentityStorageWorker { + this := &IdentityStorageWorker{ + cluster: storageLookup.cluster, + lookup: storageLookup, + storage: storageLookup.Storage, + } + return this +} + +// Receive func +func (ids *IdentityStorageWorker) Receive(c actor.Context) { + m := c.Message() + getPid, ok := m.(GetPid) + + if !ok { + return + } + + if c.Sender() == nil { + log.Println("No sender in GetPid request") + return + } + + existing, _ := ids.cluster.PidCache.Get(getPid.ClusterIdentity.Identity, getPid.ClusterIdentity.Kind) + + if existing != nil { + log.Printf("Found %s in pidcache", m.(GetPid).ClusterIdentity.ToShortString()) + c.Respond(newPidResult(existing)) + } + + return + // continue +} diff --git a/cluster/identitylookup/disthash/identity_lookup.go b/cluster/identitylookup/disthash/identity_lookup.go new file mode 100644 index 0000000000000000000000000000000000000000..f5911a7d9d630b972bf713d466b8bb2363ef750f --- /dev/null +++ b/cluster/identitylookup/disthash/identity_lookup.go @@ -0,0 +1,35 @@ +package disthash + +import ( + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/cluster" +) + +type IdentityLookup struct { + partitionManager *Manager +} + +func (p *IdentityLookup) Get(clusterIdentity *cluster.ClusterIdentity) *actor.PID { + return p.partitionManager.Get(clusterIdentity) +} + +func (p *IdentityLookup) RemovePid(clusterIdentity *cluster.ClusterIdentity, pid *actor.PID) { + activationTerminated := &cluster.ActivationTerminated{ + Pid: pid, + ClusterIdentity: clusterIdentity, + } + p.partitionManager.cluster.MemberList.BroadcastEvent(activationTerminated, true) +} + +func (p *IdentityLookup) Setup(cluster *cluster.Cluster, kinds []string, isClient bool) { + p.partitionManager = newPartitionManager(cluster) + p.partitionManager.Start() +} + +func (p *IdentityLookup) Shutdown() { + p.partitionManager.Stop() +} + +func New() cluster.IdentityLookup { + return &IdentityLookup{} +} diff --git a/cluster/identitylookup/disthash/log.go b/cluster/identitylookup/disthash/log.go new file mode 100644 index 0000000000000000000000000000000000000000..1c6f269aa942d50b7483258626f6a91f2c10c09f --- /dev/null +++ b/cluster/identitylookup/disthash/log.go @@ -0,0 +1,14 @@ +package disthash + +import ( + "gitee.com/simplexyz/simpleactor-go/log" +) + +var plog = log.New(log.DefaultLevel, "[DISTHASH]") + +// SetLogLevel sets the log level for the logger. +// +// SetLogLevel is safe to call concurrently +func SetLogLevel(level log.Level) { + plog.SetLevel(level) +} diff --git a/cluster/identitylookup/disthash/manager.go b/cluster/identitylookup/disthash/manager.go new file mode 100644 index 0000000000000000000000000000000000000000..3b0fd7ce6bb5f533d15e0f066a2b35506cf54273 --- /dev/null +++ b/cluster/identitylookup/disthash/manager.go @@ -0,0 +1,99 @@ +package disthash + +import ( + "time" + + "gitee.com/simplexyz/simpleactor-go/actor" + clustering "gitee.com/simplexyz/simpleactor-go/cluster" + "gitee.com/simplexyz/simpleactor-go/eventstream" + "gitee.com/simplexyz/simpleactor-go/log" +) + +const ( + PartitionActivatorActorName = "partition-activator" +) + +type Manager struct { + cluster *clustering.Cluster + topologySub *eventstream.Subscription + placementActor *actor.PID + rdv *clustering.Rendezvous +} + +func newPartitionManager(c *clustering.Cluster) *Manager { + return &Manager{ + cluster: c, + rdv: clustering.NewRendezvous(), + } +} + +func (pm *Manager) Start() { + plog.Info("Started partition manager") + system := pm.cluster.ActorSystem + + activatorProps := actor.PropsFromProducer(func() actor.Actor { return newPlacementActor(pm.cluster, pm) }) + pm.placementActor, _ = system.Root.SpawnNamed(activatorProps, PartitionActivatorActorName) + plog.Info("Started partition placement actor") + + pm.topologySub = system.EventStream. + Subscribe(func(ev interface{}) { + if topology, ok := ev.(*clustering.ClusterTopology); ok { + pm.onClusterTopology(topology) + } + }) +} + +func (pm *Manager) Stop() { + system := pm.cluster.ActorSystem + system.EventStream.Unsubscribe(pm.topologySub) + + err := system.Root.PoisonFuture(pm.placementActor).Wait() + if err != nil { + plog.Error("Failed to shutdown partition placement actor", log.Error(err)) + } + + plog.Info("Stopped PartitionManager") +} + +func (pm *Manager) PidOfActivatorActor(addr string) *actor.PID { + return actor.NewPID(addr, PartitionActivatorActorName) +} + +func (pm *Manager) onClusterTopology(tplg *clustering.ClusterTopology) { + plog.Info("onClusterTopology", log.Uint64("topology-hash", tplg.TopologyHash)) + + for _, m := range tplg.Members { + plog.Info("Got member ", log.String("MemberId", m.Id)) + for _, k := range m.Kinds { + plog.Info("" + m.Id + " - " + k) + } + } + + pm.rdv = clustering.NewRendezvous() + pm.rdv.UpdateMembers(tplg.Members) + pm.cluster.ActorSystem.Root.Send(pm.placementActor, tplg) +} + +func (pm *Manager) Get(identity *clustering.ClusterIdentity) *actor.PID { + ownerAddress := pm.rdv.GetByClusterIdentity(identity) + + if ownerAddress == "" { + return nil + } + + identityOwnerPid := pm.PidOfActivatorActor(ownerAddress) + request := &clustering.ActivationRequest{ + ClusterIdentity: identity, + RequestId: "aaaa", + } + future := pm.cluster.ActorSystem.Root.RequestFuture(identityOwnerPid, request, 5*time.Second) + res, err := future.Result() + if err != nil { + return nil + } + typed, ok := res.(*clustering.ActivationResponse) + if !ok { + return nil + } + return typed.Pid +} diff --git a/cluster/identitylookup/disthash/placement_actor.go b/cluster/identitylookup/disthash/placement_actor.go new file mode 100644 index 0000000000000000000000000000000000000000..a85efa2a6dbde3ee00d9911dee6f5792ad8fb98c --- /dev/null +++ b/cluster/identitylookup/disthash/placement_actor.go @@ -0,0 +1,138 @@ +package disthash + +import ( + "gitee.com/simplexyz/simpleactor-go/actor" + clustering "gitee.com/simplexyz/simpleactor-go/cluster" + "gitee.com/simplexyz/simpleactor-go/log" +) + +type GrainMeta struct { + ID *clustering.ClusterIdentity + PID *actor.PID +} + +type placementActor struct { + cluster *clustering.Cluster + partitionManager *Manager + actors map[string]GrainMeta +} + +func newPlacementActor(c *clustering.Cluster, pm *Manager) *placementActor { + return &placementActor{ + cluster: c, + partitionManager: pm, + actors: map[string]GrainMeta{}, + } +} + +func (p *placementActor) Receive(ctx actor.Context) { + switch msg := ctx.Message().(type) { + case *actor.Started: + plog.Info("Placement actor started") + case *actor.Stopping: + plog.Info("Placement actor stopping") + p.onStopping(ctx) + case *actor.Stopped: + plog.Info("Placement actor stopped") + case *actor.Terminated: + p.onTerminated(msg, ctx) + case *clustering.ActivationRequest: + p.onActivationRequest(msg, ctx) + case *clustering.ClusterTopology: + p.onClusterTopology(msg, ctx) + default: + plog.Error("Invalid message", log.TypeOf("type", msg), log.PID("sender", ctx.Sender())) + } +} + +func (p *placementActor) onTerminated(msg *actor.Terminated, ctx actor.Context) { + found, key, meta := p.pidToMeta(msg.Who) + + activationTerminated := &clustering.ActivationTerminated{ + Pid: msg.Who, + ClusterIdentity: meta.ID, + } + p.partitionManager.cluster.MemberList.BroadcastEvent(activationTerminated, true) + + if found { + delete(p.actors, *key) + } +} + +func (p *placementActor) onStopping(ctx actor.Context) { + futures := make(map[string]*actor.Future, len(p.actors)) + + for key, meta := range p.actors { + futures[key] = ctx.PoisonFuture(meta.PID) + } + + for key, future := range futures { + err := future.Wait() + if err != nil { + plog.Error("Failed to poison actor", log.String("identity", key), log.Error(err)) + } + } +} + +func (p *placementActor) onActivationRequest(msg *clustering.ActivationRequest, ctx actor.Context) { + key := msg.ClusterIdentity.AsKey() + meta, found := p.actors[key] + if found { + response := &clustering.ActivationResponse{ + Pid: meta.PID, + } + ctx.Respond(response) + return + } + + clusterKind := p.cluster.GetClusterKind(msg.ClusterIdentity.Kind) + if clusterKind == nil { + plog.Error("Unknown cluster kind", log.String("kind", msg.ClusterIdentity.Kind)) + + // TODO: what to do here? + ctx.Respond(nil) + return + } + + props := clustering.WithClusterIdentity(clusterKind.Props, msg.ClusterIdentity) + + pid := ctx.SpawnPrefix(props, msg.ClusterIdentity.Identity) + + p.actors[key] = GrainMeta{ + ID: msg.ClusterIdentity, + PID: pid, + } + + response := &clustering.ActivationResponse{ + Pid: pid, + } + + ctx.Respond(response) +} + +func (p *placementActor) pidToMeta(pid *actor.PID) (bool, *string, *GrainMeta) { + for k, v := range p.actors { + if v.PID == pid { + return true, &k, &v + } + } + return false, nil, nil +} + +func (p *placementActor) onClusterTopology(msg *clustering.ClusterTopology, ctx actor.Context) { + rdv := clustering.NewRendezvous() + rdv.UpdateMembers(msg.Members) + myAddress := p.cluster.ActorSystem.Address() + for identity, meta := range p.actors { + ownerAddress := rdv.GetByIdentity(identity) + if ownerAddress == myAddress { + + plog.Debug("Actor stays", log.String("identity", identity), log.String("owner", ownerAddress), log.String("me", myAddress)) + continue + } + + plog.Debug("Actor moved", log.String("identity", identity), log.String("owner", ownerAddress), log.String("me", myAddress)) + + ctx.Poison(meta.PID) + } +} diff --git a/cluster/identitylookup/partition/identity_actor.go b/cluster/identitylookup/partition/identity_actor.go new file mode 100644 index 0000000000000000000000000000000000000000..f7b252025607d0fdc29eb433e82306b2bfd87b72 --- /dev/null +++ b/cluster/identitylookup/partition/identity_actor.go @@ -0,0 +1,211 @@ +package partition + +import ( + "time" + + "gitee.com/simplexyz/simpleactor-go/actor" + clustering "gitee.com/simplexyz/simpleactor-go/cluster" + "gitee.com/simplexyz/simpleactor-go/log" +) + +// This actor is responsible to keep track of identities owned by this member +// it does not manage the cluster spawned actors itself, only identity->remote PID management +// TLDR; this is a partition/bucket in the distributed hash table which makes up the identity lookup +// +// for spawning/activating cluster actors see PartitionActivator.cs + +type identityActor struct { + cluster *clustering.Cluster + partitionManager *Manager + lookup map[string]*actor.PID + spawns map[string]*actor.Future + topologyHash uint64 + handoverTimeout time.Duration + rdv *clustering.Rendezvous +} + +func newIdentityActor(c *clustering.Cluster, p *Manager) *identityActor { + return &identityActor{ + cluster: c, + partitionManager: p, + handoverTimeout: 10 * time.Second, + lookup: map[string]*actor.PID{}, + spawns: map[string]*actor.Future{}, + } +} + +func (p *identityActor) Receive(ctx actor.Context) { + switch msg := ctx.Message().(type) { + case *actor.Started: + p.onStart(ctx) + case *actor.Stopped: + p.onStopped() + case *clustering.ActivationRequest: + p.onActivationRequest(msg, ctx) + case *clustering.ActivationTerminated: + p.onActivationTerminated(msg) + case *clustering.ClusterTopology: + p.onClusterTopology(msg, ctx) + default: + plog.Error("Invalid message", log.TypeOf("type", msg), log.PID("sender", ctx.Sender())) + } +} + +func (p *identityActor) onStart(ctx actor.Context) { + plog.Debug("Started PartitionIdentity") + self := ctx.Self() + ctx.ActorSystem().EventStream.Subscribe(func(evt interface{}) { + if at, ok := evt.(*clustering.ActivationTerminated); ok { + p.cluster.ActorSystem.Root.Send(self, at) + } + }) +} + +func (p *identityActor) onStopped() { + plog.Info("Stopped PartitionIdentity") +} + +func (p *identityActor) onActivationRequest(msg *clustering.ActivationRequest, ctx actor.Context) { + ownerAddress := p.rdv.GetByClusterIdentity(msg.ClusterIdentity) + + // should I own it? + if ownerAddress != ctx.Self().Address { + ownerPid := p.partitionManager.PidOfIdentityActor(ownerAddress) + ctx.Forward(ownerPid) + return + } + + // do I already own it? + if pid, ok := p.lookup[msg.ClusterIdentity.AsKey()]; ok { + respondActivation(pid, ctx) + return + } + + // Get activator + activatorAddress := p.cluster.MemberList.GetActivatorMember(msg.ClusterIdentity.Kind, ctx.Sender().Address) + activator := p.partitionManager.PidOfActivatorActor(activatorAddress) + + // No activator found, bail out and respond empty + if activator == nil { + respondEmptyActivation(ctx) + return + } + + // What is this? + // in case the actor of msg.Name is not yet spawned. there could be multiple re-entrant + // messages requesting it, we just reuse the same task for all those + // once spawned, the key is removed from this dict + res, ok := p.spawns[msg.ClusterIdentity.AsKey()] + if !ok { + res = p.spawnRemoteActor(msg, activatorAddress) + p.spawns[msg.ClusterIdentity.AsKey()] = res + } + + // execution ends here. context.ReenterAfter is invoked once the task completes + // but still within the actors sequential execution + // but other messages could have been processed in between + // Await SpawningProcess + ctx.ReenterAfter(res, func(res interface{}, err error) { + delete(p.spawns, msg.ClusterIdentity.AsKey()) + + ar, ok := res.(*clustering.ActivationResponse) + if !ok { + // spawn failed, respond empty + respondEmptyActivation(ctx) + return + } + + // do I already own it? + if pid, ok := p.lookup[msg.ClusterIdentity.AsKey()]; ok { + respondActivation(pid, ctx) + return + } + + p.lookup[msg.ClusterIdentity.AsKey()] = ar.Pid + + respondActivation(ar.Pid, ctx) + }) +} + +func respondActivation(pid *actor.PID, ctx actor.Context) { + response := &clustering.ActivationResponse{ + Pid: pid, + } + + ctx.Respond(response) +} + +func respondEmptyActivation(ctx actor.Context) { + response := &clustering.ActivationResponse{ + Pid: nil, + } + ctx.Respond(response) +} + +func (p *identityActor) onActivationTerminated(msg *clustering.ActivationTerminated) { + // //we get this via broadcast to all nodes, remove if we have it, or ignore + key := msg.ClusterIdentity.AsKey() + _, ok := p.spawns[key] + if ok { + return + } + + // Logger.LogDebug("[PartitionIdentityActor] Terminated {Pid}", msg.Pid); + p.cluster.PidCache.RemoveByValue(msg.ClusterIdentity.Identity, msg.ClusterIdentity.Kind, msg.Pid) + delete(p.lookup, key) +} + +func (p *identityActor) onClusterTopology(msg *clustering.ClusterTopology, ctx actor.Context) { + // await _cluster.MemberList.TopologyConsensus(); + if p.topologyHash == msg.TopologyHash { + return + } + + members := msg.Members + p.rdv = clustering.NewRendezvous() + p.rdv.UpdateMembers(members) + p.lookup = map[string]*actor.PID{} + futures := make([]*actor.Future, 0) + + requestMsg := &clustering.IdentityHandoverRequest{ + CurrentTopology: &clustering.IdentityHandoverRequest_Topology{ + Members: msg.Members, + TopologyHash: msg.TopologyHash, + }, + + Address: ctx.Self().Address, + } + + for _, m := range members { + placementPid := p.partitionManager.PidOfActivatorActor(m.Address()) + future := ctx.RequestFuture(placementPid, requestMsg, 5*time.Second) + + futures = append(futures, future) + } + + for _, f := range futures { + res, _ := f.Result() + if response, ok := res.(*clustering.IdentityHandover); ok { + for _, activation := range response.Actors { + p.takeOwnership(activation) + } + } + } +} + +func (p *identityActor) takeOwnership(activation *clustering.Activation) { + key := activation.ClusterIdentity.AsKey() + if existing, ok := p.lookup[key]; ok { + if existing.Address == activation.Pid.Address { + return + } + } + + p.lookup[key] = activation.Pid +} + +func (p *identityActor) spawnRemoteActor(msg *clustering.ActivationRequest, address string) *actor.Future { + activator := p.partitionManager.PidOfActivatorActor(address) + future := p.cluster.ActorSystem.Root.RequestFuture(activator, msg, 5*time.Second) + return future +} diff --git a/cluster/identitylookup/partition/identity_actor_test.go b/cluster/identitylookup/partition/identity_actor_test.go new file mode 100644 index 0000000000000000000000000000000000000000..54d1e086698b3b900a832c0b403734a61084d3db --- /dev/null +++ b/cluster/identitylookup/partition/identity_actor_test.go @@ -0,0 +1,11 @@ +package partition + +// func TestPartitionIdentityActor_handleClusterTopology(t *testing.T) { +// assert := assert.New(t) +// members := _newTopologyEventForTest(1) +// cluster := _newClusterForTest("test-partition-identityactor") +// partitionManager := newPartitionManager(cluster) +// partitionManager.StartMember() +// tplg := ClusterTopology{Members: members, EventId: 1} +// cluster.ActorSystem.EventStream.Publish(&tplg) +// } diff --git a/cluster/identitylookup/partition/identity_lookup.go b/cluster/identitylookup/partition/identity_lookup.go new file mode 100644 index 0000000000000000000000000000000000000000..6a5420b970262b2370b4f36747af6f0aed09246d --- /dev/null +++ b/cluster/identitylookup/partition/identity_lookup.go @@ -0,0 +1,35 @@ +package partition + +import ( + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/cluster" +) + +type IdentityLookup struct { + partitionManager *Manager +} + +func (p *IdentityLookup) Get(clusterIdentity *cluster.ClusterIdentity) *actor.PID { + return p.partitionManager.Get(clusterIdentity) +} + +func (p *IdentityLookup) RemovePid(clusterIdentity *cluster.ClusterIdentity, pid *actor.PID) { + activationTerminated := &cluster.ActivationTerminated{ + Pid: pid, + ClusterIdentity: clusterIdentity, + } + p.partitionManager.cluster.MemberList.BroadcastEvent(activationTerminated, true) +} + +func (p *IdentityLookup) Setup(cluster *cluster.Cluster, kinds []string, isClient bool) { + p.partitionManager = newPartitionManager(cluster) + p.partitionManager.Start() +} + +func (p *IdentityLookup) Shutdown() { + p.partitionManager.Stop() +} + +func New() cluster.IdentityLookup { + return &IdentityLookup{} +} diff --git a/cluster/identitylookup/partition/log.go b/cluster/identitylookup/partition/log.go new file mode 100644 index 0000000000000000000000000000000000000000..8f5dc6c0558a42eb54c5e7f98fc3e454a8c33b33 --- /dev/null +++ b/cluster/identitylookup/partition/log.go @@ -0,0 +1,14 @@ +package partition + +import ( + "gitee.com/simplexyz/simpleactor-go/log" +) + +var plog = log.New(log.DefaultLevel, "[PARTITION]") + +// SetLogLevel sets the log level for the logger. +// +// SetLogLevel is safe to call concurrently +func SetLogLevel(level log.Level) { + plog.SetLevel(level) +} diff --git a/cluster/identitylookup/partition/manager.go b/cluster/identitylookup/partition/manager.go new file mode 100644 index 0000000000000000000000000000000000000000..643e2fca32f2938f524c55f77469cb566d1d1ab5 --- /dev/null +++ b/cluster/identitylookup/partition/manager.go @@ -0,0 +1,110 @@ +package partition + +import ( + "time" + + "gitee.com/simplexyz/simpleactor-go/actor" + clustering "gitee.com/simplexyz/simpleactor-go/cluster" + "gitee.com/simplexyz/simpleactor-go/eventstream" + "gitee.com/simplexyz/simpleactor-go/log" +) + +const ( + ActorNameIdentity = "partition" + ActorNamePlacement = "partition-activator" +) + +type Manager struct { + cluster *clustering.Cluster + topologySub *eventstream.Subscription + identityActor *actor.PID + placementActor *actor.PID + rdv *clustering.Rendezvous +} + +func newPartitionManager(c *clustering.Cluster) *Manager { + return &Manager{ + cluster: c, + } +} + +func (pm *Manager) Start() { + plog.Info("Started partition manager") + system := pm.cluster.ActorSystem + + identityProps := actor.PropsFromProducer(func() actor.Actor { return newIdentityActor(pm.cluster, pm) }) + pm.identityActor, _ = system.Root.SpawnNamed(identityProps, ActorNameIdentity) + plog.Info("Started partition identity actor") + + activatorProps := actor.PropsFromProducer(func() actor.Actor { return newPlacementActor(pm.cluster, pm) }) + pm.placementActor, _ = system.Root.SpawnNamed(activatorProps, ActorNamePlacement) + plog.Info("Started partition placement actor") + + pm.topologySub = system.EventStream. + Subscribe(func(ev interface{}) { + // fmt.Printf("PM got event.... %v", ev) + if topology, ok := ev.(*clustering.ClusterTopology); ok { + pm.onClusterTopology(topology) + } + }) +} + +func (pm *Manager) Stop() { + system := pm.cluster.ActorSystem + system.EventStream.Unsubscribe(pm.topologySub) + + err := system.Root.PoisonFuture(pm.placementActor).Wait() + if err != nil { + plog.Error("Failed to shutdown partition placement actor", log.Error(err)) + } + + plog.Info("Stopped PartitionManager") +} + +func (pm *Manager) PidOfIdentityActor(addr string) *actor.PID { + return actor.NewPID(addr, ActorNameIdentity) +} + +func (pm *Manager) PidOfActivatorActor(addr string) *actor.PID { + return actor.NewPID(addr, ActorNamePlacement) +} + +func (pm *Manager) onClusterTopology(tplg *clustering.ClusterTopology) { + plog.Info("onClusterTopology", log.Uint64("eventId", tplg.TopologyHash)) + + for _, m := range tplg.Members { + plog.Info("Got member " + m.Id) + + for _, k := range m.Kinds { + plog.Info("" + m.Id + " - " + k) + } + } + + pm.rdv = clustering.NewRendezvous() + pm.rdv.UpdateMembers(tplg.Members) + pm.cluster.ActorSystem.Root.Send(pm.identityActor, tplg) +} + +func (pm *Manager) Get(identity *clustering.ClusterIdentity) *actor.PID { + ownerAddress := pm.rdv.GetByClusterIdentity(identity) + + if ownerAddress == "" { + return nil + } + + identityOwnerPid := pm.PidOfIdentityActor(ownerAddress) + request := &clustering.ActivationRequest{ + ClusterIdentity: identity, + RequestId: "aaaa", + } + future := pm.cluster.ActorSystem.Root.RequestFuture(identityOwnerPid, request, 5*time.Second) + res, err := future.Result() + if err != nil { + return nil + } + typed, ok := res.(*clustering.ActivationResponse) + if !ok { + return nil + } + return typed.Pid +} diff --git a/cluster/identitylookup/partition/partition.puml b/cluster/identitylookup/partition/partition.puml new file mode 100644 index 0000000000000000000000000000000000000000..69b6ef070b438607b9209bea4268dde79f7011b5 --- /dev/null +++ b/cluster/identitylookup/partition/partition.puml @@ -0,0 +1,40 @@ +@startuml + +title "Partition Sequence" + +participant "IdentityLookup@node1" as lookup #LightGreen +participant "IdentityActor@node1" as id1 #LightGreen +participant "PlacementActor@node1" as place1 #LightGreen + +participant "IdentityActor@node2" as id2 #Pink +participant "PlacementActor@node2" as place2 #Pink + + +lookup -> id1: send ActivationRequest +activate id1 + id1 -> id1: owner.address = chash(ClusterIdentity) + activate id1 + +alt owner.address == self.address (owner = chash(id)) + id1 -> place1: forward ActivationRequest + activate place1 + id1 <- place1: respond ActivationResponse + deactivate place1 + deactivate id1 + lookup <- id1: respond ActivationResponse +else + id1 -> id2: send ActivationRequest + activate id2 + id2 -> id2: owner.address = chash(ClusterIdentity) + activate id2 + id2 -> place2: forward ActivationRequest + deactivate id2 + deactivate id2 + activate place2 + id1 <- place2: respond ActivationResponse + deactivate place2 + lookup <- id1: respond ActivationResponse + deactivate id1 +end + +@enduml \ No newline at end of file diff --git a/cluster/identitylookup/partition/placement_actor.go b/cluster/identitylookup/partition/placement_actor.go new file mode 100644 index 0000000000000000000000000000000000000000..d0b12aa10cfc4aa1c4a727e73f1a7b0b9a7c8e58 --- /dev/null +++ b/cluster/identitylookup/partition/placement_actor.go @@ -0,0 +1,144 @@ +package partition + +import ( + "gitee.com/simplexyz/simpleactor-go/actor" + clustering "gitee.com/simplexyz/simpleactor-go/cluster" + "gitee.com/simplexyz/simpleactor-go/log" +) + +type GrainMeta struct { + ID *clustering.ClusterIdentity + PID *actor.PID +} + +type placementActor struct { + cluster *clustering.Cluster + partitionManager *Manager + actors map[string]GrainMeta +} + +func newPlacementActor(c *clustering.Cluster, pm *Manager) *placementActor { + return &placementActor{ + cluster: c, + partitionManager: pm, + actors: map[string]GrainMeta{}, + } +} + +func (p *placementActor) Receive(ctx actor.Context) { + switch msg := ctx.Message().(type) { + case *actor.Stopping: + plog.Info("Placement actor stopping") + p.onStopping(ctx) + case *actor.Stopped: + plog.Info("Placement actor stopped") + case *actor.Terminated: + p.onTerminated(msg, ctx) + case *clustering.IdentityHandoverRequest: + p.onIdentityHandoverRequest(msg, ctx) + case *clustering.ActivationRequest: + p.onActivationRequest(msg, ctx) + default: + plog.Error("Invalid message", log.TypeOf("type", msg), log.PID("sender", ctx.Sender())) + } +} + +func (p *placementActor) onTerminated(msg *actor.Terminated, ctx actor.Context) { + found, key, meta := p.pidToMeta(msg.Who) + + activationTerminated := &clustering.ActivationTerminated{ + Pid: msg.Who, + ClusterIdentity: meta.ID, + } + p.partitionManager.cluster.MemberList.BroadcastEvent(activationTerminated, true) + + if found { + delete(p.actors, *key) + } +} + +func (p *placementActor) onStopping(ctx actor.Context) { + futures := make(map[string]*actor.Future, len(p.actors)) + + for key, meta := range p.actors { + futures[key] = ctx.PoisonFuture(meta.PID) + } + + for key, future := range futures { + err := future.Wait() + if err != nil { + plog.Error("Failed to poison actor", log.String("identity", key), log.Error(err)) + } + } +} + +// this is pure, we do not change any state or actually move anything +// the requester also provide its own view of the world in terms of members +// TLDR; we are not using any topology state from this actor itself +func (p *placementActor) onIdentityHandoverRequest(msg *clustering.IdentityHandoverRequest, ctx actor.Context) { + count := 0 + response := &clustering.IdentityHandover{} + requestAddress := ctx.Sender().Address + rdv := clustering.NewRendezvous() + rdv.UpdateMembers(msg.CurrentTopology.Members) + for identity, meta := range p.actors { + // who owns this identity according to the requesters memberlist? + ownerAddress := rdv.GetByIdentity(identity) + // this identity is not owned by the requester + if ownerAddress != requestAddress { + continue + } + // _logger.LogDebug("Transfer {Identity} to {newOwnerAddress} -- {TopologyHash}", clusterIdentity, ownerAddress, + // msg.TopologyHash + // ); + + actorToHandOver := &clustering.Activation{ + ClusterIdentity: meta.ID, + Pid: meta.PID, + } + + response.Actors = append(response.Actors, actorToHandOver) + count++ + } + + plog.Debug("Transferred ownership to other members", log.Int("count", count)) + ctx.Respond(response) +} + +func (p *placementActor) onActivationRequest(msg *clustering.ActivationRequest, ctx actor.Context) { + key := msg.ClusterIdentity.AsKey() + meta, found := p.actors[key] + if found { + response := &clustering.ActivationResponse{ + Pid: meta.PID, + } + ctx.Respond(response) + return + } + + clusterKind := p.cluster.GetClusterKind(msg.ClusterIdentity.Kind) + + props := clustering.WithClusterIdentity(clusterKind.Props, msg.ClusterIdentity) + + pid := ctx.SpawnPrefix(props, msg.ClusterIdentity.Identity) + + p.actors[key] = GrainMeta{ + ID: msg.ClusterIdentity, + PID: pid, + } + + response := &clustering.ActivationResponse{ + Pid: pid, + } + + ctx.Respond(response) +} + +func (p *placementActor) pidToMeta(pid *actor.PID) (bool, *string, *GrainMeta) { + for k, v := range p.actors { + if v.PID == pid { + return true, &k, &v + } + } + return false, nil, nil +} diff --git a/cluster/informer.go b/cluster/informer.go new file mode 100644 index 0000000000000000000000000000000000000000..fb7af343444a6c2cdcc0d27e84eb92f9ec34aba0 --- /dev/null +++ b/cluster/informer.go @@ -0,0 +1,292 @@ +// Copyright (C) 2015-2022 Asynkron AB All rights reserved + +package cluster + +import ( + "fmt" + "math/rand" + "reflect" + "time" + + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/log" + "github.com/asynkron/gofun/set" + "google.golang.org/protobuf/proto" +) + +const ( + TopologyKey string = "topology" + HearthbeatKey string = "heathbeat" + GracefullyLeftKey string = "left" +) + +// create and seed a pseudo random numbers generator +var rnd = rand.New(rand.NewSource(time.Now().UnixMicro())) + +// The Informer data structure implements the Gossip interface +type Informer struct { + myID string + localSeqNumber int64 + state *GossipState + committedOffsets map[string]int64 + activeMemberIDs map[string]empty + otherMembers []*Member + consensusChecks *ConsensusChecks + getBlockedMembers func() set.Set[string] + gossipFanOut int + gossipMaxSend int + throttler actor.ShouldThrottle +} + +// makes sure Informer complies with the Gossip interface +var _ Gossip = (*Informer)(nil) + +// Creates a new Informer value with the given properties and returns +// back a pointer to its memory location in the heap +func newInformer(myID string, getBlockedMembers func() set.Set[string], fanOut int, maxSend int) *Informer { + informer := Informer{ + myID: myID, + state: &GossipState{ + Members: map[string]*GossipState_GossipMemberState{}, + }, + committedOffsets: map[string]int64{}, + activeMemberIDs: map[string]empty{}, + otherMembers: []*Member{}, + consensusChecks: NewConsensusChecks(), + getBlockedMembers: getBlockedMembers, + gossipFanOut: fanOut, + gossipMaxSend: maxSend, + } + informer.throttler = actor.NewThrottle(3, 60*time.Second, informer.throttledLog) + return &informer +} + +// called when there is a cluster topology update +func (inf *Informer) UpdateClusterTopology(topology *ClusterTopology) { + var others []*Member + for _, member := range topology.Members { + if member.Id != inf.myID { + others = append(others, member) + } + } + inf.otherMembers = others + + active := make(map[string]empty) + for _, member := range topology.Members { + active[member.Id] = empty{} + } + + inf.SetState(TopologyKey, topology) +} + +// sets new update key state using the given proto message +func (inf *Informer) SetState(key string, message proto.Message) { + inf.localSeqNumber = setKey(inf.state, key, message, inf.myID, inf.localSeqNumber) + + //if inf.throttler() == actor.Open { + // sequenceNumbers := map[string]uint64{} + // + // for _, memberState := range inf.state.Members { + // for key, value := range memberState.Values { + // sequenceNumbers[key] = uint64(value.SequenceNumber) + // } + // } + // + // // plog.Debug("Setting state", log.String("key", key), log.String("value", message.String()), log.Object("state", sequenceNumbers)) + //} + + if _, ok := inf.state.Members[inf.myID]; !ok { + plog.Error("State corrupt") + } + + inf.checkConsensusKey(key) +} + +// sends this informer local state to remote informers chosen randomly +// from the slice of other members known by this informer until gossipFanOut +// number of sent has been reached +func (inf *Informer) SendState(sendStateToMember LocalStateSender) { + // inf.purgeBannedMembers() // TODO + for _, member := range inf.otherMembers { + ensureMemberStateExists(inf.state, member.Id) + } + + // make a copy of the otherMembers so we can sort it randomly + otherMembers := make([]*Member, len(inf.otherMembers)) + copy(otherMembers, inf.otherMembers) + + // shuffles the order of the slice elements + rnd.Shuffle(len(otherMembers), func(i, j int) { + otherMembers[i], otherMembers[j] = otherMembers[j], otherMembers[i] + }) + + fanOutCount := 0 + for _, member := range otherMembers { + memberState := inf.GetMemberStateDelta(member.Id) + if !memberState.HasState { + // nothing has change, skip it + continue + } + + // fire and forget, we handle results in ReenterAfter + sendStateToMember(memberState, member) + fanOutCount++ + + // we reached our limit, break + if fanOutCount >= inf.gossipFanOut { + break + } + } +} + +func (inf *Informer) GetMemberStateDelta(targetMemberID string) *MemberStateDelta { + var count int + + // newState will old the final new state to be sent + newState := GossipState{Members: make(map[string]*GossipState_GossipMemberState)} + + // hashmaps in Go are random by nature so no need to randomize state.Members + pendingOffsets := inf.committedOffsets + + // create a new map with gossipMaxSend entries max + members := make(map[string]*GossipState_GossipMemberState) + + // add ourselves to the gossip list if we are in the members state + if member, ok := inf.state.Members[inf.myID]; ok { + members[inf.myID] = member + count++ + } + + // Go hash maps are unordered by nature so we don't need to randomize them + // iterate over our state members skipping ourselves and add them to the + // local `newState` variable until gossipMaxSend is reached + for id, member := range inf.state.Members { + if id == inf.myID { + continue + } + + count++ + members[id] = member + + if count > inf.gossipMaxSend { + break + } + } + + // now we iterate over our subset of members and proceed to send them if applicable + for memberID, memberState := range members { + + // create an empty state + newMemberState := GossipState_GossipMemberState{ + Values: make(map[string]*GossipKeyValue), + } + + watermarkKey := fmt.Sprintf("%s.%s", targetMemberID, memberID) + + // get the water mark + watermark := inf.committedOffsets[watermarkKey] + newWatermark := watermark + + // for each value in member state + for key, value := range memberState.Values { + + if value.SequenceNumber <= watermark { + continue + } + + if value.SequenceNumber > newWatermark { + newWatermark = value.SequenceNumber + } + + newMemberState.Values[key] = value + } + + // do not send memberStates that we have no new data for + if len(newMemberState.Values) > 0 { + newState.Members[memberID] = &newMemberState + pendingOffsets[watermarkKey] = newWatermark + } + } + + hasState := reflect.DeepEqual(inf.committedOffsets, pendingOffsets) + memberState := &MemberStateDelta{ + TargetMemberID: targetMemberID, + HasState: hasState, + State: &newState, + CommitOffsets: func() { + inf.commitPendingOffsets(pendingOffsets) + }, + } + + return memberState +} + +// adds a new consensus checker to this informer +func (inf *Informer) AddConsensusCheck(id string, check *ConsensusCheck) { + inf.consensusChecks.Add(id, check) + + // check when adding, if we are already consistent + check.check(inf.state, inf.activeMemberIDs) +} + +// removes a consensus checker from this informer +func (inf *Informer) RemoveConsensusCheck(id string) { + inf.consensusChecks.Remove(id) +} + +// retrieves this informer current state for the given key +// returns map containing each known member id and their value +func (inf *Informer) GetState(key string) map[string]*GossipKeyValue { + entries := make(map[string]*GossipKeyValue) + + for memberID, memberState := range inf.state.Members { + if value, ok := memberState.Values[key]; ok { + entries[memberID] = value + } + } + + return entries +} + +// receives a remote informer state +func (inf *Informer) ReceiveState(remoteState *GossipState) []*GossipUpdate { + updates, newState, updatedKeys := mergeState(inf.state, remoteState) + if len(updates) == 0 { + return nil + } + + inf.state = newState + keys := make([]string, 0, len(updatedKeys)) + for k := range updatedKeys { + keys = append(keys, k) + } + + inf.CheckConsensus(keys...) + return updates +} + +// check consensus for the given keys +func (inf *Informer) CheckConsensus(updatedKeys ...string) { + for _, consensusCheck := range inf.consensusChecks.GetByUpdatedKeys(updatedKeys) { + consensusCheck.check(inf.state, inf.activeMemberIDs) + } +} + +// runs checkers on key updates +func (inf *Informer) checkConsensusKey(updatedKey string) { + for _, consensusCheck := range inf.consensusChecks.GetByUpdatedKey(updatedKey) { + consensusCheck.check(inf.state, inf.activeMemberIDs) + } +} + +func (inf *Informer) commitPendingOffsets(offsets map[string]int64) { + for key, seqNumber := range offsets { + if offset, ok := inf.committedOffsets[key]; !ok || offset < seqNumber { + inf.committedOffsets[key] = seqNumber + } + } +} + +func (inf *Informer) throttledLog(counter int32) { + plog.Debug("[Gossip] Setting State", log.Int("throttled", int(counter))) +} diff --git a/cluster/informer_test.go b/cluster/informer_test.go new file mode 100644 index 0000000000000000000000000000000000000000..edbe95ac8ceab0eb4d645dc457353ebe7493e62e --- /dev/null +++ b/cluster/informer_test.go @@ -0,0 +1,210 @@ +package cluster + +import ( + "fmt" + "sync" + "testing" + + "github.com/asynkron/gofun/set" + "google.golang.org/protobuf/types/known/anypb" +) + +func TestInformer_SetState(t *testing.T) { + t.Parallel() + + a := func() set.Set[string] { + return set.New[string]() + } + + s := &MemberHeartbeat{ + ActorStatistics: &ActorStatistics{}, + } + + i := newInformer("member1", a, 3, 3) + i.SetState("heartbeat", s) +} + +func TestInformer_GetState(t *testing.T) { + t.Parallel() + + a := func() set.Set[string] { + return set.New[string]() + } + + s := &MemberHeartbeat{ + ActorStatistics: &ActorStatistics{}, + } + + i := newInformer("member1", a, 3, 3) + i.SetState("heartbeat", s) + + m := i.GetState("heartbeat") + + x, ok := m["member1"] + + if !ok { + t.Error("not ok") + } + + var s2 MemberHeartbeat + err := x.Value.UnmarshalTo(&s2) + if err != nil { + t.Error("unmarshal state error") + } +} + +func TestInformer_ReceiveState(t *testing.T) { + t.Parallel() + + a := func() set.Set[string] { + return set.New[string]() + } + + s := &MemberHeartbeat{ + ActorStatistics: &ActorStatistics{}, + } + dummyValue, _ := anypb.New(s) + + i := newInformer("member1", a, 3, 3) + i.SetState("heartbeat", s) + + remoteState := &GossipState{ + Members: GossipMemberStates{ + "member2": { + Values: GossipKeyValues{ + "heartbeat": { + Value: dummyValue, + SequenceNumber: 1, + }, + }, + }, + }, + } + + i.ReceiveState(remoteState) + + m := i.GetState("heartbeat") + + var ok bool + + m1, ok := m["member1"] + + if !ok { + t.Error("member1 is missing") + } + + var s1 MemberHeartbeat + + err := m1.Value.UnmarshalTo(&s1) + if err != nil { + t.Error("unmarshal member1 state error") + } + + // ensure we see member2 after receiving state + m2, ok := m["member2"] + + if !ok { + t.Error("member2 is missing") + } + + var s2 MemberHeartbeat + + err = m2.Value.UnmarshalTo(&s2) + + if err != nil { + t.Error("unmarshal member2 state error") + } +} + +func TestInformer_SendState(t *testing.T) { + t.Parallel() + + a := func() set.Set[string] { + return set.New[string]() + } + wg := &sync.WaitGroup{} + wg.Add(1) + + sendState := func(memberStateDelta *MemberStateDelta, member *Member) { + fmt.Printf("%+v\n", memberStateDelta) //nolint:forbidigo + wg.Done() + } + + s := &MemberHeartbeat{ + ActorStatistics: &ActorStatistics{}, + } + + i := newInformer("member1", a, 3, 3) + i.SetState("heartbeat", s) + // the cluster sees two nodes. itself and member2 + i.UpdateClusterTopology(&ClusterTopology{ + Members: []*Member{ + { + Id: "member2", + Host: "member2", + Port: 123, + }, + { + Id: "member1", + Host: "member1", + Port: 333, + }, + }, + }) + + // gossip never sends to self, so the only member we can send to is member2 + i.SendState(sendState) + wg.Wait() +} + +func TestInformer_UpdateClusterTopology(t *testing.T) { + t.Parallel() + + a := func() set.Set[string] { + return set.New[string]() + } + + s := &MemberHeartbeat{ + ActorStatistics: &ActorStatistics{}, + } + i := newInformer("member1", a, 3, 3) + i.SetState("heartbeat", s) + // the cluster sees two nodes. itself and member2 + i.UpdateClusterTopology(&ClusterTopology{ + Members: []*Member{ + { + Id: "member2", + Host: "member2", + Port: 123, + }, + { + Id: "member1", + Host: "member1", + Port: 333, + }, + }, + }) + + // TODO: how do we check that the cluster topology was updated? +} + +func TestInformer_GetMemberStateDelta(t *testing.T) { + t.Parallel() + + a := func() set.Set[string] { + return set.New[string]() + } + + s := &MemberHeartbeat{ + ActorStatistics: &ActorStatistics{}, + } + + i := newInformer("member1", a, 3, 3) + i.SetState("heartbeat", s) + + m := i.GetMemberStateDelta("member1") + + if m == nil { + t.Error("member state delta is nil") + } +} diff --git a/cluster/key_value_store.go b/cluster/key_value_store.go new file mode 100644 index 0000000000000000000000000000000000000000..e342b1181af3b278155b44a0be32581a6b1b5b72 --- /dev/null +++ b/cluster/key_value_store.go @@ -0,0 +1,25 @@ +package cluster + +import "golang.org/x/net/context" + +// KeyValueStore is a distributed key value store +type KeyValueStore[T any] interface { + // Set the value for the given key. + Set(ctx context.Context, key string, value T) error + // Get the value for the given key.. + Get(ctx context.Context, key string) (T, error) + // Clear the value for the given key. + Clear(ctx context.Context, key string) error +} + +// EmptyKeyValueStore is a key value store that does nothing. +type EmptyKeyValueStore[T any] struct{} + +func (e *EmptyKeyValueStore[T]) Set(_ context.Context, _ string, _ T) error { return nil } + +func (e *EmptyKeyValueStore[T]) Get(_ context.Context, _ string) (T, error) { + var r T + return r, nil +} + +func (e *EmptyKeyValueStore[T]) Clear(_ context.Context, _ string) error { return nil } diff --git a/cluster/kind.go b/cluster/kind.go new file mode 100644 index 0000000000000000000000000000000000000000..5ba59be116efd17b51d25ef37bce459901dbfdc9 --- /dev/null +++ b/cluster/kind.go @@ -0,0 +1,57 @@ +package cluster + +import ( + "sync/atomic" + + "gitee.com/simplexyz/simpleactor-go/actor" +) + +// Kind represents the kinds of actors a cluster can manage +type Kind struct { + Kind string + Props *actor.Props + StrategyBuilder func(*Cluster) MemberStrategy +} + +// NewKind creates a new instance of a kind +func NewKind(kind string, props *actor.Props) *Kind { + // add cluster middleware + p := props.Clone(withClusterReceiveMiddleware()) + return &Kind{ + Kind: kind, + Props: p, + StrategyBuilder: nil, + } +} + +func (k *Kind) WithMemberStrategy(strategyBuilder func(*Cluster) MemberStrategy) { + k.StrategyBuilder = strategyBuilder +} + +func (k *Kind) Build(cluster *Cluster) *ActivatedKind { + var strategy MemberStrategy = nil + if k.StrategyBuilder != nil { + strategy = k.StrategyBuilder(cluster) + } + + return &ActivatedKind{ + Kind: k.Kind, + Props: k.Props, + Strategy: strategy, + } +} + +type ActivatedKind struct { + Kind string + Props *actor.Props + Strategy MemberStrategy + count int32 +} + +func (ak *ActivatedKind) Inc() { + atomic.AddInt32(&ak.count, 1) +} + +func (ak *ActivatedKind) Dev() { + atomic.AddInt32(&ak.count, -1) +} diff --git a/cluster/log.go b/cluster/log.go new file mode 100644 index 0000000000000000000000000000000000000000..cff25db22ae8afcafcea60931d80a902edaf5126 --- /dev/null +++ b/cluster/log.go @@ -0,0 +1,14 @@ +package cluster + +import ( + "gitee.com/simplexyz/simpleactor-go/log" +) + +var plog = log.New(log.DefaultLevel, "[CLUSTER]") + +// SetLogLevel sets the log level for the logger. +// +// SetLogLevel is safe to call concurrently +func SetLogLevel(level log.Level) { + plog.SetLevel(level) +} diff --git a/cluster/member.go b/cluster/member.go new file mode 100644 index 0000000000000000000000000000000000000000..a6f586405120ce53a2b09a5e4d3f6d30aacd7afb --- /dev/null +++ b/cluster/member.go @@ -0,0 +1,80 @@ +package cluster + +import ( + "sort" + "strconv" + "strings" + + murmur32 "github.com/twmb/murmur3" +) + +type Members []*Member + +func (m *Members) ToSet() *MemberSet { + return NewMemberSet(*m) +} + +func (m *Member) HasKind(kind string) bool { + for _, k := range m.Kinds { + if k == kind { + return true + } + } + + return false +} + +// Address return a "host:port". +// Member defined by protos.proto +func (m *Member) Address() string { + return m.Host + ":" + strconv.FormatInt(int64(m.Port), 10) +} + +func TopologyHash(members Members) uint64 { + // C# version + // var x = membersByMemberId.Select(m => m.Id).OrderBy(i => i).ToArray(); + // var key = string.Join("", x); + // var hashBytes = MurmurHash2.Hash(key); + // return hashBytes; + + sort.Slice(members, func(i, j int) bool { + return members[i].Id < members[j].Id + }) + + // I assume this is not the fastest way to do this? + s := "" + for _, m := range members { + s += m.Id + } + + // TODO: this HAS to be compatible with the same hashBytes in .NET + // add plenty of tests + hash := murmur32.Sum64([]byte(s)) + + return hash +} + +func MembersToMap(members Members) map[string]*Member { + mapp := make(map[string]*Member) + for _, m := range members { + mapp[m.Id] = m + } + + return mapp +} + +func SortMembers(members Members) { + sort.Slice(members, func(i, j int) bool { + addrI := members[i].Id + addrJ := members[j].Id + + return strings.Compare(addrI, addrJ) > 0 + }) +} + +func CopySortMembers(members Members) Members { + tmp := append(make(Members, 0, len(members)), members...) + SortMembers(tmp) + + return tmp +} diff --git a/cluster/member_list.go b/cluster/member_list.go new file mode 100644 index 0000000000000000000000000000000000000000..4acbb943d373402fd9f539bb807991e27160355c --- /dev/null +++ b/cluster/member_list.go @@ -0,0 +1,259 @@ +package cluster + +import ( + "context" + "sync" + + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/eventstream" + "gitee.com/simplexyz/simpleactor-go/log" + "gitee.com/simplexyz/simpleactor-go/remote" + "google.golang.org/protobuf/types/known/anypb" +) + +// MemberList is responsible to keep track of the current cluster topology +// it does so by listening to changes from the ClusterProvider. +// the default ClusterProvider is consul.ConsulProvider which uses the Consul HTTP API to scan for changes +type MemberList struct { + cluster *Cluster + mutex sync.RWMutex + members *MemberSet + memberStrategyByKind map[string]MemberStrategy + + eventSteam *eventstream.EventStream + topologyConsensus ConsensusHandler +} + +func NewMemberList(cluster *Cluster) *MemberList { + memberList := &MemberList{ + cluster: cluster, + members: emptyMemberSet, + memberStrategyByKind: make(map[string]MemberStrategy), + eventSteam: cluster.ActorSystem.EventStream, + } + memberList.eventSteam.Subscribe(func(evt interface{}) { + switch t := evt.(type) { + case *GossipUpdate: + if t.Key != "topology" { + break + } + + // get blocked members from all other member states + // and merge that without own blocked set + var topology ClusterTopology + if err := t.Value.UnmarshalTo(&topology); err != nil { + plog.Warn("could not unpack into ClusterTopology proto.Message form Any", log.Error(err)) + + break + } + blocked := topology.Blocked + memberList.cluster.Remote.BlockList().Block(blocked...) + } + }) + + return memberList +} + +func (ml *MemberList) stopMemberList() { + // ml.cluster.ActorSystem.EventStream.Unsubscribe(ml.membershipSub) +} + +func (ml *MemberList) InitializeTopologyConsensus() { + ml.topologyConsensus = ml.cluster.Gossip.RegisterConsensusCheck("topology", func(any *anypb.Any) interface{} { + var topology ClusterTopology + if unpackErr := any.UnmarshalTo(&topology); unpackErr != nil { + plog.Error("could not unpack topology message", log.Error(unpackErr)) + + return nil + } + + return topology.TopologyHash + }) +} + +func (ml *MemberList) TopologyConsensus(ctx context.Context) (uint64, bool) { + result, ok := ml.topologyConsensus.TryGetConsensus(ctx) + if ok { + res, _ := result.(uint64) + + return res, true + } + + return 0, false +} + +func (ml *MemberList) getPartitionMember(name, kind string) string { + ml.mutex.RLock() + defer ml.mutex.RUnlock() + + var res string + if memberStrategy, ok := ml.memberStrategyByKind[kind]; ok { + res = memberStrategy.GetPartition(name) + } + + return res +} + +func (ml *MemberList) getPartitionMemberV2(clusterIdentity *ClusterIdentity) string { + ml.mutex.RLock() + defer ml.mutex.RUnlock() + + if ms, ok := ml.memberStrategyByKind[clusterIdentity.Kind]; ok { + return ms.GetPartition(clusterIdentity.Identity) + } + + return "" +} + +func (ml *MemberList) GetActivatorMember(kind string, requestSourceAddress string) string { + ml.mutex.RLock() + defer ml.mutex.RUnlock() + + var res string + if memberStrategy, ok := ml.memberStrategyByKind[kind]; ok { + res = memberStrategy.GetActivator(requestSourceAddress) + } + + return res +} + +func (ml *MemberList) Length() int { + return ml.members.Len() +} + +func (ml *MemberList) Members() *MemberSet { + return ml.members +} + +func (ml *MemberList) UpdateClusterTopology(members Members) { + ml.mutex.Lock() + defer ml.mutex.Unlock() + + // TLDR: + // this method basically filters out any member status in the blocked list + // then makes a delta between new and old members + // notifying the cluster accordingly which members left or joined + + topology, done, active, joined, left := ml.getTopologyChanges(members) + if done { + return + } + + // include any new blocked members into the known set of blocked members + for _, m := range left.Members() { + ml.cluster.Remote.BlockList().Block(m.Id) + } + + ml.members = active + + // notify that these members left + for _, m := range left.Members() { + ml.memberLeave(m) + ml.TerminateMember(m) + } + + // notify that these members joined + for _, m := range joined.Members() { + ml.memberJoin(m) + } + + ml.cluster.ActorSystem.EventStream.Publish(topology) + + plog.Info("Updated ClusterTopology", + log.Uint64("topology-hash", topology.TopologyHash), + log.Int("members", len(topology.Members)), + log.Int("joined", len(topology.Joined)), + log.Int("left", len(topology.Left)), + log.Int("blocked", len(topology.Blocked)), + log.Int("membersFromProvider", len(members))) +} + +func (ml *MemberList) memberJoin(joiningMember *Member) { + plog.Info("member joined", log.String("member", joiningMember.Id)) + + for _, kind := range joiningMember.Kinds { + if ml.memberStrategyByKind[kind] == nil { + ml.memberStrategyByKind[kind] = ml.getMemberStrategyByKind(kind) + } + + ml.memberStrategyByKind[kind].AddMember(joiningMember) + } +} + +func (ml *MemberList) memberLeave(leavingMember *Member) { + for _, kind := range leavingMember.Kinds { + if ml.memberStrategyByKind[kind] == nil { + continue + } + + ml.memberStrategyByKind[kind].RemoveMember(leavingMember) + } +} + +func (ml *MemberList) getTopologyChanges(members Members) (topology *ClusterTopology, unchanged bool, active *MemberSet, joined *MemberSet, left *MemberSet) { + memberSet := NewMemberSet(members) + + // get active members + // (this bit means that we will never allow a member that failed a health check to join back in) + blocked := ml.cluster.GetBlockedMembers().ToSlice() + + active = memberSet.ExceptIds(blocked) + + // nothing changed? exit + if active.Equals(ml.members) { + return nil, true, nil, nil, nil + } + + left = ml.members.Except(active) + joined = active.Except(ml.members) + + topology = &ClusterTopology{ + TopologyHash: active.TopologyHash(), + Members: active.Members(), + Left: left.Members(), + Joined: joined.Members(), + } + + return topology, false, active, joined, left +} + +func (ml *MemberList) TerminateMember(m *Member) { + // tell the world that this endpoint should is no longer relevant + ml.cluster.ActorSystem.EventStream.Publish(&remote.EndpointTerminatedEvent{ + Address: m.Address(), + }) +} + +func (ml *MemberList) BroadcastEvent(message interface{}, includeSelf bool) { + for _, m := range ml.members.members { + if !includeSelf && m.Id == ml.cluster.ActorSystem.ID { + continue + } + + pid := actor.NewPID(m.Address(), "eventstream") + ml.cluster.ActorSystem.Root.Send(pid, message) + } +} + +func (ml *MemberList) ContainsMemberID(memberID string) bool { + return ml.members.ContainsID(memberID) +} + +func (ml *MemberList) getMemberStrategyByKind(kind string) MemberStrategy { + plog.Info("creating member strategy", log.String("kind", kind)) + + clusterKind, ok := ml.cluster.TryGetClusterKind(kind) + + if ok { + if clusterKind.Strategy != nil { + return clusterKind.Strategy + } + } + + strategy := ml.cluster.Config.MemberStrategyBuilder(ml.cluster, kind) + if strategy != nil { + return strategy + } + + return newDefaultMemberStrategy(ml.cluster, kind) +} diff --git a/cluster/member_list_test.go b/cluster/member_list_test.go new file mode 100644 index 0000000000000000000000000000000000000000..7170d2114d9a6010573e42b39518864768e6eb4d --- /dev/null +++ b/cluster/member_list_test.go @@ -0,0 +1,286 @@ +package cluster + +import ( + "fmt" + "sort" + "sync" + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +//func TestPublishRaceCondition(t *testing.T) { +// actorSystem := actor.NewActorSystem() +// c := New(actorSystem, Configure("mycluster", nil, nil, remote.Configure("127.0.0.1", 0))) +// NewMemberList(c) +// rounds := 1000 +// +// var wg sync.WaitGroup +// wg.Add(2 * rounds) +// +// go func() { +// for i := 0; i < rounds; i++ { +// actorSystem.EventStream.Publish(TopologyEvent(Members{{}, {}})) +// actorSystem.EventStream.Publish(TopologyEvent(Members{{}})) +// wg.Done() +// } +// }() +// +// go func() { +// for i := 0; i < rounds; i++ { +// s := actorSystem.EventStream.Subscribe(func(evt interface{}) {}) +// actorSystem.EventStream.Unsubscribe(s) +// wg.Done() +// } +// }() +// +// if waitTimeout(&wg, 2*time.Second) { +// t.Error("Should not run into a timeout") +// } +//} + +// https://stackoverflow.com/questions/32840687/timeout-for-waitgroup-wait +func waitTimeout(wg *sync.WaitGroup, timeout time.Duration) bool { + c := make(chan struct{}) + go func() { + defer close(c) + wg.Wait() + }() + select { + case <-c: + return false // completed normally + case <-time.After(timeout): + return true // timed out + } +} + +func TestMemberList_UpdateClusterTopology(t *testing.T) { + c := newClusterForTest("test-UpdateClusterTopology", nil) + obj := NewMemberList(c) + empty := make([]*Member, 0) + + t.Run("init", func(t *testing.T) { + assert := assert.New(t) + members := newMembersForTest(2) + changes, unchanged, actives, _, _ := obj.getTopologyChanges(members) + assert.False(unchanged) + expected := &ClusterTopology{TopologyHash: TopologyHash(members), Members: members, Joined: members, Left: empty} + assert.Equal(expected.TopologyHash, changes.TopologyHash) + + var m1, m2 *MemberSet + m1 = NewMemberSet(expected.Members) + m2 = NewMemberSet(changes.Members) + assert.Equal(m1, m2) + + m1 = NewMemberSet(expected.Joined) + m2 = NewMemberSet(changes.Joined) + assert.Equal(m1, m2) + + m1 = NewMemberSet(expected.Left) + m2 = NewMemberSet(changes.Left) + assert.Equal(m1, m2) + + // current members + obj.members = actives + }) + + t.Run("join", func(t *testing.T) { + assert := assert.New(t) + assert.Equal(2, obj.members.Len()) + members := newMembersForTest(4) + changes, unchanged, actives, _, _ := obj.getTopologyChanges(members) + assert.False(unchanged) + // _sorted(changes) + expected := &ClusterTopology{TopologyHash: TopologyHash(members), Members: members, Joined: members[2:4], Left: empty} + assert.Equal(expected.TopologyHash, changes.TopologyHash) + + var m1, m2 *MemberSet + m1 = NewMemberSet(expected.Members) + m2 = NewMemberSet(changes.Members) + assert.Equal(m1, m2) + + m1 = NewMemberSet(expected.Joined) + m2 = NewMemberSet(changes.Joined) + assert.Equal(m1, m2) + + m1 = NewMemberSet(expected.Left) + m2 = NewMemberSet(changes.Left) + assert.Equal(m1, m2) + + obj.members = actives + }) + + t.Run("left", func(t *testing.T) { + assert := assert.New(t) + assert.Equal(4, obj.members.Len()) + members := newMembersForTest(4) + changes, _, _, _, _ := obj.getTopologyChanges(members[2:4]) + expected := &ClusterTopology{TopologyHash: TopologyHash(members[2:4]), Members: members[2:4], Joined: empty, Left: members[0:2]} + assert.Equal(expected.TopologyHash, changes.TopologyHash) + + var m1, m2 *MemberSet + m1 = NewMemberSet(expected.Members) + m2 = NewMemberSet(changes.Members) + assert.Equal(m1, m2) + + m1 = NewMemberSet(expected.Joined) + m2 = NewMemberSet(changes.Joined) + assert.Equal(m1, m2) + + m1 = NewMemberSet(expected.Left) + m2 = NewMemberSet(changes.Left) + assert.Equal(m1, m2) + }) +} + +func newMembersForTest(count int, kinds ...string) Members { + if len(kinds) == 0 { + kinds = append(kinds, "kind") + } + members := make(Members, count) + for i := 0; i < count; i++ { + members[i] = &Member{ + Id: fmt.Sprintf("memberId-%d", i), + Host: "127.0.0.1", + Port: int32(i), + Kinds: kinds, + } + } + return members +} + +func TestMemberList_UpdateClusterTopology2(t *testing.T) { + c := newClusterForTest("test-UpdateClusterTopology", nil) + + obj := NewMemberList(c) + dumpMembers := func(list Members) { + t.Logf("membersByMemberId=%d", len(list)) + + for _, m := range list { + t.Logf("\t%s", m.Address()) + } + } + + empty := make([]*Member, 0) + + _ = dumpMembers + _sorted := func(tpl *ClusterTopology) { + _sortMembers := func(list Members) { + sort.Slice(list, func(i, j int) bool { + return (list)[i].Port < (list)[j].Port + }) + } + _sortMembers(tpl.Members) + _sortMembers(tpl.Left) + _sortMembers(tpl.Joined) + } + + a := assert.New(t) + members := newMembersForTest(2) + changes, _, _, _, _ := obj.getTopologyChanges(members) //nolint:dogsled + _sorted(changes) + + expected := &ClusterTopology{TopologyHash: TopologyHash(members), Members: members, Joined: members, Left: empty} + + a.Equal(expected.TopologyHash, changes.TopologyHash) + + var m1, m2 *MemberSet + m1 = NewMemberSet(expected.Members) + m2 = NewMemberSet(changes.Members) + a.Equal(m1, m2) + + m1 = NewMemberSet(expected.Joined) + m2 = NewMemberSet(changes.Joined) + a.Equal(m1, m2) + + m1 = NewMemberSet(expected.Left) + m2 = NewMemberSet(changes.Left) + a.Equal(m1, m2) +} + +func TestMemberList_getPartitionMember(t *testing.T) { + t.Parallel() + + c := newClusterForTest("test-memberlist", nil) + obj := NewMemberList(c) + + for _, v := range []int{1, 2, 10, 100, 1000} { + members := newMembersForTest(v) + obj.UpdateClusterTopology(members) + + testName := fmt.Sprintf("member*%d", v) + t.Run(testName, func(t *testing.T) { + //assert := assert.New(t) + // + //identity := NewClusterIdentity("name", "kind") + //// address := obj.getPartitionMemberV2(identity) + //// assert.NotEmpty(address) + // + //identity = NewClusterIdentity("name", "nonkind") + //// address = obj.getPartitionMemberV2(identity) + //// assert.Empty(address) + }) + } +} + +//func BenchmarkMemberList_getPartitionMemberV2(b *testing.B) { +// SetLogLevel(log.ErrorLevel) +// actorSystem := actor.NewActorSystem() +// c := New(actorSystem, Configure("mycluster", nil, nil, remote.Configure("127.0.0.1", 0))) +// obj := NewMemberList(c) +// for i, v := range []int{1, 2, 3, 5, 10, 100, 1000, 2000} { +// members := _newTopologyEventForTest(v) +// obj.UpdateClusterTopology(members) +// testName := fmt.Sprintf("member*%d", v) +// runtime.GC() +// +// identity := &ClusterIdentity{Identity: fmt.Sprintf("name-%d", rand.Int()), Kind: "kind"} +// b.Run(testName, func(b *testing.B) { +// for i := 0; i < b.N; i++ { +// address := obj.getPartitionMemberV2(identity) +// if address == "" { +// b.Fatalf("empty address membersByMemberId=%d", v) +// } +// } +// }) +// } +//} + +//func TestMemberList_getPartitionMemberV2(t *testing.T) { +// assert := assert.New(t) +// +// tplg := _newTopologyEventForTest(10) +// c := _newClusterForTest("test-memberlist") +// obj := NewMemberList(c) +// obj.UpdateClusterTopology(tplg, 1) +// +// assert.Contains(obj.memberStrategyByKind, "kind") +// addr := obj.getPartitionMemberV2(&ClusterIdentity{Kind: "kind", Identity: "name"}) +// assert.NotEmpty(addr) +// +// // consistent +// for i := 0; i < 10; i++ { +// addr2 := obj.getPartitionMemberV2(&ClusterIdentity{Kind: "kind", Identity: "name"}) +// assert.NotEmpty(addr2) +// assert.Equal(addr, addr2) +// } +//} + +func TestMemberList_newMemberStrategies(t *testing.T) { + t.Parallel() + a := assert.New(t) + + c := newClusterForTest("test-memberlist", nil) + obj := NewMemberList(c) + + for _, v := range []int{1, 10, 100, 1000} { + members := newMembersForTest(v, "kind1", "kind2") + obj.UpdateClusterTopology(members) + a.Equal(2, len(obj.memberStrategyByKind)) + a.Contains(obj.memberStrategyByKind, "kind1") + + a.Equal(v, len(obj.memberStrategyByKind["kind1"].GetAllMembers())) + a.Equal(v, len(obj.memberStrategyByKind["kind2"].GetAllMembers())) + } +} diff --git a/cluster/member_state_delta.go b/cluster/member_state_delta.go new file mode 100644 index 0000000000000000000000000000000000000000..01dd405a623bbc79a8e727b96de8b028e936673c --- /dev/null +++ b/cluster/member_state_delta.go @@ -0,0 +1,10 @@ +// Copyright (C) 2015-2022 Asynkron AB All rights reserved + +package cluster + +type MemberStateDelta struct { + TargetMemberID string + HasState bool + State *GossipState + CommitOffsets func() +} diff --git a/cluster/member_status.go b/cluster/member_status.go new file mode 100644 index 0000000000000000000000000000000000000000..048e0dac093afcba6e69834ce5b9a607b58f46cb --- /dev/null +++ b/cluster/member_status.go @@ -0,0 +1,11 @@ +package cluster + +type MemberStatus struct { + Member + MemberID string // for compatibility + Alive bool +} + +func (m *MemberStatus) Address() string { + return m.Member.Address() +} diff --git a/cluster/member_status_events.go b/cluster/member_status_events.go new file mode 100644 index 0000000000000000000000000000000000000000..e67caa978b628e4f30a402f9ed5d9a75ef107923 --- /dev/null +++ b/cluster/member_status_events.go @@ -0,0 +1,52 @@ +package cluster + +import "fmt" + +type MemberStatusEvent interface { + MemberStatusEvent() + GetKinds() []string +} + +type MemberMeta struct { + Host string + Port int + Kinds []string +} + +func (e *MemberMeta) Name() string { + return fmt.Sprintf("%v:%v", e.Host, e.Port) +} + +func (e *MemberMeta) GetKinds() []string { + return e.Kinds +} + +type MemberJoinedEvent struct { + MemberMeta +} + +func (*MemberJoinedEvent) MemberStatusEvent() {} + +type MemberRejoinedEvent struct { + MemberMeta +} + +func (*MemberRejoinedEvent) MemberStatusEvent() {} + +type MemberLeftEvent struct { + MemberMeta +} + +func (*MemberLeftEvent) MemberStatusEvent() {} + +type MemberUnavailableEvent struct { + MemberMeta +} + +func (*MemberUnavailableEvent) MemberStatusEvent() {} + +type MemberAvailableEvent struct { + MemberMeta +} + +func (*MemberAvailableEvent) MemberStatusEvent() {} diff --git a/cluster/member_strategy.go b/cluster/member_strategy.go new file mode 100644 index 0000000000000000000000000000000000000000..d35ee3c3669c321849ec462d902d657567d5c4d0 --- /dev/null +++ b/cluster/member_strategy.go @@ -0,0 +1,58 @@ +package cluster + +type MemberStrategy interface { + GetAllMembers() Members + AddMember(member *Member) + RemoveMember(member *Member) + GetPartition(key string) string + GetActivator(senderAddress string) string +} + +type simpleMemberStrategy struct { + members Members + rr *SimpleRoundRobin + rdv *Rendezvous +} + +func newDefaultMemberStrategy(cluster *Cluster, kind string) MemberStrategy { + ms := &simpleMemberStrategy{members: make(Members, 0)} + ms.rr = NewSimpleRoundRobin(MemberStrategy(ms)) + ms.rdv = NewRendezvous() + return ms +} + +func (m *simpleMemberStrategy) AddMember(member *Member) { + m.members = append(m.members, member) + m.rdv.UpdateMembers(m.members) +} + +func (m *simpleMemberStrategy) UpdateMember(member *Member) { + for i, mb := range m.members { + if mb.Address() == member.Address() { + m.members[i] = member + return + } + } +} + +func (m *simpleMemberStrategy) RemoveMember(member *Member) { + for i, mb := range m.members { + if mb.Address() == member.Address() { + m.members = append(m.members[:i], m.members[i+1:]...) + m.rdv.UpdateMembers(m.members) + return + } + } +} + +func (m *simpleMemberStrategy) GetAllMembers() Members { + return m.members +} + +func (m *simpleMemberStrategy) GetPartition(key string) string { + return m.rdv.GetByIdentity(key) +} + +func (m *simpleMemberStrategy) GetActivator(senderAddress string) string { + return m.rr.GetByRoundRobin() +} diff --git a/cluster/members.go b/cluster/members.go new file mode 100644 index 0000000000000000000000000000000000000000..0a82186e3c441ab31aeba7b6c0b00cd53dbcb5b6 --- /dev/null +++ b/cluster/members.go @@ -0,0 +1,99 @@ +package cluster + +import "github.com/asynkron/gofun/set" + +type MemberSet struct { + topologyHash uint64 + members Members + lookup map[string]*Member +} + +var emptyMemberSet = NewMemberSet(make(Members, 0)) + +func NewMemberSet(members Members) *MemberSet { + members = CopySortMembers(members) + lookup := MembersToMap(members) + ms := &MemberSet{ + topologyHash: TopologyHash(members), + members: members, + lookup: lookup, + } + + return ms +} + +func (ms *MemberSet) Len() int { + return len(ms.members) +} + +func (ms *MemberSet) TopologyHash() uint64 { + return ms.topologyHash +} + +func (ms *MemberSet) Members() Members { + return ms.members +} + +func (ms *MemberSet) ContainsID(id string) bool { + _, ok := ms.lookup[id] + + return ok +} + +func (ms *MemberSet) GetMemberById(id string) *Member { + member, _ := ms.lookup[id] + + return member +} + +func (ms *MemberSet) Except(other *MemberSet) *MemberSet { + res := make(Members, 0) + + for _, m := range ms.members { + if other.ContainsID(m.Id) { + continue + } + + res = append(res, m) + } + + return NewMemberSet(res) +} + +func (ms *MemberSet) ExceptIds(ids []string) *MemberSet { + other := set.New(ids...) + res := make(Members, 0) + + for _, m := range ms.members { + if other.Contains(m.Id) { + continue + } + + res = append(res, m) + } + + return NewMemberSet(res) +} + +func (ms *MemberSet) Union(other *MemberSet) *MemberSet { + mapp := make(map[string]*Member, 0) + for _, m := range ms.members { + mapp[m.Id] = m + } + + for _, m := range other.members { + mapp[m.Id] = m + } + + res := make(Members, 0) + + for _, m := range mapp { + res = append(res, m) + } + + return NewMemberSet(res) +} + +func (ms *MemberSet) Equals(other *MemberSet) bool { + return ms.topologyHash == other.topologyHash +} diff --git a/cluster/messages.go b/cluster/messages.go new file mode 100644 index 0000000000000000000000000000000000000000..6c67d7eba57396c983fb00bec2f2fc7dd088c1c9 --- /dev/null +++ b/cluster/messages.go @@ -0,0 +1,75 @@ +package cluster + +import ( + "google.golang.org/protobuf/proto" +) + +// Used to query the GossipActor about a given key status +type GetGossipStateRequest struct { + Key string +} + +// Create a new GetGossipStateRequest value and return it back +func NewGetGossipStateRequest(key string) GetGossipStateRequest { + request := GetGossipStateRequest{Key: key} + return request +} + +// Used by the GossipActor to send back the status value of a given key +type GetGossipStateResponse struct { + State map[string]*GossipKeyValue +} + +func NewGetGossipStateResponse(state map[string]*GossipKeyValue) GetGossipStateResponse { + value := GetGossipStateResponse{ + State: state, + } + return value +} + +// Used to setup Gossip Status Keys in the GossipActor +type SetGossipStateKey struct { + Key string + Value proto.Message +} + +// Create a new SetGossipStateKey value with the given data and return it back +func NewGossipStateKey(key string, value proto.Message) SetGossipStateKey { + statusKey := SetGossipStateKey{ + Key: key, + Value: value, + } + return statusKey +} + +type SendGossipStateRequest struct{} + +type SendGossipStateResponse struct{} + +// Used by the GossipActor to respond SetGossipStatus requests +type SetGossipStateResponse struct{} + +type AddConsensusCheck struct { + ID string + Check *ConsensusCheck +} + +// Mimic .NET ReenterAfterCancellation on GossipActor +type RemoveConsensusCheck struct { + ID string +} + +func NewAddConsensusCheck(id string, check *ConsensusCheck) AddConsensusCheck { + value := AddConsensusCheck{ + ID: id, + Check: check, + } + return value +} + +func NewRemoveConsensusCheck(id string) RemoveConsensusCheck { + value := RemoveConsensusCheck{ + ID: id, + } + return value +} diff --git a/cluster/options.go b/cluster/options.go new file mode 100644 index 0000000000000000000000000000000000000000..50fcae4e6ff2272ee4b267f8e2313002fd1a865e --- /dev/null +++ b/cluster/options.go @@ -0,0 +1,3 @@ +package cluster + +type Option func(g *Gossiper) diff --git a/cluster/pid_cache.go b/cluster/pid_cache.go new file mode 100644 index 0000000000000000000000000000000000000000..e5af267f1143b7c0090876ba178c000111d31b64 --- /dev/null +++ b/cluster/pid_cache.go @@ -0,0 +1,68 @@ +package cluster + +import ( + "gitee.com/simplexyz/simpleactor-go/actor" + cmap "github.com/orcaman/concurrent-map" +) + +type PidCacheValue struct { + cache cmap.ConcurrentMap +} + +func NewPidCache() *PidCacheValue { + pidCache := &PidCacheValue{ + cache: cmap.New(), + } + + return pidCache +} + +func key(identity string, kind string) string { + return identity + "." + kind +} + +func (c *PidCacheValue) Get(identity string, kind string) (*actor.PID, bool) { + k := key(identity, kind) + v, ok := c.cache.Get(k) + + if !ok { + return nil, false + } + + return v.(*actor.PID), true +} + +func (c *PidCacheValue) Set(identity string, kind string, pid *actor.PID) { + k := key(identity, kind) + c.cache.Set(k, pid) +} + +func (c *PidCacheValue) RemoveByValue(identity string, kind string, pid *actor.PID) { + k := key(identity, kind) + + c.cache.RemoveCb(k, func(key string, v interface{}, exists bool) bool { + if !exists { + return false + } + + existing, _ := v.(*actor.PID) + + return existing.Equal(pid) + }) +} + +func (c *PidCacheValue) Remove(identity string, kind string) { + k := key(identity, kind) + c.cache.Remove(k) +} + +func (c *PidCacheValue) RemoveByMember(member *Member) { + addr := member.Address() + + for item := range c.cache.IterBuffered() { + pid, _ := item.Val.(*actor.PID) + if pid.Address == addr { + c.cache.Remove(item.Key) + } + } +} diff --git a/cluster/pid_cache_test.go b/cluster/pid_cache_test.go new file mode 100644 index 0000000000000000000000000000000000000000..176fd29be8ff7614e5a54959eb27e4936ff30efa --- /dev/null +++ b/cluster/pid_cache_test.go @@ -0,0 +1,89 @@ +package cluster + +import ( + "testing" + + "gitee.com/simplexyz/simpleactor-go/actor" +) + +func TestPidCacheValue_Set_Test(t *testing.T) { + t.Parallel() + + pidCache := NewPidCache() + pid := actor.NewPID("abc", "def") + pidCache.Set("abc", "k", pid) + res, _ := pidCache.Get("abc", "k") + + if !res.Equal(pid) { + t.Errorf("Expected %v, got %v", pid, res) + } +} + +func TestPidCacheValue_Get(t *testing.T) { + t.Parallel() + + pidCache := NewPidCache() + pid := actor.NewPID("abc", "def") + pidCache.Set("abc", "k", pid) + res, _ := pidCache.Get("abc", "k") + + if !res.Equal(pid) { + t.Errorf("Expected %v, got %v", pid, res) + } +} + +func TestPidCacheValue_Remove(t *testing.T) { + t.Parallel() + + pidCache := NewPidCache() + pid := actor.NewPID("abc", "def") + pidCache.Set("abc", "k", pid) + pidCache.Remove("abc", "k") + res, _ := pidCache.Get("abc", "k") + + if res != nil { + t.Errorf("Expected nil, got %v", res) + } +} + +func TestPidCacheValue_RemoveByMember(t *testing.T) { + t.Parallel() + + member := &Member{ + Host: "abc", + Port: 123, + } + + pidCache := NewPidCache() + pid := actor.NewPID("abc:123", "def") + pidCache.Set("abc", "k", pid) + pidCache.RemoveByMember(member) + res, _ := pidCache.Get("abc", "k") + + if res != nil { + t.Errorf("Expected nil, got %v", res) + } +} + +func TestPidCacheValue_RemoveByValue(t *testing.T) { + t.Parallel() + + pidCache := NewPidCache() + pid := actor.NewPID("abc", "def1234") + pid2 := actor.NewPID("abc", "def3532534") + + pidCache.Set("abc", "k", pid) + pidCache.RemoveByValue("abc", "k", pid2) + res, _ := pidCache.Get("abc", "k") + + if res == nil { + t.Errorf("Expected %v, got %v", pid, res) + } + + pidCache.RemoveByValue("abc", "k", pid) + res, _ = pidCache.Get("abc", "k") + + if res != nil { + t.Errorf("Expected nil, got %v", res) + } +} diff --git a/cluster/pubsub.go b/cluster/pubsub.go new file mode 100644 index 0000000000000000000000000000000000000000..97294737ee577713e888a4243bb281d8bd4ab7c5 --- /dev/null +++ b/cluster/pubsub.go @@ -0,0 +1,59 @@ +package cluster + +import ( + "time" + + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/extensions" +) + +const PubSubDeliveryName = "$pubsub-delivery" + +var pubsubExtensionID = extensions.NextExtensionID() + +type PubSub struct { + cluster *Cluster +} + +func NewPubSub(cluster *Cluster) *PubSub { + p := &PubSub{ + cluster: cluster, + } + cluster.ActorSystem.Extensions.Register(p) + return p +} + +// Start the PubSubMemberDeliveryActor +func (p *PubSub) Start() { + props := actor.PropsFromProducer(func() actor.Actor { + return NewPubSubMemberDeliveryActor(p.cluster.Config.PubSubConfig.SubscriberTimeout) + }) + _, err := p.cluster.ActorSystem.Root.SpawnNamed(props, PubSubDeliveryName) + if err != nil { + panic(err) // let it crash + } + plog.Info("Started Cluster PubSub") +} + +func (p *PubSub) ExtensionID() extensions.ExtensionID { + return pubsubExtensionID +} + +type PubSubConfig struct { + // SubscriberTimeout is a timeout used when delivering a message batch to a subscriber. Default is 5s. + // + // This value gets rounded to seconds for optimization of cancellation token creation. Note that internally, + // cluster request is used to deliver messages to ClusterIdentity subscribers. + SubscriberTimeout time.Duration +} + +func newPubSubConfig() *PubSubConfig { + return &PubSubConfig{ + SubscriberTimeout: 5 * time.Second, + } +} + +// GetPubSub returns the PubSub extension from the actor system +func GetPubSub(system *actor.ActorSystem) *PubSub { + return system.Extensions.Get(pubsubExtensionID).(*PubSub) +} diff --git a/cluster/pubsub.pb.go b/cluster/pubsub.pb.go new file mode 100644 index 0000000000000000000000000000000000000000..b19a78c4a759f26d9b1c649dfe2e4cec840ed24f --- /dev/null +++ b/cluster/pubsub.pb.go @@ -0,0 +1,1346 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.28.1 +// protoc v3.21.9 +// source: pubsub.proto + +package cluster + +import ( + actor "gitee.com/simplexyz/simpleactor-go/actor" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + durationpb "google.golang.org/protobuf/types/known/durationpb" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// Delivery status as seen by the delivery actor +type DeliveryStatus int32 + +const ( + // Message was put in the queue of the subscriber + DeliveryStatus_Delivered DeliveryStatus = 0 + // Message did not reach subscriber, because it was dead + DeliveryStatus_SubscriberNoLongerReachable DeliveryStatus = 1 + // Delivery timed out + DeliveryStatus_Timeout DeliveryStatus = 2 + // Some other problem happened + DeliveryStatus_OtherError DeliveryStatus = 127 +) + +// Enum value maps for DeliveryStatus. +var ( + DeliveryStatus_name = map[int32]string{ + 0: "Delivered", + 1: "SubscriberNoLongerReachable", + 2: "Timeout", + 127: "OtherError", + } + DeliveryStatus_value = map[string]int32{ + "Delivered": 0, + "SubscriberNoLongerReachable": 1, + "Timeout": 2, + "OtherError": 127, + } +) + +func (x DeliveryStatus) Enum() *DeliveryStatus { + p := new(DeliveryStatus) + *p = x + return p +} + +func (x DeliveryStatus) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (DeliveryStatus) Descriptor() protoreflect.EnumDescriptor { + return file_pubsub_proto_enumTypes[0].Descriptor() +} + +func (DeliveryStatus) Type() protoreflect.EnumType { + return &file_pubsub_proto_enumTypes[0] +} + +func (x DeliveryStatus) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use DeliveryStatus.Descriptor instead. +func (DeliveryStatus) EnumDescriptor() ([]byte, []int) { + return file_pubsub_proto_rawDescGZIP(), []int{0} +} + +// Status of the whole published batch or single message +type PublishStatus int32 + +const ( + // Batch or message was successfully published according to the delivery guarantees + PublishStatus_Ok PublishStatus = 0 + // Topic failed to forward the message + PublishStatus_Failed PublishStatus = 1 +) + +// Enum value maps for PublishStatus. +var ( + PublishStatus_name = map[int32]string{ + 0: "Ok", + 1: "Failed", + } + PublishStatus_value = map[string]int32{ + "Ok": 0, + "Failed": 1, + } +) + +func (x PublishStatus) Enum() *PublishStatus { + p := new(PublishStatus) + *p = x + return p +} + +func (x PublishStatus) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (PublishStatus) Descriptor() protoreflect.EnumDescriptor { + return file_pubsub_proto_enumTypes[1].Descriptor() +} + +func (PublishStatus) Type() protoreflect.EnumType { + return &file_pubsub_proto_enumTypes[1] +} + +func (x PublishStatus) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use PublishStatus.Descriptor instead. +func (PublishStatus) EnumDescriptor() ([]byte, []int) { + return file_pubsub_proto_rawDescGZIP(), []int{1} +} + +// Identifies a subscriber by either ClusterIdentity or PID +type SubscriberIdentity struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Types that are assignable to Identity: + // + // *SubscriberIdentity_Pid + // *SubscriberIdentity_ClusterIdentity + Identity isSubscriberIdentity_Identity `protobuf_oneof:"Identity"` +} + +func (x *SubscriberIdentity) Reset() { + *x = SubscriberIdentity{} + if protoimpl.UnsafeEnabled { + mi := &file_pubsub_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SubscriberIdentity) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SubscriberIdentity) ProtoMessage() {} + +func (x *SubscriberIdentity) ProtoReflect() protoreflect.Message { + mi := &file_pubsub_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SubscriberIdentity.ProtoReflect.Descriptor instead. +func (*SubscriberIdentity) Descriptor() ([]byte, []int) { + return file_pubsub_proto_rawDescGZIP(), []int{0} +} + +func (m *SubscriberIdentity) GetIdentity() isSubscriberIdentity_Identity { + if m != nil { + return m.Identity + } + return nil +} + +func (x *SubscriberIdentity) GetPid() *actor.PID { + if x, ok := x.GetIdentity().(*SubscriberIdentity_Pid); ok { + return x.Pid + } + return nil +} + +func (x *SubscriberIdentity) GetClusterIdentity() *ClusterIdentity { + if x, ok := x.GetIdentity().(*SubscriberIdentity_ClusterIdentity); ok { + return x.ClusterIdentity + } + return nil +} + +type isSubscriberIdentity_Identity interface { + isSubscriberIdentity_Identity() +} + +type SubscriberIdentity_Pid struct { + Pid *actor.PID `protobuf:"bytes,1,opt,name=pid,proto3,oneof"` +} + +type SubscriberIdentity_ClusterIdentity struct { + ClusterIdentity *ClusterIdentity `protobuf:"bytes,2,opt,name=cluster_identity,json=clusterIdentity,proto3,oneof"` +} + +func (*SubscriberIdentity_Pid) isSubscriberIdentity_Identity() {} + +func (*SubscriberIdentity_ClusterIdentity) isSubscriberIdentity_Identity() {} + +// First request to initialize the actor. +type Initialize struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + IdleTimeout *durationpb.Duration `protobuf:"bytes,1,opt,name=idleTimeout,proto3" json:"idleTimeout,omitempty"` +} + +func (x *Initialize) Reset() { + *x = Initialize{} + if protoimpl.UnsafeEnabled { + mi := &file_pubsub_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Initialize) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Initialize) ProtoMessage() {} + +func (x *Initialize) ProtoReflect() protoreflect.Message { + mi := &file_pubsub_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Initialize.ProtoReflect.Descriptor instead. +func (*Initialize) Descriptor() ([]byte, []int) { + return file_pubsub_proto_rawDescGZIP(), []int{1} +} + +func (x *Initialize) GetIdleTimeout() *durationpb.Duration { + if x != nil { + return x.IdleTimeout + } + return nil +} + +type Acknowledge struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *Acknowledge) Reset() { + *x = Acknowledge{} + if protoimpl.UnsafeEnabled { + mi := &file_pubsub_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Acknowledge) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Acknowledge) ProtoMessage() {} + +func (x *Acknowledge) ProtoReflect() protoreflect.Message { + mi := &file_pubsub_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Acknowledge.ProtoReflect.Descriptor instead. +func (*Acknowledge) Descriptor() ([]byte, []int) { + return file_pubsub_proto_rawDescGZIP(), []int{2} +} + +// A list of subscribers +type Subscribers struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Subscribers []*SubscriberIdentity `protobuf:"bytes,1,rep,name=subscribers,proto3" json:"subscribers,omitempty"` +} + +func (x *Subscribers) Reset() { + *x = Subscribers{} + if protoimpl.UnsafeEnabled { + mi := &file_pubsub_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Subscribers) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Subscribers) ProtoMessage() {} + +func (x *Subscribers) ProtoReflect() protoreflect.Message { + mi := &file_pubsub_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Subscribers.ProtoReflect.Descriptor instead. +func (*Subscribers) Descriptor() ([]byte, []int) { + return file_pubsub_proto_rawDescGZIP(), []int{3} +} + +func (x *Subscribers) GetSubscribers() []*SubscriberIdentity { + if x != nil { + return x.Subscribers + } + return nil +} + +// Sent to topic actor to add a subscriber +type SubscribeRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Subscriber *SubscriberIdentity `protobuf:"bytes,1,opt,name=subscriber,proto3" json:"subscriber,omitempty"` +} + +func (x *SubscribeRequest) Reset() { + *x = SubscribeRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_pubsub_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SubscribeRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SubscribeRequest) ProtoMessage() {} + +func (x *SubscribeRequest) ProtoReflect() protoreflect.Message { + mi := &file_pubsub_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SubscribeRequest.ProtoReflect.Descriptor instead. +func (*SubscribeRequest) Descriptor() ([]byte, []int) { + return file_pubsub_proto_rawDescGZIP(), []int{4} +} + +func (x *SubscribeRequest) GetSubscriber() *SubscriberIdentity { + if x != nil { + return x.Subscriber + } + return nil +} + +// Subscribe acknowledgement +type SubscribeResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *SubscribeResponse) Reset() { + *x = SubscribeResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_pubsub_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SubscribeResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SubscribeResponse) ProtoMessage() {} + +func (x *SubscribeResponse) ProtoReflect() protoreflect.Message { + mi := &file_pubsub_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SubscribeResponse.ProtoReflect.Descriptor instead. +func (*SubscribeResponse) Descriptor() ([]byte, []int) { + return file_pubsub_proto_rawDescGZIP(), []int{5} +} + +// Sent to topic actor to remove a subscriber +type UnsubscribeRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Subscriber *SubscriberIdentity `protobuf:"bytes,1,opt,name=subscriber,proto3" json:"subscriber,omitempty"` +} + +func (x *UnsubscribeRequest) Reset() { + *x = UnsubscribeRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_pubsub_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UnsubscribeRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UnsubscribeRequest) ProtoMessage() {} + +func (x *UnsubscribeRequest) ProtoReflect() protoreflect.Message { + mi := &file_pubsub_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UnsubscribeRequest.ProtoReflect.Descriptor instead. +func (*UnsubscribeRequest) Descriptor() ([]byte, []int) { + return file_pubsub_proto_rawDescGZIP(), []int{6} +} + +func (x *UnsubscribeRequest) GetSubscriber() *SubscriberIdentity { + if x != nil { + return x.Subscriber + } + return nil +} + +// Unsubscribe acknowledgement +type UnsubscribeResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *UnsubscribeResponse) Reset() { + *x = UnsubscribeResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_pubsub_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UnsubscribeResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UnsubscribeResponse) ProtoMessage() {} + +func (x *UnsubscribeResponse) ProtoReflect() protoreflect.Message { + mi := &file_pubsub_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UnsubscribeResponse.ProtoReflect.Descriptor instead. +func (*UnsubscribeResponse) Descriptor() ([]byte, []int) { + return file_pubsub_proto_rawDescGZIP(), []int{7} +} + +// Message sent from publisher to topic actor +// See also PubSubBatch +type PubSubBatchTransport struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + TypeNames []string `protobuf:"bytes,1,rep,name=type_names,json=typeNames,proto3" json:"type_names,omitempty"` + Envelopes []*PubSubEnvelope `protobuf:"bytes,2,rep,name=envelopes,proto3" json:"envelopes,omitempty"` +} + +func (x *PubSubBatchTransport) Reset() { + *x = PubSubBatchTransport{} + if protoimpl.UnsafeEnabled { + mi := &file_pubsub_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PubSubBatchTransport) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PubSubBatchTransport) ProtoMessage() {} + +func (x *PubSubBatchTransport) ProtoReflect() protoreflect.Message { + mi := &file_pubsub_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PubSubBatchTransport.ProtoReflect.Descriptor instead. +func (*PubSubBatchTransport) Descriptor() ([]byte, []int) { + return file_pubsub_proto_rawDescGZIP(), []int{8} +} + +func (x *PubSubBatchTransport) GetTypeNames() []string { + if x != nil { + return x.TypeNames + } + return nil +} + +func (x *PubSubBatchTransport) GetEnvelopes() []*PubSubEnvelope { + if x != nil { + return x.Envelopes + } + return nil +} + +// Contains message byte representation and type reference +type PubSubEnvelope struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + TypeId int32 `protobuf:"varint,1,opt,name=type_id,json=typeId,proto3" json:"type_id,omitempty"` + MessageData []byte `protobuf:"bytes,2,opt,name=message_data,json=messageData,proto3" json:"message_data,omitempty"` + SerializerId int32 `protobuf:"varint,3,opt,name=serializer_id,json=serializerId,proto3" json:"serializer_id,omitempty"` +} + +func (x *PubSubEnvelope) Reset() { + *x = PubSubEnvelope{} + if protoimpl.UnsafeEnabled { + mi := &file_pubsub_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PubSubEnvelope) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PubSubEnvelope) ProtoMessage() {} + +func (x *PubSubEnvelope) ProtoReflect() protoreflect.Message { + mi := &file_pubsub_proto_msgTypes[9] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PubSubEnvelope.ProtoReflect.Descriptor instead. +func (*PubSubEnvelope) Descriptor() ([]byte, []int) { + return file_pubsub_proto_rawDescGZIP(), []int{9} +} + +func (x *PubSubEnvelope) GetTypeId() int32 { + if x != nil { + return x.TypeId + } + return 0 +} + +func (x *PubSubEnvelope) GetMessageData() []byte { + if x != nil { + return x.MessageData + } + return nil +} + +func (x *PubSubEnvelope) GetSerializerId() int32 { + if x != nil { + return x.SerializerId + } + return 0 +} + +// Message sent from topic to delivery actor +type DeliverBatchRequestTransport struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Subscribers *Subscribers `protobuf:"bytes,1,opt,name=subscribers,proto3" json:"subscribers,omitempty"` + Batch *PubSubBatchTransport `protobuf:"bytes,2,opt,name=batch,proto3" json:"batch,omitempty"` + Topic string `protobuf:"bytes,3,opt,name=topic,proto3" json:"topic,omitempty"` +} + +func (x *DeliverBatchRequestTransport) Reset() { + *x = DeliverBatchRequestTransport{} + if protoimpl.UnsafeEnabled { + mi := &file_pubsub_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DeliverBatchRequestTransport) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeliverBatchRequestTransport) ProtoMessage() {} + +func (x *DeliverBatchRequestTransport) ProtoReflect() protoreflect.Message { + mi := &file_pubsub_proto_msgTypes[10] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeliverBatchRequestTransport.ProtoReflect.Descriptor instead. +func (*DeliverBatchRequestTransport) Descriptor() ([]byte, []int) { + return file_pubsub_proto_rawDescGZIP(), []int{10} +} + +func (x *DeliverBatchRequestTransport) GetSubscribers() *Subscribers { + if x != nil { + return x.Subscribers + } + return nil +} + +func (x *DeliverBatchRequestTransport) GetBatch() *PubSubBatchTransport { + if x != nil { + return x.Batch + } + return nil +} + +func (x *DeliverBatchRequestTransport) GetTopic() string { + if x != nil { + return x.Topic + } + return "" +} + +// Message sent from delivery actor to topic to notify of subscribers that fail to process the messages +type NotifyAboutFailingSubscribersRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + InvalidDeliveries []*SubscriberDeliveryReport `protobuf:"bytes,1,rep,name=invalid_deliveries,json=invalidDeliveries,proto3" json:"invalid_deliveries,omitempty"` +} + +func (x *NotifyAboutFailingSubscribersRequest) Reset() { + *x = NotifyAboutFailingSubscribersRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_pubsub_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *NotifyAboutFailingSubscribersRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NotifyAboutFailingSubscribersRequest) ProtoMessage() {} + +func (x *NotifyAboutFailingSubscribersRequest) ProtoReflect() protoreflect.Message { + mi := &file_pubsub_proto_msgTypes[11] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use NotifyAboutFailingSubscribersRequest.ProtoReflect.Descriptor instead. +func (*NotifyAboutFailingSubscribersRequest) Descriptor() ([]byte, []int) { + return file_pubsub_proto_rawDescGZIP(), []int{11} +} + +func (x *NotifyAboutFailingSubscribersRequest) GetInvalidDeliveries() []*SubscriberDeliveryReport { + if x != nil { + return x.InvalidDeliveries + } + return nil +} + +// Ack to the delivery actor after notification of subscribers that fail to process the messages +type NotifyAboutFailingSubscribersResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *NotifyAboutFailingSubscribersResponse) Reset() { + *x = NotifyAboutFailingSubscribersResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_pubsub_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *NotifyAboutFailingSubscribersResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NotifyAboutFailingSubscribersResponse) ProtoMessage() {} + +func (x *NotifyAboutFailingSubscribersResponse) ProtoReflect() protoreflect.Message { + mi := &file_pubsub_proto_msgTypes[12] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use NotifyAboutFailingSubscribersResponse.ProtoReflect.Descriptor instead. +func (*NotifyAboutFailingSubscribersResponse) Descriptor() ([]byte, []int) { + return file_pubsub_proto_rawDescGZIP(), []int{12} +} + +// Contains information about a failed delivery +type SubscriberDeliveryReport struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Subscriber *SubscriberIdentity `protobuf:"bytes,1,opt,name=subscriber,proto3" json:"subscriber,omitempty"` + Status DeliveryStatus `protobuf:"varint,2,opt,name=status,proto3,enum=cluster.DeliveryStatus" json:"status,omitempty"` +} + +func (x *SubscriberDeliveryReport) Reset() { + *x = SubscriberDeliveryReport{} + if protoimpl.UnsafeEnabled { + mi := &file_pubsub_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SubscriberDeliveryReport) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SubscriberDeliveryReport) ProtoMessage() {} + +func (x *SubscriberDeliveryReport) ProtoReflect() protoreflect.Message { + mi := &file_pubsub_proto_msgTypes[13] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SubscriberDeliveryReport.ProtoReflect.Descriptor instead. +func (*SubscriberDeliveryReport) Descriptor() ([]byte, []int) { + return file_pubsub_proto_rawDescGZIP(), []int{13} +} + +func (x *SubscriberDeliveryReport) GetSubscriber() *SubscriberIdentity { + if x != nil { + return x.Subscriber + } + return nil +} + +func (x *SubscriberDeliveryReport) GetStatus() DeliveryStatus { + if x != nil { + return x.Status + } + return DeliveryStatus_Delivered +} + +// Message posted to subscriber's mailbox, that is then unrolled to single messages, and has ability to auto respond +// See also PubSubAutoRespondBatch +type PubSubAutoRespondBatchTransport struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + TypeNames []string `protobuf:"bytes,1,rep,name=type_names,json=typeNames,proto3" json:"type_names,omitempty"` + Envelopes []*PubSubEnvelope `protobuf:"bytes,2,rep,name=envelopes,proto3" json:"envelopes,omitempty"` +} + +func (x *PubSubAutoRespondBatchTransport) Reset() { + *x = PubSubAutoRespondBatchTransport{} + if protoimpl.UnsafeEnabled { + mi := &file_pubsub_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PubSubAutoRespondBatchTransport) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PubSubAutoRespondBatchTransport) ProtoMessage() {} + +func (x *PubSubAutoRespondBatchTransport) ProtoReflect() protoreflect.Message { + mi := &file_pubsub_proto_msgTypes[14] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PubSubAutoRespondBatchTransport.ProtoReflect.Descriptor instead. +func (*PubSubAutoRespondBatchTransport) Descriptor() ([]byte, []int) { + return file_pubsub_proto_rawDescGZIP(), []int{14} +} + +func (x *PubSubAutoRespondBatchTransport) GetTypeNames() []string { + if x != nil { + return x.TypeNames + } + return nil +} + +func (x *PubSubAutoRespondBatchTransport) GetEnvelopes() []*PubSubEnvelope { + if x != nil { + return x.Envelopes + } + return nil +} + +// Publish ack/nack response +type PublishResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Status of the whole published batch or single message + Status PublishStatus `protobuf:"varint,1,opt,name=status,proto3,enum=cluster.PublishStatus" json:"status,omitempty"` +} + +func (x *PublishResponse) Reset() { + *x = PublishResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_pubsub_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PublishResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PublishResponse) ProtoMessage() {} + +func (x *PublishResponse) ProtoReflect() protoreflect.Message { + mi := &file_pubsub_proto_msgTypes[15] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PublishResponse.ProtoReflect.Descriptor instead. +func (*PublishResponse) Descriptor() ([]byte, []int) { + return file_pubsub_proto_rawDescGZIP(), []int{15} +} + +func (x *PublishResponse) GetStatus() PublishStatus { + if x != nil { + return x.Status + } + return PublishStatus_Ok +} + +var File_pubsub_proto protoreflect.FileDescriptor + +var file_pubsub_proto_rawDesc = []byte{ + 0x0a, 0x0c, 0x70, 0x75, 0x62, 0x73, 0x75, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x07, + 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x1a, 0x0d, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x0b, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x22, 0x87, 0x01, 0x0a, 0x12, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, + 0x65, 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x12, 0x1e, 0x0a, 0x03, 0x70, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x2e, + 0x50, 0x49, 0x44, 0x48, 0x00, 0x52, 0x03, 0x70, 0x69, 0x64, 0x12, 0x45, 0x0a, 0x10, 0x63, 0x6c, + 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x2e, 0x43, + 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x48, 0x00, + 0x52, 0x0f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, + 0x79, 0x42, 0x0a, 0x0a, 0x08, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x22, 0x49, 0x0a, + 0x0a, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x12, 0x3b, 0x0a, 0x0b, 0x69, + 0x64, 0x6c, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x69, 0x64, 0x6c, + 0x65, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x22, 0x0d, 0x0a, 0x0b, 0x41, 0x63, 0x6b, 0x6e, + 0x6f, 0x77, 0x6c, 0x65, 0x64, 0x67, 0x65, 0x22, 0x4c, 0x0a, 0x0b, 0x53, 0x75, 0x62, 0x73, 0x63, + 0x72, 0x69, 0x62, 0x65, 0x72, 0x73, 0x12, 0x3d, 0x0a, 0x0b, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, + 0x69, 0x62, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x63, 0x6c, + 0x75, 0x73, 0x74, 0x65, 0x72, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x72, + 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, 0x0b, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, + 0x69, 0x62, 0x65, 0x72, 0x73, 0x22, 0x4f, 0x0a, 0x10, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, + 0x62, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3b, 0x0a, 0x0a, 0x73, 0x75, 0x62, + 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, + 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, + 0x65, 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, 0x0a, 0x73, 0x75, 0x62, 0x73, + 0x63, 0x72, 0x69, 0x62, 0x65, 0x72, 0x22, 0x13, 0x0a, 0x11, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, + 0x69, 0x62, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x51, 0x0a, 0x12, 0x55, + 0x6e, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x3b, 0x0a, 0x0a, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x72, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x2e, + 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, + 0x74, 0x79, 0x52, 0x0a, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x72, 0x22, 0x15, + 0x0a, 0x13, 0x55, 0x6e, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x6c, 0x0a, 0x14, 0x50, 0x75, 0x62, 0x53, 0x75, 0x62, 0x42, + 0x61, 0x74, 0x63, 0x68, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x1d, 0x0a, + 0x0a, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x09, 0x52, 0x09, 0x74, 0x79, 0x70, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x35, 0x0a, 0x09, + 0x65, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x17, 0x2e, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x2e, 0x50, 0x75, 0x62, 0x53, 0x75, 0x62, + 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x52, 0x09, 0x65, 0x6e, 0x76, 0x65, 0x6c, 0x6f, + 0x70, 0x65, 0x73, 0x22, 0x71, 0x0a, 0x0e, 0x50, 0x75, 0x62, 0x53, 0x75, 0x62, 0x45, 0x6e, 0x76, + 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x74, 0x79, 0x70, 0x65, 0x49, 0x64, 0x12, 0x21, + 0x0a, 0x0c, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x44, 0x61, 0x74, + 0x61, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x72, 0x5f, + 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, + 0x69, 0x7a, 0x65, 0x72, 0x49, 0x64, 0x22, 0xa1, 0x01, 0x0a, 0x1c, 0x44, 0x65, 0x6c, 0x69, 0x76, + 0x65, 0x72, 0x42, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x72, + 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x36, 0x0a, 0x0b, 0x73, 0x75, 0x62, 0x73, 0x63, + 0x72, 0x69, 0x62, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x63, + 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, + 0x72, 0x73, 0x52, 0x0b, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x72, 0x73, 0x12, + 0x33, 0x0a, 0x05, 0x62, 0x61, 0x74, 0x63, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, + 0x2e, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x2e, 0x50, 0x75, 0x62, 0x53, 0x75, 0x62, 0x42, + 0x61, 0x74, 0x63, 0x68, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x05, 0x62, + 0x61, 0x74, 0x63, 0x68, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x22, 0x78, 0x0a, 0x24, 0x4e, 0x6f, + 0x74, 0x69, 0x66, 0x79, 0x41, 0x62, 0x6f, 0x75, 0x74, 0x46, 0x61, 0x69, 0x6c, 0x69, 0x6e, 0x67, + 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x50, 0x0a, 0x12, 0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x5f, 0x64, 0x65, + 0x6c, 0x69, 0x76, 0x65, 0x72, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, + 0x2e, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, + 0x62, 0x65, 0x72, 0x44, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x79, 0x52, 0x65, 0x70, 0x6f, 0x72, + 0x74, 0x52, 0x11, 0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x44, 0x65, 0x6c, 0x69, 0x76, 0x65, + 0x72, 0x69, 0x65, 0x73, 0x22, 0x27, 0x0a, 0x25, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x41, 0x62, + 0x6f, 0x75, 0x74, 0x46, 0x61, 0x69, 0x6c, 0x69, 0x6e, 0x67, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, + 0x69, 0x62, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x88, 0x01, + 0x0a, 0x18, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x72, 0x44, 0x65, 0x6c, 0x69, + 0x76, 0x65, 0x72, 0x79, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x3b, 0x0a, 0x0a, 0x73, 0x75, + 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, + 0x2e, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, + 0x62, 0x65, 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, 0x0a, 0x73, 0x75, 0x62, + 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x72, 0x12, 0x2f, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, + 0x72, 0x2e, 0x44, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x79, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x77, 0x0a, 0x1f, 0x50, 0x75, 0x62, 0x53, + 0x75, 0x62, 0x41, 0x75, 0x74, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x64, 0x42, 0x61, 0x74, + 0x63, 0x68, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x74, + 0x79, 0x70, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, + 0x09, 0x74, 0x79, 0x70, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x35, 0x0a, 0x09, 0x65, 0x6e, + 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, + 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x2e, 0x50, 0x75, 0x62, 0x53, 0x75, 0x62, 0x45, 0x6e, + 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x52, 0x09, 0x65, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, + 0x73, 0x22, 0x41, 0x0a, 0x0f, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2e, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0e, 0x32, 0x16, 0x2e, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x2e, 0x50, + 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x2a, 0x5d, 0x0a, 0x0e, 0x44, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x79, + 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x0d, 0x0a, 0x09, 0x44, 0x65, 0x6c, 0x69, 0x76, 0x65, + 0x72, 0x65, 0x64, 0x10, 0x00, 0x12, 0x1f, 0x0a, 0x1b, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, + 0x62, 0x65, 0x72, 0x4e, 0x6f, 0x4c, 0x6f, 0x6e, 0x67, 0x65, 0x72, 0x52, 0x65, 0x61, 0x63, 0x68, + 0x61, 0x62, 0x6c, 0x65, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, + 0x74, 0x10, 0x02, 0x12, 0x0e, 0x0a, 0x0a, 0x4f, 0x74, 0x68, 0x65, 0x72, 0x45, 0x72, 0x72, 0x6f, + 0x72, 0x10, 0x7f, 0x2a, 0x23, 0x0a, 0x0d, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x53, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x6b, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, + 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x10, 0x01, 0x42, 0x2c, 0x5a, 0x2a, 0x2f, 0x67, 0x69, 0x74, + 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x73, 0x79, 0x6e, 0x6b, 0x72, 0x6f, 0x6e, + 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x2d, 0x67, 0x6f, 0x2f, 0x63, + 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_pubsub_proto_rawDescOnce sync.Once + file_pubsub_proto_rawDescData = file_pubsub_proto_rawDesc +) + +func file_pubsub_proto_rawDescGZIP() []byte { + file_pubsub_proto_rawDescOnce.Do(func() { + file_pubsub_proto_rawDescData = protoimpl.X.CompressGZIP(file_pubsub_proto_rawDescData) + }) + return file_pubsub_proto_rawDescData +} + +var file_pubsub_proto_enumTypes = make([]protoimpl.EnumInfo, 2) +var file_pubsub_proto_msgTypes = make([]protoimpl.MessageInfo, 16) +var file_pubsub_proto_goTypes = []interface{}{ + (DeliveryStatus)(0), // 0: cluster.DeliveryStatus + (PublishStatus)(0), // 1: cluster.PublishStatus + (*SubscriberIdentity)(nil), // 2: cluster.SubscriberIdentity + (*Initialize)(nil), // 3: cluster.Initialize + (*Acknowledge)(nil), // 4: cluster.Acknowledge + (*Subscribers)(nil), // 5: cluster.Subscribers + (*SubscribeRequest)(nil), // 6: cluster.SubscribeRequest + (*SubscribeResponse)(nil), // 7: cluster.SubscribeResponse + (*UnsubscribeRequest)(nil), // 8: cluster.UnsubscribeRequest + (*UnsubscribeResponse)(nil), // 9: cluster.UnsubscribeResponse + (*PubSubBatchTransport)(nil), // 10: cluster.PubSubBatchTransport + (*PubSubEnvelope)(nil), // 11: cluster.PubSubEnvelope + (*DeliverBatchRequestTransport)(nil), // 12: cluster.DeliverBatchRequestTransport + (*NotifyAboutFailingSubscribersRequest)(nil), // 13: cluster.NotifyAboutFailingSubscribersRequest + (*NotifyAboutFailingSubscribersResponse)(nil), // 14: cluster.NotifyAboutFailingSubscribersResponse + (*SubscriberDeliveryReport)(nil), // 15: cluster.SubscriberDeliveryReport + (*PubSubAutoRespondBatchTransport)(nil), // 16: cluster.PubSubAutoRespondBatchTransport + (*PublishResponse)(nil), // 17: cluster.PublishResponse + (*actor.PID)(nil), // 18: actor.PID + (*ClusterIdentity)(nil), // 19: cluster.ClusterIdentity + (*durationpb.Duration)(nil), // 20: google.protobuf.Duration +} +var file_pubsub_proto_depIdxs = []int32{ + 18, // 0: cluster.SubscriberIdentity.pid:type_name -> actor.PID + 19, // 1: cluster.SubscriberIdentity.cluster_identity:type_name -> cluster.ClusterIdentity + 20, // 2: cluster.Initialize.idleTimeout:type_name -> google.protobuf.Duration + 2, // 3: cluster.Subscribers.subscribers:type_name -> cluster.SubscriberIdentity + 2, // 4: cluster.SubscribeRequest.subscriber:type_name -> cluster.SubscriberIdentity + 2, // 5: cluster.UnsubscribeRequest.subscriber:type_name -> cluster.SubscriberIdentity + 11, // 6: cluster.PubSubBatchTransport.envelopes:type_name -> cluster.PubSubEnvelope + 5, // 7: cluster.DeliverBatchRequestTransport.subscribers:type_name -> cluster.Subscribers + 10, // 8: cluster.DeliverBatchRequestTransport.batch:type_name -> cluster.PubSubBatchTransport + 15, // 9: cluster.NotifyAboutFailingSubscribersRequest.invalid_deliveries:type_name -> cluster.SubscriberDeliveryReport + 2, // 10: cluster.SubscriberDeliveryReport.subscriber:type_name -> cluster.SubscriberIdentity + 0, // 11: cluster.SubscriberDeliveryReport.status:type_name -> cluster.DeliveryStatus + 11, // 12: cluster.PubSubAutoRespondBatchTransport.envelopes:type_name -> cluster.PubSubEnvelope + 1, // 13: cluster.PublishResponse.status:type_name -> cluster.PublishStatus + 14, // [14:14] is the sub-list for method output_type + 14, // [14:14] is the sub-list for method input_type + 14, // [14:14] is the sub-list for extension type_name + 14, // [14:14] is the sub-list for extension extendee + 0, // [0:14] is the sub-list for field type_name +} + +func init() { file_pubsub_proto_init() } +func file_pubsub_proto_init() { + if File_pubsub_proto != nil { + return + } + file_cluster_proto_init() + if !protoimpl.UnsafeEnabled { + file_pubsub_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SubscriberIdentity); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pubsub_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Initialize); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pubsub_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Acknowledge); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pubsub_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Subscribers); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pubsub_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SubscribeRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pubsub_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SubscribeResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pubsub_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UnsubscribeRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pubsub_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UnsubscribeResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pubsub_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PubSubBatchTransport); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pubsub_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PubSubEnvelope); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pubsub_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DeliverBatchRequestTransport); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pubsub_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NotifyAboutFailingSubscribersRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pubsub_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NotifyAboutFailingSubscribersResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pubsub_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SubscriberDeliveryReport); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pubsub_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PubSubAutoRespondBatchTransport); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pubsub_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PublishResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + file_pubsub_proto_msgTypes[0].OneofWrappers = []interface{}{ + (*SubscriberIdentity_Pid)(nil), + (*SubscriberIdentity_ClusterIdentity)(nil), + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_pubsub_proto_rawDesc, + NumEnums: 2, + NumMessages: 16, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_pubsub_proto_goTypes, + DependencyIndexes: file_pubsub_proto_depIdxs, + EnumInfos: file_pubsub_proto_enumTypes, + MessageInfos: file_pubsub_proto_msgTypes, + }.Build() + File_pubsub_proto = out.File + file_pubsub_proto_rawDesc = nil + file_pubsub_proto_goTypes = nil + file_pubsub_proto_depIdxs = nil +} diff --git a/cluster/pubsub.proto b/cluster/pubsub.proto new file mode 100644 index 0000000000000000000000000000000000000000..18314f16732323166c3d38911ef05b8fc0848662 --- /dev/null +++ b/cluster/pubsub.proto @@ -0,0 +1,115 @@ +syntax = "proto3"; +package cluster; +option go_package = "/gitee.com/simplexyz/simpleactor-go/cluster"; + +import "cluster.proto"; +import "google/protobuf/duration.proto"; +import "actor.proto"; + +// Identifies a subscriber by either ClusterIdentity or PID +message SubscriberIdentity { + oneof Identity { + actor.PID pid = 1; + cluster.ClusterIdentity cluster_identity = 2; + } +} + +// First request to initialize the actor. +message Initialize { + google.protobuf.Duration idleTimeout = 1; +} + +message Acknowledge {} + +// A list of subscribers +message Subscribers { + repeated SubscriberIdentity subscribers = 1; +} + +// Sent to topic actor to add a subscriber +message SubscribeRequest { + SubscriberIdentity subscriber = 1; +} + +// Subscribe acknowledgement +message SubscribeResponse {} + +// Sent to topic actor to remove a subscriber +message UnsubscribeRequest { + SubscriberIdentity subscriber = 1; +} + +// Unsubscribe acknowledgement +message UnsubscribeResponse {} + +// Message sent from publisher to topic actor +// See also PubSubBatch +message PubSubBatchTransport { + repeated string type_names = 1; + repeated PubSubEnvelope envelopes = 2; +} + +// Contains message byte representation and type reference +message PubSubEnvelope { + int32 type_id = 1; + bytes message_data = 2; + int32 serializer_id = 3; +} + +// Message sent from topic to delivery actor +message DeliverBatchRequestTransport { + Subscribers subscribers = 1; + PubSubBatchTransport batch = 2; + string topic = 3; +} + +// Message sent from delivery actor to topic to notify of subscribers that fail to process the messages +message NotifyAboutFailingSubscribersRequest { + repeated SubscriberDeliveryReport invalid_deliveries = 1; +} + +// Ack to the delivery actor after notification of subscribers that fail to process the messages +message NotifyAboutFailingSubscribersResponse {} + +// Contains information about a failed delivery +message SubscriberDeliveryReport { + SubscriberIdentity subscriber = 1; + DeliveryStatus status = 2; +} + +// Delivery status as seen by the delivery actor +enum DeliveryStatus { + // Message was put in the queue of the subscriber + Delivered = 0; + + // Message did not reach subscriber, because it was dead + SubscriberNoLongerReachable = 1; + + // Delivery timed out + Timeout = 2; + + // Some other problem happened + OtherError = 127; +} + +// Message posted to subscriber's mailbox, that is then unrolled to single messages, and has ability to auto respond +// See also PubSubAutoRespondBatch +message PubSubAutoRespondBatchTransport { + repeated string type_names = 1; + repeated PubSubEnvelope envelopes = 2; +} + +// Status of the whole published batch or single message +enum PublishStatus { + // Batch or message was successfully published according to the delivery guarantees + Ok = 0; + + // Topic failed to forward the message + Failed = 1; +} + +// Publish ack/nack response +message PublishResponse { + // Status of the whole published batch or single message + PublishStatus status = 1; +} diff --git a/cluster/pubsub_batch.go b/cluster/pubsub_batch.go new file mode 100644 index 0000000000000000000000000000000000000000..5bcad054fd07f62b5ed5a0272c71261061db4e1f --- /dev/null +++ b/cluster/pubsub_batch.go @@ -0,0 +1,119 @@ +package cluster + +import ( + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/remote" +) + +type PubSubBatch struct { + Envelopes []interface{} +} + +// Serialize converts a PubSubBatch to a PubSubBatchTransport. +func (b *PubSubBatch) Serialize() remote.RootSerialized { + batch := &PubSubBatchTransport{ + TypeNames: make([]string, 0), + Envelopes: make([]*PubSubEnvelope, 0), + } + + for _, envelope := range b.Envelopes { + var serializerId int32 + messageData, typeName, err := remote.Serialize(envelope, serializerId) + if err != nil { + panic(err) + } + // batch.TypeNames.IndexOf(typeName) + typeIndex := -1 + for i, t := range batch.TypeNames { + if t == typeName { + typeIndex = i + break + } + } + if typeIndex == -1 { + batch.TypeNames = append(batch.TypeNames, typeName) + typeIndex = len(batch.TypeNames) - 1 + } + batch.Envelopes = append(batch.Envelopes, &PubSubEnvelope{ + MessageData: messageData, + TypeId: int32(typeIndex), + SerializerId: serializerId, + }) + } + return batch +} + +// Deserialize converts a PubSubBatchTransport to a PubSubBatch. +func (t *PubSubBatchTransport) Deserialize() remote.RootSerializable { + b := &PubSubBatch{ + Envelopes: make([]interface{}, 0), + } + + for _, envelope := range t.Envelopes { + message, err := remote.Deserialize(envelope.MessageData, t.TypeNames[envelope.TypeId], envelope.SerializerId) + if err != nil { + panic(err) + } + b.Envelopes = append(b.Envelopes, message) + } + return b +} + +type DeliverBatchRequest struct { + Subscribers *Subscribers + PubSubBatch *PubSubBatch + Topic string +} + +func (d *DeliverBatchRequest) Serialize() remote.RootSerialized { + return &DeliverBatchRequestTransport{ + Subscribers: d.Subscribers, + Batch: d.PubSubBatch.Serialize().(*PubSubBatchTransport), + Topic: d.Topic, + } +} + +func (t *DeliverBatchRequestTransport) Deserialize() remote.RootSerializable { + return &DeliverBatchRequest{ + Subscribers: t.Subscribers, + PubSubBatch: t.Batch.Deserialize().(*PubSubBatch), + Topic: t.Topic, + } +} + +type PubSubAutoRespondBatch struct { + Envelopes []interface{} +} + +// Serialize converts a PubSubAutoRespondBatch to a PubSubAutoRespondBatchTransport. +func (b *PubSubAutoRespondBatch) Serialize() remote.RootSerialized { + batch := &PubSubBatch{Envelopes: b.Envelopes} + transport := batch.Serialize().(*PubSubBatchTransport) + return &PubSubAutoRespondBatchTransport{ + TypeNames: transport.TypeNames, + Envelopes: transport.Envelopes, + } +} + +// GetAutoResponse returns a PublishResponse. +func (b *PubSubAutoRespondBatch) GetAutoResponse(_ actor.Context) interface{} { + return &PublishResponse{ + Status: PublishStatus_Ok, + } +} + +// GetMessages returns the message. +func (b *PubSubAutoRespondBatch) GetMessages() []interface{} { + return b.Envelopes +} + +// Deserialize converts a PubSubAutoRespondBatchTransport to a PubSubAutoRespondBatch. +func (t *PubSubAutoRespondBatchTransport) Deserialize() remote.RootSerializable { + batch := &PubSubBatchTransport{ + TypeNames: t.TypeNames, + Envelopes: t.Envelopes, + } + return &PubSubAutoRespondBatch{ + Envelopes: batch.Deserialize().(*PubSubBatch).Envelopes, + } +} diff --git a/cluster/pubsub_delivery.go b/cluster/pubsub_delivery.go new file mode 100644 index 0000000000000000000000000000000000000000..34125087840c7dcd6b3a78e20a8eed490c98d142 --- /dev/null +++ b/cluster/pubsub_delivery.go @@ -0,0 +1,106 @@ +package cluster + +import ( + "time" + + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/log" + "gitee.com/simplexyz/simpleactor-go/remote" +) + +var pubsubMemberDeliveryLogThrottle = actor.NewThrottle(10, time.Second, func(i int32) { + plog.Warn("[PubSubMemberDeliveryActor] Throttled logs", log.Int("count", int(i))) +}) + +type PubSubMemberDeliveryActor struct { + subscriberTimeout time.Duration +} + +func NewPubSubMemberDeliveryActor(subscriberTimeout time.Duration) *PubSubMemberDeliveryActor { + return &PubSubMemberDeliveryActor{ + subscriberTimeout: subscriberTimeout, + } +} + +func (p *PubSubMemberDeliveryActor) Receive(c actor.Context) { + if batch, ok := c.Message().(*DeliverBatchRequest); ok { + topicBatch := &PubSubAutoRespondBatch{Envelopes: batch.PubSubBatch.Envelopes} + siList := batch.Subscribers.Subscribers + + invalidDeliveries := make([]*SubscriberDeliveryReport, 0, len(siList)) + + type futureWithIdentity struct { + future *actor.Future + identity *SubscriberIdentity + } + futureList := make([]futureWithIdentity, 0, len(siList)) + for _, identity := range siList { + f := p.DeliverBatch(c, topicBatch, identity) + if f != nil { + futureList = append(futureList, futureWithIdentity{future: f, identity: identity}) + } + } + + for _, fWithIdentity := range futureList { + _, err := fWithIdentity.future.Result() + identityLog := func(err error) { + if pubsubMemberDeliveryLogThrottle() == actor.Open { + if fWithIdentity.identity.GetPid() != nil { + plog.Info("Pub-sub message delivered to PID", log.String("pid", fWithIdentity.identity.GetPid().String())) + } else if fWithIdentity.identity.GetClusterIdentity() != nil { + plog.Info("Pub-sub message delivered to cluster identity", log.String("cluster identity", fWithIdentity.identity.GetClusterIdentity().String())) + } + } + } + + status := DeliveryStatus_Delivered + if err != nil { + switch err { + case actor.ErrTimeout, remote.ErrTimeout: + identityLog(err) + status = DeliveryStatus_Timeout + case actor.ErrDeadLetter, remote.ErrDeadLetter: + identityLog(err) + status = DeliveryStatus_SubscriberNoLongerReachable + default: + identityLog(err) + status = DeliveryStatus_OtherError + } + } + if status != DeliveryStatus_Delivered { + invalidDeliveries = append(invalidDeliveries, &SubscriberDeliveryReport{Status: status, Subscriber: fWithIdentity.identity}) + } + } + + if len(invalidDeliveries) > 0 { + cluster := GetCluster(c.ActorSystem()) + // we use cluster.Call to locate the topic actor in the cluster + _, _ = cluster.Call(batch.Topic, TopicActorKind, &NotifyAboutFailingSubscribersRequest{InvalidDeliveries: invalidDeliveries}) + } + } +} + +// DeliverBatch delivers PubSubAutoRespondBatch to SubscriberIdentity. +func (p *PubSubMemberDeliveryActor) DeliverBatch(c actor.Context, batch *PubSubAutoRespondBatch, s *SubscriberIdentity) *actor.Future { + if pid := s.GetPid(); pid != nil { + return p.DeliverToPid(c, batch, pid) + } + if ci := s.GetClusterIdentity(); ci != nil { + return p.DeliverToClusterIdentity(c, batch, ci) + } + return nil +} + +// DeliverToPid delivers PubSubAutoRespondBatch to PID. +func (p *PubSubMemberDeliveryActor) DeliverToPid(c actor.Context, batch *PubSubAutoRespondBatch, pid *actor.PID) *actor.Future { + return c.RequestFuture(pid, batch, p.subscriberTimeout) +} + +// DeliverToClusterIdentity delivers PubSubAutoRespondBatch to ClusterIdentity. +func (p *PubSubMemberDeliveryActor) DeliverToClusterIdentity(c actor.Context, batch *PubSubAutoRespondBatch, ci *ClusterIdentity) *actor.Future { + cluster := GetCluster(c.ActorSystem()) + // deliver to virtual actor + // delivery should always be possible, since a virtual actor always exists + pid := cluster.Get(ci.Identity, ci.Kind) + return c.RequestFuture(pid, batch, p.subscriberTimeout) +} diff --git a/cluster/pubsub_extensions.go b/cluster/pubsub_extensions.go new file mode 100644 index 0000000000000000000000000000000000000000..0c752b85879d3e4625c7e57a56a085145bf81030 --- /dev/null +++ b/cluster/pubsub_extensions.go @@ -0,0 +1,77 @@ +package cluster + +import ( + "gitee.com/simplexyz/simpleactor-go/actor" +) + +// Publisher creates a new PubSub publisher that publishes messages directly to the TopicActor +func (c *Cluster) Publisher() Publisher { + return NewPublisher(c) +} + +// BatchingProducer create a new PubSub batching producer for specified topic, that publishes directly to the topic actor +func (c *Cluster) BatchingProducer(topic string, opts ...BatchingProducerConfigOption) *BatchingProducer { + return NewBatchingProducer(c.Publisher(), topic, opts...) +} + +// SubscribeByPid subscribes to a PubSub topic by subscriber PID +func (c *Cluster) SubscribeByPid(topic string, pid *actor.PID, opts ...GrainCallOption) (*SubscribeResponse, error) { + res, err := c.Call(topic, TopicActorKind, &SubscribeRequest{ + Subscriber: &SubscriberIdentity{Identity: &SubscriberIdentity_Pid{Pid: pid}}, + }, opts...) + if err != nil { + return nil, err + } + return res.(*SubscribeResponse), err +} + +// SubscribeByClusterIdentity subscribes to a PubSub topic by cluster identity +func (c *Cluster) SubscribeByClusterIdentity(topic string, identity *ClusterIdentity, opts ...GrainCallOption) (*SubscribeResponse, error) { + res, err := c.Call(topic, TopicActorKind, &SubscribeRequest{ + Subscriber: &SubscriberIdentity{Identity: &SubscriberIdentity_ClusterIdentity{ClusterIdentity: identity}}, + }, opts...) + if err != nil { + return nil, err + } + return res.(*SubscribeResponse), err +} + +// SubscribeWithReceive subscribe to a PubSub topic by providing a Receive function, that will be used to spawn a subscriber actor +func (c *Cluster) SubscribeWithReceive(topic string, receive actor.ReceiveFunc, opts ...GrainCallOption) (*SubscribeResponse, error) { + props := actor.PropsFromFunc(receive) + pid := c.ActorSystem.Root.Spawn(props) + return c.SubscribeByPid(topic, pid, opts...) +} + +// UnsubscribeByPid unsubscribes from a PubSub topic by subscriber PID +func (c *Cluster) UnsubscribeByPid(topic string, pid *actor.PID, opts ...GrainCallOption) (*UnsubscribeResponse, error) { + res, err := c.Call(topic, TopicActorKind, &UnsubscribeRequest{ + Subscriber: &SubscriberIdentity{Identity: &SubscriberIdentity_Pid{Pid: pid}}, + }, opts...) + if err != nil { + return nil, err + } + return res.(*UnsubscribeResponse), err +} + +// UnsubscribeByClusterIdentity unsubscribes from a PubSub topic by cluster identity +func (c *Cluster) UnsubscribeByClusterIdentity(topic string, identity *ClusterIdentity, opts ...GrainCallOption) (*UnsubscribeResponse, error) { + res, err := c.Call(topic, TopicActorKind, &UnsubscribeRequest{ + Subscriber: &SubscriberIdentity{Identity: &SubscriberIdentity_ClusterIdentity{ClusterIdentity: identity}}, + }, opts...) + if err != nil { + return nil, err + } + return res.(*UnsubscribeResponse), err +} + +// UnsubscribeByIdentityAndKind unsubscribes from a PubSub topic by cluster identity +func (c *Cluster) UnsubscribeByIdentityAndKind(topic string, identity string, kind string, opts ...GrainCallOption) (*UnsubscribeResponse, error) { + res, err := c.Call(topic, TopicActorKind, &UnsubscribeRequest{ + Subscriber: &SubscriberIdentity{Identity: &SubscriberIdentity_ClusterIdentity{ClusterIdentity: NewClusterIdentity(identity, kind)}}, + }, opts...) + if err != nil { + return nil, err + } + return res.(*UnsubscribeResponse), err +} diff --git a/cluster/pubsub_producer.go b/cluster/pubsub_producer.go new file mode 100644 index 0000000000000000000000000000000000000000..cef9b0567cb2db1de6c25507d2bfd57e7d4dc0d1 --- /dev/null +++ b/cluster/pubsub_producer.go @@ -0,0 +1,586 @@ +package cluster + +import ( + "sync" + "sync/atomic" + "time" + + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/internal/queue/mpsc" + "gitee.com/simplexyz/simpleactor-go/log" + "golang.org/x/net/context" +) + +// PublishingErrorHandler decides what to do with a publishing error in BatchingProducer +type PublishingErrorHandler func(retries int, e error, batch *PubSubBatch) *PublishingErrorDecision + +type BatchingProducerConfig struct { + // Maximum size of the published batch. Default: 2000. + BatchSize int + // Max size of the requests waiting in queue. If value is provided, the producer will throw + // ProducerQueueFullException when queue size is exceeded. If 0 or unset, the queue is unbounded + // Note that bounded queue has better performance than unbounded queue. + // Default: 0 (unbounded) + MaxQueueSize int + + // How long to wait for the publishing to complete. + // Default: 5s + PublishTimeout time.Duration + + // Error handler that can decide what to do with an error when publishing a batch. + // Default: Fail and stop the BatchingProducer + OnPublishingError PublishingErrorHandler + + // A throttle for logging from this producer. By default, a throttle shared between all instances of + // BatchingProducer is used, that allows for 10 events in 1 second. + LogThrottle actor.ShouldThrottle + + // Optional idle timeout which will specify to the `IPublisher` how long it should wait before invoking clean + // up code to recover resources. + PublisherIdleTimeout time.Duration +} + +var defaultBatchingProducerLogThrottle = actor.NewThrottle(10, time.Second, func(i int32) { + plog.Info("[BatchingProducer] Throttled logs", log.Int("count", int(i))) +}) + +func newBatchingProducerConfig(opts ...BatchingProducerConfigOption) *BatchingProducerConfig { + config := &BatchingProducerConfig{ + BatchSize: 2000, + PublishTimeout: 5 * time.Second, + OnPublishingError: func(retries int, e error, batch *PubSubBatch) *PublishingErrorDecision { + return FailBatchAndStop + }, + LogThrottle: defaultBatchingProducerLogThrottle, + } + + for _, opt := range opts { + opt(config) + } + + return config +} + +type BatchingProducer struct { + config *BatchingProducerConfig + topic string + publisher Publisher + publisherChannel channel[produceMessage] + loopCancel context.CancelFunc + loopDone chan struct{} + msgLeft uint32 +} + +func NewBatchingProducer(publisher Publisher, topic string, opts ...BatchingProducerConfigOption) *BatchingProducer { + config := newBatchingProducerConfig(opts...) + p := &BatchingProducer{ + config: config, + topic: topic, + publisher: publisher, + msgLeft: 0, + loopDone: make(chan struct{}), + } + if config.MaxQueueSize > 0 { + p.publisherChannel = newBoundedChannel[produceMessage](config.MaxQueueSize) + } else { + p.publisherChannel = newUnboundedChannel[produceMessage]() + } + ctx, cancelFunc := context.WithCancel(context.Background()) + p.loopCancel = cancelFunc + go p.publishLoop(ctx) + + return p +} + +type pubsubBatchWithReceipts struct { + batch *PubSubBatch + ctxArr []context.Context +} + +// newPubSubBatchWithReceipts creates a new pubsubBatchWithReceipts +func newPubSubBatchWithReceipts() *pubsubBatchWithReceipts { + return &pubsubBatchWithReceipts{ + batch: &PubSubBatch{Envelopes: make([]interface{}, 0, 10)}, + ctxArr: make([]context.Context, 0, 10), + } +} + +type produceMessage struct { + message interface{} + ctx context.Context +} + +// Dispose stops the producer and releases all resources. +func (p *BatchingProducer) Dispose() { + p.loopCancel() + p.publisherChannel.broadcast() + <-p.loopDone +} + +// ProduceProcessInfo is the context for a Produce call +type ProduceProcessInfo struct { + Finished chan struct{} + Err error + cancelFunc context.CancelFunc + cancelled chan struct{} +} + +// IsCancelled returns true if the context has been cancelled +func (p *ProduceProcessInfo) IsCancelled() bool { + select { + case <-p.cancelled: + return true + default: + return false + } +} + +// IsFinished returns true if the context has been finished +func (p *ProduceProcessInfo) IsFinished() bool { + select { + case <-p.Finished: + return true + default: + return false + } +} + +// setErr sets the error for the ProduceProcessInfo +func (p *ProduceProcessInfo) setErr(err error) { + p.Err = err + p.cancelFunc() + close(p.Finished) +} + +// cancel the ProduceProcessInfo context +func (p *ProduceProcessInfo) cancel() { + p.cancelFunc() + close(p.Finished) + close(p.cancelled) +} + +// success closes the ProduceProcessInfo Finished channel +func (p *ProduceProcessInfo) success() { + p.cancelFunc() + close(p.Finished) +} + +type produceProcessInfoKey struct{} + +// GetProduceProcessInfo adds a new produce info to the BatchingProducer.Produce context +func (p *BatchingProducer) getProduceProcessInfo(ctx context.Context) *ProduceProcessInfo { + return ctx.Value(produceProcessInfoKey{}).(*ProduceProcessInfo) +} + +// Produce a message to producer queue. The return info can be used to wait for the message to be published. +func (p *BatchingProducer) Produce(ctx context.Context, message interface{}) (*ProduceProcessInfo, error) { + ctx, cancel := context.WithCancel(ctx) + info := &ProduceProcessInfo{ + Finished: make(chan struct{}), + cancelled: make(chan struct{}), + cancelFunc: cancel, + } + ctx = context.WithValue(ctx, produceProcessInfoKey{}, info) + if !p.publisherChannel.tryWrite(produceMessage{ + message: message, + ctx: ctx, + }) { + if p.publisherChannel.isComplete() { + return info, &InvalidOperationException{Topic: p.topic} + } + return info, &ProducerQueueFullException{topic: p.topic} + } + return info, nil +} + +// publishLoop is the main loop of the producer. It reads messages from the queue and publishes them in batches. +func (p *BatchingProducer) publishLoop(ctx context.Context) { + defer close(p.loopDone) + + plog.Debug("Producer is starting the publisher loop for topic", log.String("topic", p.topic)) + batchWrapper := newPubSubBatchWithReceipts() + + handleUnrecoverableError := func(err error) { + p.stopAcceptingNewMessages() + if p.config.LogThrottle() == actor.Open { + plog.Error("Error in the publisher loop of Producer for topic", log.String("topic", p.topic), log.Error(err)) + } + p.failBatch(batchWrapper, err) + p.failPendingMessages(err) + } + + _, err := p.publisher.Initialize(ctx, p.topic, PublisherConfig{IdleTimeout: p.config.PublisherIdleTimeout}) + if err != nil && err != context.Canceled { + handleUnrecoverableError(err) + } + +loop: + for { + select { + case <-ctx.Done(): + p.stopAcceptingNewMessages() + break loop + default: + if msg, ok := p.publisherChannel.tryRead(); ok { + + // if msg ctx not done + select { + case <-msg.ctx.Done(): + p.getProduceProcessInfo(msg.ctx).cancel() + default: + batchWrapper.batch.Envelopes = append(batchWrapper.batch.Envelopes, msg.message) + batchWrapper.ctxArr = append(batchWrapper.ctxArr, msg.ctx) + } + + if len(batchWrapper.batch.Envelopes) < p.config.BatchSize { + continue + } + + err := p.publishBatch(ctx, batchWrapper) + if err != nil { + handleUnrecoverableError(err) + break loop + } + batchWrapper = newPubSubBatchWithReceipts() + } else { + if len(batchWrapper.batch.Envelopes) > 0 { + err := p.publishBatch(ctx, batchWrapper) + if err != nil { + handleUnrecoverableError(err) + break loop + } + batchWrapper = newPubSubBatchWithReceipts() + } + p.publisherChannel.waitToRead() + } + } + } + p.cancelBatch(batchWrapper) + p.cancelPendingMessages() +} + +// cancelPendingMessages cancels all pending messages +func (p *BatchingProducer) cancelPendingMessages() { + for { + if msg, ok := p.publisherChannel.tryRead(); ok { + p.getProduceProcessInfo(msg.ctx).cancel() + } else { + break + } + } +} + +// cancelBatch cancels all contexts in the batch wrapper +func (p *BatchingProducer) cancelBatch(batchWrapper *pubsubBatchWithReceipts) { + for _, ctx := range batchWrapper.ctxArr { + p.getProduceProcessInfo(ctx).cancel() + } + + // ensure once cancelled, we won't touch the batch anymore + p.clearBatch(batchWrapper) +} + +// failPendingMessages fails all pending messages +func (p *BatchingProducer) failPendingMessages(err error) { + for { + if msg, ok := p.publisherChannel.tryRead(); ok { + p.getProduceProcessInfo(msg.ctx).setErr(err) + } else { + break + } + } +} + +// failBatch marks all contexts in the batch wrapper as failed +func (p *BatchingProducer) failBatch(batchWrapper *pubsubBatchWithReceipts, err error) { + for _, ctx := range batchWrapper.ctxArr { + p.getProduceProcessInfo(ctx).setErr(err) + } + + // ensure once failed, we won't touch the batch anymore + p.clearBatch(batchWrapper) +} + +// clearBatch clears the batch wrapper +func (p *BatchingProducer) clearBatch(batchWrapper *pubsubBatchWithReceipts) { + batchWrapper.batch = &PubSubBatch{Envelopes: make([]interface{}, 0, 10)} + batchWrapper.ctxArr = batchWrapper.ctxArr[:0] +} + +// completeBatch marks all contexts in the batch wrapper as completed +func (p *BatchingProducer) completeBatch(batchWrapper *pubsubBatchWithReceipts) { + for _, ctx := range batchWrapper.ctxArr { + p.getProduceProcessInfo(ctx).success() + } + + // ensure once completed, we won't touch the batch anymore + p.clearBatch(batchWrapper) +} + +// removeCancelledFromBatch removes all cancelled contexts from the batch wrapper +func (p *BatchingProducer) removeCancelledFromBatch(batchWrapper *pubsubBatchWithReceipts) { + for i := len(batchWrapper.ctxArr) - 1; i >= 0; i-- { + select { + case <-batchWrapper.ctxArr[i].Done(): + info := p.getProduceProcessInfo(batchWrapper.ctxArr[i]) + select { + case <-info.Finished: + // if the message is already finished, we don't need to do anything + default: + info.cancel() + } + + batchWrapper.batch.Envelopes = append(batchWrapper.batch.Envelopes[:i], batchWrapper.batch.Envelopes[i+1:]...) + batchWrapper.ctxArr = append(batchWrapper.ctxArr[:i], batchWrapper.ctxArr[i+1:]...) + default: + continue + } + } +} + +// stopAcceptingNewMessages stops accepting new messages into the channel. +func (p *BatchingProducer) stopAcceptingNewMessages() { + p.publisherChannel.complete() +} + +// publishBatch publishes a batch of messages using Publisher. +func (p *BatchingProducer) publishBatch(ctx context.Context, batchWrapper *pubsubBatchWithReceipts) error { + retries := 0 + retry := true + +loop: + for retry { + select { + case <-ctx.Done(): + p.cancelBatch(batchWrapper) + break loop + default: + retries++ + _, err := p.publisher.PublishBatch(ctx, p.topic, batchWrapper.batch, WithTimeout(p.config.PublishTimeout)) + if err != nil { + decision := p.config.OnPublishingError(retries, err, batchWrapper.batch) + if decision == FailBatchAndStop { + p.stopAcceptingNewMessages() + p.failBatch(batchWrapper, err) + return err // let the main producer loop exit + } + + if p.config.LogThrottle() == actor.Open { + plog.Warn("Error while publishing batch", log.Error(err)) + } + + if decision == FailBatchAndContinue { + p.failBatch(batchWrapper, err) + return nil + } + + // the decision is to retry + // if any of the messages have been canceled in the meantime, remove them and cancel the delivery report + p.removeCancelledFromBatch(batchWrapper) + + if len(batchWrapper.batch.Envelopes) == 0 { + retry = false + } else if decision.Delay > 0 { + time.Sleep(decision.Delay) + } + + continue + } + + retry = false + p.completeBatch(batchWrapper) + } + } + + return nil +} + +type ProducerQueueFullException struct { + topic string +} + +func (p *ProducerQueueFullException) Error() string { + return "Producer for topic " + p.topic + " has full queue" +} + +func (p *ProducerQueueFullException) Is(target error) bool { + _, ok := target.(*ProducerQueueFullException) + return ok +} + +type InvalidOperationException struct { + Topic string +} + +func (i *InvalidOperationException) Is(err error) bool { + _, ok := err.(*InvalidOperationException) + return ok +} + +func (i *InvalidOperationException) Error() string { + return "Producer for topic " + i.Topic + " is stopped, cannot produce more messages." +} + +// channel is a wrapper around a channel that can be used to read and write messages. +// messages must be pointers. +type channel[T any] interface { + tryWrite(msg T) bool + tryRead() (T, bool) + isComplete() bool + complete() + empty() bool + waitToRead() + broadcast() +} + +// BoundedChannel is a bounded channel with the given capacity. +type boundedChannel[T any] struct { + capacity int + c chan T + quit chan struct{} + once *sync.Once + cond *sync.Cond + left *atomic.Bool +} + +func (b *boundedChannel[T]) tryWrite(msg T) bool { + select { + case b.c <- msg: + b.cond.Broadcast() + return true + case <-b.quit: + return false + default: + return false + } +} + +func (b *boundedChannel[T]) tryRead() (msg T, ok bool) { + var msgDefault T + select { + case msg, ok = <-b.c: + return + default: + return msgDefault, false + } +} + +func (b *boundedChannel[T]) isComplete() bool { + select { + case <-b.quit: + return true + default: + return false + } +} + +func (b *boundedChannel[T]) complete() { + b.once.Do(func() { + close(b.quit) + }) +} + +func (b *boundedChannel[T]) empty() bool { + return len(b.c) == 0 +} + +func (b *boundedChannel[T]) waitToRead() { + b.cond.L.Lock() + defer b.cond.L.Unlock() + for b.empty() && !b.left.Load() { + b.cond.Wait() + } + b.left.Store(false) +} + +func (b *boundedChannel[T]) broadcast() { + b.left.Store(true) + b.cond.Broadcast() +} + +// newBoundedChannel creates a new bounded channel with the given capacity. +func newBoundedChannel[T any](capacity int) channel[T] { + return &boundedChannel[T]{ + capacity: capacity, + c: make(chan T, capacity), + quit: make(chan struct{}), + cond: sync.NewCond(&sync.Mutex{}), + once: &sync.Once{}, + left: &atomic.Bool{}, + } +} + +// UnboundedChannel is an unbounded channel. +type unboundedChannel[T any] struct { + queue *mpsc.Queue + quit chan struct{} + once *sync.Once + cond *sync.Cond + left *atomic.Bool +} + +func (u *unboundedChannel[T]) tryWrite(msg T) bool { + select { + case <-u.quit: + return false + default: + u.queue.Push(msg) + u.cond.Broadcast() + return true + } +} + +func (u *unboundedChannel[T]) tryRead() (T, bool) { + var msg T + tmp := u.queue.Pop() + if tmp == nil { + return msg, false + } else { + u.cond.Broadcast() + return tmp.(T), true + } +} + +func (u *unboundedChannel[T]) complete() { + u.once.Do(func() { + close(u.quit) + }) +} + +func (u *unboundedChannel[T]) isComplete() bool { + select { + case <-u.quit: + return true + default: + return false + } +} + +func (u *unboundedChannel[T]) empty() bool { + return u.queue.Empty() +} + +func (u *unboundedChannel[T]) waitToRead() { + u.cond.L.Lock() + defer u.cond.L.Unlock() + for u.empty() && !u.left.Load() { + u.cond.Wait() + } + u.left.Store(false) +} + +func (u *unboundedChannel[T]) broadcast() { + u.left.Store(true) + u.cond.Broadcast() +} + +// newUnboundedChannel creates a new unbounded channel. +func newUnboundedChannel[T any]() channel[T] { + return &unboundedChannel[T]{ + queue: mpsc.New(), + quit: make(chan struct{}), + cond: sync.NewCond(&sync.Mutex{}), + once: &sync.Once{}, + left: &atomic.Bool{}, + } +} diff --git a/cluster/pubsub_producer_opts.go b/cluster/pubsub_producer_opts.go new file mode 100644 index 0000000000000000000000000000000000000000..af21282ab69c1590d1de82d8b19a47a0cc553b51 --- /dev/null +++ b/cluster/pubsub_producer_opts.go @@ -0,0 +1,82 @@ +package cluster + +import ( + "time" + + "gitee.com/simplexyz/simpleactor-go/actor" +) + +type BatchingProducerConfigOption func(config *BatchingProducerConfig) + +// WithBatchingProducerBatchSize sets maximum size of the published batch. Default: 2000. +func WithBatchingProducerBatchSize(batchSize int) BatchingProducerConfigOption { + return func(config *BatchingProducerConfig) { + config.BatchSize = batchSize + } +} + +// WithBatchingProducerMaxQueueSize set max size of the requests waiting in queue. If value is provided, the producer will throw +// ProducerQueueFullException when queue size is exceeded. If 0 or unset, the queue is unbounded +// Note that bounded queue has better performance than unbounded queue. +// Default: 0 (unbounded) +func WithBatchingProducerMaxQueueSize(maxQueueSize int) BatchingProducerConfigOption { + return func(config *BatchingProducerConfig) { + config.MaxQueueSize = maxQueueSize + } +} + +// WithBatchingProducerPublishTimeout sets how long to wait for the publishing to complete. +// Default: 5s +func WithBatchingProducerPublishTimeout(publishTimeout time.Duration) BatchingProducerConfigOption { + return func(config *BatchingProducerConfig) { + config.PublishTimeout = publishTimeout + } +} + +// WithBatchingProducerOnPublishingError sets error handler that can decide what to do with an error when publishing a batch. +// Default: Fail and stop the BatchingProducer +func WithBatchingProducerOnPublishingError(onPublishingError PublishingErrorHandler) BatchingProducerConfigOption { + return func(config *BatchingProducerConfig) { + config.OnPublishingError = onPublishingError + } +} + +// WithBatchingProducerLogThrottle sets a throttle for logging from this producer. By default, a throttle shared between all instances of +// BatchingProducer is used, that allows for 10 events in 10 seconds. +func WithBatchingProducerLogThrottle(logThrottle actor.ShouldThrottle) BatchingProducerConfigOption { + return func(config *BatchingProducerConfig) { + config.LogThrottle = logThrottle + } +} + +// WithBatchingProducerPublisherIdleTimeout sets an optional idle timeout which will specify to the `IPublisher` how long it should wait before invoking clean +// up code to recover resources. +func WithBatchingProducerPublisherIdleTimeout(publisherIdleTimeout time.Duration) BatchingProducerConfigOption { + return func(config *BatchingProducerConfig) { + config.PublisherIdleTimeout = publisherIdleTimeout + } +} + +type PublishingErrorDecision struct { + Delay time.Duration +} + +// NewPublishingErrorDecision creates a new PublishingErrorDecision +func NewPublishingErrorDecision(delay time.Duration) *PublishingErrorDecision { + return &PublishingErrorDecision{Delay: delay} +} + +// RetryBatchAfter returns a new PublishingErrorDecision with the Delay set to the given duration +func RetryBatchAfter(delay time.Duration) *PublishingErrorDecision { + return NewPublishingErrorDecision(delay) +} + +// FailBatchAndStop causes the BatchingProducer to stop and fail the pending messages +var FailBatchAndStop = NewPublishingErrorDecision(0) + +// FailBatchAndContinue skips the current batch and proceeds to the next one. The delivery reports (tasks) related to that batch are still +// failed with the exception that triggered the error handling. +var FailBatchAndContinue = NewPublishingErrorDecision(0) + +// RetryBatchImmediately retries the current batch immediately +var RetryBatchImmediately = NewPublishingErrorDecision(0) diff --git a/cluster/pubsub_producer_test.go b/cluster/pubsub_producer_test.go new file mode 100644 index 0000000000000000000000000000000000000000..865c0f7572ad327e78f5bb68f1b3b019b3266b16 --- /dev/null +++ b/cluster/pubsub_producer_test.go @@ -0,0 +1,352 @@ +package cluster + +import ( + "context" + "testing" + "time" + + "github.com/stretchr/testify/suite" +) + +type PubSubBatchingProducerTestSuite struct { + suite.Suite + batchesSent []*PubSubBatch +} + +func (suite *PubSubBatchingProducerTestSuite) SetupTest() { + suite.batchesSent = make([]*PubSubBatch, 0) +} + +func (suite *PubSubBatchingProducerTestSuite) allSentNumbersShouldEqual(batchesSent []*PubSubBatch, nums ...int) { + allNumbers := make([]int, 0, len(nums)) + for _, batch := range batchesSent { + for _, envelope := range batch.Envelopes { + allNumbers = append(allNumbers, int(envelope.(*TestMessage).Number)) + } + } + suite.Assert().ElementsMatch(nums, allNumbers) +} + +func (suite *PubSubBatchingProducerTestSuite) iter(from, to int) []int { + nums := make([]int, 0, to-from) + for i := from; i < to; i++ { + nums = append(nums, i) + } + return nums +} + +func (suite *PubSubBatchingProducerTestSuite) record(batch *PubSubBatch) (*PublishResponse, error) { + b := &PubSubBatch{Envelopes: make([]interface{}, 0, len(batch.Envelopes))} + b.Envelopes = append(b.Envelopes, batch.Envelopes...) + + suite.batchesSent = append(suite.batchesSent, b) + return &PublishResponse{Status: PublishStatus_Ok}, nil +} + +func (suite *PubSubBatchingProducerTestSuite) wait(_ *PubSubBatch) (*PublishResponse, error) { + time.Sleep(time.Second * 1) + return &PublishResponse{Status: PublishStatus_Ok}, nil +} + +func (suite *PubSubBatchingProducerTestSuite) waitThenFail(_ *PubSubBatch) (*PublishResponse, error) { + time.Sleep(time.Millisecond * 500) + return &PublishResponse{Status: PublishStatus_Failed}, &testException{} +} + +func (suite *PubSubBatchingProducerTestSuite) fail(_ *PubSubBatch) (*PublishResponse, error) { + return &PublishResponse{Status: PublishStatus_Failed}, &testException{} +} + +func (suite *PubSubBatchingProducerTestSuite) failTimesThenSucceed(times int) func(*PubSubBatch) (*PublishResponse, error) { + count := 0 + return func(batch *PubSubBatch) (*PublishResponse, error) { + count++ + if count <= times { + return &PublishResponse{Status: PublishStatus_Failed}, &testException{} + } + return suite.record(batch) + } +} + +func (suite *PubSubBatchingProducerTestSuite) timeout() (*PublishResponse, error) { + return nil, nil +} + +func (suite *PubSubBatchingProducerTestSuite) TestProducerSendsMessagesInBatches() { + producer := NewBatchingProducer(newMockPublisher(suite.record), "topic", WithBatchingProducerBatchSize(10)) + defer producer.Dispose() + + infos := make([]*ProduceProcessInfo, 0, 10000) + for i := 0; i < 10000; i++ { + info, err := producer.Produce(context.Background(), &TestMessage{Number: int32(i)}) + suite.Assert().NoError(err) + infos = append(infos, info) + } + for _, info := range infos { + <-info.Finished + suite.Assert().Nil(info.Err) + } + + anyBatchesEnvelopesCountIsGreaterThanOne := false + for _, batch := range suite.batchesSent { + if len(batch.Envelopes) > 1 { + anyBatchesEnvelopesCountIsGreaterThanOne = true + break + } + } + suite.Assert().True(anyBatchesEnvelopesCountIsGreaterThanOne, "messages should be batched") + + allBatchesEnvelopeCountAreLessThanBatchSize := true + for _, batch := range suite.batchesSent { + if len(batch.Envelopes) > 10 { + allBatchesEnvelopeCountAreLessThanBatchSize = false + break + } + } + suite.Assert().True(allBatchesEnvelopeCountAreLessThanBatchSize, "batches should not exceed configured size") + + suite.allSentNumbersShouldEqual(suite.batchesSent, suite.iter(0, 10000)...) +} + +func (suite *PubSubBatchingProducerTestSuite) TestPublishingThroughStoppedProducerThrows() { + producer := NewBatchingProducer(newMockPublisher(suite.record), "topic", WithBatchingProducerBatchSize(10)) + producer.Dispose() + + _, err := producer.Produce(context.Background(), &TestMessage{Number: 1}) + suite.Assert().ErrorIs(err, &InvalidOperationException{Topic: "topic"}) +} + +func (suite *PubSubBatchingProducerTestSuite) TestAllPendingTasksCompleteWhenProducerIsStopped() { + provider := NewBatchingProducer(newMockPublisher(suite.wait), "topic", WithBatchingProducerBatchSize(5)) + + infoList := make([]*ProduceProcessInfo, 0, 100) + for i := 0; i < 100; i++ { + info, err := provider.Produce(context.Background(), &TestMessage{Number: int32(i)}) + suite.Assert().NoError(err) + infoList = append(infoList, info) + } + + provider.Dispose() + + for _, info := range infoList { + <-info.Finished + suite.Assert().Nil(info.Err) + } +} + +func (suite *PubSubBatchingProducerTestSuite) TestAllPendingTasksCompleteWhenProducerFails() { + producer := NewBatchingProducer(newMockPublisher(suite.waitThenFail), "topic", WithBatchingProducerBatchSize(5)) + defer producer.Dispose() + + infoList := make([]*ProduceProcessInfo, 0, 100) + for i := 0; i < 100; i++ { + info, err := producer.Produce(context.Background(), &TestMessage{Number: int32(i)}) + suite.Assert().NoError(err) + infoList = append(infoList, info) + } + + for _, info := range infoList { + <-info.Finished + suite.Assert().Error(info.Err) + } +} + +func (suite *PubSubBatchingProducerTestSuite) TestPublishingThroughFailedProducerThrows() { + producer := NewBatchingProducer(newMockPublisher(suite.fail), "topic", WithBatchingProducerBatchSize(10)) + defer producer.Dispose() + + info, err := producer.Produce(context.Background(), &TestMessage{Number: 1}) + suite.Assert().NoError(err) + <-info.Finished + suite.Assert().ErrorIs(info.Err, &testException{}) + + _, err = producer.Produce(context.Background(), &TestMessage{Number: 1}) + suite.Assert().ErrorIs(err, &InvalidOperationException{Topic: "topic"}) +} + +func (suite *PubSubBatchingProducerTestSuite) TestThrowsWhenQueueFull() { + producer := NewBatchingProducer(newMockPublisher(suite.record), "topic", WithBatchingProducerBatchSize(1), WithBatchingProducerMaxQueueSize(10)) + defer producer.Dispose() + + hasError := false + for i := 0; i < 20; i++ { + _, err := producer.Produce(context.Background(), &TestMessage{Number: int32(i)}) + if err != nil { + hasError = true + suite.Assert().ErrorIs(err, &ProducerQueueFullException{}) + } + } + suite.Assert().True(hasError) +} + +func (suite *PubSubBatchingProducerTestSuite) TestCanCancelPublishingAMessage() { + producer := NewBatchingProducer(newMockPublisher(suite.record), "topic", WithBatchingProducerBatchSize(1), WithBatchingProducerMaxQueueSize(10)) + defer producer.Dispose() + + messageWithoutCancellation := &TestMessage{Number: 1} + t1, err := producer.Produce(context.Background(), messageWithoutCancellation) + suite.Assert().NoError(err) + + ctx, cancel := context.WithCancel(context.Background()) + t2, err := producer.Produce(ctx, &TestMessage{Number: 2}) + cancel() + suite.Assert().NoError(err) + + <-t1.Finished + suite.Assert().NoError(t1.Err) + <-t2.Finished + suite.Assert().True(t2.IsCancelled()) + + suite.allSentNumbersShouldEqual(suite.batchesSent, 1) +} + +func (suite *PubSubBatchingProducerTestSuite) TestCanRetryOnPublishingError() { + retries := make([]int, 0, 10) + producer := NewBatchingProducer(newMockPublisher(suite.failTimesThenSucceed(3)), "topic", + WithBatchingProducerBatchSize(1), + WithBatchingProducerOnPublishingError(func(retry int, e error, batch *PubSubBatch) *PublishingErrorDecision { + retries = append(retries, retry) + return RetryBatchImmediately + })) + defer producer.Dispose() + + info, err := producer.Produce(context.Background(), &TestMessage{Number: 1}) + suite.Assert().NoError(err) + + <-info.Finished + suite.Assert().Equal([]int{1, 2, 3}, retries) +} + +func (suite *PubSubBatchingProducerTestSuite) TestCanSkipBatchOnPublishingError() { + producer := NewBatchingProducer(newMockPublisher(suite.failTimesThenSucceed(1)), "topic", + WithBatchingProducerBatchSize(1), + WithBatchingProducerOnPublishingError(func(retry int, e error, batch *PubSubBatch) *PublishingErrorDecision { + return FailBatchAndContinue + })) + defer producer.Dispose() + + t1, err := producer.Produce(context.Background(), &TestMessage{Number: 1}) + suite.Assert().NoError(err) + + t2, err := producer.Produce(context.Background(), &TestMessage{Number: 2}) + suite.Assert().NoError(err) + + <-t1.Finished + suite.Assert().ErrorIs(t1.Err, &testException{}) + <-t2.Finished + suite.Assert().NoError(t2.Err) +} + +func (suite *PubSubBatchingProducerTestSuite) TestCanStopProducerWhenRetryingInfinitely() { + producer := NewBatchingProducer(newMockPublisher(suite.fail), "topic", + WithBatchingProducerBatchSize(1), + WithBatchingProducerOnPublishingError(func(retry int, e error, batch *PubSubBatch) *PublishingErrorDecision { + return RetryBatchImmediately + })) + + t1, err := producer.Produce(context.Background(), &TestMessage{Number: 1}) + suite.Assert().NoError(err) + + time.Sleep(50 * time.Millisecond) + producer.Dispose() + suite.Assert().True(t1.IsCancelled()) +} + +func (suite *PubSubBatchingProducerTestSuite) TestIfMessageIsCancelledMeanwhileRetryingItIsNotPublished() { + publisher := newOptionalFailureMockPublisher(true) + producer := NewBatchingProducer(publisher, "topic", + WithBatchingProducerBatchSize(1), + WithBatchingProducerOnPublishingError(func(retry int, e error, batch *PubSubBatch) *PublishingErrorDecision { + return RetryBatchImmediately + })) + defer producer.Dispose() + ctx, cancel := context.WithCancel(context.Background()) + t1, err := producer.Produce(ctx, &TestMessage{Number: 1}) + suite.Assert().NoError(err) + + // give it a moment to spin + time.Sleep(50 * time.Millisecond) + + // cancel the message publish + cancel() + <-t1.Finished + suite.Assert().True(t1.IsCancelled()) + + suite.Assert().Len(publisher.sentBatches, 0) + publisher.shouldFail = false + t2, err := producer.Produce(context.Background(), &TestMessage{Number: 2}) + suite.Assert().NoError(err) + <-t2.Finished + suite.Assert().NoError(t2.Err) + + suite.allSentNumbersShouldEqual(publisher.sentBatches, 2) +} + +func (suite *PubSubBatchingProducerTestSuite) TestCanHandlePublishTimeouts() { +} + +// In order for 'go test' to run this suite, we need to create +// a normal test function and pass our suite to suite.Run +func TestPubSubBatchingTestSuite(t *testing.T) { + suite.Run(t, new(PubSubBatchingProducerTestSuite)) +} + +type mockPublisher struct { + publish func(*PubSubBatch) (*PublishResponse, error) +} + +func newMockPublisher(publish func(*PubSubBatch) (*PublishResponse, error)) *mockPublisher { + return &mockPublisher{publish: publish} +} + +func (m *mockPublisher) Initialize(_ context.Context, topic string, config PublisherConfig) (*Acknowledge, error) { + return &Acknowledge{}, nil +} + +func (m *mockPublisher) PublishBatch(_ context.Context, topic string, batch *PubSubBatch, opts ...GrainCallOption) (*PublishResponse, error) { + return m.publish(batch) +} + +func (m *mockPublisher) Publish(_ context.Context, topic string, message interface{}, opts ...GrainCallOption) (*PublishResponse, error) { + return m.publish(&PubSubBatch{Envelopes: []interface{}{message}}) +} + +type optionalFailureMockPublisher struct { + sentBatches []*PubSubBatch + shouldFail bool +} + +// newOptionalFailureMockPublisher creates a mock publisher that can be configured to fail or not +func newOptionalFailureMockPublisher(shouldFail bool) *optionalFailureMockPublisher { + return &optionalFailureMockPublisher{shouldFail: shouldFail} +} + +func (o *optionalFailureMockPublisher) Initialize(ctx context.Context, topic string, config PublisherConfig) (*Acknowledge, error) { + return &Acknowledge{}, nil +} + +func (o *optionalFailureMockPublisher) PublishBatch(ctx context.Context, topic string, batch *PubSubBatch, opts ...GrainCallOption) (*PublishResponse, error) { + if o.shouldFail { + return nil, &testException{} + } + copiedBatch := &PubSubBatch{Envelopes: make([]interface{}, len(batch.Envelopes))} + copy(copiedBatch.Envelopes, batch.Envelopes) + + o.sentBatches = append(o.sentBatches, copiedBatch) + return &PublishResponse{}, nil +} + +func (o *optionalFailureMockPublisher) Publish(ctx context.Context, topic string, message interface{}, opts ...GrainCallOption) (*PublishResponse, error) { + return o.PublishBatch(ctx, topic, &PubSubBatch{Envelopes: []interface{}{message}}, opts...) +} + +type testException struct{} + +func (t *testException) Error() string { + return "test exception" +} + +func (t *testException) Is(err error) bool { + _, ok := err.(*testException) + return ok +} diff --git a/cluster/pubsub_publisher.go b/cluster/pubsub_publisher.go new file mode 100644 index 0000000000000000000000000000000000000000..44e31a6c021ce8629b4c27b409382a43b3d2312e --- /dev/null +++ b/cluster/pubsub_publisher.go @@ -0,0 +1,67 @@ +package cluster + +import ( + "context" + "time" + + "google.golang.org/protobuf/types/known/durationpb" +) + +type PublisherConfig struct { + IdleTimeout time.Duration +} + +type Publisher interface { + // Initialize the internal mechanisms of this publisher. + Initialize(ctx context.Context, topic string, config PublisherConfig) (*Acknowledge, error) + + // PublishBatch publishes a batch of messages to the topic. + PublishBatch(ctx context.Context, topic string, batch *PubSubBatch, opts ...GrainCallOption) (*PublishResponse, error) + + // Publish publishes a single message to the topic. + Publish(ctx context.Context, topic string, message interface{}, opts ...GrainCallOption) (*PublishResponse, error) +} + +type defaultPublisher struct { + cluster *Cluster +} + +func NewPublisher(cluster *Cluster) Publisher { + return &defaultPublisher{ + cluster: cluster, + } +} + +func (p *defaultPublisher) Initialize(ctx context.Context, topic string, config PublisherConfig) (*Acknowledge, error) { + select { + case <-ctx.Done(): + return nil, ctx.Err() + default: + res, err := p.cluster.Call(topic, TopicActorKind, &Initialize{ + IdleTimeout: durationpb.New(config.IdleTimeout), + }) + if err != nil { + return nil, err + } + return res.(*Acknowledge), err + } +} + +func (p *defaultPublisher) PublishBatch(ctx context.Context, topic string, batch *PubSubBatch, opts ...GrainCallOption) (*PublishResponse, error) { + select { + case <-ctx.Done(): + return nil, ctx.Err() + default: + res, err := p.cluster.Call(topic, TopicActorKind, batch, opts...) + if err != nil { + return nil, err + } + return res.(*PublishResponse), err + } +} + +func (p *defaultPublisher) Publish(ctx context.Context, topic string, message interface{}, opts ...GrainCallOption) (*PublishResponse, error) { + return p.PublishBatch(ctx, topic, &PubSubBatch{ + Envelopes: []interface{}{message}, + }, opts...) +} diff --git a/cluster/pubsub_test.pb.go b/cluster/pubsub_test.pb.go new file mode 100644 index 0000000000000000000000000000000000000000..62f0ac87451d773e23d35acf33178f8b0b4167b0 --- /dev/null +++ b/cluster/pubsub_test.pb.go @@ -0,0 +1,144 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.28.1 +// protoc v3.21.9 +// source: pubsub_test.proto + +package cluster + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type TestMessage struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Number int32 `protobuf:"varint,1,opt,name=number,proto3" json:"number,omitempty"` +} + +func (x *TestMessage) Reset() { + *x = TestMessage{} + if protoimpl.UnsafeEnabled { + mi := &file_pubsub_test_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *TestMessage) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TestMessage) ProtoMessage() {} + +func (x *TestMessage) ProtoReflect() protoreflect.Message { + mi := &file_pubsub_test_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TestMessage.ProtoReflect.Descriptor instead. +func (*TestMessage) Descriptor() ([]byte, []int) { + return file_pubsub_test_proto_rawDescGZIP(), []int{0} +} + +func (x *TestMessage) GetNumber() int32 { + if x != nil { + return x.Number + } + return 0 +} + +var File_pubsub_test_proto protoreflect.FileDescriptor + +var file_pubsub_test_proto_rawDesc = []byte{ + 0x0a, 0x11, 0x70, 0x75, 0x62, 0x73, 0x75, 0x62, 0x5f, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x12, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x22, 0x25, 0x0a, 0x0b, + 0x54, 0x65, 0x73, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6e, + 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x6e, 0x75, 0x6d, + 0x62, 0x65, 0x72, 0x42, 0x2c, 0x5a, 0x2a, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x61, 0x73, 0x79, 0x6e, 0x6b, 0x72, 0x6f, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x2d, 0x67, 0x6f, 0x2f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, + 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_pubsub_test_proto_rawDescOnce sync.Once + file_pubsub_test_proto_rawDescData = file_pubsub_test_proto_rawDesc +) + +func file_pubsub_test_proto_rawDescGZIP() []byte { + file_pubsub_test_proto_rawDescOnce.Do(func() { + file_pubsub_test_proto_rawDescData = protoimpl.X.CompressGZIP(file_pubsub_test_proto_rawDescData) + }) + return file_pubsub_test_proto_rawDescData +} + +var file_pubsub_test_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_pubsub_test_proto_goTypes = []interface{}{ + (*TestMessage)(nil), // 0: cluster.TestMessage +} +var file_pubsub_test_proto_depIdxs = []int32{ + 0, // [0:0] is the sub-list for method output_type + 0, // [0:0] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_pubsub_test_proto_init() } +func file_pubsub_test_proto_init() { + if File_pubsub_test_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_pubsub_test_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TestMessage); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_pubsub_test_proto_rawDesc, + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_pubsub_test_proto_goTypes, + DependencyIndexes: file_pubsub_test_proto_depIdxs, + MessageInfos: file_pubsub_test_proto_msgTypes, + }.Build() + File_pubsub_test_proto = out.File + file_pubsub_test_proto_rawDesc = nil + file_pubsub_test_proto_goTypes = nil + file_pubsub_test_proto_depIdxs = nil +} diff --git a/cluster/pubsub_test.proto b/cluster/pubsub_test.proto new file mode 100644 index 0000000000000000000000000000000000000000..76ed34c6cf146d1d602760871b6a426ec40bacba --- /dev/null +++ b/cluster/pubsub_test.proto @@ -0,0 +1,7 @@ +syntax = "proto3"; +package cluster; +option go_package = "/gitee.com/simplexyz/simpleactor-go/cluster"; + +message TestMessage { + int32 number = 1; +} diff --git a/cluster/pubsub_topic.go b/cluster/pubsub_topic.go new file mode 100644 index 0000000000000000000000000000000000000000..bed2ddfb5c4d03e54162ebb4a1f03e17e75c64d8 --- /dev/null +++ b/cluster/pubsub_topic.go @@ -0,0 +1,360 @@ +package cluster + +import ( + "context" + "strings" + "time" + + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/eventstream" + "gitee.com/simplexyz/simpleactor-go/log" + "golang.org/x/exp/maps" +) + +const TopicActorKind = "prototopic" + +var topicLogThrottle = actor.NewThrottle(10, time.Second, func(count int32) { + plog.Info("[TopicActor] Throttled logs", log.Int("count", int(count))) +}) + +type TopicActor struct { + topic string + subscribers map[subscribeIdentityStruct]*SubscriberIdentity + subscriptionStore KeyValueStore[*Subscribers] + topologySubscription *eventstream.Subscription +} + +func NewTopicActor(store KeyValueStore[*Subscribers]) *TopicActor { + return &TopicActor{ + subscriptionStore: store, + subscribers: make(map[subscribeIdentityStruct]*SubscriberIdentity), + } +} + +func (t *TopicActor) Receive(c actor.Context) { + switch msg := c.Message().(type) { + case *actor.Started: + t.onStarted(c) + case *actor.Stopping: + t.onStopping(c) + case *actor.ReceiveTimeout: + t.onReceiveTimeout(c) + case *Initialize: + t.onInitialize(c, msg) + case *SubscribeRequest: + t.onSubscribe(c, msg) + case *UnsubscribeRequest: + t.onUnsubscribe(c, msg) + case *PubSubBatch: + t.onPubSubBatch(c, msg) + case *NotifyAboutFailingSubscribersRequest: + t.onNotifyAboutFailingSubscribers(c, msg) + case *ClusterTopology: + t.onClusterTopologyChanged(c, msg) + } +} + +func (t *TopicActor) onStarted(c actor.Context) { + t.topic = GetClusterIdentity(c).Identity + t.topologySubscription = c.ActorSystem().EventStream.Subscribe(func(evt interface{}) { + if clusterTopology, ok := evt.(*ClusterTopology); ok { + c.Send(c.Self(), clusterTopology) + } + }) + + sub := t.loadSubscriptions(t.topic) + if sub.Subscribers != nil { + for _, subscriber := range sub.Subscribers { + t.subscribers[newSubscribeIdentityStruct(subscriber)] = subscriber + } + } + t.unsubscribeSubscribersOnMembersThatLeft(c) + + plog.Debug("Topic started", log.String("topic", t.topic)) +} + +func (t *TopicActor) onStopping(c actor.Context) { + if t.topologySubscription != nil { + c.ActorSystem().EventStream.Unsubscribe(t.topologySubscription) + t.topologySubscription = nil + } +} + +func (t *TopicActor) onReceiveTimeout(c actor.Context) { + c.Stop(c.Self()) +} + +func (t *TopicActor) onInitialize(c actor.Context, msg *Initialize) { + if msg.IdleTimeout != nil { + duration := msg.IdleTimeout.AsDuration() + if duration > 0 { + c.SetReceiveTimeout(duration) + } + } + c.Respond(&Acknowledge{}) +} + +type pidAndSubscriber struct { + pid *actor.PID + subscriber *SubscriberIdentity +} + +// onPubSubBatch handles a PubSubBatch message, sends the message to all subscribers +func (t *TopicActor) onPubSubBatch(c actor.Context, batch *PubSubBatch) { + // map subscribers to map[address][](pid, subscriber) + members := make(map[string][]pidAndSubscriber) + for _, identity := range t.subscribers { + pid := t.getPID(c, identity) + if pid != nil { + members[pid.Address] = append(members[pid.Address], pidAndSubscriber{pid: pid, subscriber: identity}) + } + } + + // send message to each member + for address, member := range members { + subscribersOnMember := t.getSubscribersForAddress(member) + deliveryMessage := &DeliverBatchRequest{ + Subscribers: subscribersOnMember, + PubSubBatch: batch, + Topic: t.topic, + } + deliveryPid := actor.NewPID(address, PubSubDeliveryName) + c.Send(deliveryPid, deliveryMessage) + } + c.Respond(&PublishResponse{}) +} + +// getSubscribersForAddress returns the subscribers for the given member list +func (t *TopicActor) getSubscribersForAddress(members []pidAndSubscriber) *Subscribers { + subscribers := make([]*SubscriberIdentity, len(members)) + for i, member := range members { + subscribers[i] = member.subscriber + } + return &Subscribers{Subscribers: subscribers} +} + +// getPID returns the PID of the subscriber +func (t *TopicActor) getPID(c actor.Context, subscriber *SubscriberIdentity) *actor.PID { + if pid := subscriber.GetPid(); pid != nil { + return pid + } + + return t.getClusterIdentityPid(c, subscriber.GetClusterIdentity()) +} + +// getClusterIdentityPid returns the PID of the clusterIdentity actor +func (t *TopicActor) getClusterIdentityPid(c actor.Context, identity *ClusterIdentity) *actor.PID { + if identity == nil { + return nil + } + + return GetCluster(c.ActorSystem()).Get(identity.Identity, identity.Kind) +} + +// onNotifyAboutFailingSubscribers handles a NotifyAboutFailingSubscribersRequest message +func (t *TopicActor) onNotifyAboutFailingSubscribers(c actor.Context, msg *NotifyAboutFailingSubscribersRequest) { + t.unsubscribeUnreachablePidSubscribers(c, msg.InvalidDeliveries) + t.logDeliveryErrors(msg.InvalidDeliveries) + c.Respond(&NotifyAboutFailingSubscribersResponse{}) +} + +// logDeliveryErrors logs the delivery errors in one log line +func (t *TopicActor) logDeliveryErrors(reports []*SubscriberDeliveryReport) { + if len(reports) > 0 || topicLogThrottle() == actor.Open { + subscribers := make([]string, len(reports)) + for i, report := range reports { + subscribers[i] = report.Subscriber.String() + } + plog.Error("Topic following subscribers could not process the batch", log.String("topic", t.topic), log.String("subscribers", strings.Join(subscribers, ","))) + } +} + +// unsubscribeUnreachablePidSubscribers deletes all subscribers that have a PID that is unreachable +func (t *TopicActor) unsubscribeUnreachablePidSubscribers(_ actor.Context, allInvalidDeliveryReports []*SubscriberDeliveryReport) { + subscribers := make([]subscribeIdentityStruct, 0, len(allInvalidDeliveryReports)) + for _, r := range allInvalidDeliveryReports { + if r.Subscriber.GetPid() != nil && r.Status == DeliveryStatus_SubscriberNoLongerReachable { + subscribers = append(subscribers, newSubscribeIdentityStruct(r.Subscriber)) + } + } + t.removeSubscribers(subscribers) +} + +// onClusterTopologyChanged handles a ClusterTopology message +func (t *TopicActor) onClusterTopologyChanged(_ actor.Context, msg *ClusterTopology) { + if len(msg.Left) > 0 { + addressMap := make(map[string]struct{}) + for _, member := range msg.Left { + addressMap[member.Address()] = struct{}{} + } + + subscribersThatLeft := make([]subscribeIdentityStruct, 0, len(msg.Left)) + + for identityStruct, identity := range t.subscribers { + if pid := identity.GetPid(); pid != nil { + if _, ok := addressMap[pid.Address]; ok { + subscribersThatLeft = append(subscribersThatLeft, identityStruct) + } + } + } + t.removeSubscribers(subscribersThatLeft) + } +} + +// unsubscribeSubscribersOnMembersThatLeft removes subscribers that are on members that left the clusterIdentity +func (t *TopicActor) unsubscribeSubscribersOnMembersThatLeft(c actor.Context) { + members := GetCluster(c.ActorSystem()).MemberList.Members() + activeMemberAddresses := make(map[string]struct{}) + for _, member := range members.Members() { + activeMemberAddresses[member.Address()] = struct{}{} + } + + subscribersThatLeft := make([]subscribeIdentityStruct, 0) + for s := range t.subscribers { + if s.isPID { + if _, ok := activeMemberAddresses[s.pid.address]; !ok { + subscribersThatLeft = append(subscribersThatLeft, s) + } + } + } + t.removeSubscribers(subscribersThatLeft) +} + +// removeSubscribers remove subscribers from the topic +func (t *TopicActor) removeSubscribers(subscribersThatLeft []subscribeIdentityStruct) { + if len(subscribersThatLeft) > 0 { + for _, subscriber := range subscribersThatLeft { + delete(t.subscribers, subscriber) + } + if topicLogThrottle() == actor.Open { + plog.Warn("Topic removed subscribers, because they are dead or they are on members that left the clusterIdentity:", log.String("topic", t.topic), log.Object("subscribers", subscribersThatLeft)) + } + t.saveSubscriptionsInTopicActor() + } +} + +// loadSubscriptions loads the subscriptions for the topic from the subscription store +func (t *TopicActor) loadSubscriptions(topic string) *Subscribers { + // TODO: cancellation logic config? + state, err := t.subscriptionStore.Get(context.Background(), topic) + if err != nil { + if topicLogThrottle() == actor.Open { + plog.Error("Error when loading subscriptions", log.String("topic", topic), log.Error(err)) + } + return &Subscribers{} + } + if state == nil { + return &Subscribers{} + } + plog.Debug("Loaded subscriptions for topic", log.String("topic", topic), log.Object("subscriptions", state)) + return state +} + +// saveSubscriptionsInTopicActor saves the TopicActor.subscribers for the TopicActor.topic to the subscription store +func (t *TopicActor) saveSubscriptionsInTopicActor() { + t.saveSubscriptions(t.topic, &Subscribers{Subscribers: maps.Values(t.subscribers)}) +} + +// saveSubscriptions saves the subscribers for the topic to the subscription store +func (t *TopicActor) saveSubscriptions(topic string, subscribers *Subscribers) { + // TODO: cancellation logic config? + plog.Debug("Saving subscriptions for topic", log.String("topic", topic), log.Object("subscriptions", subscribers)) + err := t.subscriptionStore.Set(context.Background(), topic, subscribers) + if err != nil && topicLogThrottle() == actor.Open { + plog.Error("Error when saving subscriptions", log.String("topic", topic), log.Error(err)) + } +} + +func (t *TopicActor) onUnsubscribe(c actor.Context, msg *UnsubscribeRequest) { + delete(t.subscribers, newSubscribeIdentityStruct(msg.Subscriber)) + t.saveSubscriptionsInTopicActor() + c.Respond(&UnsubscribeResponse{}) +} + +func (t *TopicActor) onSubscribe(c actor.Context, msg *SubscribeRequest) { + t.subscribers[newSubscribeIdentityStruct(msg.Subscriber)] = msg.Subscriber + plog.Debug("Topic subscribed", log.String("topic", t.topic), log.Object("subscriber", msg.Subscriber)) + t.saveSubscriptionsInTopicActor() + c.Respond(&SubscribeResponse{}) +} + +// pidStruct is a struct that represents a PID +// It is used to implement the comparison interface +type pidStruct struct { + address string + id string + requestId uint32 +} + +// newPIDStruct creates a new pidStruct from a *actor.PID +func newPidStruct(pid *actor.PID) pidStruct { + return pidStruct{ + address: pid.Address, + id: pid.Id, + requestId: pid.RequestId, + } +} + +// toPID converts a pidStruct to a *actor.PID +func (p pidStruct) toPID() *actor.PID { + return &actor.PID{ + Address: p.address, + Id: p.id, + RequestId: p.requestId, + } +} + +type clusterIdentityStruct struct { + identity string + kind string +} + +// newClusterIdentityStruct creates a new clusterIdentityStruct from a *ClusterIdentity +func newClusterIdentityStruct(clusterIdentity *ClusterIdentity) clusterIdentityStruct { + return clusterIdentityStruct{ + identity: clusterIdentity.Identity, + kind: clusterIdentity.Kind, + } +} + +// toClusterIdentity converts a clusterIdentityStruct to a *ClusterIdentity +func (c clusterIdentityStruct) toClusterIdentity() *ClusterIdentity { + return &ClusterIdentity{ + Identity: c.identity, + Kind: c.kind, + } +} + +// subscriberIdentityStruct is a struct that represents a SubscriberIdentity +// It is used to implement the comparison interface +type subscribeIdentityStruct struct { + isPID bool + pid pidStruct + clusterIdentity clusterIdentityStruct +} + +// newSubscriberIdentityStruct creates a new subscriberIdentityStruct from a *SubscriberIdentity +func newSubscribeIdentityStruct(subscriberIdentity *SubscriberIdentity) subscribeIdentityStruct { + if subscriberIdentity.GetPid() != nil { + return subscribeIdentityStruct{ + isPID: true, + pid: newPidStruct(subscriberIdentity.GetPid()), + } + } + return subscribeIdentityStruct{ + isPID: false, + clusterIdentity: newClusterIdentityStruct(subscriberIdentity.GetClusterIdentity()), + } +} + +// toSubscriberIdentity converts a subscribeIdentityStruct to a *SubscriberIdentity +func (s subscribeIdentityStruct) toSubscriberIdentity() *SubscriberIdentity { + if s.isPID { + return &SubscriberIdentity{ + Identity: &SubscriberIdentity_Pid{Pid: s.pid.toPID()}, + } + } + return &SubscriberIdentity{ + Identity: &SubscriberIdentity_ClusterIdentity{ClusterIdentity: s.clusterIdentity.toClusterIdentity()}, + } +} diff --git a/cluster/rendezvous.go b/cluster/rendezvous.go new file mode 100644 index 0000000000000000000000000000000000000000..be58be8b6ba18ec31a1b433a59f442af86889528 --- /dev/null +++ b/cluster/rendezvous.go @@ -0,0 +1,112 @@ +package cluster + +// Rendezvous.go +// A revised FNV1A32 version of +// https://github.com/tysonmote/rendezvous/blob/master/rendezvous.go + +import ( + "hash" + "hash/fnv" + "strings" + "sync" +) + +type memberData struct { + member *Member + hashBytes []byte +} +type Rendezvous struct { + mutex sync.RWMutex + hasher hash.Hash32 + hasherLock sync.Mutex + members []*memberData +} + +func NewRendezvous() *Rendezvous { + return &Rendezvous{ + hasher: fnv.New32a(), + members: make([]*memberData, 0), + } +} + +func (r *Rendezvous) GetByClusterIdentity(ci *ClusterIdentity) string { + r.mutex.RLock() + defer r.mutex.RUnlock() + + identity := ci.Identity + m := r.memberDataByKind(ci.Kind) + + l := len(m) + + if l == 0 { + return "" + } + + if l == 1 { + return m[0].member.Address() + } + + keyBytes := []byte(identity) + + var maxScore uint32 + var maxMember *memberData + var score uint32 + + for _, node := range m { + score = r.hash(node.hashBytes, keyBytes) + if score > maxScore { + maxScore = score + maxMember = node + } + } + + if maxMember == nil { + return "" + } + return maxMember.member.Address() +} + +func (r *Rendezvous) GetByIdentity(identity string) string { + parts := strings.SplitN(identity, "/", 2) + + return r.GetByClusterIdentity(&ClusterIdentity{ + Kind: parts[0], + Identity: parts[1], + }) +} + +func (r *Rendezvous) memberDataByKind(kind string) []*memberData { + m := make([]*memberData, 0) + for _, md := range r.members { + if md.member.HasKind(kind) { + m = append(m, md) + } + } + return m +} + +func (r *Rendezvous) UpdateMembers(members Members) { + r.mutex.Lock() + defer r.mutex.Unlock() + + tmp := members.ToSet() + r.members = make([]*memberData, 0) + + for _, m := range tmp.Members() { + keyBytes := []byte(m.Address()) // TODO: should be utf8 to match .net + r.members = append(r.members, &memberData{ + member: m, + hashBytes: keyBytes, + }) + } +} + +func (r *Rendezvous) hash(node, key []byte) uint32 { + r.hasherLock.Lock() + defer r.hasherLock.Unlock() + + r.hasher.Reset() + r.hasher.Write(key) + r.hasher.Write(node) + return r.hasher.Sum32() +} diff --git a/cluster/rendezvous_test.go b/cluster/rendezvous_test.go new file mode 100644 index 0000000000000000000000000000000000000000..2b7ee04616eb4d8678f161dbe25b26350f2b17e5 --- /dev/null +++ b/cluster/rendezvous_test.go @@ -0,0 +1,32 @@ +package cluster + +import ( + "fmt" + "runtime" + "testing" + + "gitee.com/simplexyz/simpleactor-go/log" +) + +func Benchmark_Rendezvous_Get(b *testing.B) { + SetLogLevel(log.ErrorLevel) + for _, v := range []int{1, 2, 3, 5, 10, 100, 1000, 2000} { + members := newMembersForTest(v) + ms := newDefaultMemberStrategy(nil, "kind").(*simpleMemberStrategy) + for _, member := range members { + ms.AddMember(member) + } + obj := NewRendezvous() + obj.UpdateMembers(members) + testName := fmt.Sprintf("member*%d", v) + runtime.GC() + b.Run(testName, func(b *testing.B) { + for i := 0; i < b.N; i++ { + address := obj.GetByIdentity("kind/0123456789abcdefghijklmnopqrstuvwxyz") + if address == "" { + b.Fatalf("empty address res=%d", len(members)) + } + } + }) + } +} diff --git a/cluster/round_robin.go b/cluster/round_robin.go new file mode 100644 index 0000000000000000000000000000000000000000..05093b06c543f0b244a274a2a5d78587fdf327b3 --- /dev/null +++ b/cluster/round_robin.go @@ -0,0 +1,25 @@ +package cluster + +import "sync/atomic" + +type SimpleRoundRobin struct { + val int32 + m MemberStrategy +} + +func NewSimpleRoundRobin(memberStrategy MemberStrategy) *SimpleRoundRobin { + return &SimpleRoundRobin{m: memberStrategy} +} + +func (r *SimpleRoundRobin) GetByRoundRobin() string { + members := r.m.GetAllMembers() + l := len(members) + if l == 0 { + return "" + } + if l == 1 { + return members[0].Address() + } + nv := atomic.AddInt32(&r.val, 1) + return members[int(nv)%l].Address() +} diff --git a/cluster/types.go b/cluster/types.go new file mode 100644 index 0000000000000000000000000000000000000000..ebc370b0b2168983d158b2983d817e72c46a9131 --- /dev/null +++ b/cluster/types.go @@ -0,0 +1,5 @@ +package cluster + +type GossipMemberStates = map[string]*GossipMemberState + +type GossipKeyValues = map[string]*GossipKeyValue diff --git a/ctxext/extensions.go b/ctxext/extensions.go new file mode 100644 index 0000000000000000000000000000000000000000..37725e191209a0adb7f9cd6af7c8514057eefc13 --- /dev/null +++ b/ctxext/extensions.go @@ -0,0 +1,42 @@ +package ctxext + +import "sync/atomic" + +type ContextExtensionID int32 + +var currentContextExtensionID int32 + +type ContextExtension interface { + ExtensionID() ContextExtensionID +} + +type ContextExtensions struct { + extensions []ContextExtension +} + +func NewContextExtensions() *ContextExtensions { + ex := &ContextExtensions{ + extensions: make([]ContextExtension, 3), + } + + return ex +} + +func NextContextExtensionID() ContextExtensionID { + id := atomic.AddInt32(¤tContextExtensionID, 1) + return ContextExtensionID(id) +} + +func (ex *ContextExtensions) Get(id ContextExtensionID) ContextExtension { + return ex.extensions[id] +} + +func (ex *ContextExtensions) Set(extension ContextExtension) { + id := int32(extension.ExtensionID()) + if id >= int32(len(ex.extensions)) { + newExtensions := make([]ContextExtension, id*2) + copy(newExtensions, ex.extensions) + ex.extensions = newExtensions + } + ex.extensions[id] = extension +} diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..9aaf3fe0294dec81d0e202263a9581b087ef63c0 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,39 @@ +# docker-compose up -d +# @see https://docs.docker.com/compose/compose-file/ +version: '3.4' +services: + consul: + container_name: dev-consul + image: consul:1.10 + restart: unless-stopped + ports: + - 8500:8500 + - 8600:8600/udp + command: agent -server -ui -node=server-1 -bootstrap-expect=1 -client=0.0.0.0 + + + etcd: + container_name: dev-etcd + image: quay.io/coreos/etcd:v3.5.0 + restart: unless-stopped + ports: + - 2379:2379 + - 2380:2380 + command: > + /usr/local/bin/etcd + --name node1 + --data-dir=/etcd-data + --listen-client-urls http://0.0.0.0:2379 + --listen-peer-urls http://0.0.0.0:2380 + --advertise-client-urls http://127.0.0.1:2379 + --initial-advertise-peer-urls http://127.0.0.1:2380 + --initial-cluster node1=http://127.0.0.1:2380 + --auto-compaction-mode=periodic + --auto-compaction-retention=30m + + zookeeper: + container_name: dev-zookeeper + image: zookeeper:3.7 + restart: unless-stopped + ports: + - 8000:2181 diff --git a/eventstream/doc.go b/eventstream/doc.go new file mode 100644 index 0000000000000000000000000000000000000000..d28e206faa699253d392ccee0633ba98debf32c5 --- /dev/null +++ b/eventstream/doc.go @@ -0,0 +1,4 @@ +/* +Package eventstream implements a publisher / subscriber. +*/ +package eventstream diff --git a/eventstream/eventstream.go b/eventstream/eventstream.go new file mode 100644 index 0000000000000000000000000000000000000000..8fca317ae82fc518b73acad93e379c16cc647060 --- /dev/null +++ b/eventstream/eventstream.go @@ -0,0 +1,145 @@ +package eventstream + +import ( + "sync" + "sync/atomic" +) + +// Handler defines a callback function that must be pass when subscribing. +type Handler func(interface{}) + +// Predicate is a function used to filter messages before being forwarded to a subscriber +type Predicate func(evt interface{}) bool + +type EventStream struct { + sync.RWMutex + + // slice containing our subscriptions + subscriptions []*Subscription + + // Atomically maintained elements counter + counter int32 +} + +// Create a new EventStream value and returns it back. +func NewEventStream() *EventStream { + es := &EventStream{ + subscriptions: []*Subscription{}, + } + + return es +} + +// Subscribe the given handler to the EventStream +func (es *EventStream) Subscribe(handler Handler) *Subscription { + sub := &Subscription{ + handler: handler, + active: 1, + } + + es.Lock() + defer es.Unlock() + + sub.id = es.counter + es.counter++ + es.subscriptions = append(es.subscriptions, sub) + + return sub +} + +// SubscribeWithPredicate creates a new Subscription value and sets a predicate to filter messages passed to +// the subscriber, it returns a pointer to the Subscription value +func (es *EventStream) SubscribeWithPredicate(handler Handler, p Predicate) *Subscription { + sub := es.Subscribe(handler) + sub.p = p + + return sub +} + +// Unsubscribes the given subscription from the EventStream +func (es *EventStream) Unsubscribe(sub *Subscription) { + if sub == nil { + return + } + + if sub.IsActive() { + es.Lock() + defer es.Unlock() + + if sub.Deactivate() { + if es.counter == 0 { + es.subscriptions = nil + + return + } + + l := es.counter - 1 + es.subscriptions[sub.id] = es.subscriptions[l] + es.subscriptions[sub.id].id = sub.id + es.subscriptions[l] = nil + es.subscriptions = es.subscriptions[:l] + es.counter-- + + if es.counter == 0 { + es.subscriptions = nil + } + } + } +} + +// Publishes the given event to all the subscribers in the stream +func (es *EventStream) Publish(evt interface{}) { + subs := make([]*Subscription, 0, es.Length()) + es.RLock() + for _, sub := range es.subscriptions { + if sub.IsActive() { + subs = append(subs, sub) + } + } + es.RUnlock() + + for _, sub := range subs { + // there is a subscription predicate and it didn't pass, return + if sub.p != nil && !sub.p(evt) { + continue + } + + // finally here, lets execute our handler + sub.handler(evt) + } +} + +// Returns an integer that represents the current number of subscribers to the stream +func (es *EventStream) Length() int32 { + es.RLock() + defer es.RUnlock() + return es.counter +} + +// Subscription is returned from the Subscribe function. +// +// This value and can be passed to Unsubscribe when the observer is no longer interested in receiving messages +type Subscription struct { + id int32 + handler Handler + p Predicate + active uint32 +} + +// Activates the Subscription setting its active flag as 1, if the subscription +// was already active it returns false, true otherwise +func (s *Subscription) Activate() bool { + return atomic.CompareAndSwapUint32(&s.active, 0, 1) +} + +// Deactivates the Subscription setting its active flag as 0, if the subscription +// was already inactive it returns false, true otherwise +func (s *Subscription) Deactivate() bool { + return atomic.CompareAndSwapUint32(&s.active, 1, 0) +} + +// Returns true if the active flag of the Subscription is set as 1 +// otherwise it returns false +func (s *Subscription) IsActive() bool { + return atomic.LoadUint32(&s.active) == 1 +} diff --git a/eventstream/eventstream_example_subscribe_test.go b/eventstream/eventstream_example_subscribe_test.go new file mode 100644 index 0000000000000000000000000000000000000000..d74ba5a3b5bfa692e0756ac0445986a5630b723c --- /dev/null +++ b/eventstream/eventstream_example_subscribe_test.go @@ -0,0 +1,30 @@ +package eventstream_test + +import ( + "fmt" + + "gitee.com/simplexyz/simpleactor-go/eventstream" +) + +// Subscribe subscribes to events +func ExampleEventStream_Subscribe() { + es := eventstream.NewEventStream() + handler := func(event interface{}) { + fmt.Println(event) + } + + // only allow strings + predicate := func(event interface{}) bool { + _, ok := event.(string) + return ok + } + + sub := es.SubscribeWithPredicate(handler, predicate) + + es.Publish("Hello World") + es.Publish(1) + + es.Unsubscribe(sub) + + // Output: Hello World +} diff --git a/eventstream/eventstream_test.go b/eventstream/eventstream_test.go new file mode 100644 index 0000000000000000000000000000000000000000..ee74fcb8ef70dc2ce86d6b5727267f87aef2b467 --- /dev/null +++ b/eventstream/eventstream_test.go @@ -0,0 +1,101 @@ +package eventstream_test + +import ( + "testing" + + "gitee.com/simplexyz/simpleactor-go/eventstream" + "github.com/stretchr/testify/assert" +) + +func TestEventStream_Subscribe(t *testing.T) { + es := &eventstream.EventStream{} + s := es.Subscribe(func(interface{}) {}) + assert.NotNil(t, s) + assert.Equal(t, es.Length(), int32(1)) +} + +func TestEventStream_Unsubscribe(t *testing.T) { + es := &eventstream.EventStream{} + var c1, c2 int + + s1 := es.Subscribe(func(interface{}) { c1++ }) + s2 := es.Subscribe(func(interface{}) { c2++ }) + assert.Equal(t, es.Length(), int32(2)) + + es.Unsubscribe(s2) + assert.Equal(t, es.Length(), int32(1)) + + es.Publish(1) + assert.Equal(t, 1, c1) + + es.Unsubscribe(s1) + assert.Equal(t, es.Length(), int32(0)) + + es.Publish(1) + assert.Equal(t, 1, c1) + assert.Equal(t, 0, c2) +} + +func TestEventStream_Publish(t *testing.T) { + es := &eventstream.EventStream{} + + var v int + es.Subscribe(func(m interface{}) { v = m.(int) }) + + es.Publish(1) + assert.Equal(t, 1, v) + + es.Publish(100) + assert.Equal(t, 100, v) +} + +func TestEventStream_Subscribe_WithPredicate_IsCalled(t *testing.T) { + called := false + es := &eventstream.EventStream{} + es.SubscribeWithPredicate( + func(interface{}) { called = true }, + func(m interface{}) bool { return true }, + ) + es.Publish("") + + assert.True(t, called) +} + +func TestEventStream_Subscribe_WithPredicate_IsNotCalled(t *testing.T) { + called := false + es := &eventstream.EventStream{} + es.SubscribeWithPredicate( + func(interface{}) { called = true }, + func(m interface{}) bool { return false }, + ) + es.Publish("") + + assert.False(t, called) +} + +type Event struct { + i int +} + +func BenchmarkEventStream(b *testing.B) { + es := eventstream.NewEventStream() + subs := make([]*eventstream.Subscription, 10) + for i := 0; i < b.N; i++ { + for j := 0; j < 10; j++ { + sub := es.Subscribe(func(evt interface{}) { + if e := evt.(*Event); e.i != i { + b.Fatalf("expected i to be %d but its value is %d", i, e.i) + } + }) + subs[j] = sub + } + + es.Publish(&Event{i: i}) + for j := range subs { + es.Unsubscribe(subs[j]) + if subs[j].IsActive() { + b.Fatal("subscription should not be active") + } + } + } +} diff --git a/extensions/extensions.go b/extensions/extensions.go new file mode 100644 index 0000000000000000000000000000000000000000..ca3eb893f13414445cb5db7b95e6f5f9b5e99dea --- /dev/null +++ b/extensions/extensions.go @@ -0,0 +1,38 @@ +package extensions + +import "sync/atomic" + +type ExtensionID int32 + +var currentID int32 //nolint:gochecknoglobals + +type Extension interface { + ExtensionID() ExtensionID +} + +type Extensions struct { + extensions []Extension +} + +func NewExtensions() *Extensions { + ex := &Extensions{ + extensions: make([]Extension, 100), + } + + return ex +} + +func NextExtensionID() ExtensionID { + id := atomic.AddInt32(¤tID, 1) + + return ExtensionID(id) +} + +func (ex *Extensions) Get(id ExtensionID) Extension { + return ex.extensions[id] +} + +func (ex *Extensions) Register(extension Extension) { + id := extension.ExtensionID() + ex.extensions[id] = extension +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000000000000000000000000000000000000..1891f2fce45e5c7cbd674e75384ca0fa64827fce --- /dev/null +++ b/go.mod @@ -0,0 +1,117 @@ +module gitee.com/simplexyz/simpleactor-go + +go 1.21 + +require ( + github.com/Workiva/go-datastructures v1.1.1 + github.com/asynkron/gofun v0.0.0-20220329210725-34fed760f4c2 + github.com/couchbase/gocb v1.6.7 + github.com/emirpasic/gods v1.18.1 + github.com/gogo/protobuf v1.3.2 + github.com/google/uuid v1.3.0 + github.com/hashicorp/consul/api v1.18.0 + github.com/opentracing/opentracing-go v1.2.0 + github.com/orcaman/concurrent-map v1.0.0 + github.com/serialx/hashring v0.0.0-20200727003509-22c0c7ab6b1b + github.com/stretchr/testify v1.8.4 + go.opentelemetry.io/otel v1.16.0 + go.opentelemetry.io/otel/exporters/prometheus v0.39.0 + go.opentelemetry.io/otel/metric v1.16.0 + go.opentelemetry.io/otel/sdk/metric v0.39.0 + golang.org/x/net v0.17.0 + google.golang.org/grpc v1.58.3 + google.golang.org/protobuf v1.31.0 +) + +require ( + github.com/go-zookeeper/zk v1.0.3 + github.com/golang/mock v1.5.0 + github.com/labstack/echo v3.3.10+incompatible + github.com/lithammer/shortuuid/v4 v4.0.0 + github.com/twmb/murmur3 v1.1.6 + go.etcd.io/etcd/client/v3 v3.5.7 + golang.org/x/exp v0.0.0-20220518171630-0b5c67f07fdf + golang.org/x/sync v0.3.0 + k8s.io/api v0.26.1 + k8s.io/apimachinery v0.26.1 + k8s.io/client-go v0.26.1 +) + +require google.golang.org/genproto/googleapis/rpc v0.0.0-20231009173412-8bfb1ae86b6c // indirect + +require ( + github.com/armon/go-metrics v0.4.0 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/coreos/go-semver v0.3.0 // indirect + github.com/coreos/go-systemd/v22 v22.3.2 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/emicklei/go-restful/v3 v3.9.0 // indirect + github.com/fatih/color v1.13.0 // indirect + github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-openapi/jsonpointer v0.19.5 // indirect + github.com/go-openapi/jsonreference v0.20.0 // indirect + github.com/go-openapi/swag v0.21.1 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/golang/snappy v0.0.4 // indirect + github.com/google/gnostic v0.6.9 // indirect + github.com/google/go-cmp v0.5.9 // indirect + github.com/google/gofuzz v1.2.0 // indirect + github.com/hashicorp/go-cleanhttp v0.5.2 // indirect + github.com/hashicorp/go-hclog v1.2.0 // indirect + github.com/hashicorp/go-immutable-radix v1.3.1 // indirect + github.com/hashicorp/go-msgpack v0.5.5 // indirect + github.com/hashicorp/go-rootcerts v1.0.2 // indirect + github.com/hashicorp/golang-lru v0.5.4 // indirect + github.com/hashicorp/serf v0.10.1 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/labstack/gommon v0.3.1 // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/mattn/go-colorable v0.1.12 // indirect + github.com/mattn/go-isatty v0.0.14 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/prometheus/client_golang v1.16.0 + github.com/prometheus/client_model v0.4.0 // indirect + github.com/prometheus/common v0.42.0 // indirect + github.com/prometheus/procfs v0.10.1 // indirect + github.com/rogpeppe/go-internal v1.11.0 // indirect + github.com/stretchr/objx v0.5.0 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/valyala/fasttemplate v1.2.1 // indirect + go.etcd.io/etcd/api/v3 v3.5.7 // indirect + go.etcd.io/etcd/client/pkg/v3 v3.5.7 // indirect + go.opentelemetry.io/otel/sdk v1.16.0 // indirect + go.opentelemetry.io/otel/trace v1.16.0 // indirect + go.uber.org/atomic v1.9.0 // indirect + go.uber.org/multierr v1.8.0 // indirect + go.uber.org/zap v1.21.0 // indirect + golang.org/x/crypto v0.14.0 // indirect + golang.org/x/oauth2 v0.10.0 // indirect + golang.org/x/sys v0.13.0 // indirect + golang.org/x/term v0.13.0 // indirect + golang.org/x/text v0.13.0 // indirect + golang.org/x/time v0.0.0-20220411224347-583f2d630306 // indirect + google.golang.org/appengine v1.6.7 // indirect + google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97 // indirect + gopkg.in/couchbase/gocbcore.v7 v7.1.18 // indirect + gopkg.in/couchbaselabs/gocbconnstr.v1 v1.0.4 // indirect + gopkg.in/couchbaselabs/gojcbmock.v1 v1.0.4 // indirect + gopkg.in/couchbaselabs/jsonx.v1 v1.0.1 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + k8s.io/klog/v2 v2.80.1 // indirect + k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 // indirect + k8s.io/utils v0.0.0-20221107191617-1a15be271d1d // indirect + sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect + sigs.k8s.io/yaml v1.3.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000000000000000000000000000000000000..e9480f8ee01b9832b64541f8c492f7b4965f42d3 --- /dev/null +++ b/go.sum @@ -0,0 +1,571 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/Workiva/go-datastructures v1.1.1 h1:9G5u1UqKt6ABseAffHGNfbNQd7omRlWE5QaxNruzhE0= +github.com/Workiva/go-datastructures v1.1.1/go.mod h1:1yZL+zfsztete+ePzZz/Zb1/t5BnDuE2Ya2MMGhzP6A= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-metrics v0.4.0 h1:yCQqn7dwca4ITXb+CbubHmedzaQYHhNhrEXLYUeEe8Q= +github.com/armon/go-metrics v0.4.0/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/asynkron/gofun v0.0.0-20220329210725-34fed760f4c2 h1:jEsFZ9d/ieJGVrx3fSPi8oe/qv21fRmyUL5cS3ZEn5A= +github.com/asynkron/gofun v0.0.0-20220329210725-34fed760f4c2/go.mod h1:5GMOSqaYxNWwuVRWyampTPJEntwz7Mj9J8v1a7gSU2E= +github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= +github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI= +github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/couchbase/gocb v1.6.7 h1:3sED4tqmzuKOQU2I/4u8ljrIXBe4lCzYhuD+/kPCyqs= +github.com/couchbase/gocb v1.6.7/go.mod h1:AtRhXLpjgHmkRgG3e0K9t41qnWFonb8iohS/u/TZzxM= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= +github.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE= +github.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= +github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/flowstack/go-jsonschema v0.1.1/go.mod h1:yL7fNggx1o8rm9RlgXv7hTBWxdBM0rVwpMwimd3F3N0= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= +github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonreference v0.20.0 h1:MYlu0sBgChmCfJxxUKZ8g1cPWFOB37YSZqewK7OKeyA= +github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.21.1 h1:wm0rhTb5z7qpJRHBdPOMuY4QjVUMbF6/kwoYeRAOrKU= +github.com/go-openapi/swag v0.21.1/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-zookeeper/zk v1.0.3 h1:7M2kwOsc//9VeeFiPtf+uSJlVpU66x9Ba5+8XK7/TDg= +github.com/go-zookeeper/zk v1.0.3/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.5.0 h1:jlYHihg//f7RRwuPfptm04yp4s7O6Kw8EZiVYIGcH0g= +github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/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.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= +github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= +github.com/google/gnostic v0.6.9 h1:ZK/5VhkoX835RikCHpSUJV9a+S3e1zLh59YnyWeBW+0= +github.com/google/gnostic v0.6.9/go.mod h1:Nm8234We1lq6iB9OmlgNv3nH91XLLVZHCDayfA3xq+E= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/hashicorp/consul/api v1.18.0 h1:R7PPNzTCeN6VuQNDwwhZWJvzCtGSrNpJqfb22h3yH9g= +github.com/hashicorp/consul/api v1.18.0/go.mod h1:owRRGJ9M5xReDC5nfT8FTJrNAPbT4NM6p/k+d03q2v4= +github.com/hashicorp/consul/sdk v0.13.0 h1:lce3nFlpv8humJL8rNrrGHYSKc3q+Kxfeg3Ii1m6ZWU= +github.com/hashicorp/consul/sdk v0.13.0/go.mod h1:0hs/l5fOVhJy/VdcoaNqUSi2AUs95eF5WKtv+EYIQqE= +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.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= +github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-hclog v1.2.0 h1:La19f8d7WIlm4ogzNHB0JGqs5AUDAZ2UfCY4sJXcJdM= +github.com/hashicorp/go-hclog v1.2.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= +github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-msgpack v0.5.5 h1:i9R9JSrqIz0QVLz3sz+i3YJdT7TTSLcfLLzJi9aZTuI= +github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-multierror v1.1.0 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g4TbElacI= +github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= +github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= +github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= +github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc= +github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= +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/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE= +github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.2.1 h1:zEfKbn2+PDgroKdiOzqiE8rsmLqU2uwi5PB5pBJ3TkI= +github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= +github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= +github.com/hashicorp/memberlist v0.5.0 h1:EtYPN8DpAURiapus508I4n9CzHs2W+8NZGbmmR/prTM= +github.com/hashicorp/memberlist v0.5.0/go.mod h1:yvyXLpo0QaGE59Y7hDTsTzDD25JYBZ4mHgHUZ8lrOI0= +github.com/hashicorp/serf v0.10.1 h1:Z1H2J60yRKvfDYAOZLd2MU0ND4AH/WDz7xYHDWQsIPY= +github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/labstack/echo v3.3.10+incompatible h1:pGRcYk231ExFAyoAjAfD85kQzRJCRI8bbnE7CX5OEgg= +github.com/labstack/echo v3.3.10+incompatible/go.mod h1:0INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s= +github.com/labstack/gommon v0.3.1 h1:OomWaJXm7xR6L1HmEtGyQf26TEn7V6X88mktX9kee9o= +github.com/labstack/gommon v0.3.1/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM= +github.com/lithammer/shortuuid/v4 v4.0.0 h1:QRbbVkfgNippHOS8PXDkti4NaWeyYfcBTHtw7k08o4c= +github.com/lithammer/shortuuid/v4 v4.0.0/go.mod h1:Zs8puNcrvf2rV9rTH51ZLLcj7ZXqQI3lv67aw4KiB1Y= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= +github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= +github.com/miekg/dns v1.1.41 h1:WMszZWJG0XmzbK9FEmzH2TVcqYzFesusSIB41b8KHxY= +github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= +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-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/onsi/ginkgo/v2 v2.4.0 h1:+Ig9nvqgS5OBSACXNk15PLdp0U9XPYROt9CFzVdFGIs= +github.com/onsi/ginkgo/v2 v2.4.0/go.mod h1:iHkDK1fKGcBoEHT5W7YBq4RFWaQulw+caOMkAt4OrFo= +github.com/onsi/gomega v1.23.0 h1:/oxKu9c2HVap+F3PfKort2Hw5DEU+HGlW8n+tguWsys= +github.com/onsi/gomega v1.23.0/go.mod h1:Z/NWtiqwBrwUt4/2loMmHL63EDLnYHmVbuBpDr2vQAg= +github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= +github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= +github.com/orcaman/concurrent-map v1.0.0 h1:I/2A2XPCb4IuQWcQhBhSwGfiuybl/J0ev9HDbW65HOY= +github.com/orcaman/concurrent-map v1.0.0/go.mod h1:Lu3tH6HLW3feq74c2GC+jIMS/K2CFcDWnWD9XkenwhI= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= +github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= +github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= +github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= +github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= +github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= +github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= +github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +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/serialx/hashring v0.0.0-20200727003509-22c0c7ab6b1b h1:h+3JX2VoWTFuyQEo87pStk/a99dzIO1mM9KxIyLPGTU= +github.com/serialx/hashring v0.0.0-20200727003509-22c0c7ab6b1b/go.mod h1:/yeG0My1xr/u+HZrFQ1tOQQQQrOawfyMUH13ai5brBc= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/tinylib/msgp v1.1.5/go.mod h1:eQsjooMTnV42mHu917E26IogZ2930nFyBQdofk10Udg= +github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q= +github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= +github.com/twmb/murmur3 v1.1.6 h1:mqrRot1BRxm+Yct+vavLMou2/iJt0tNVTTC0QoIjaZg= +github.com/twmb/murmur3 v1.1.6/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasttemplate v1.2.1 h1:TVEnxayobAdVkhQfrfes2IzOB6o+z4roRkPF52WA1u4= +github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +go.etcd.io/etcd/api/v3 v3.5.7 h1:sbcmosSVesNrWOJ58ZQFitHMdncusIifYcrBfwrlJSY= +go.etcd.io/etcd/api/v3 v3.5.7/go.mod h1:9qew1gCdDDLu+VwmeG+iFpL+QlpHTo7iubavdVDgCAA= +go.etcd.io/etcd/client/pkg/v3 v3.5.7 h1:y3kf5Gbp4e4q7egZdn5T7W9TSHUvkClN6u+Rq9mEOmg= +go.etcd.io/etcd/client/pkg/v3 v3.5.7/go.mod h1:o0Abi1MK86iad3YrWhgUsbGx1pmTS+hrORWc2CamuhY= +go.etcd.io/etcd/client/v3 v3.5.7 h1:u/OhpiuCgYY8awOHlhIhmGIGpxfBU/GZBUP3m/3/Iz4= +go.etcd.io/etcd/client/v3 v3.5.7/go.mod h1:sOWmj9DZUMyAngS7QQwCyAXXAL6WhgTOPLNS/NabQgw= +go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s= +go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4= +go.opentelemetry.io/otel/exporters/prometheus v0.39.0 h1:whAaiHxOatgtKd+w0dOi//1KUxj3KoPINZdtDaDj3IA= +go.opentelemetry.io/otel/exporters/prometheus v0.39.0/go.mod h1:4jo5Q4CROlCpSPsXLhymi+LYrDXd2ObU5wbKayfZs7Y= +go.opentelemetry.io/otel/metric v1.16.0 h1:RbrpwVG1Hfv85LgnZ7+txXioPDoh6EdbZHo26Q3hqOo= +go.opentelemetry.io/otel/metric v1.16.0/go.mod h1:QE47cpOmkwipPiefDwo2wDzwJrlfxxNYodqc4xnGCo4= +go.opentelemetry.io/otel/sdk v1.16.0 h1:Z1Ok1YsijYL0CSJpHt4cS3wDDh7p572grzNrBMiMWgE= +go.opentelemetry.io/otel/sdk v1.16.0/go.mod h1:tMsIuKXuuIWPBAOrH+eHtvhTL+SntFtXF9QD68aP6p4= +go.opentelemetry.io/otel/sdk/metric v0.39.0 h1:Kun8i1eYf48kHH83RucG93ffz0zGV1sh46FAScOTuDI= +go.opentelemetry.io/otel/sdk/metric v0.39.0/go.mod h1:piDIRgjcK7u0HCL5pCA4e74qpK/jk3NiUoAHATVAmiI= +go.opentelemetry.io/otel/trace v1.16.0 h1:8JRpaObFoW0pxuVPapkgH8UhHQj+bJW8jJsCZEu5MQs= +go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= +go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= +go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8= +go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= +go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8= +go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20220518171630-0b5c67f07fdf h1:oXVg4h2qJDd9htKxb5SCpFBHLipW6hXmL3qpUixS2jw= +golang.org/x/exp v0.0.0-20220518171630-0b5c67f07fdf/go.mod h1:yh0Ynu2b5ZUe3MQfp2nM0ecK7wsgouWTDN0FNeJuIys= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= +golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.6.0 h1:Lh8GPgSKBfWSwFvtuWOfeI3aAAnbXTSutYxJiOJFgIw= +golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw= +golang.org/x/oauth2 v0.10.0 h1:zHCpF2Khkwy4mMB4bv0U37YtJdTGW8jI0glAApi0Kh8= +golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= +golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.12.0 h1:/ZfYdc3zq+q02Rv9vGqTeSItdzZTSNDmfTi0mBAuidU= +golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= +golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= +golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/time v0.0.0-20220411224347-583f2d630306 h1:+gHMid33q6pen7kv9xvT+JRinntgeXO2AeZVd0AWD3w= +golang.org/x/time v0.0.0-20220411224347-583f2d630306/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 h1:DdoeryqhaXp1LtT/emMP1BRJPHHKFi5akj/nbx/zNTA= +google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= +google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97 h1:SeZZZx0cP0fqUyA+oRzP9k7cSwJlvDFiROO72uwD6i0= +google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97/go.mod h1:t1VqOqqvce95G3hIDCT5FeO3YUc6Q4Oe24L/+rNMxRk= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231009173412-8bfb1ae86b6c h1:jHkCUWkseRf+W+edG5hMzr/Uh1xkDREY4caybAq4dpY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231009173412-8bfb1ae86b6c/go.mod h1:4cYg8o5yUbm77w8ZX00LhMVNl/YVBFJRYWDc0uYWMs0= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.55.0 h1:3Oj82/tFSCeUrRTg/5E/7d/W5A1tj6Ky1ABAuZuv5ag= +google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8= +google.golang.org/grpc v1.58.3 h1:BjnpXut1btbtgN/6sp+brB2Kbm2LjNXnidYujAVbSoQ= +google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/couchbase/gocbcore.v7 v7.1.18 h1:d4yfIXWdf/ZmyuJjwRVVlGT/yqx8ICy6fcT/ViaMZsI= +gopkg.in/couchbase/gocbcore.v7 v7.1.18/go.mod h1:48d2Be0MxRtsyuvn+mWzqmoGUG9uA00ghopzOs148/E= +gopkg.in/couchbaselabs/gocbconnstr.v1 v1.0.4 h1:VVVoIV/nSw1w9ZnTEOjmkeJVcAzaCyxEujKglarxz7U= +gopkg.in/couchbaselabs/gocbconnstr.v1 v1.0.4/go.mod h1:ZjII0iKx4Veo6N6da+pEZu/ptNyKLg9QTVt7fFmR6sw= +gopkg.in/couchbaselabs/gojcbmock.v1 v1.0.4 h1:r5WoWGyeTJQiNGsoWAsMJfz0JFF14xc2TJrYSs09VXk= +gopkg.in/couchbaselabs/gojcbmock.v1 v1.0.4/go.mod h1:jl/gd/aQ2S8whKVSTnsPs6n7BPeaAuw9UglBD/OF7eo= +gopkg.in/couchbaselabs/jsonx.v1 v1.0.1 h1:giDAdTGcyXUuY+uFCWeJ2foukiqMTYl4ORSxCi/ybcc= +gopkg.in/couchbaselabs/jsonx.v1 v1.0.1/go.mod h1:oR201IRovxvLW/eISevH12/+MiKHtNQAKfcX8iWZvJY= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/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= +k8s.io/api v0.26.1 h1:f+SWYiPd/GsiWwVRz+NbFyCgvv75Pk9NK6dlkZgpCRQ= +k8s.io/api v0.26.1/go.mod h1:xd/GBNgR0f707+ATNyPmQ1oyKSgndzXij81FzWGsejg= +k8s.io/apimachinery v0.26.1 h1:8EZ/eGJL+hY/MYCNwhmDzVqq2lPl3N3Bo8rvweJwXUQ= +k8s.io/apimachinery v0.26.1/go.mod h1:tnPmbONNJ7ByJNz9+n9kMjNP8ON+1qoAIIC70lztu74= +k8s.io/client-go v0.26.1 h1:87CXzYJnAMGaa/IDDfRdhTzxk/wzGZ+/HUQpqgVSZXU= +k8s.io/client-go v0.26.1/go.mod h1:IWNSglg+rQ3OcvDkhY6+QLeasV4OYHDjdqeWkDQZwGE= +k8s.io/klog/v2 v2.80.1 h1:atnLQ121W371wYYFawwYx1aEY2eUfs4l3J72wtgAwV4= +k8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 h1:+70TFaan3hfJzs+7VK2o+OGxg8HsuBr/5f6tVAjDu6E= +k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280/go.mod h1:+Axhij7bCpeqhklhUTe3xmOn6bWxolyZEeyaFpjGtl4= +k8s.io/utils v0.0.0-20221107191617-1a15be271d1d h1:0Smp/HP1OH4Rvhe+4B8nWGERtlqAGSftbSbbmm45oFs= +k8s.io/utils v0.0.0-20221107191617-1a15be271d1d/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k= +sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= +sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= +sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= +sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/internal/core/debug.go b/internal/core/debug.go new file mode 100644 index 0000000000000000000000000000000000000000..766068fff1627fba06ae046a4a8c8dc8c84441b7 --- /dev/null +++ b/internal/core/debug.go @@ -0,0 +1,35 @@ +package core + +import ( + "fmt" + "runtime" + "strings" +) + +func IdentifyPanic() string { + var name, file string + var line int + var pc [16]uintptr + + n := runtime.Callers(3, pc[:]) + for _, pc := range pc[:n] { + fn := runtime.FuncForPC(pc) + if fn == nil { + continue + } + file, line = fn.FileLine(pc) + name = fn.Name() + if !strings.HasPrefix(name, "runtime.") { + break + } + } + + switch { + case name != "": + return fmt.Sprintf("%v:%v", name, line) + case file != "": + return fmt.Sprintf("%v:%v", file, line) + } + + return fmt.Sprintf("pc:%x", pc) +} diff --git a/internal/queue/goring/LICENSE b/internal/queue/goring/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..c0ee81299bd368b4c38a7947d1651a1e32348313 --- /dev/null +++ b/internal/queue/goring/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + 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. diff --git a/internal/queue/goring/queue.go b/internal/queue/goring/queue.go new file mode 100644 index 0000000000000000000000000000000000000000..d5b5db7c13f655d9c36559af0a266e75a21dc088 --- /dev/null +++ b/internal/queue/goring/queue.go @@ -0,0 +1,110 @@ +package goring + +import ( + "sync" + "sync/atomic" +) + +type ringBuffer struct { + buffer []interface{} + head int64 + tail int64 + mod int64 +} + +type Queue struct { + len int64 + content *ringBuffer + lock sync.Mutex +} + +func New(initialSize int64) *Queue { + return &Queue{ + content: &ringBuffer{ + buffer: make([]interface{}, initialSize), + head: 0, + tail: 0, + mod: initialSize, + }, + len: 0, + } +} + +func (q *Queue) Push(item interface{}) { + q.lock.Lock() + c := q.content + c.tail = (c.tail + 1) % c.mod + if c.tail == c.head { + var fillFactor int64 = 2 + // we need to resize + + newLen := c.mod * fillFactor + newBuff := make([]interface{}, newLen) + + for i := int64(0); i < c.mod; i++ { + buffIndex := (c.tail + i) % c.mod + newBuff[i] = c.buffer[buffIndex] + } + // set the new buffer and reset head and tail + newContent := &ringBuffer{ + buffer: newBuff, + head: 0, + tail: c.mod, + mod: newLen, + } + q.content = newContent + } + atomic.AddInt64(&q.len, 1) + q.content.buffer[q.content.tail] = item + q.lock.Unlock() +} + +func (q *Queue) Length() int64 { + return atomic.LoadInt64(&q.len) +} + +func (q *Queue) Empty() bool { + return q.Length() == 0 +} + +// single consumer +func (q *Queue) Pop() (interface{}, bool) { + if q.Empty() { + return nil, false + } + // as we are a single consumer, no other thread can have poped the items there are guaranteed to be items now + + q.lock.Lock() + c := q.content + c.head = (c.head + 1) % c.mod + res := c.buffer[c.head] + c.buffer[c.head] = nil + atomic.AddInt64(&q.len, -1) + q.lock.Unlock() + return res, true +} + +func (q *Queue) PopMany(count int64) ([]interface{}, bool) { + if q.Empty() { + return nil, false + } + + q.lock.Lock() + c := q.content + + if count >= q.len { + count = q.len + } + atomic.AddInt64(&q.len, -count) + + buffer := make([]interface{}, count) + for i := int64(0); i < count; i++ { + pos := (c.head + 1 + i) % c.mod + buffer[i] = c.buffer[pos] + c.buffer[pos] = nil + } + c.head = (c.head + count) % c.mod + + q.lock.Unlock() + return buffer, true +} diff --git a/internal/queue/goring/queue_test.go b/internal/queue/goring/queue_test.go new file mode 100644 index 0000000000000000000000000000000000000000..797631789ec30c4cdeccfacad1fb46d083875ff6 --- /dev/null +++ b/internal/queue/goring/queue_test.go @@ -0,0 +1,112 @@ +package goring + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestPushPop(t *testing.T) { + q := New(10) + q.Push("hello") + res, _ := q.Pop() + assert.Equal(t, "hello", res) + assert.True(t, q.Empty()) +} + +func TestPushPopRepeated(t *testing.T) { + q := New(10) + for i := 0; i < 100; i++ { + q.Push("hello") + res, _ := q.Pop() + assert.Equal(t, "hello", res) + assert.True(t, q.Empty()) + } +} + +func TestPushPopMany(t *testing.T) { + q := New(10) + for i := 0; i < 10000; i++ { + item := fmt.Sprintf("hello%v", i) + q.Push(item) + res, _ := q.Pop() + assert.Equal(t, item, res) + } + assert.True(t, q.Empty()) +} + +func TestPushPopMany2(t *testing.T) { + q := New(10) + for i := 0; i < 10000; i++ { + item := fmt.Sprintf("hello%v", i) + q.Push(item) + } + for i := 0; i < 10000; i++ { + item := fmt.Sprintf("hello%v", i) + res, _ := q.Pop() + assert.Equal(t, item, res) + } + assert.True(t, q.Empty()) +} + +//func TestLfQueueConsistency(t *testing.T) { +// max := 1000000 +// c := 100 +// var wg sync.WaitGroup +// wg.Add(1) +// q := New(2) +// go func() { +// i := 0 +// seen := make(map[string]string) +// for { +// r, ok := q.Pop() +// if !ok { +// runtime.Gosched() +// +// continue +// } +// i++ +// if r == nil { +// log.Printf("%#v, %#v", q, q.content) +// panic("consistency failure") +// } +// s := r.(string) +// _, present := seen[s] +// if present { +// log.Printf("item have already been seen %v", s) +// t.FailNow() +// } +// seen[s] = s +// +// if i == max { +// wg.Done() +// return +// } +// } +// }() +// +// for j := 0; j < c; j++ { +// jj := j +// cmax := max / c +// go func() { +// for i := 0; i < cmax; i++ { +// if rand.Intn(10) == 0 { +// time.Sleep(time.Duration(rand.Intn(1000))) +// } +// q.Push(fmt.Sprintf("%v %v", jj, i)) +// } +// }() +// } +// +// wg.Wait() +// time.Sleep(500 * time.Millisecond) +// // queue should be empty +// for i := 0; i < 100; i++ { +// r, ok := q.Pop() +// if ok { +// log.Printf("unexpected result %+v", r) +// t.FailNow() +// } +// } +//} diff --git a/internal/queue/mpsc/mpsc.go b/internal/queue/mpsc/mpsc.go new file mode 100644 index 0000000000000000000000000000000000000000..f23f7c3f5005ab7ad95a473ba1f8ba373b4cd5d8 --- /dev/null +++ b/internal/queue/mpsc/mpsc.go @@ -0,0 +1,66 @@ +// Package mpsc provides an efficient implementation of a multi-producer, single-consumer lock-free queue. +// +// The Push function is safe to call from multiple goroutines. The Pop and Empty APIs must only be +// called from a single, consumer goroutine. +package mpsc + +// This implementation is based on http://www.1024cores.net/home/lock-free-algorithms/queues/non-intrusive-mpsc-node-based-queue + +import ( + "sync/atomic" + "unsafe" +) + +type node struct { + next *node + val interface{} +} + +type Queue struct { + head, tail *node +} + +func New() *Queue { + q := &Queue{} + stub := &node{} + q.head = stub + q.tail = stub + return q +} + +// Push adds x to the back of the queue. +// +// Push can be safely called from multiple goroutines +func (q *Queue) Push(x interface{}) { + n := new(node) + n.val = x + // current producer acquires head node + prev := (*node)(atomic.SwapPointer((*unsafe.Pointer)(unsafe.Pointer(&q.head)), unsafe.Pointer(n))) + + // release node to consumer + atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&prev.next)), unsafe.Pointer(n)) +} + +// Pop removes the item from the front of the queue or nil if the queue is empty +// +// Pop must be called from a single, consumer goroutine +func (q *Queue) Pop() interface{} { + tail := q.tail + next := (*node)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&tail.next)))) // acquire + if next != nil { + q.tail = next + v := next.val + next.val = nil + return v + } + return nil +} + +// Empty returns true if the queue is empty +// +// Empty must be called from a single, consumer goroutine +func (q *Queue) Empty() bool { + tail := q.tail + next := (*node)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&tail.next)))) + return next == nil +} diff --git a/internal/queue/mpsc/mpsc_test.go b/internal/queue/mpsc/mpsc_test.go new file mode 100644 index 0000000000000000000000000000000000000000..b11a9b2798585f747d6b3fcf8e42c304cff5f464 --- /dev/null +++ b/internal/queue/mpsc/mpsc_test.go @@ -0,0 +1,239 @@ +package mpsc + +import ( + "fmt" + "runtime" + "sync" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestQueue_PushPop(t *testing.T) { + q := New() + + q.Push(1) + q.Push(2) + assert.Equal(t, 1, q.Pop()) + assert.Equal(t, 2, q.Pop()) + assert.True(t, q.Empty()) +} + +func TestQueue_Empty(t *testing.T) { + q := New() + assert.True(t, q.Empty()) + q.Push(1) + assert.False(t, q.Empty()) +} + +func TestQueue_PushPopOneProducer(t *testing.T) { + expCount := 100 + + var wg sync.WaitGroup + wg.Add(1) + q := New() + go func() { + i := 0 + for { + r := q.Pop() + if r == nil { + runtime.Gosched() + continue + } + i++ + if i == expCount { + wg.Done() + return + } + } + }() + + var val interface{} = "foo" + + for i := 0; i < expCount; i++ { + q.Push(val) + } + + wg.Wait() +} + +//func TestMpscQueueConsistency(t *testing.T) { +// max := 1000000 +// c := runtime.NumCPU() / 2 +// cmax := max / c +// var wg sync.WaitGroup +// wg.Add(1) +// q := New() +// +// go func() { +// i := 0 +// seen := make(map[string]string) +// for { +// r := q.Pop() +// if r == nil { +// runtime.Gosched() +// +// continue +// } +// i++ +// s, _ := r.(string) +// _, present := seen[s] +// if present { +// log.Printf("item have already been seen %v", s) +// t.FailNow() +// } +// seen[s] = s +// if i == cmax*c { +// wg.Done() +// return +// } +// } +// }() +// +// for j := 0; j < c; j++ { +// jj := j +// go func() { +// for i := 0; i < cmax; i++ { +// if rand.Intn(10) == 0 { +// time.Sleep(time.Duration(rand.Intn(1000))) +// } +// q.Push(fmt.Sprintf("%v %v", jj, i)) +// } +// }() +// } +// +// wg.Wait() +// time.Sleep(500 * time.Millisecond) +// // queue should be empty +// for i := 0; i < 100; i++ { +// r := q.Pop() +// if r != nil { +// log.Printf("unexpected result %+v", r) +// t.FailNow() +// } +// } +//} + +func benchmarkPushPop(count, c int) { + var wg sync.WaitGroup + wg.Add(1) + q := New() + go func() { + i := 0 + for { + r := q.Pop() + if r == nil { + runtime.Gosched() + continue + } + i++ + if i == count { + wg.Done() + return + } + } + }() + + var val interface{} = "foo" + + for i := 0; i < c; i++ { + go func(n int) { + for n > 0 { + q.Push(val) + n-- + } + }(count / c) + } + + wg.Wait() +} + +func benchmarkChannelPushPop(count, c int) { + var wg sync.WaitGroup + wg.Add(1) + ch := make(chan interface{}, 100) + go func() { + i := 0 + for { + <-ch + i++ + if i == count { + wg.Done() + return + } + } + }() + + var val interface{} = "foo" + + for i := 0; i < c; i++ { + go func(n int) { + for n > 0 { + ch <- val + n-- + } + }(count / c) + } +} + +func BenchmarkPushPop(b *testing.B) { + benchmarks := []struct { + count int + concurrency int + }{ + { + count: 10000, + concurrency: 1, + }, + { + count: 10000, + concurrency: 2, + }, + { + count: 10000, + concurrency: 4, + }, + { + count: 10000, + concurrency: 8, + }, + } + for _, bm := range benchmarks { + b.Run(fmt.Sprintf("%d_%d", bm.count, bm.concurrency), func(b *testing.B) { + for i := 0; i < b.N; i++ { + benchmarkPushPop(bm.count, bm.concurrency) + } + }) + } +} + +func BenchmarkChannelPushPop(b *testing.B) { + benchmarks := []struct { + count int + concurrency int + }{ + { + count: 10000, + concurrency: 1, + }, + { + count: 10000, + concurrency: 2, + }, + { + count: 10000, + concurrency: 4, + }, + { + count: 10000, + concurrency: 8, + }, + } + for _, bm := range benchmarks { + b.Run(fmt.Sprintf("%d_%d", bm.count, bm.concurrency), func(b *testing.B) { + for i := 0; i < b.N; i++ { + benchmarkChannelPushPop(bm.count, bm.concurrency) + } + }) + } +} diff --git a/log/caller.go b/log/caller.go new file mode 100644 index 0000000000000000000000000000000000000000..6166aadd13e194e47cb00e369cbba7cbeac0203d --- /dev/null +++ b/log/caller.go @@ -0,0 +1,34 @@ +package log + +import ( + "runtime" + "strconv" + "strings" +) + +type CallerInfo struct { + fname string + line int +} + +func newCallerInfo(skip int) CallerInfo { + _, file, no, ok := runtime.Caller(skip) + if !ok { + return CallerInfo{"", 0} + } + return CallerInfo{fname: file, line: no} +} + +func (ci *CallerInfo) ShortFileName() string { + fname := ci.fname + idx := strings.LastIndexByte(fname, '/') + if idx >= len(fname) { + } else { + fname = fname[idx+1:] + } + return fname +} + +func (ci *CallerInfo) String() string { + return ci.ShortFileName() + ":" + strconv.Itoa(ci.line) +} diff --git a/log/encoder.go b/log/encoder.go new file mode 100644 index 0000000000000000000000000000000000000000..1dcc9192870443584331f6fffd79744c4cbf4207 --- /dev/null +++ b/log/encoder.go @@ -0,0 +1,20 @@ +package log + +import ( + "reflect" + "time" +) + +type Encoder interface { + EncodeBool(key string, val bool) + EncodeFloat64(key string, val float64) + EncodeInt(key string, val int) + EncodeInt64(key string, val int64) + EncodeDuration(key string, val time.Duration) + EncodeUint(key string, val uint) + EncodeUint64(key string, val uint64) + EncodeString(key string, val string) + EncodeObject(key string, val interface{}) + EncodeType(key string, val reflect.Type) + EncodeCaller(key string, val CallerInfo) +} diff --git a/log/event.go b/log/event.go new file mode 100644 index 0000000000000000000000000000000000000000..d019b7cc4a2766ccd5466ce35e64e15da3eea856 --- /dev/null +++ b/log/event.go @@ -0,0 +1,13 @@ +package log + +import "time" + +type Event struct { + Time time.Time + Level Level + Prefix string + Caller CallerInfo + Message string + Context []Field + Fields []Field +} diff --git a/log/field.go b/log/field.go new file mode 100644 index 0000000000000000000000000000000000000000..5ca16a4b845182279710f976cd5c08248af18f66 --- /dev/null +++ b/log/field.go @@ -0,0 +1,221 @@ +package log + +import ( + "fmt" + "math" + "reflect" + "runtime" + "strings" + "time" +) + +type fieldType int + +const ( + unknownType fieldType = iota + boolType + floatType + intType + int64Type + durationType + uintType + uint64Type + stringType + stringerType + errorType + objectType + typeOfType + skipType + callerType +) + +type Field struct { + key string + fieldType fieldType + val int64 + str string + obj interface{} +} + +// Bool constructs a Field with the given key and value. +func Bool(key string, val bool) Field { + var ival int64 + if val { + ival = 1 + } + + return Field{key: key, fieldType: boolType, val: ival} +} + +// Float64 constructs a Field with the given key and value. +func Float64(key string, val float64) Field { + return Field{key: key, fieldType: floatType, val: int64(math.Float64bits(val))} +} + +// Int constructs a Field with the given key and value. Marshaling ints is lazy. +func Int(key string, val int) Field { + return Field{key: key, fieldType: intType, val: int64(val)} +} + +// Int64 constructs a Field with the given key and value. +func Int64(key string, val int64) Field { + return Field{key: key, fieldType: int64Type, val: val} +} + +// Uint constructs a Field with the given key and value. +func Uint(key string, val uint) Field { + return Field{key: key, fieldType: uintType, val: int64(val)} +} + +// Uint64 constructs a Field with the given key and value. +func Uint64(key string, val uint64) Field { + return Field{key: key, fieldType: uint64Type, val: int64(val)} +} + +// String constructs a Field with the given key and value. +func String(key string, val string) Field { + return Field{key: key, fieldType: stringType, str: val} +} + +// PID constructs a Field with the given key and value. +func PID(key string, val fmt.Stringer) Field { + if val == nil { + return Field{key: key, fieldType: objectType, obj: val} + } + return Field{key: key, fieldType: stringerType, obj: val} +} + +// Stringer constructs a Field with the given key and the output of the value's +// String method. The String is not evaluated until encoding. +func Stringer(key string, val fmt.Stringer) Field { + if val == nil { + return Field{key: key, fieldType: objectType, obj: val} + } + return Field{key: key, fieldType: stringerType, obj: val} +} + +// Time constructs a Field with the given key and value. It represents a +// time.Time as a floating-point number of seconds since the Unix epoch. +func Time(key string, val time.Time) Field { + return Float64(key, float64(val.UnixNano())/float64(time.Second)) +} + +// Error constructs a Field that lazily stores err.Error() under the key +// "error". If passed a nil error, the field is skipped. +func Error(err error) Field { + if err == nil { + return Field{fieldType: skipType} + } + return Field{key: "error", fieldType: errorType, obj: err} +} + +// Stack constructs a Field that stores a stacktrace under the key "stacktrace". +// +// This is eager and therefore an expensive operation. +func Stack() Field { + var name, file string + var line int + var pc [16]uintptr + + n := runtime.Callers(4, pc[:]) + callers := pc[:n] + frames := runtime.CallersFrames(callers) + for { + frame, more := frames.Next() + file = frame.File + line = frame.Line + name = frame.Function + if !strings.HasPrefix(name, "runtime.") || !more { + break + } + } + + var str string + switch { + case name != "": + str = fmt.Sprintf("%v:%v", name, line) + case file != "": + str = fmt.Sprintf("%v:%v", file, line) + default: + str = fmt.Sprintf("pc:%x", pc) + } + return String("stacktrace", str) +} + +// Duration constructs a Field with the given key and value. +func Duration(key string, val time.Duration) Field { + return Field{key: key, fieldType: durationType, val: int64(val)} +} + +// Object constructs a field with the given key and an arbitrary object. +func Object(key string, val interface{}) Field { + return Field{key: key, fieldType: objectType, obj: val} +} + +// TypeOf constructs a field with the given key and an arbitrary object that will log the type information lazily. +func TypeOf(key string, val interface{}) Field { + return Field{key: key, fieldType: typeOfType, obj: val} +} + +// Message constructs a field to store the message under the key message +func Message(val interface{}) Field { + return Field{key: "message", fieldType: objectType, obj: val} +} + +// CallerInfo is constructs with runtime.Caller + +// CallerSkip constructs a field with function name and number of line +func CallerSkip(skip int) Field { + _, file, no, ok := runtime.Caller(skip) + if !ok { + return Field{key: "caller", fieldType: stringType, obj: "nil"} + } + return Field{ + key: "caller", + fieldType: callerType, + obj: CallerInfo{ + fname: file, + line: no, + }, + } +} + +func Caller() Field { + return CallerSkip(2) +} + +// Encode encodes a field to a type safe val via the encoder. +func (f Field) Encode(enc Encoder) { + switch f.fieldType { + case boolType: + enc.EncodeBool(f.key, f.val == 1) + case floatType: + enc.EncodeFloat64(f.key, math.Float64frombits(uint64(f.val))) + case intType: + enc.EncodeInt(f.key, int(f.val)) + case int64Type: + enc.EncodeInt64(f.key, f.val) + case durationType: + enc.EncodeDuration(f.key, time.Duration(f.val)) + case uintType: + enc.EncodeUint(f.key, uint(f.val)) + case uint64Type: + enc.EncodeUint64(f.key, uint64(f.val)) + case stringType: + enc.EncodeString(f.key, f.str) + case stringerType: + enc.EncodeString(f.key, f.obj.(fmt.Stringer).String()) + case errorType: + enc.EncodeString(f.key, f.obj.(error).Error()) + case objectType: + enc.EncodeObject(f.key, f.obj) + case typeOfType: + enc.EncodeType(f.key, reflect.TypeOf(f.obj)) + case callerType: + enc.EncodeCaller(f.key, f.obj.(CallerInfo)) + case skipType: + break + default: + panic(fmt.Sprintf("unknown field type found: %v", f)) + } +} diff --git a/log/log.go b/log/log.go new file mode 100644 index 0000000000000000000000000000000000000000..ba94f7aba23421ed15d6d38c2ea30639c9c1f79a --- /dev/null +++ b/log/log.go @@ -0,0 +1,123 @@ +/* +Package log provides simple log interfaces +*/ +package log + +import ( + "sync/atomic" + "time" +) + +// Level of log. +type Level int32 + +const ( + MinLevel = Level(iota) + DebugLevel + InfoLevel + WarnLevel + ErrorLevel + OffLevel + DefaultLevel +) + +var levelNames = [OffLevel + 1]string{"- ", "DEBUG", "INFO ", "WARN", "ERROR", "- "} + +func (l Level) String() string { + return levelNames[int(l)] +} + +type Logger struct { + level Level + prefix string + context []Field + enableCaller bool +} + +// New a Logger +func New(level Level, prefix string, context ...Field) *Logger { + opts := Current + if level == DefaultLevel { + level = opts.logLevel + } + return &Logger{ + level: level, + prefix: prefix, + context: context, + enableCaller: opts.enableCaller, + } +} + +func (l *Logger) WithCaller() *Logger { + l.enableCaller = true + return l +} + +func (l *Logger) With(fields ...Field) *Logger { + var ctx []Field + + ll := len(l.context) + len(fields) + if ll > 0 { + ctx = make([]Field, 0, ll) + if len(l.context) > 0 { + ctx = append(ctx, l.context...) + } + + if len(fields) > 0 { + ctx = append(ctx, fields...) + } + } + + return &Logger{ + level: l.level, + prefix: l.prefix, + context: ctx, + } +} + +func (l *Logger) Level() Level { + return Level(atomic.LoadInt32((*int32)(&l.level))) +} + +func (l *Logger) SetLevel(level Level) { + atomic.StoreInt32((*int32)(&l.level), int32(level)) +} + +func (l *Logger) newEvent(msg string, level Level, fields ...Field) Event { + ev := Event{ + Time: time.Now(), + Level: level, + Prefix: l.prefix, + Message: msg, + Context: l.context, + Fields: fields, + } + if l.enableCaller { + ev.Caller = newCallerInfo(3) + } + return ev +} + +func (l *Logger) Debug(msg string, fields ...Field) { + if l.Level() <= DebugLevel { + es.Publish(l.newEvent(msg, DebugLevel, fields...)) + } +} + +func (l *Logger) Info(msg string, fields ...Field) { + if l.Level() <= InfoLevel { + es.Publish(l.newEvent(msg, InfoLevel, fields...)) + } +} + +func (l *Logger) Warn(msg string, fields ...Field) { + if l.Level() <= WarnLevel { + es.Publish(l.newEvent(msg, WarnLevel, fields...)) + } +} + +func (l *Logger) Error(msg string, fields ...Field) { + if l.Level() <= ErrorLevel { + es.Publish(l.newEvent(msg, ErrorLevel, fields...)) + } +} diff --git a/log/log_test.go b/log/log_test.go new file mode 100644 index 0000000000000000000000000000000000000000..43ed1b9a35c24a2d9027ba25bd865b9a87dc172e --- /dev/null +++ b/log/log_test.go @@ -0,0 +1,53 @@ +package log + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestLogger_With(t *testing.T) { + base := New(DebugLevel, "", Field{key: "first"}) + l := base.With(Field{key: "second"}) + + assert.Equal(t, []Field{{key: "first"}, {key: "second"}}, l.context) +} + +func Benchmark_OffLevel_TwoFields(b *testing.B) { + l := New(MinLevel, "") + for i := 0; i < b.N; i++ { + l.Debug("foo", Int("bar", 32), Bool("fum", false)) + } +} + +func Benchmark_OffLevel_OnlyContext(b *testing.B) { + l := New(MinLevel, "", Int("bar", 32), Bool("fum", false)) + for i := 0; i < b.N; i++ { + l.Debug("foo") + } +} + +func Benchmark_DebugLevel_OnlyContext_OneSubscriber(b *testing.B) { + Unsubscribe(sub) + s1 := Subscribe(func(Event) {}) + + l := New(DebugLevel, "", Int("bar", 32), Bool("fum", false)) + for i := 0; i < b.N; i++ { + l.Debug("foo") + } + Unsubscribe(s1) +} + +func Benchmark_DebugLevel_OnlyContext_MultipleSubscribers(b *testing.B) { + Unsubscribe(sub) + s1 := Subscribe(func(Event) {}) + s2 := Subscribe(func(Event) {}) + + l := New(DebugLevel, "", Int("bar", 32), Bool("fum", false)) + for i := 0; i < b.N; i++ { + l.Debug("foo") + } + + Unsubscribe(s1) + Unsubscribe(s2) +} diff --git a/log/options.go b/log/options.go new file mode 100644 index 0000000000000000000000000000000000000000..d9dcaceb18d753e9b0e103ffccfbd1278f7666ae --- /dev/null +++ b/log/options.go @@ -0,0 +1,77 @@ +package log + +import ( + "os" +) + +var ( + Development = &Options{ + logLevel: DebugLevel, + enableCaller: true, + } + + Production = &Options{ + logLevel: InfoLevel, + enableCaller: false, + } + + Current = Production +) + +func init() { + env := os.Getenv("PROTO_ACTOR_ENV") + switch env { + case "dev": + Current = Development + case "prod": + Current = Production + default: + Current = Production + } +} + +// Options for log. +type Options struct { + logLevel Level + enableCaller bool +} + +// Setup is used to configure the log system +func (o *Options) With(opts ...option) *Options { + cloned := *o + for _, opt := range opts { + opt(&cloned) + } + return &cloned +} + +type option func(*Options) + +// WithEventSubscriber option replaces the default Event subscriber with fn. +// +// Specifying nil will disable logging of events. +func WithEventSubscriber(fn func(evt Event)) option { + return func(opts *Options) { + resetEventSubscriber(fn) + } +} + +// WithCaller option will print the file name and line number. +func WithCaller(enabled bool) option { + return func(opts *Options) { + opts.enableCaller = enabled + } +} + +func WithDefaultLevel(level Level) option { + if level == DefaultLevel { + level = InfoLevel + } + return func(opts *Options) { + opts.logLevel = level + } +} + +func SetOptions(opts ...option) { + Current = Current.With(opts...) +} diff --git a/log/stream.go b/log/stream.go new file mode 100644 index 0000000000000000000000000000000000000000..9cc39aab6b6c7523c360b9febb472279f028df3d --- /dev/null +++ b/log/stream.go @@ -0,0 +1,85 @@ +package log + +import "sync" + +var es = &eventStream{} + +func Subscribe(fn func(evt Event)) *Subscription { + return es.Subscribe(fn) +} + +func Unsubscribe(sub *Subscription) { + es.Unsubscribe(sub) +} + +type eventStream struct { + sync.RWMutex + subscriptions []*Subscription +} + +func (es *eventStream) Subscribe(fn func(evt Event)) *Subscription { + es.Lock() + sub := &Subscription{ + es: es, + i: len(es.subscriptions), + fn: fn, + } + es.subscriptions = append(es.subscriptions, sub) + es.Unlock() + return sub +} + +func (es *eventStream) Unsubscribe(sub *Subscription) { + if sub.i == -1 { + return + } + + es.Lock() + i := sub.i + l := len(es.subscriptions) - 1 + + es.subscriptions[i] = es.subscriptions[l] + es.subscriptions[i].i = i + es.subscriptions[l] = nil + es.subscriptions = es.subscriptions[:l] + sub.i = -1 + + // TODO(SGC): implement resizing + if len(es.subscriptions) == 0 { + es.subscriptions = nil + } + + es.Unlock() +} + +func (es *eventStream) Publish(evt Event) { + es.RLock() + defer es.RUnlock() + + for _, s := range es.subscriptions { + if evt.Level >= s.l { + s.fn(evt) + } + } +} + +// Subscription is returned from the Subscribe function. +// +// This value and can be passed to Unsubscribe when the observer is no longer interested in receiving messages +type Subscription struct { + es *eventStream + i int + fn func(event Event) + l Level +} + +// WithMinLevel filter messages below the provided level +// +// For example, setting ErrorLevel will only pass error messages. Setting MinLevel will +// allow all messages, and is the default. +func (s *Subscription) WithMinLevel(level Level) *Subscription { + s.es.Lock() + s.l = level + s.es.Unlock() + return s +} diff --git a/log/string_encoder.go b/log/string_encoder.go new file mode 100644 index 0000000000000000000000000000000000000000..70d7d9c8afe00f08c0a07a4d56fd27f97ff7efba --- /dev/null +++ b/log/string_encoder.go @@ -0,0 +1,204 @@ +package log + +import ( + "bytes" + "fmt" + "io" + "os" + "reflect" + "strconv" + "strings" + "time" +) + +type ioLogger struct { + c chan Event + out io.Writer + buf []byte +} + +var ( + noStdErrLogs bool + sub *Subscription +) + +// Disables Proto.Actor standard error logs if there is one +// or more additional log subscribers registered +func SetNoStdErrLogs() { + if len(es.subscriptions) >= 2 { + noStdErrLogs = true + } +} + +func init() { + l := &ioLogger{c: make(chan Event, 100), out: os.Stderr} + resetEventSubscriber(func(evt Event) { + l.c <- evt + }) + go l.listenEvent() +} + +func resetEventSubscriber(f func(evt Event)) { + if sub != nil { + Unsubscribe(sub) + sub = nil + } + sub = Subscribe(f) +} + +func (l *ioLogger) listenEvent() { + for true { + if noStdErrLogs { + Unsubscribe(sub) + break + } + + e := <-l.c + l.writeEvent(e) + } +} + +// Cheap integer to fixed-width decimal ASCII. Give a negative width to avoid zero-padding. +func itoa(buf *bytes.Buffer, i int, wid int) { + // Assemble decimal in reverse order. + var b [20]byte + bp := len(b) - 1 + for i >= 10 || wid > 1 { + wid-- + q := i / 10 + b[bp] = byte('0' + i - q*10) + bp-- + i = q + } + // i < 10 + b[bp] = byte('0' + i) + buf.Write(b[bp:]) +} + +func (l *ioLogger) formatHeader(buf *bytes.Buffer, prefix string, t time.Time, loglv Level) { + // Y/M/D + year, month, day := t.Date() + itoa(buf, year, 4) + buf.WriteByte('/') + itoa(buf, int(month), 2) + buf.WriteByte('/') + itoa(buf, day, 2) + buf.WriteByte(' ') + + // H/M/S + hour, min, sec := t.Clock() + itoa(buf, hour, 2) + buf.WriteByte(':') + itoa(buf, min, 2) + buf.WriteByte(':') + itoa(buf, sec, 2) + + // no microseconds + // *buf = append(*buf, '.') + // itoa(buf, t.Nanosecond()/1e3, 6) + + // log level + buf.WriteByte(' ') + buf.WriteString(loglv.String()) + buf.WriteByte(' ') + + // prefix + if len(prefix) > 0 { + buf.WriteString(prefix) + } + buf.WriteByte('\t') +} + +func (l *ioLogger) formatCaller(buf *bytes.Buffer, caller *CallerInfo) { + fname := caller.ShortFileName() + buf.WriteString(fname) + buf.WriteByte(':') + buf.WriteString(strconv.Itoa(caller.line)) + if v := (32 - len(fname)); v > 16 { + buf.Write([]byte{'\t', '\t', '\t'}) + } else if v > 8 { + buf.Write([]byte{'\t', '\t'}) + } else { + buf.WriteByte('\t') + } +} + +func (l *ioLogger) writeEvent(e Event) { + buf := bytes.Buffer{} + l.formatHeader(&buf, e.Prefix, e.Time, e.Level) + if e.Caller.line > 0 { + l.formatCaller(&buf, &e.Caller) + } + if len(e.Message) > 0 { + buf.WriteString(e.Message) + buf.WriteByte(' ') + } + + wr := ioEncoder{&buf} + for _, f := range e.Context { + f.Encode(wr) + buf.WriteByte(' ') + } + for _, f := range e.Fields { + f.Encode(wr) + buf.WriteByte(' ') + } + buf.WriteByte('\n') + l.out.Write(buf.Bytes()) + buf.Reset() +} + +type ioEncoder struct { + io.Writer +} + +func (e ioEncoder) EncodeBool(key string, val bool) { + fmt.Fprintf(e, "%s=%t", key, val) +} + +func (e ioEncoder) EncodeFloat64(key string, val float64) { + fmt.Fprintf(e, "%s=%f", key, val) +} + +func (e ioEncoder) EncodeInt(key string, val int) { + fmt.Fprintf(e, "%s=%d", key, val) +} + +func (e ioEncoder) EncodeInt64(key string, val int64) { + fmt.Fprintf(e, "%s=%d", key, val) +} + +func (e ioEncoder) EncodeDuration(key string, val time.Duration) { + fmt.Fprintf(e, "%s=%s", key, val) +} + +func (e ioEncoder) EncodeUint(key string, val uint) { + fmt.Fprintf(e, "%s=%d", key, val) +} + +func (e ioEncoder) EncodeUint64(key string, val uint64) { + fmt.Fprintf(e, "%s=%d", key, val) +} + +func (e ioEncoder) EncodeString(key string, val string) { + fmt.Fprintf(e, "%s=%q", key, val) +} + +func (e ioEncoder) EncodeObject(key string, val interface{}) { + fmt.Fprintf(e, "%s=%v", key, val) +} + +func (e ioEncoder) EncodeType(key string, val reflect.Type) { + fmt.Fprintf(e, "%s=%v", key, val) +} + +func (e ioEncoder) EncodeCaller(key string, val CallerInfo) { + fname := val.fname + idx := strings.LastIndexByte(fname, '/') + if idx >= len(fname) { + // fname = fname + } else { + fname = fname[idx+1:] + } + fmt.Fprintf(e, "%s=%s:%d", key, fname, val.line) +} diff --git a/make.bat b/make.bat new file mode 100644 index 0000000000000000000000000000000000000000..1a66113401a1629b924ede1476faed0e57871354 --- /dev/null +++ b/make.bat @@ -0,0 +1,61 @@ +@setlocal enabledelayedexpansion +@setlocal enableextensions + +::@set PACKAGE_PATH=gitee.com\simplexyz\simpleactor-go +@set WORK_DIR=%~dp0 +::@set GOPATH=%WORK_DIR% +::@set GOPROXY=https://goproxy.cn + +@IF "%1" == "" call :all & cd %WORK_DIR% & goto :exit + +@IF "%1" == "all" call :all & cd %WORK_DIR% & goto :exit + +@IF "%1" == "clean-log" call :clean-log & cd %WORK_DIR% & goto :exit + +@IF "%1" == "mod-tidy" call :mod-tidy & cd %WORK_DIR% & goto :EXIT + +@IF "%1" == "update-proto" call :update-proto & cd %WORK_DIR% & goto :exit + +@echo unsupported operate [%1] + +@goto :exit + + +:all +@echo make all begin +@echo make all end +@goto :exit + + +:clean-log +del /f /s /q /a *.log +@goto exit + + +:mod-tidy +@echo [mod-tidy] begin +@echo off +@for /R %WORK_DIR% %%f in (*.mod) do ( + @set "GO_MOD_FILE_DIR=%%~dpf" + @cd !GO_MOD_FILE_DIR! + echo go mod tidy in [!GO_MOD_FILE_DIR!] + go mod tidy +) +@echo on +@echo [mod-tidy] end +@goto :EXIT + +:update-proto +@echo [update-proto] begin +go get -u github.com/gogo/protobuf/proto +go get -u github.com/gogo/protobuf/protoc-gen-gogo +go get -u github.com/gogo/protobuf/gogoproto +go get -u github.com/gogo/protobuf/protoc-gen-gofast +go get -u google.golang.org/grpc +go get -u github.com/gogo/protobuf/protoc-gen-gogofast +go get -u github.com/gogo/protobuf/protoc-gen-gogofaster +go get -u github.com/gogo/protobuf/protoc-gen-gogoslick +@echo [update-proto] end +@goto :exit + +:exit diff --git a/metrics/actor_metrics.go b/metrics/actor_metrics.go new file mode 100644 index 0000000000000000000000000000000000000000..663cc6182dbf96e66de5138c0c2c4c764d2b2027 --- /dev/null +++ b/metrics/actor_metrics.go @@ -0,0 +1,156 @@ +// Copyright (C) 2017 - 2022 Asynkron.se + +package metrics + +import ( + "fmt" + "sync" + + "gitee.com/simplexyz/simpleactor-go/log" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/metric" +) + +const LibName string = "protoactor" + +type ActorMetrics struct { + // Mutual Exclusion Primitive to use with ActorMailboxLength + mu *sync.Mutex + + // MetricsID + ID string + + // Actors + ActorFailureCount metric.Int64Counter + ActorMailboxLength metric.Int64ObservableGauge + ActorMessageReceiveHistogram metric.Float64Histogram + ActorRestartedCount metric.Int64Counter + ActorSpawnCount metric.Int64Counter + ActorStoppedCount metric.Int64Counter + + // Deadletters + DeadLetterCount metric.Int64Counter + + // Futures + FuturesStartedCount metric.Int64Counter + FuturesCompletedCount metric.Int64Counter + FuturesTimedOutCount metric.Int64Counter + + // Threadpool + ThreadPoolLatency metric.Int64Histogram +} + +// NewActorMetrics creates a new ActorMetrics value and returns a pointer to it +func NewActorMetrics() *ActorMetrics { + instruments := newInstruments() + return instruments +} + +// newInstruments will create instruments using a meter from +// the given provider p +func newInstruments() *ActorMetrics { + meter := otel.Meter(LibName) + instruments := ActorMetrics{mu: &sync.Mutex{}} + + var err error + + if instruments.ActorFailureCount, err = meter.Int64Counter( + "protoactor_actor_failure_count", + metric.WithDescription("Number of actor failures"), + metric.WithUnit("1"), + ); err != nil { + err = fmt.Errorf("failed to create ActorFailureCount instrument, %w", err) + plog.Error(err.Error(), log.Error(err)) + } + + if instruments.ActorMessageReceiveHistogram, err = meter.Float64Histogram( + "protoactor_actor_message_receive_duration_seconds", + metric.WithDescription("Actor's messages received duration in seconds"), + ); err != nil { + err = fmt.Errorf("failed to create ActorMessageReceiveHistogram instrument, %w", err) + plog.Error(err.Error(), log.Error(err)) + } + + if instruments.ActorRestartedCount, err = meter.Int64Counter( + "protoactor_actor_restarted_count", + metric.WithDescription("Number of actors restarts"), + metric.WithUnit("1"), + ); err != nil { + err = fmt.Errorf("failed to create ActorRestartedCount instrument, %w", err) + plog.Error(err.Error(), log.Error(err)) + } + + if instruments.ActorStoppedCount, err = meter.Int64Counter( + "protoactor_actor_stopped_count", + metric.WithDescription("Number of actors stopped"), + metric.WithUnit("1"), + ); err != nil { + err = fmt.Errorf("failed to create ActorStoppedCount instrument, %w", err) + plog.Error(err.Error(), log.Error(err)) + } + + if instruments.ActorSpawnCount, err = meter.Int64Counter( + "protoactor_actor_spawn_count", + metric.WithDescription("Number of actors spawn"), + metric.WithUnit("1"), + ); err != nil { + err = fmt.Errorf("failed to create ActorSpawnCount instrument, %w", err) + plog.Error(err.Error(), log.Error(err)) + } + + if instruments.DeadLetterCount, err = meter.Int64Counter( + "protoactor_deadletter_count", + metric.WithDescription("Number of deadletters"), + metric.WithUnit("1"), + ); err != nil { + err = fmt.Errorf("failed to create DeadLetterCount instrument, %w", err) + plog.Error(err.Error(), log.Error(err)) + } + + if instruments.FuturesCompletedCount, err = meter.Int64Counter( + "protoactor_futures_completed_count", + metric.WithDescription("Number of futures completed"), + metric.WithUnit("1"), + ); err != nil { + err = fmt.Errorf("failed to create FuturesCompletedCount instrument, %w", err) + plog.Error(err.Error(), log.Error(err)) + } + + if instruments.FuturesStartedCount, err = meter.Int64Counter( + "protoactor_futures_started_count", + metric.WithDescription("Number of futures started"), + metric.WithUnit("1"), + ); err != nil { + err = fmt.Errorf("failed to create FuturesStartedCount instrument, %w", err) + plog.Error(err.Error(), log.Error(err)) + } + + if instruments.FuturesTimedOutCount, err = meter.Int64Counter( + "protoactor_futures_timed_out_count", + metric.WithDescription("Number of futures timed out"), + metric.WithUnit("1"), + ); err != nil { + err = fmt.Errorf("failed to create FuturesTimedOutCount instrument, %w", err) + plog.Error(err.Error(), log.Error(err)) + } + + if instruments.ThreadPoolLatency, err = meter.Int64Histogram( + "protoactor_thread_pool_latency_duration_seconds", + metric.WithDescription("History of latency in second"), + metric.WithUnit("ms"), + ); err != nil { + err = fmt.Errorf("failed to create ThreadPoolLatency instrument, %w", err) + plog.Error(err.Error(), log.Error(err)) + } + + return &instruments +} + +// SetActorMailboxLengthGauge makes sure access to ActorMailboxLength is sequenced +func (am *ActorMetrics) SetActorMailboxLengthGauge(gauge metric.Int64ObservableGauge) { + // lock our mutex + am.mu.Lock() + defer am.mu.Unlock() + + am.ActorMailboxLength = gauge +} diff --git a/metrics/log.go b/metrics/log.go new file mode 100644 index 0000000000000000000000000000000000000000..1210329e557e7bcae91e1c8155e014de67311e3d --- /dev/null +++ b/metrics/log.go @@ -0,0 +1,14 @@ +// Copyright (C) 2017 - 2022 Asynkron.se + +package metrics + +import "gitee.com/simplexyz/simpleactor-go/log" + +var plog = log.New(log.DefaultLevel, "[METRICS]") + +// SetLogLevel sets the log level for the logger. +// +// SetLogLevel is safe to call concurrently +func SetLogLevel(level log.Level) { + plog.SetLevel(level) +} diff --git a/metrics/metrics.go b/metrics/metrics.go new file mode 100644 index 0000000000000000000000000000000000000000..ddcfdfb20ac509b1d83012926e54891f7f9f489f --- /dev/null +++ b/metrics/metrics.go @@ -0,0 +1,55 @@ +// Copyright (C) 2017 - 2022 Asynkron.se + +package metrics + +import ( + "fmt" + "sync" + + "gitee.com/simplexyz/simpleactor-go/log" + "go.opentelemetry.io/otel/metric" +) + +const InternalActorMetrics string = "internal.actor.metrics" + +type ProtoMetrics struct { + mu sync.Mutex + actorMetrics *ActorMetrics + knownMetrics map[string]*ActorMetrics +} + +func NewProtoMetrics(provider metric.MeterProvider) *ProtoMetrics { + protoMetrics := ProtoMetrics{ + actorMetrics: NewActorMetrics(), + knownMetrics: make(map[string]*ActorMetrics), + } + + protoMetrics.Register(InternalActorMetrics, protoMetrics.actorMetrics) + return &protoMetrics +} + +func (pm *ProtoMetrics) Instruments() *ActorMetrics { return pm.actorMetrics } + +func (pm *ProtoMetrics) Register(key string, instance *ActorMetrics) { + pm.mu.Lock() + defer pm.mu.Unlock() + + if _, ok := pm.knownMetrics[key]; ok { + err := fmt.Errorf("could not register instance %#v of metrics, %s already registered", instance, key) + plog.Error(err.Error(), log.Error(err)) + return + } + + pm.knownMetrics[key] = instance +} + +func (pm *ProtoMetrics) Get(key string) *ActorMetrics { + metrics, ok := pm.knownMetrics[key] + if !ok { + err := fmt.Errorf("unknown metrics for the given %s key", key) + plog.Error(err.Error(), log.Error(err)) + return nil + } + + return metrics +} diff --git a/persistence/in_memory_provider.go b/persistence/in_memory_provider.go new file mode 100644 index 0000000000000000000000000000000000000000..9e178e8feb529712eeb1fe422eea00b1b5133f69 --- /dev/null +++ b/persistence/in_memory_provider.go @@ -0,0 +1,85 @@ +package persistence + +import ( + "sync" + + "google.golang.org/protobuf/proto" +) + +type entry struct { + eventIndex int // the event index right after snapshot + snapshot proto.Message + events []proto.Message +} + +type InMemoryProvider struct { + snapshotInterval int + mu sync.RWMutex + store map[string]*entry // actorName -> a persistence entry +} + +func NewInMemoryProvider(snapshotInterval int) *InMemoryProvider { + return &InMemoryProvider{ + snapshotInterval: snapshotInterval, + store: make(map[string]*entry), + } +} + +// loadOrInit returns the existing entry for actorName if present. +// Otherwise, it initializes and returns an empty entry. +// The loaded result is true if the entry was loaded, false if initialized. +func (provider *InMemoryProvider) loadOrInit(actorName string) (e *entry, loaded bool) { + provider.mu.RLock() + e, ok := provider.store[actorName] + provider.mu.RUnlock() + + if !ok { + provider.mu.Lock() + e = &entry{} + provider.store[actorName] = e + provider.mu.Unlock() + } + + return e, ok +} + +func (provider *InMemoryProvider) Restart() {} + +func (provider *InMemoryProvider) GetSnapshotInterval() int { + return provider.snapshotInterval +} + +func (provider *InMemoryProvider) GetSnapshot(actorName string) (snapshot interface{}, eventIndex int, ok bool) { + entry, loaded := provider.loadOrInit(actorName) + if !loaded || entry.snapshot == nil { + return nil, 0, false + } + return entry.snapshot, entry.eventIndex, true +} + +func (provider *InMemoryProvider) PersistSnapshot(actorName string, eventIndex int, snapshot proto.Message) { + entry, _ := provider.loadOrInit(actorName) + entry.eventIndex = eventIndex + entry.snapshot = snapshot +} + +func (provider *InMemoryProvider) DeleteSnapshots(actorName string, inclusiveToIndex int) { +} + +func (provider *InMemoryProvider) GetEvents(actorName string, eventIndexStart int, eventIndexEnd int, callback func(e interface{})) { + entry, _ := provider.loadOrInit(actorName) + if eventIndexEnd == 0 { + eventIndexEnd = len(entry.events) + } + for _, e := range entry.events[eventIndexStart:eventIndexEnd] { + callback(e) + } +} + +func (provider *InMemoryProvider) PersistEvent(actorName string, eventIndex int, event proto.Message) { + entry, _ := provider.loadOrInit(actorName) + entry.events = append(entry.events, event) +} + +func (provider *InMemoryProvider) DeleteEvents(actorName string, inclusiveToIndex int) { +} diff --git a/persistence/messages.go b/persistence/messages.go new file mode 100644 index 0000000000000000000000000000000000000000..45d0f3c8989bf45ec30cb546a5dd07798b7596d8 --- /dev/null +++ b/persistence/messages.go @@ -0,0 +1,10 @@ +package persistence + +type ( + Replay struct{} + ReplayComplete struct{} + OfferSnapshot struct { + Snapshot interface{} + } +) +type RequestSnapshot struct{} diff --git a/persistence/persistence_provider.go b/persistence/persistence_provider.go new file mode 100644 index 0000000000000000000000000000000000000000..da9a0b4fbfa8b050b599192df3e12f030fa78f51 --- /dev/null +++ b/persistence/persistence_provider.go @@ -0,0 +1,31 @@ +package persistence + +import ( + "google.golang.org/protobuf/proto" +) + +// Provider is the abstraction used for persistence +type Provider interface { + GetState() ProviderState +} + +// ProviderState is an object containing the implementation for the provider +type ProviderState interface { + SnapshotStore + EventStore + + Restart() + GetSnapshotInterval() int +} + +type SnapshotStore interface { + GetSnapshot(actorName string) (snapshot interface{}, eventIndex int, ok bool) + PersistSnapshot(actorName string, snapshotIndex int, snapshot proto.Message) + DeleteSnapshots(actorName string, inclusiveToIndex int) +} + +type EventStore interface { + GetEvents(actorName string, eventIndexStart int, eventIndexEnd int, callback func(e interface{})) + PersistEvent(actorName string, eventIndex int, event proto.Message) + DeleteEvents(actorName string, inclusiveToIndex int) +} diff --git a/persistence/plugin.go b/persistence/plugin.go new file mode 100644 index 0000000000000000000000000000000000000000..66b43174d1c9fbd98f7c06b6f96517ae810c467e --- /dev/null +++ b/persistence/plugin.go @@ -0,0 +1,75 @@ +package persistence + +import ( + "gitee.com/simplexyz/simpleactor-go/actor" + "google.golang.org/protobuf/proto" +) + +type persistent interface { + init(provider Provider, context actor.Context) + PersistReceive(message proto.Message) + PersistSnapshot(snapshot proto.Message) + Recovering() bool + Name() string +} + +type Mixin struct { + eventIndex int + providerState ProviderState + name string + receiver receiver + recovering bool +} + +// enforces that Mixin implements persistent interface +// (if they diverge, code breaks in other packages) +var _ persistent = (*Mixin)(nil) + +func (mixin *Mixin) Recovering() bool { + return mixin.recovering +} + +func (mixin *Mixin) Name() string { + return mixin.name +} + +func (mixin *Mixin) PersistReceive(message proto.Message) { + mixin.providerState.PersistEvent(mixin.Name(), mixin.eventIndex, message) + if mixin.eventIndex%mixin.providerState.GetSnapshotInterval() == 0 { + mixin.receiver.Receive(&actor.MessageEnvelope{Message: &RequestSnapshot{}}) + } + mixin.eventIndex++ +} + +func (mixin *Mixin) PersistSnapshot(snapshot proto.Message) { + mixin.providerState.PersistSnapshot(mixin.Name(), mixin.eventIndex, snapshot) +} + +func (mixin *Mixin) init(provider Provider, context actor.Context) { + if mixin.providerState == nil { + mixin.providerState = provider.GetState() + } + + receiver := context.(receiver) + + mixin.name = context.Self().Id + mixin.eventIndex = 0 + mixin.receiver = receiver + mixin.recovering = true + + mixin.providerState.Restart() + if snapshot, eventIndex, ok := mixin.providerState.GetSnapshot(mixin.Name()); ok { + mixin.eventIndex = eventIndex + receiver.Receive(&actor.MessageEnvelope{Message: snapshot}) + } + mixin.providerState.GetEvents(mixin.Name(), mixin.eventIndex, 0 /* 0 means max */, func(e interface{}) { + receiver.Receive(&actor.MessageEnvelope{Message: e}) + mixin.eventIndex++ + }) + mixin.recovering = false + receiver.Receive(&actor.MessageEnvelope{Message: &ReplayComplete{}}) +} + +type receiver interface { + Receive(message *actor.MessageEnvelope) +} diff --git a/persistence/plugin_test.go b/persistence/plugin_test.go new file mode 100644 index 0000000000000000000000000000000000000000..fc07a73ebc7550accd93f10158e84111670cf785 --- /dev/null +++ b/persistence/plugin_test.go @@ -0,0 +1,178 @@ +package persistence + +import ( + "fmt" + "sync" + "testing" + + "google.golang.org/protobuf/proto" + + "gitee.com/simplexyz/simpleactor-go/actor" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +/* +Use some common types from persistence example to setup +test cases +*/ + +const ActorName = "demo.actor" + +var system = actor.NewActorSystem() + +type dataStore struct { + providerState ProviderState +} + +// initData sets up a data store +// it adds one event to set state for every sting passed in +// set the last snapshot to given index of those events +func initData(snapshotInterval, lastSnapshot int, states ...string) *dataStore { + // add all events + state := NewInMemoryProvider(snapshotInterval) + for i, s := range states { + state.PersistEvent(ActorName, i, newMessage(s)) + } + // mark one as a snapshot + if lastSnapshot < len(states) { + snapshot := states[lastSnapshot] + state.PersistSnapshot( + ActorName, lastSnapshot, newSnapshot(snapshot), + ) + } + return &dataStore{providerState: state} +} + +func (p *dataStore) GetState() ProviderState { + return p.providerState +} + +type protoMsg struct { + proto.Message + state string +} + +func (p *protoMsg) Reset() {} +func (p *protoMsg) String() string { return p.state } +func (p *protoMsg) ProtoMessage() {} + +type ( + Message struct{ protoMsg } + Snapshot struct{ protoMsg } + Query struct{ protoMsg } +) + +func newMessage(state string) *Message { + return &Message{protoMsg: protoMsg{state: state}} +} + +func newSnapshot(state string) *Snapshot { + return &Snapshot{protoMsg: protoMsg{state: state}} +} + +type myActor struct { + Mixin + state string +} + +var _ actor.Actor = (*myActor)(nil) + +func makeActor() actor.Actor { + return &myActor{} +} + +var ( + queryWg sync.WaitGroup + queryState string +) + +func (a *myActor) Receive(ctx actor.Context) { + switch msg := ctx.Message().(type) { + case *RequestSnapshot: + // PersistSnapshot when requested + a.PersistSnapshot(newSnapshot(a.state)) + case *Snapshot: + // Restore from Snapshot + a.state = msg.state + case *Message: + // Persist all events received outside of recovery + if !a.Recovering() { + a.PersistReceive(msg) + } + // Set state to whatever message says + a.state = msg.state + case *Query: + // TODO: this is poorly writen... + // I have no idea how to synchronously block on the + // receipt of a message for test cases. + queryState = a.state + queryWg.Done() + } +} + +/****** test code *******/ + +func TestRecovery(t *testing.T) { + cases := []struct { + init *dataStore + msgs []string + afterMsgs string + }{ + // replay with no state + 0: {initData(5, 0), nil, ""}, + + // replay directly on snapshot, no more messages + 1: {initData(8, 2, "a", "b", "c"), nil, "c"}, + + // replay with snapshot and events, add another event + 2: {initData(8, 1, "a", "b", "c"), []string{"d"}, "d"}, + + // replay state and add an event, which triggers snapshot + 3: {initData(4, 1, "a", "b", "c"), []string{"d"}, "d"}, + + // replay state and add an event, which triggers snapshot, + // and then another one + 4: {initData(4, 1, "a", "b", "c"), []string{"d", "e"}, "e"}, + } + + for i, tc := range cases { + t.Run(fmt.Sprintf("case-%d", i), func(t *testing.T) { + rootContext := system.Root + props := actor.PropsFromProducer(makeActor, + actor.WithReceiverMiddleware(Using(tc.init))) + pid, err := rootContext.SpawnNamed(props, ActorName) + require.NoError(t, err) + + // send a bunch of messages + for _, msg := range tc.msgs { + rootContext.Send(pid, newMessage(msg)) + } + + // ugly way to block on a response.... + // TODO: I need some help here + queryWg.Add(1) + rootContext.Send(pid, &Query{}) + queryWg.Wait() + // check the state after all these messages + assert.Equal(t, tc.afterMsgs, queryState) + + // wait for shutdown + _ = rootContext.PoisonFuture(pid).Wait() + + pid, err = rootContext.SpawnNamed(props, ActorName) + require.NoError(t, err) + + // ugly way to block on a response.... + // TODO: I need some help here + queryWg.Add(1) + rootContext.Send(pid, &Query{}) + queryWg.Wait() + // check the state after all these messages + assert.Equal(t, tc.afterMsgs, queryState) + + // shutdown at end of test for cleanup + _ = rootContext.PoisonFuture(pid).Wait() + }) + } +} diff --git a/persistence/protocb/config.go b/persistence/protocb/config.go new file mode 100644 index 0000000000000000000000000000000000000000..12183df56198699f1b4ccf850a01db217dedfba2 --- /dev/null +++ b/persistence/protocb/config.go @@ -0,0 +1,20 @@ +package protocb + +type couchbaseConfig struct { + async bool + snapshotInterval int +} + +type CouchbaseOption func(*couchbaseConfig) + +func WithAsync() CouchbaseOption { + return func(config *couchbaseConfig) { + config.async = true + } +} + +func WithSnapshot(interval int) CouchbaseOption { + return func(config *couchbaseConfig) { + config.snapshotInterval = interval + } +} diff --git a/persistence/protocb/envelope.go b/persistence/protocb/envelope.go new file mode 100644 index 0000000000000000000000000000000000000000..3836963aa324d795afd3a0e089277bef0f7c66db --- /dev/null +++ b/persistence/protocb/envelope.go @@ -0,0 +1,46 @@ +package protocb + +import ( + "encoding/json" + "log" + + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/reflect/protoreflect" + "google.golang.org/protobuf/reflect/protoregistry" +) + +type envelope struct { + Type string `json:"type"` // reflected message type so we can deserialize back + Message json.RawMessage `json:"event"` // this is still protobuf but the json form + EventIndex int `json:"eventIndex"` // event index in the event stream + DocType string `json:"doctype"` // type snapshot or event +} + +func newEnvelope(message proto.Message, doctype string, eventIndex int) *envelope { + typeName := proto.MessageName(message) + bytes, err := json.Marshal(message) + if err != nil { + log.Fatal(err) + } + envelope := &envelope{ + Type: string(typeName), + Message: bytes, + EventIndex: eventIndex, + DocType: doctype, + } + return envelope +} + +func (envelope *envelope) message() proto.Message { + mt, err := protoregistry.GlobalTypes.FindMessageByName(protoreflect.FullName(envelope.Type)) + if err != nil { + log.Fatal(err) + } + + pm := mt.New().Interface() + err = json.Unmarshal(envelope.Message, pm) + if err != nil { + log.Fatal(err) + } + return pm +} diff --git a/persistence/protocb/json_transcoder.go b/persistence/protocb/json_transcoder.go new file mode 100644 index 0000000000000000000000000000000000000000..959eea4ea95d042f3c3d4a6f5d3c4eea8588d804 --- /dev/null +++ b/persistence/protocb/json_transcoder.go @@ -0,0 +1,21 @@ +package protocb + +import "encoding/json" + +type transcoder struct{} + +func (t transcoder) Decode(bytes []byte, flags uint32, out interface{}) error { + err := json.Unmarshal(bytes, &out) + if err != nil { + return err + } + return nil +} + +func (t transcoder) Encode(value interface{}) ([]byte, uint32, error) { + bytes, err := json.Marshal(value) + if err != nil { + return nil, 0, err + } + return bytes, 0, nil +} diff --git a/persistence/protocb/key.go b/persistence/protocb/key.go new file mode 100644 index 0000000000000000000000000000000000000000..7672df9210b45c47ecb437d253373d910474c570 --- /dev/null +++ b/persistence/protocb/key.go @@ -0,0 +1,13 @@ +package protocb + +import "fmt" + +func formatEventKey(actorName string, eventIndex int) string { + key := fmt.Sprintf("%v-event-%010d", actorName, eventIndex) + return key +} + +func formatSnapshotKey(actorName string, eventIndex int) string { + key := fmt.Sprintf("%v-snapshot-%010d", actorName, eventIndex) + return key +} diff --git a/persistence/protocb/provider.go b/persistence/protocb/provider.go new file mode 100644 index 0000000000000000000000000000000000000000..bcd3a950a4c97f314641147825d494d2504a1355 --- /dev/null +++ b/persistence/protocb/provider.go @@ -0,0 +1,55 @@ +package protocb + +import ( + "log" + "time" + + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/persistence" + "github.com/couchbase/gocb" +) + +type Provider struct { + async bool + bucket *gocb.Bucket + bucketName string + snapshotInterval int + writer *actor.PID +} + +func (provider *Provider) GetState() persistence.ProviderState { + return &cbState{ + Provider: provider, + } +} + +func New(actorSystem *actor.ActorSystem, bucketName string, baseU string, options ...CouchbaseOption) *Provider { + c, err := gocb.Connect(baseU) + if err != nil { + log.Fatalf("Error connecting: %v", err) + } + bucket, err := c.OpenBucketWithMt(bucketName, "") + if err != nil { + log.Fatalf("Error getting bucket: %v", err) + } + bucket.SetTranscoder(transcoder{}) + + config := &couchbaseConfig{} + for _, option := range options { + option(config) + } + + provider := &Provider{ + snapshotInterval: config.snapshotInterval, + async: config.async, + bucket: bucket, + bucketName: bucketName, + } + + if config.async { + pid := actorSystem.Root.Spawn(actor.PropsFromFunc(newWriter(time.Second / 10000))) + provider.writer = pid + } + + return provider +} diff --git a/persistence/protocb/provider_state.go b/persistence/protocb/provider_state.go new file mode 100644 index 0000000000000000000000000000000000000000..6eb1b8dd9a5e5f6a955403c4c9aa61db701a48e5 --- /dev/null +++ b/persistence/protocb/provider_state.go @@ -0,0 +1,122 @@ +package protocb + +import ( + "log" + "sync" + + "github.com/couchbase/gocb" + "google.golang.org/protobuf/proto" +) + +type cbState struct { + *Provider + wg sync.WaitGroup +} + +func (state *cbState) Restart() { + // wait for any pending writes to complete + state.wg.Wait() +} + +func (state *cbState) GetEvents(actorName string, eventIndexStart int, eventIndexEnd int, callback func(event interface{})) { + q := gocb.NewN1qlQuery("SELECT b.* FROM `" + state.bucketName + "` b WHERE meta(b).id >= $1 and meta(b).id <= $2") + q.Consistency(gocb.RequestPlus) + + // read all + if eventIndexEnd == 0 { + eventIndexEnd = 9999999999 + } + + var p []interface{} + p = append(p, formatEventKey(actorName, eventIndexStart)) + p = append(p, formatEventKey(actorName, eventIndexEnd)) + + rows, err := state.bucket.ExecuteN1qlQuery(q, p) + if err != nil { + log.Fatalf("Error executing N1ql: %v", err) + } + defer func() { + err := rows.Close() + if err != nil { + log.Fatalf("Error closing gocb reader: %v", err) + } + }() + + var row envelope + i := eventIndexStart + for rows.Next(&row) { + e := row.message() + if row.EventIndex != i { + log.Printf("%v, Invalid actor state, missing event %v", actorName, i) + return + } + callback(e) + i++ + } +} + +func (state *cbState) GetSnapshot(actorName string) (snapshot interface{}, eventIndex int, ok bool) { + q := gocb.NewN1qlQuery("SELECT b.* FROM `" + state.bucketName + "` b WHERE meta(b).id >= $1 and meta(b).id <= $2 order by b.eventIndex desc limit 1") + q.Consistency(gocb.RequestPlus) + + var p []interface{} + p = append(p, formatSnapshotKey(actorName, 0)) + p = append(p, formatSnapshotKey(actorName, 9999999999)) + + rows, err := state.bucket.ExecuteN1qlQuery(q, p) + if err != nil { + log.Fatalf("Error executing N1ql: %v", err) + } + defer func() { + err := rows.Close() + if err != nil { + log.Fatalf("Error closing gocb reader: %v", err) + } + }() + + var row envelope + if rows.Next(&row) { + return row.message(), row.EventIndex, true + } + return nil, 0, false +} + +func (provider *Provider) GetSnapshotInterval() int { + return provider.snapshotInterval +} + +func (state *cbState) PersistEvent(actorName string, eventIndex int, event proto.Message) { + key := formatEventKey(actorName, eventIndex) + envelope := newEnvelope(event, "event", eventIndex) + state.persistEnvelope(key, envelope) +} + +func (state *cbState) DeleteEvents(actorName string, inclusiveToIndex int) { + panic("implement me") +} + +func (state *cbState) PersistSnapshot(actorName string, eventIndex int, snapshot proto.Message) { + key := formatSnapshotKey(actorName, eventIndex) + envelope := newEnvelope(snapshot, "snapshot", eventIndex) + state.persistEnvelope(key, envelope) +} + +func (state *cbState) DeleteSnapshots(actorName string, inclusiveToIndex int) { + panic("implement me") +} + +func (state *cbState) persistEnvelope(key string, envelope *envelope) { + state.wg.Add(1) + persist := func() { + _, err := state.bucket.Insert(key, envelope, 0) + if err != nil { + log.Fatal(err) + } + state.wg.Done() + } + if state.async { + // state.writer.Tell(&write{fun: persist}) + } else { + persist() + } +} diff --git a/persistence/protocb/writer.go b/persistence/protocb/writer.go new file mode 100644 index 0000000000000000000000000000000000000000..6b021efe503bf617c9516ccb4e749f12144fbcaf --- /dev/null +++ b/persistence/protocb/writer.go @@ -0,0 +1,21 @@ +package protocb + +import ( + "time" + + "gitee.com/simplexyz/simpleactor-go/actor" +) + +type write struct { + fun func() +} + +func newWriter(rate time.Duration) func(actor.Context) { + return func(context actor.Context) { + switch msg := context.Message().(type) { + case *write: + go msg.fun() + // time.Sleep(rate) + } + } +} diff --git a/persistence/receiver.go b/persistence/receiver.go new file mode 100644 index 0000000000000000000000000000000000000000..a26d9b8315d96c1511aca439778bbc68053ee4d2 --- /dev/null +++ b/persistence/receiver.go @@ -0,0 +1,33 @@ +package persistence + +import ( + "log" + "reflect" + + "gitee.com/simplexyz/simpleactor-go/actor" +) + +func Using(provider Provider) func(next actor.ReceiverFunc) actor.ReceiverFunc { + return func(next actor.ReceiverFunc) actor.ReceiverFunc { + fn := func(ctx actor.ReceiverContext, env *actor.MessageEnvelope) { + switch env.Message.(type) { + + // intercept the started event, handle it and then apply the persistence init logic + case *actor.Started: + next(ctx, env) + + // check if the actor is persistent + if p, ok := ctx.Actor().(persistent); ok { + // initialize it + p.init(provider, ctx.(actor.Context)) + } else { + // not an persistent actor, bail out + log.Fatalf("Actor type %v is not persistent", reflect.TypeOf(ctx.Actor())) + } + default: + next(ctx, env) + } + } + return fn + } +} diff --git a/plugin/passivation.go b/plugin/passivation.go new file mode 100644 index 0000000000000000000000000000000000000000..71340add2b697303c66d3229bc0df74dd3cd8436 --- /dev/null +++ b/plugin/passivation.go @@ -0,0 +1,69 @@ +package plugin + +import ( + "log" + "sync/atomic" + "time" + + "gitee.com/simplexyz/simpleactor-go/actor" +) + +type PassivationAware interface { + Init(*actor.ActorSystem, *actor.PID, time.Duration) + Reset(time.Duration) + Cancel() +} + +type PassivationHolder struct { + timer *time.Timer + done int32 +} + +func (state *PassivationHolder) Reset(duration time.Duration) { + if state.timer == nil { + log.Fatalf("Cannot reset passivation of a non-started actor") + } + if atomic.LoadInt32(&state.done) == 0 { + state.timer.Reset(duration) + } +} + +func (state *PassivationHolder) Init(actorSystem *actor.ActorSystem, pid *actor.PID, duration time.Duration) { + state.timer = time.NewTimer(duration) + state.done = 0 + go func() { + select { + case <-state.timer.C: + actorSystem.Root.Stop(pid) + atomic.StoreInt32(&state.done, 1) + break + } + }() +} + +func (state *PassivationHolder) Cancel() { + if state.timer != nil { + state.timer.Stop() + } +} + +type PassivationPlugin struct { + Duration time.Duration +} + +func (pp *PassivationPlugin) OnStart(ctx actor.ReceiverContext) { + if a, ok := ctx.Actor().(PassivationAware); ok { + a.Init(ctx.ActorSystem(), ctx.Self(), pp.Duration) + } +} + +func (pp *PassivationPlugin) OnOtherMessage(ctx actor.ReceiverContext, env *actor.MessageEnvelope) { + if p, ok := ctx.Actor().(PassivationAware); ok { + switch env.Message.(type) { + case *actor.Stopped: + p.Cancel() + default: + p.Reset(pp.Duration) + } + } +} diff --git a/plugin/passivation_test.go b/plugin/passivation_test.go new file mode 100644 index 0000000000000000000000000000000000000000..d2ce4d4497ec892c8ea2fd39640dd8723f872ddf --- /dev/null +++ b/plugin/passivation_test.go @@ -0,0 +1,54 @@ +package plugin + +import ( + "testing" + "time" + + "gitee.com/simplexyz/simpleactor-go/actor" + "github.com/stretchr/testify/assert" +) + +var system = actor.NewActorSystem() + +type SmartActor struct { + PassivationHolder +} + +func (state *SmartActor) Receive(context actor.Context) { + switch context.Message().(type) { + } +} + +func TestPassivation(t *testing.T) { + if testing.Short() { + t.SkipNow() + } + + UnitOfTime := 200 * time.Millisecond + PassivationDuration := 3 * UnitOfTime + rootContext := system.Root + props := actor. + PropsFromProducer(func() actor.Actor { return &SmartActor{} }, + actor.WithReceiverMiddleware(Use(&PassivationPlugin{Duration: PassivationDuration}))) + + pid := rootContext.Spawn(props) + time.Sleep(UnitOfTime) + time.Sleep(UnitOfTime) + { + _, found := system.ProcessRegistry.GetLocal(pid.Id) + assert.True(t, found) + } + rootContext.Send(pid, "keepalive") + time.Sleep(UnitOfTime) + time.Sleep(UnitOfTime) + { + _, found := system.ProcessRegistry.GetLocal(pid.Id) + assert.True(t, found) + } + time.Sleep(UnitOfTime) + time.Sleep(UnitOfTime) + { + _, found := system.ProcessRegistry.GetLocal(pid.Id) + assert.False(t, found) + } +} diff --git a/plugin/plugin.go b/plugin/plugin.go new file mode 100644 index 0000000000000000000000000000000000000000..fb581c3d1b33bc7fc693932a58d72179a0ebcc48 --- /dev/null +++ b/plugin/plugin.go @@ -0,0 +1,27 @@ +package plugin + +import ( + "gitee.com/simplexyz/simpleactor-go/actor" +) + +type plugin interface { + OnStart(actor.ReceiverContext) + OnOtherMessage(actor.ReceiverContext, *actor.MessageEnvelope) +} + +func Use(plugin plugin) func(next actor.ReceiverFunc) actor.ReceiverFunc { + return func(next actor.ReceiverFunc) actor.ReceiverFunc { + fn := func(context actor.ReceiverContext, env *actor.MessageEnvelope) { + switch env.Message.(type) { + case *actor.Started: + plugin.OnStart(context) + default: + plugin.OnOtherMessage(context, env) + } + + next(context, env) + } + + return fn + } +} diff --git a/proto/actor.proto b/proto/actor.proto new file mode 100644 index 0000000000000000000000000000000000000000..f2ec3477ead2cacd0f5059cc1d9dcc7741170663 --- /dev/null +++ b/proto/actor.proto @@ -0,0 +1,47 @@ +syntax = "proto3"; +package actor; +option go_package = "gitee.com/simplexyz/simpleactor-go/actor"; + +message PID { + string Address = 1; + string ID = 2; + uint32 RequestID = 3; +} + +//user messages +message PoisonPill { +} + +message DeadLetterResponse { + PID Target = 1; +} + +//system messages +message Watch { + PID Watcher = 1; +} + +message Unwatch { + PID Watcher = 1; +} + +message Terminated { + PID Who = 1; + TerminatedReason Why = 2; +} + +enum TerminatedReason { + Stopped = 0; + AddressTerminated = 1; + NotFound = 2; +} + +message Stop { +} + +message Touch { +} + +message Touched { + PID Who = 1; +} \ No newline at end of file diff --git a/proto/gen_actor.bat b/proto/gen_actor.bat new file mode 100644 index 0000000000000000000000000000000000000000..d4bdfdbd539f24071ddbe8d066b3520eb5a94a20 --- /dev/null +++ b/proto/gen_actor.bat @@ -0,0 +1 @@ +protoc --go_out=..\actor --go_opt=paths=source_relative --proto_path=. actor.proto \ No newline at end of file diff --git a/protobuf/protoc-gen-gograinv2/Makefile b/protobuf/protoc-gen-gograinv2/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..523ee9e7866a32c3c04473a7a37e650ceced927d --- /dev/null +++ b/protobuf/protoc-gen-gograinv2/Makefile @@ -0,0 +1,4 @@ +install: + go build . + @echo installing to ~/go/bin/protoc-gen-gograinv2 + mv ./protoc-gen-gograinv2 ~/go/bin/ diff --git a/protobuf/protoc-gen-gograinv2/main.go b/protobuf/protoc-gen-gograinv2/main.go new file mode 100644 index 0000000000000000000000000000000000000000..81a4cc193b2cdb59566ff8078c488f41ae2558f6 --- /dev/null +++ b/protobuf/protoc-gen-gograinv2/main.go @@ -0,0 +1,67 @@ +package main + +import ( + "bytes" + "strings" + "text/template" + + google_protobuf "github.com/gogo/protobuf/protoc-gen-gogo/descriptor" + plugin "github.com/gogo/protobuf/protoc-gen-gogo/plugin" + "github.com/gogo/protobuf/vanity/command" +) + +func main() { + req := command.Read() + resp := generateCode(req, "_protoactor.go", true) + command.Write(resp) +} + +func removePackagePrefix(name string, pname string) string { + return strings.Replace(name, "."+pname+".", "", 1) +} + +func generateCode(req *plugin.CodeGeneratorRequest, filenameSuffix string, goFmt bool) *plugin.CodeGeneratorResponse { + response := &plugin.CodeGeneratorResponse{} + for _, f := range req.GetProtoFile() { + if !inStringSlice(f.GetName(), req.FileToGenerate) { + continue + } + + s := generate(f) + + // we only generate grains for proto files containing valid service definition + if len(f.GetService()) > 0 { + fileName := strings.Replace(f.GetName(), ".", "_", 1) + "actor.go" + r := &plugin.CodeGeneratorResponse_File{ + Content: &s, + Name: &fileName, + } + + response.File = append(response.File, r) + } + } + + return response +} + +func inStringSlice(val string, ss []string) bool { + for _, s := range ss { + if val == s { + return true + } + } + return false +} + +func generate(file *google_protobuf.FileDescriptorProto) string { + pkg := ProtoAst(file) + + t := template.New("grain") + t, _ = t.Parse(code) + + var doc bytes.Buffer + t.Execute(&doc, pkg) + s := doc.String() + + return s +} diff --git a/protobuf/protoc-gen-gograinv2/proto.go b/protobuf/protoc-gen-gograinv2/proto.go new file mode 100644 index 0000000000000000000000000000000000000000..5342c0e3af71218bedcafcc34a8d923ac3ce207f --- /dev/null +++ b/protobuf/protoc-gen-gograinv2/proto.go @@ -0,0 +1,165 @@ +package main + +import ( + "bytes" + "strings" + "unicode" + "unicode/utf8" + + gogo "github.com/gogo/protobuf/protoc-gen-gogo/descriptor" +) + +// code lifted from gogo proto +var isGoKeyword = map[string]bool{ + "break": true, + "case": true, + "chan": true, + "const": true, + "continue": true, + "default": true, + "else": true, + "defer": true, + "fallthrough": true, + "for": true, + "func": true, + "go": true, + "goto": true, + "if": true, + "import": true, + "interface": true, + "map": true, + "package": true, + "range": true, + "return": true, + "select": true, + "struct": true, + "switch": true, + "type": true, + "var": true, +} + +// ProtoFile reprpesents a parsed proto file +type ProtoFile struct { + PackageName string + Namespace string + Messages []*ProtoMessage + Services []*ProtoService +} + +// ProtoMessage represents a parsed message in a proto file +type ProtoMessage struct { + Name string + PascalName string +} + +// ProtoService represents a parsed service in a proto file +type ProtoService struct { + Name string + PascalName string + Methods []*ProtoMethod +} + +// ProtoMethod represents a parsed method in a proto service +type ProtoMethod struct { + Index int + Name string + PascalName string + Input *ProtoMessage + Output *ProtoMessage + InputStream bool + OutputStream bool +} + +// ProtoAst transforms a FileDescriptor to an AST that can be used for code generation +func ProtoAst(file *gogo.FileDescriptorProto) *ProtoFile { + pkg := &ProtoFile{} + pkg.Namespace = file.GetOptions().GetCsharpNamespace() + + // let us check the option go_package is defined in the file and use that one instead of the + // default one + var packageName string + if file.GetOptions().GetGoPackage() != "" { + packageName = cleanPackageName(file.GetOptions().GetGoPackage()) + } else { + packageName = cleanPackageName(file.GetPackage()) + } + + // let us the go package name + pkg.PackageName = packageName + + messages := make(map[string]*ProtoMessage) + for _, message := range file.GetMessageType() { + m := &ProtoMessage{} + m.Name = message.GetName() + m.PascalName = MakeFirstLowerCase(m.Name) + pkg.Messages = append(pkg.Messages, m) + messages[m.Name] = m + } + + for _, service := range file.GetService() { + s := &ProtoService{} + s.Name = service.GetName() + s.PascalName = MakeFirstLowerCase(s.Name) + pkg.Services = append(pkg.Services, s) + + for i, method := range service.GetMethod() { + m := &ProtoMethod{} + m.Index = i + m.Name = method.GetName() + m.PascalName = MakeFirstLowerCase(m.Name) + // m.InputStream = *method.ClientStreaming + // m.OutputStream = *method.ServerStreaming + input := removePackagePrefix(method.GetInputType(), file.GetPackage()) + output := removePackagePrefix(method.GetOutputType(), file.GetPackage()) + m.Input = messages[input] + m.Output = messages[output] + s.Methods = append(s.Methods, m) + } + } + return pkg +} + +func goPkgLastElement(full string) string { + pkgSplitted := strings.Split(full, "/") + return pkgSplitted[len(pkgSplitted)-1] +} + +// MakeFirstLowerCase makes the first character in a string lower case +func MakeFirstLowerCase(s string) string { + if len(s) < 2 { + return strings.ToLower(s) + } + + bts := []byte(s) + + lc := bytes.ToLower([]byte{bts[0]}) + rest := bts[1:] + + return string(bytes.Join([][]byte{lc, rest}, nil)) +} + +// cleanPackageName lifted from gogo generator +// https://github.com/gogo/protobuf/blob/master/protoc-gen-gogo/generator/generator.go#L695 +func cleanPackageName(name string) string { + parts := strings.Split(name, "/") + name = parts[len(parts)-1] + + name = strings.Map(badToUnderscore, name) + // Identifier must not be keyword: insert _. + if isGoKeyword[name] { + name = "_" + name + } + // Identifier must not begin with digit: insert _. + if r, _ := utf8.DecodeRuneInString(name); unicode.IsDigit(r) { + name = "_" + name + } + return name +} + +// badToUnderscore lifted from gogo generator +func badToUnderscore(r rune) rune { + if unicode.IsLetter(r) || unicode.IsDigit(r) || r == '_' { + return r + } + return '_' +} diff --git a/protobuf/protoc-gen-gograinv2/template.go b/protobuf/protoc-gen-gograinv2/template.go new file mode 100644 index 0000000000000000000000000000000000000000..c7191d084e9a99e30dd315ad2921053f00bc3ff9 --- /dev/null +++ b/protobuf/protoc-gen-gograinv2/template.go @@ -0,0 +1,177 @@ +package main + +const code = `{{ if .Services -}} +// Package {{.PackageName}} is generated by protoactor-go/protoc-gen-gograin@0.1.0 +package {{.PackageName}} + +import ( + "errors" + "fmt" + "math" + "time" + + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/cluster" + logmod "gitee.com/simplexyz/simpleactor-go/log" + "google.golang.org/protobuf/proto" +) + +var ( + plog = logmod.New(logmod.InfoLevel, "[GRAIN][{{.PackageName}}]") + _ = proto.Marshal + _ = fmt.Errorf + _ = math.Inf +) + +// SetLogLevel sets the log level. +func SetLogLevel(level logmod.Level) { + plog.SetLevel(level) +} + +{{ range $service := .Services -}} +var x{{ $service.Name }}Factory func() {{ $service.Name }} + +// {{ $service.Name }}Factory produces a {{ $service.Name }} +func {{ $service.Name }}Factory(factory func() {{ $service.Name }}) { + x{{ $service.Name }}Factory = factory +} + +// Get{{ $service.Name }}GrainClient instantiates a new {{ $service.Name }}GrainClient with given Identity +func Get{{ $service.Name }}GrainClient(c *cluster.Cluster, id string) *{{ $service.Name }}GrainClient { + if c == nil { + panic(fmt.Errorf("nil cluster instance")) + } + if id == "" { + panic(fmt.Errorf("empty id")) + } + return &{{ $service.Name }}GrainClient{Identity: id, cluster: c} +} + +// Get{{ $service.Name }}Kind instantiates a new cluster.Kind for {{ $service.Name }} +func Get{{ $service.Name }}Kind(opts ...actor.PropsOption) *cluster.Kind { + props := actor.PropsFromProducer(func() actor.Actor { + return &{{ $service.Name }}Actor{ + Timeout: 60 * time.Second, + } + }, opts...) + kind := cluster.NewKind("{{ $service.Name }}", props) + return kind +} + +// Get{{ $service.Name }}Kind instantiates a new cluster.Kind for {{ $service.Name }} +func New{{ $service.Name }}Kind(factory func() {{ $service.Name }}, timeout time.Duration ,opts ...actor.PropsOption) *cluster.Kind { + x{{ $service.Name }}Factory = factory + props := actor.PropsFromProducer(func() actor.Actor { + return &{{ $service.Name }}Actor{ + Timeout: timeout, + } + }, opts...) + kind := cluster.NewKind("{{ $service.Name }}", props) + return kind +} + +// {{ $service.Name }} interfaces the services available to the {{ $service.Name }} +type {{ $service.Name }} interface { + Init(ctx cluster.GrainContext) + Terminate(ctx cluster.GrainContext) + ReceiveDefault(ctx cluster.GrainContext) + {{ range $method := $service.Methods -}} + {{ $method.Name }}(*{{ $method.Input.Name }}, cluster.GrainContext) (*{{ $method.Output.Name }}, error) + {{ end }} +} + +// {{ $service.Name }}GrainClient holds the base data for the {{ $service.Name }}Grain +type {{ $service.Name }}GrainClient struct { + Identity string + cluster *cluster.Cluster +} +{{ range $method := $service.Methods}} +// {{ $method.Name }} requests the execution on to the cluster with CallOptions +func (g *{{ $service.Name }}GrainClient) {{ $method.Name }}(r *{{ $method.Input.Name }}, opts ...cluster.GrainCallOption) (*{{ $method.Output.Name }}, error) { + bytes, err := proto.Marshal(r) + if err != nil { + return nil, err + } + reqMsg := &cluster.GrainRequest{MethodIndex: {{ $method.Index }}, MessageData: bytes} + resp, err := g.cluster.Call(g.Identity, "{{ $service.Name }}", reqMsg, opts...) + if err != nil { + return nil, err + } + switch msg := resp.(type) { + case *cluster.GrainResponse: + result := &{{ $method.Output.Name }}{} + err = proto.Unmarshal(msg.MessageData, result) + if err != nil { + return nil, err + } + return result, nil + case *cluster.GrainErrorResponse: + return nil, errors.New(msg.Err) + default: + return nil, errors.New("unknown response") + } +} +{{ end }} + +// {{ $service.Name }}Actor represents the actor structure +type {{ $service.Name }}Actor struct { + ctx cluster.GrainContext + inner {{ $service.Name }} + Timeout time.Duration +} + +// Receive ensures the lifecycle of the actor for the received message +func (a *{{ $service.Name }}Actor) Receive(ctx actor.Context) { + switch msg := ctx.Message().(type) { + case *actor.Started: //pass + case *cluster.ClusterInit: + a.ctx = cluster.NewGrainContext(ctx, msg.Identity, msg.Cluster) + a.inner = x{{ $service.Name }}Factory() + a.inner.Init(a.ctx) + + if a.Timeout > 0 { + ctx.SetReceiveTimeout(a.Timeout) + } + case *actor.ReceiveTimeout: + ctx.Poison(ctx.Self()) + case *actor.Stopped: + a.inner.Terminate(a.ctx) + case actor.AutoReceiveMessage: // pass + case actor.SystemMessage: // pass + + case *cluster.GrainRequest: + switch msg.MethodIndex { + {{ range $method := $service.Methods -}} + case {{ $method.Index }}: + req := &{{ $method.Input.Name }}{} + err := proto.Unmarshal(msg.MessageData, req) + if err != nil { + plog.Error("{{ $method.Name }}({{ $method.Input.Name }}) proto.Unmarshal failed.", logmod.Error(err)) + resp := &cluster.GrainErrorResponse{Err: err.Error()} + ctx.Respond(resp) + return + } + r0, err := a.inner.{{ $method.Name }}(req, a.ctx) + if err != nil { + resp := &cluster.GrainErrorResponse{Err: err.Error()} + ctx.Respond(resp) + return + } + bytes, err := proto.Marshal(r0) + if err != nil { + plog.Error("{{ $method.Name }}({{ $method.Input.Name }}) proto.Marshal failed", logmod.Error(err)) + resp := &cluster.GrainErrorResponse{Err: err.Error()} + ctx.Respond(resp) + return + } + resp := &cluster.GrainResponse{MessageData: bytes} + ctx.Respond(resp) + {{ end }} + } + default: + a.inner.ReceiveDefault(a.ctx) + } +} +{{ end -}} +{{ end -}} +` diff --git a/remote/activator_actor.go b/remote/activator_actor.go new file mode 100644 index 0000000000000000000000000000000000000000..19581c0d385b0fdfdc75d55b99bf4ffac5ad56d1 --- /dev/null +++ b/remote/activator_actor.go @@ -0,0 +1,143 @@ +package remote + +import ( + "errors" + "fmt" + "time" + + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/log" +) + +// Register a known actor props by name +func (r *Remote) Register(kind string, props *actor.Props) { + r.kinds[kind] = props +} + +// GetKnownKinds returns a slice of known actor "Kinds" +func (r *Remote) GetKnownKinds() []string { + keys := make([]string, 0, len(r.kinds)) + for k := range r.kinds { + keys = append(keys, k) + } + return keys +} + +type activator struct { + remote *Remote +} + +// ErrActivatorUnavailable : this error will not panic the Activator. +// It simply tells Partition this Activator is not available +// Partition will then find next available Activator to spawn +var ErrActivatorUnavailable = &ActivatorError{ResponseStatusCodeUNAVAILABLE.ToInt32(), true} + +type ActivatorError struct { + Code int32 + DoNotPanic bool +} + +func (e *ActivatorError) Error() string { + return fmt.Sprint(e.Code) +} + +// ActivatorForAddress returns a PID for the activator at the given address +func (r *Remote) ActivatorForAddress(address string) *actor.PID { + pid := actor.NewPID(address, "activator") + return pid +} + +// SpawnFuture spawns a remote actor and returns a Future that completes once the actor is started +func (r *Remote) SpawnFuture(address, name, kind string, timeout time.Duration) *actor.Future { + activator := r.ActivatorForAddress(address) + f := r.actorSystem.Root.RequestFuture(activator, &ActorPidRequest{ + Name: name, + Kind: kind, + }, timeout) + return f +} + +// Spawn spawns a remote actor of a given type at a given address +func (r *Remote) Spawn(address, kind string, timeout time.Duration) (*ActorPidResponse, error) { + return r.SpawnNamed(address, "", kind, timeout) +} + +// SpawnNamed spawns a named remote actor of a given type at a given address +func (r *Remote) SpawnNamed(address, name, kind string, timeout time.Duration) (*ActorPidResponse, error) { + res, err := r.SpawnFuture(address, name, kind, timeout).Result() + if err != nil { + return nil, err + } + switch msg := res.(type) { + case *ActorPidResponse: + return msg, nil + default: + return nil, errors.New("remote: Unknown response when remote activating") + } +} + +func newActivatorActor(remote *Remote) actor.Producer { + return func() actor.Actor { + return &activator{ + remote: remote, + } + } +} + +func (a *activator) Receive(context actor.Context) { + switch msg := context.Message().(type) { + case *actor.Started: + plog.Info("Started Activator") + case *Ping: + context.Respond(&Pong{}) + case *ActorPidRequest: + props, exist := a.remote.kinds[msg.Kind] + + // if props not exist, return error and panic + if !exist { + response := &ActorPidResponse{ + StatusCode: ResponseStatusCodeERROR.ToInt32(), + } + context.Respond(response) + panic(fmt.Errorf("no Props found for kind %s", msg.Kind)) + } + + name := msg.Name + + // unnamed actor, assign auto ExtensionID + if name == "" { + name = context.ActorSystem().ProcessRegistry.NextId() + } + + pid, err := context.SpawnNamed(props, "Remote$"+name) + + if err == nil { + response := &ActorPidResponse{Pid: pid} + context.Respond(response) + } else if err == actor.ErrNameExists { + response := &ActorPidResponse{ + Pid: pid, + StatusCode: ResponseStatusCodePROCESSNAMEALREADYEXIST.ToInt32(), + } + context.Respond(response) + } else if aErr, ok := err.(*ActivatorError); ok { + response := &ActorPidResponse{ + StatusCode: aErr.Code, + } + context.Respond(response) + if !aErr.DoNotPanic { + panic(err) + } + } else { + response := &ActorPidResponse{ + StatusCode: ResponseStatusCodeERROR.ToInt32(), + } + context.Respond(response) + panic(err) + } + case actor.SystemMessage, actor.AutoReceiveMessage: + // ignore + default: + plog.Error("Activator received unknown message", log.TypeOf("type", msg), log.Message(msg)) + } +} diff --git a/remote/blocklist.go b/remote/blocklist.go new file mode 100644 index 0000000000000000000000000000000000000000..326ac56b00cb3394073defc9bf6fb3e4ab8dc29a --- /dev/null +++ b/remote/blocklist.go @@ -0,0 +1,52 @@ +/* + Copyright (C) 2017 - 2022 Asynkron.se +*/ + +package remote + +import ( + "sync" + + "github.com/asynkron/gofun/set" +) + +// TODO: document it +type BlockList struct { + mu *sync.RWMutex + blockedMembers *set.ImmutableSet[string] +} + +func NewBlockList() *BlockList { + blocklist := BlockList{ + mu: &sync.RWMutex{}, + blockedMembers: set.NewImmutable[string](), + } + return &blocklist +} + +func (bl *BlockList) BlockedMembers() set.Set[string] { + return bl.blockedMembers +} + +// Block adds the given memberID list to the BlockList +func (bl *BlockList) Block(memberIDs ...string) { + // acquire our mutual exclusion primitive + bl.mu.Lock() + defer bl.mu.Unlock() + + bl.blockedMembers = bl.blockedMembers.AddRange(memberIDs...) +} + +// IsBlocked returns true if the given memberID string has been +// ever added to the BlockList +func (bl *BlockList) IsBlocked(memberID string) bool { + // acquire our mutual exclusion primitive for reading + return bl.blockedMembers.Contains(memberID) +} + +// Len returns the number of blocked members +func (bl *BlockList) Len() int { + bl.mu.RLock() + defer bl.mu.RUnlock() + return bl.blockedMembers.Size() +} diff --git a/remote/build.sh b/remote/build.sh new file mode 100644 index 0000000000000000000000000000000000000000..8fd39b18cd3343ac0eab0015508c761b511838a4 --- /dev/null +++ b/remote/build.sh @@ -0,0 +1,2 @@ +protoc -I=../actor --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative --proto_path=. remote.proto + diff --git a/remote/config-opts.go b/remote/config-opts.go new file mode 100644 index 0000000000000000000000000000000000000000..124e4ccf3d7c9bb6bf2618d9edefe353cfefeac4 --- /dev/null +++ b/remote/config-opts.go @@ -0,0 +1,70 @@ +package remote + +import "google.golang.org/grpc" + +type ConfigOption func(config *Config) + +// WithEndpointWriterBatchSize sets the batch size for the endpoint writer +func WithEndpointWriterBatchSize(batchSize int) ConfigOption { + return func(config *Config) { + config.EndpointWriterBatchSize = batchSize + } +} + +// WithEndpointWriterQueueSize sets the queue size for the endpoint writer +func WithEndpointWriterQueueSize(queueSize int) ConfigOption { + return func(config *Config) { + config.EndpointWriterQueueSize = queueSize + } +} + +// WithEndpointManagerBatchSize sets the batch size for the endpoint manager +func WithEndpointManagerBatchSize(batchSize int) ConfigOption { + return func(config *Config) { + config.EndpointManagerBatchSize = batchSize + } +} + +// WithEndpointManagerQueueSize sets the queue size for the endpoint manager +func WithEndpointManagerQueueSize(queueSize int) ConfigOption { + return func(config *Config) { + config.EndpointManagerQueueSize = queueSize + } +} + +// WithDialOptions sets the dial options for the remote +func WithDialOptions(options ...grpc.DialOption) ConfigOption { + return func(config *Config) { + config.DialOptions = options + } +} + +// WithServerOptions sets the server options for the remote +func WithServerOptions(options ...grpc.ServerOption) ConfigOption { + return func(config *Config) { + config.ServerOptions = options + } +} + +// WithCallOptions sets the call options for the remote +func WithCallOptions(options ...grpc.CallOption) ConfigOption { + return func(config *Config) { + config.CallOptions = options + } +} + +// WithAdvertisedHost sets the advertised host for the remote +func WithAdvertisedHost(address string) ConfigOption { + return func(config *Config) { + config.AdvertisedHost = address + } +} + +// WithKinds adds the kinds to the remote +func WithKinds(kinds ...*Kind) ConfigOption { + return func(config *Config) { + for _, k := range kinds { + config.Kinds[k.Kind] = k.Props + } + } +} diff --git a/remote/config.go b/remote/config.go new file mode 100644 index 0000000000000000000000000000000000000000..5dc4e109c16dda8517369d8c82d7bdb00790487d --- /dev/null +++ b/remote/config.go @@ -0,0 +1,59 @@ +package remote + +import ( + "fmt" + + "gitee.com/simplexyz/simpleactor-go/actor" + "google.golang.org/grpc" +) + +func defaultConfig() *Config { + return &Config{ + AdvertisedHost: "", + DialOptions: []grpc.DialOption{grpc.WithInsecure()}, + EndpointWriterBatchSize: 1000, + EndpointManagerBatchSize: 1000, + EndpointWriterQueueSize: 1000000, + EndpointManagerQueueSize: 1000000, + Kinds: make(map[string]*actor.Props), + MaxRetryCount: 5, + } +} + +func newConfig(options ...ConfigOption) *Config { + config := defaultConfig() + for _, option := range options { + option(config) + } + return config +} + +// Address returns the address of the remote +func (rc Config) Address() string { + return fmt.Sprintf("%v:%v", rc.Host, rc.Port) +} + +// Configure configures the remote +func Configure(host string, port int, options ...ConfigOption) *Config { + c := newConfig(options...) + c.Host = host + c.Port = port + + return c +} + +// Config is the configuration for the remote +type Config struct { + Host string + Port int + AdvertisedHost string + ServerOptions []grpc.ServerOption + CallOptions []grpc.CallOption + DialOptions []grpc.DialOption + EndpointWriterBatchSize int + EndpointWriterQueueSize int + EndpointManagerBatchSize int + EndpointManagerQueueSize int + Kinds map[string]*actor.Props + MaxRetryCount int +} diff --git a/remote/doc.go b/remote/doc.go new file mode 100644 index 0000000000000000000000000000000000000000..5744e869e97538e53aa85ce934adbaa7f7a96639 --- /dev/null +++ b/remote/doc.go @@ -0,0 +1,4 @@ +/* +Package remote provides access to actors across a network or other I/O connection. +*/ +package remote diff --git a/remote/endpoint_manager.go b/remote/endpoint_manager.go new file mode 100644 index 0000000000000000000000000000000000000000..e087fd31dcaef9234860a220795a1ed284c25234 --- /dev/null +++ b/remote/endpoint_manager.go @@ -0,0 +1,298 @@ +package remote + +import ( + "sync" + "sync/atomic" + "time" + + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/eventstream" + "gitee.com/simplexyz/simpleactor-go/log" +) + +type endpointLazy struct { + // valueFunc func() *endpoint + unloaded uint32 + once sync.Once + endpoint atomic.Value + manager *endpointManager + address string +} + +func NewEndpointLazy(em *endpointManager, address string) *endpointLazy { + return &endpointLazy{ + manager: em, + address: address, + } +} + +func (el *endpointLazy) connect() { + em := el.manager + system := em.remote.actorSystem + rst, _ := system.Root.RequestFuture(em.endpointSupervisor, el.address, -1).Result() + ep := rst.(*endpoint) + el.Set(ep) +} + +func (el *endpointLazy) Set(ep *endpoint) { + el.endpoint.Store(ep) +} + +func (el *endpointLazy) Get() *endpoint { + el.once.Do(el.connect) + ep := el.endpoint.Load() + return ep.(*endpoint) +} + +type endpoint struct { + writer *actor.PID + watcher *actor.PID +} + +func (ep *endpoint) Address() string { + return ep.watcher.GetAddress() +} + +type endpointManager struct { + connections *sync.Map + remote *Remote + endpointSub *eventstream.Subscription + endpointSupervisor *actor.PID + activator *actor.PID + stopped bool + endpointReaderConnections *sync.Map +} + +func newEndpointManager(r *Remote) *endpointManager { + return &endpointManager{ + connections: &sync.Map{}, + remote: r, + stopped: false, + endpointReaderConnections: &sync.Map{}, + } +} + +func (em *endpointManager) start() { + eventStream := em.remote.actorSystem.EventStream + em.endpointSub = eventStream. + SubscribeWithPredicate(em.endpointEvent, func(m interface{}) bool { + switch m.(type) { + case *EndpointTerminatedEvent, *EndpointConnectedEvent: + return true + } + return false + }) + em.startActivator() + em.startSupervisor() + + if err := em.waiting(3 * time.Second); err != nil { + panic(err) + } + plog.Info("Started EndpointManager") +} + +func (em *endpointManager) waiting(timeout time.Duration) error { + ctx := em.remote.actorSystem.Root + if _, err := ctx.RequestFuture(em.activator, &Ping{}, timeout).Result(); err != nil { + return err + } + return nil +} + +func (em *endpointManager) stop() { + em.stopped = true + r := em.remote + r.actorSystem.EventStream.Unsubscribe(em.endpointSub) + if err := em.stopActivator(); err != nil { + plog.Error("stop endpoint activator failed", log.Error(err)) + } + if err := em.stopSupervisor(); err != nil { + plog.Error("stop endpoint supervisor failed", log.Error(err)) + } + em.endpointSub = nil + em.connections = nil + if em.endpointReaderConnections != nil { + em.endpointReaderConnections.Range(func(key interface{}, value interface{}) bool { + channel := value.(chan bool) + channel <- true + em.endpointReaderConnections.Delete(key) + return true + }) + } + plog.Info("Stopped EndpointManager") +} + +func (em *endpointManager) startActivator() { + p := newActivatorActor(em.remote) + props := actor.PropsFromProducer(p, actor.WithGuardian(actor.RestartingSupervisorStrategy())) + pid, err := em.remote.actorSystem.Root.SpawnNamed(props, "activator") + if err != nil { + panic(err) + } + em.activator = pid +} + +func (em *endpointManager) stopActivator() error { + return em.remote.actorSystem.Root.StopFuture(em.activator).Wait() +} + +func (em *endpointManager) startSupervisor() { + r := em.remote + props := actor.PropsFromProducer(func() actor.Actor { + return newEndpointSupervisor(r) + }, + actor.WithGuardian(actor.RestartingSupervisorStrategy()), + actor.WithSupervisor(actor.RestartingSupervisorStrategy()), + actor.WithDispatcher(actor.NewSynchronizedDispatcher(300))) + + pid, err := r.actorSystem.Root.SpawnNamed(props, "EndpointSupervisor") + if err != nil { + panic(err) + } + em.endpointSupervisor = pid +} + +func (em *endpointManager) stopSupervisor() error { + r := em.remote + return r.actorSystem.Root.StopFuture(em.endpointSupervisor).Wait() +} + +func (em *endpointManager) endpointEvent(evn interface{}) { + switch msg := evn.(type) { + case *EndpointTerminatedEvent: + plog.Debug("EndpointManager received endpoint terminated event, removing endpoint", log.Message(evn)) + em.removeEndpoint(msg) + case *EndpointConnectedEvent: + endpoint := em.ensureConnected(msg.Address) + em.remote.actorSystem.Root.Send(endpoint.watcher, msg) + } +} + +func (em *endpointManager) remoteTerminate(msg *remoteTerminate) { + if em.stopped { + return + } + address := msg.Watchee.Address + endpoint := em.ensureConnected(address) + em.remote.actorSystem.Root.Send(endpoint.watcher, msg) +} + +func (em *endpointManager) remoteWatch(msg *remoteWatch) { + if em.stopped { + return + } + address := msg.Watchee.Address + endpoint := em.ensureConnected(address) + em.remote.actorSystem.Root.Send(endpoint.watcher, msg) +} + +func (em *endpointManager) remoteUnwatch(msg *remoteUnwatch) { + if em.stopped { + return + } + address := msg.Watchee.Address + endpoint := em.ensureConnected(address) + em.remote.actorSystem.Root.Send(endpoint.watcher, msg) +} + +func (em *endpointManager) remoteDeliver(msg *remoteDeliver) { + if em.stopped { + // send to deadletter + em.remote.actorSystem.EventStream.Publish(&actor.DeadLetterEvent{ + PID: msg.target, + Message: msg.message, + Sender: msg.sender, + }) + return + } + address := msg.target.Address + endpoint := em.ensureConnected(address) + em.remote.actorSystem.Root.Send(endpoint.writer, msg) +} + +func (em *endpointManager) ensureConnected(address string) *endpoint { + e, ok := em.connections.Load(address) + if !ok { + el := NewEndpointLazy(em, address) + e, _ = em.connections.LoadOrStore(address, el) + } + el := e.(*endpointLazy) + return el.Get() +} + +// func (em *endpointManager) ensureConnected(address string) *endpoint { +// e, ok := em.connections.Load(address) +// if !ok { +// el := &endpointLazy{} +// var once sync.Once +// el.valueFunc = func() *endpoint { +// once.Do(func() { +// rst, _ := em.remote.actorSystem.Root.RequestFuture(em.endpointSupervisor, address, -1).Result() +// ep := rst.(*endpoint) +// el.valueFunc = func() *endpoint { +// return ep +// } +// }) +// return el.valueFunc() +// } +// e, _ = em.connections.LoadOrStore(address, el) +// } + +// el := e.(*endpointLazy) +// return el.valueFunc() +// } + +func (em *endpointManager) removeEndpoint(msg *EndpointTerminatedEvent) { + v, ok := em.connections.Load(msg.Address) + if ok { + le := v.(*endpointLazy) + if atomic.CompareAndSwapUint32(&le.unloaded, 0, 1) { + em.connections.Delete(msg.Address) + ep := le.Get() + plog.Debug("Sending EndpointTerminatedEvent to EndpointWatcher ans EndpointWriter", log.String("address", msg.Address)) + em.remote.actorSystem.Root.Send(ep.watcher, msg) + em.remote.actorSystem.Root.Send(ep.writer, msg) + } + } +} + +type endpointSupervisor struct { + remote *Remote +} + +func newEndpointSupervisor(remote *Remote) actor.Actor { + return &endpointSupervisor{ + remote: remote, + } +} + +func (state *endpointSupervisor) Receive(ctx actor.Context) { + if address, ok := ctx.Message().(string); ok { + plog.Debug("EndpointSupervisor spawning EndpointWriter and EndpointWatcher", log.String("address", address)) + e := &endpoint{ + writer: state.spawnEndpointWriter(state.remote, address, ctx), + watcher: state.spawnEndpointWatcher(state.remote, address, ctx), + } + ctx.Respond(e) + } +} + +func (state *endpointSupervisor) HandleFailure(actorSystem *actor.ActorSystem, supervisor actor.Supervisor, child *actor.PID, rs *actor.RestartStatistics, reason interface{}, message interface{}) { + plog.Debug("EndpointSupervisor handling failure", log.Object("reason", reason), log.Message(message)) + supervisor.RestartChildren(child) +} + +func (state *endpointSupervisor) spawnEndpointWriter(remote *Remote, address string, ctx actor.Context) *actor.PID { + props := actor. + PropsFromProducer(endpointWriterProducer(remote, address, remote.config), + actor.WithMailbox(endpointWriterMailboxProducer(remote.config.EndpointWriterBatchSize, remote.config.EndpointWriterQueueSize))) + pid := ctx.Spawn(props) + return pid +} + +func (state *endpointSupervisor) spawnEndpointWatcher(remote *Remote, address string, ctx actor.Context) *actor.PID { + props := actor. + PropsFromProducer(newEndpointWatcher(remote, address)) + pid := ctx.Spawn(props) + return pid +} diff --git a/remote/endpoint_reader.go b/remote/endpoint_reader.go new file mode 100644 index 0000000000000000000000000000000000000000..a40ea4913d3120577d1de9707555da8f1e7b9f74 --- /dev/null +++ b/remote/endpoint_reader.go @@ -0,0 +1,253 @@ +package remote + +import ( + "errors" + "io" + + "google.golang.org/protobuf/proto" + + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/log" + "golang.org/x/net/context" +) + +type endpointReader struct { + suspended bool + remote *Remote +} + +func (s *endpointReader) mustEmbedUnimplementedRemotingServer() { + // TODO implement me + panic("implement me") +} + +func (s *endpointReader) ListProcesses(ctx context.Context, request *ListProcessesRequest) (*ListProcessesResponse, error) { + panic("implement me") +} + +func (s *endpointReader) GetProcessDiagnostics(ctx context.Context, request *GetProcessDiagnosticsRequest) (*GetProcessDiagnosticsResponse, error) { + panic("implement me") +} + +func newEndpointReader(r *Remote) *endpointReader { + return &endpointReader{ + remote: r, + } +} + +func (s *endpointReader) Receive(stream Remoting_ReceiveServer) error { + disconnectChan := make(chan bool, 1) + s.remote.edpManager.endpointReaderConnections.Store(stream, disconnectChan) + defer func() { + close(disconnectChan) + }() + + go func() { + // endpointManager sends true + // endpointReader sends false + if <-disconnectChan { + plog.Debug("EndpointReader is telling to remote that it's leaving") + err := stream.Send(&RemoteMessage{ + MessageType: &RemoteMessage_DisconnectRequest{ + DisconnectRequest: &DisconnectRequest{}, + }, + }) + if err != nil { + plog.Error("EndpointReader failed to send disconnection message", log.Error(err)) + } + } else { + s.remote.edpManager.endpointReaderConnections.Delete(stream) + plog.Debug("EndpointReader removed active endpoint from endpointManager") + } + }() + + for { + msg, err := stream.Recv() + switch { + case errors.Is(err, io.EOF): + plog.Info("EndpointReader stream closed") + disconnectChan <- false + return nil + case err != nil: + plog.Info("EndpointReader failed to read", log.Error(err)) + return err + case s.suspended: + continue + } + + switch t := msg.MessageType.(type) { + case *RemoteMessage_ConnectRequest: + plog.Debug("EndpointReader received connect request", log.Message(t.ConnectRequest)) + c := t.ConnectRequest + _, err := s.OnConnectRequest(stream, c) + if err != nil { + plog.Error("EndpointReader failed to handle connect request", log.Error(err)) + return err + } + case *RemoteMessage_MessageBatch: + m := t.MessageBatch + err := s.onMessageBatch(m) + if err != nil { + return err + } + default: + { + plog.Warn("EndpointReader received unknown message type") + } + } + } +} + +func (s *endpointReader) OnConnectRequest(stream Remoting_ReceiveServer, c *ConnectRequest) (bool, error) { + switch tt := c.ConnectionType.(type) { + case *ConnectRequest_ServerConnection: + { + sc := tt.ServerConnection + s.onServerConnection(stream, sc) + } + case *ConnectRequest_ClientConnection: + { + // TODO implement me + plog.Error("ClientConnection not implemented") + } + default: + plog.Error("EndpointReader received unknown connection type") + return true, nil + } + return false, nil +} + +func (s *endpointReader) onMessageBatch(m *MessageBatch) error { + var ( + sender *actor.PID + target *actor.PID + ) + + for _, envelope := range m.Envelopes { + data := envelope.MessageData + + sender = deserializeSender(sender, envelope.Sender, envelope.SenderRequestId, m.Senders) + target = deserializeTarget(target, envelope.Target, envelope.TargetRequestId, m.Targets) + if target == nil { + plog.Error("EndpointReader received message with unknown target", log.Int("target", int(envelope.Target)), log.Int("targetRequestId", int(envelope.TargetRequestId))) + return errors.New("unknown target") + } + + message, err := Deserialize(data, m.TypeNames[envelope.TypeId], envelope.SerializerId) + if err != nil { + plog.Error("EndpointReader failed to deserialize", log.Error(err)) + return err + } + + // translate from on-the-wire representation to in-process representation + // this only applies to root level messages, and never on nested child messages + if v, ok := message.(RootSerialized); ok { + message = v.Deserialize() + } + + switch msg := message.(type) { + case *actor.Terminated: + rt := &remoteTerminate{ + Watchee: msg.Who, + Watcher: target, + } + s.remote.edpManager.remoteTerminate(rt) + case actor.SystemMessage: + ref, _ := s.remote.actorSystem.ProcessRegistry.GetLocal(target.ID) + ref.SendSystemMessage(target, msg) + default: + var header map[string]string + + // fast path + if sender == nil && envelope.MessageHeader == nil { + s.remote.actorSystem.Root.Send(target, message) + continue + } + + // slow path + if envelope.MessageHeader != nil { + header = envelope.MessageHeader.HeaderData + } + localEnvelope := &actor.MessageEnvelope{ + Header: header, + Message: message, + Sender: sender, + } + s.remote.actorSystem.Root.Send(target, localEnvelope) + } + } + return nil +} + +func deserializeSender(pid *actor.PID, index int32, requestId uint32, arr []*actor.PID) *actor.PID { + if index == 0 { + pid = nil + } else { + pid = arr[index-1] + + // if request id is used. make sure to clone the PID first, so we don't corrupt the lookup + if requestId > 0 { + pid, _ = proto.Clone(pid).(*actor.PID) + pid.RequestID = requestId + } + } + return pid +} + +func deserializeTarget(pid *actor.PID, index int32, requestId uint32, arr []*actor.PID) *actor.PID { + pid = arr[index] + + // if request id is used. make sure to clone the PID first, so we don't corrupt the lookup + if requestId > 0 { + pid, _ = proto.Clone(pid).(*actor.PID) + pid.RequestID = requestId + } + + return pid +} + +func (s *endpointReader) onServerConnection(stream Remoting_ReceiveServer, sc *ServerConnection) { + if s.remote.BlockList().IsBlocked(sc.SystemId) { + plog.Debug("EndpointReader is blocked", log.String("systemId", sc.SystemId)) + + err := stream.Send( + &RemoteMessage{ + MessageType: &RemoteMessage_ConnectResponse{ + ConnectResponse: &ConnectResponse{ + Blocked: true, + MemberId: s.remote.actorSystem.ID, + }, + }, + }) + if err != nil { + plog.Error("EndpointReader failed to send ConnectResponse message", log.Error(err)) + } + + address := sc.Address + systemID := sc.SystemId + + // TODO + _ = address + _ = systemID + } else { + err := stream.Send( + &RemoteMessage{ + MessageType: &RemoteMessage_ConnectResponse{ + ConnectResponse: &ConnectResponse{ + Blocked: false, + MemberId: s.remote.actorSystem.ID, + }, + }, + }) + if err != nil { + plog.Error("EndpointReader failed to send ConnectResponse message", log.Error(err)) + } + } +} + +func (s *endpointReader) suspend(toSuspend bool) { + s.suspended = toSuspend + if toSuspend { + plog.Debug("Suspended EndpointReader") + } +} diff --git a/remote/endpoint_watcher.go b/remote/endpoint_watcher.go new file mode 100644 index 0000000000000000000000000000000000000000..d05cc85c6c6a1996638e7e5123a6b67688a76d4a --- /dev/null +++ b/remote/endpoint_watcher.go @@ -0,0 +1,152 @@ +package remote + +import ( + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/log" +) + +func newEndpointWatcher(remote *Remote, address string) actor.Producer { + return func() actor.Actor { + watcher := &endpointWatcher{ + behavior: actor.NewBehavior(), + address: address, + remote: remote, + } + watcher.behavior.Become(watcher.connected) + return watcher + } +} + +type endpointWatcher struct { + behavior actor.Behavior + address string + watched map[string]*actor.PIDSet // key is the watching PID string, value is the watched PID + remote *Remote +} + +func (state *endpointWatcher) initialize() { + plog.Info("Started EndpointWatcher", log.String("address", state.address)) + state.watched = make(map[string]*actor.PIDSet) +} + +func (state *endpointWatcher) Receive(ctx actor.Context) { + state.behavior.Receive(ctx) +} + +func (state *endpointWatcher) connected(ctx actor.Context) { + switch msg := ctx.Message().(type) { + case *actor.Started: + state.initialize() + + case *remoteTerminate: + // delete the watch entries + if pidSet, ok := state.watched[msg.Watcher.ID]; ok { + pidSet.Remove(msg.Watchee) + if pidSet.Len() == 0 { + delete(state.watched, msg.Watcher.ID) + } + } + + terminated := &actor.Terminated{ + Who: msg.Watchee, + Why: actor.TerminatedReason_Stopped, + } + ref, ok := state.remote.actorSystem.ProcessRegistry.GetLocal(msg.Watcher.ID) + if ok { + ref.SendSystemMessage(msg.Watcher, terminated) + } + case *EndpointConnectedEvent: + // Already connected, pass + case *EndpointTerminatedEvent: + plog.Info("EndpointWatcher handling terminated", + log.String("address", state.address), log.Int("watched", len(state.watched))) + + for id, pidSet := range state.watched { + // try to find the watcher ExtensionID in the local actor registry + ref, ok := state.remote.actorSystem.ProcessRegistry.GetLocal(id) + if ok { + pidSet.ForEach(func(i int, pid *actor.PID) { + // create a terminated event for the Watched actor + terminated := &actor.Terminated{ + Who: pid, + Why: actor.TerminatedReason_AddressTerminated, + } + + watcher := state.remote.actorSystem.NewLocalPID(id) + // send the address Terminated event to the Watcher + ref.SendSystemMessage(watcher, terminated) + }) + } + } + + // Clear watcher's map + state.watched = make(map[string]*actor.PIDSet) + state.behavior.Become(state.terminated) + ctx.Stop(ctx.Self()) + + case *remoteWatch: + // add watchee to watcher's map + if pidSet, ok := state.watched[msg.Watcher.ID]; ok { + pidSet.Add(msg.Watchee) + } else { + state.watched[msg.Watcher.ID] = actor.NewPIDSet(msg.Watchee) + } + + // recreate the Watch command + w := &actor.Watch{ + Watcher: msg.Watcher, + } + + // pass it off to the remote PID + state.remote.SendMessage(msg.Watchee, nil, w, nil, -1) + + case *remoteUnwatch: + // delete the watch entries + if pidSet, ok := state.watched[msg.Watcher.ID]; ok { + pidSet.Remove(msg.Watchee) + if pidSet.Len() == 0 { + delete(state.watched, msg.Watcher.ID) + } + } + + // recreate the Unwatch command + uw := &actor.Unwatch{ + Watcher: msg.Watcher, + } + + // pass it off to the remote PID + state.remote.SendMessage(msg.Watchee, nil, uw, nil, -1) + case actor.SystemMessage, actor.AutoReceiveMessage: + // ignore + default: + plog.Error("EndpointWatcher received unknown message", log.String("address", state.address), log.Message(msg)) + } +} + +func (state *endpointWatcher) terminated(ctx actor.Context) { + switch msg := ctx.Message().(type) { + case *remoteWatch: + // try to find the watcher ExtensionID in the local actor registry + ref, ok := state.remote.actorSystem.ProcessRegistry.GetLocal(msg.Watcher.ID) + + if ok { + // create a terminated event for the Watched actor + terminated := &actor.Terminated{ + Who: msg.Watchee, + Why: actor.TerminatedReason_AddressTerminated, + } + // send the address Terminated event to the Watcher + ref.SendSystemMessage(msg.Watcher, terminated) + } + case *EndpointConnectedEvent: + plog.Info("EndpointWatcher handling restart", log.String("address", state.address)) + state.behavior.Become(state.connected) + case *remoteTerminate, *EndpointTerminatedEvent, *remoteUnwatch: + // pass + plog.Error("EndpointWatcher receive message for already terminated endpoint", log.String("address", state.address), log.Message(msg)) + case actor.SystemMessage, actor.AutoReceiveMessage: + // ignore + default: + plog.Error("EndpointWatcher received unknown message", log.String("address", state.address), log.TypeOf("type", msg), log.Message(msg)) + } +} diff --git a/remote/endpoint_writer.go b/remote/endpoint_writer.go new file mode 100644 index 0000000000000000000000000000000000000000..740ec7f8e861cc369a911df7b79f6575596256c7 --- /dev/null +++ b/remote/endpoint_writer.go @@ -0,0 +1,338 @@ +package remote + +import ( + "errors" + "io" + "time" + + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/log" + "golang.org/x/net/context" + "google.golang.org/grpc" + "google.golang.org/protobuf/proto" +) + +func endpointWriterProducer(remote *Remote, address string, config *Config) actor.Producer { + return func() actor.Actor { + return &endpointWriter{ + address: address, + config: config, + remote: remote, + } + } +} + +type endpointWriter struct { + config *Config + address string + conn *grpc.ClientConn + stream Remoting_ReceiveClient + remote *Remote +} + +type restartAfterConnectFailure struct { + err error +} + +func (state *endpointWriter) initialize(ctx actor.Context) { + now := time.Now() + plog.Info("Started EndpointWriter. connecting", log.String("address", state.address)) + + var err error + + for i := 0; i < state.remote.config.MaxRetryCount; i++ { + err = state.initializeInternal() + if err != nil { + plog.Error("EndpointWriter failed to connect", log.String("address", state.address), log.Error(err), log.Int("retry", i)) + // Wait 2 seconds to restart and retry + // Replace with Exponential Backoff + time.Sleep(2 * time.Second) + continue + } + + break + } + + if err != nil { + terminated := &EndpointTerminatedEvent{ + Address: state.address, + } + state.remote.actorSystem.EventStream.Publish(terminated) + + return + + // plog.Error("EndpointWriter failed to connect", log.String("address", state.address), log.Error(err)) + + // Wait 2 seconds to restart and retry + // TODO: Replace with Exponential Backoff + // send this as a message to self - do not block the mailbox processing + // if in the meantime the actor is stopped (EndpointTerminated event), the message will be ignored (deadlettered) + // TODO: would it be a better idea to just publish EndpointTerminatedEvent here? to use the same path as when the connection is lost? + // time.AfterFunc(2*time.Second, func() { + // ctx.Send(ctx.Self(), &restartAfterConnectFailure{err}) + // }) + + } + + plog.Info("EndpointWriter connected", log.String("address", state.address), log.Duration("cost", time.Since(now))) +} + +func (state *endpointWriter) initializeInternal() error { + conn, err := grpc.Dial(state.address, state.config.DialOptions...) + if err != nil { + return err + } + state.conn = conn + c := NewRemotingClient(conn) + stream, err := c.Receive(context.Background(), state.config.CallOptions...) + if err != nil { + plog.Error("EndpointWriter failed to create receive stream", log.String("address", state.address), log.Error(err)) + return err + } + state.stream = stream + + err = stream.Send(&RemoteMessage{ + MessageType: &RemoteMessage_ConnectRequest{ + ConnectRequest: &ConnectRequest{ + ConnectionType: &ConnectRequest_ServerConnection{ + ServerConnection: &ServerConnection{ + SystemId: state.remote.actorSystem.ID, + Address: state.remote.actorSystem.Address(), + }, + }, + }, + }, + }) + if err != nil { + plog.Error("EndpointWriter failed to send connect request", log.String("address", state.address), log.Error(err)) + return err + } + + connection, err := stream.Recv() + if err != nil { + plog.Error("EndpointWriter failed to receive connect response", log.String("address", state.address), log.Error(err)) + return err + } + + switch connection.MessageType.(type) { + case *RemoteMessage_ConnectResponse: + plog.Debug("Received connect response", log.String("fromAddress", state.address)) + // TODO: handle blocked status received from remote server + break + default: + plog.Error("EndpointWriter got invalid connect response", log.String("address", state.address), log.TypeOf("type", connection.MessageType)) + return errors.New("invalid connect response") + } + + go func() { + for { + _, err := stream.Recv() + switch { + case errors.Is(err, io.EOF): + plog.Debug("EndpointWriter stream completed", log.String("address", state.address)) + return + case err != nil: + plog.Error("EndpointWriter lost connection", log.String("address", state.address), log.Error(err)) + terminated := &EndpointTerminatedEvent{ + Address: state.address, + } + state.remote.actorSystem.EventStream.Publish(terminated) + return + default: // DisconnectRequest + plog.Info("EndpointWriter got DisconnectRequest form remote", log.String("address", state.address)) + terminated := &EndpointTerminatedEvent{ + Address: state.address, + } + state.remote.actorSystem.EventStream.Publish(terminated) + } + } + }() + + connected := &EndpointConnectedEvent{Address: state.address} + state.remote.actorSystem.EventStream.Publish(connected) + return nil +} + +func (state *endpointWriter) sendEnvelopes(msg []interface{}, ctx actor.Context) { + envelopes := make([]*MessageEnvelope, len(msg)) + + // type name uniqueness map name string to type index + typeNames := make(map[string]int32) + typeNamesArr := make([]string, 0) + + targetNames := make(map[string]int32) + targetNamesArr := make([]*actor.PID, 0) + + senderNames := make(map[string]int32) + senderNamesArr := make([]*actor.PID, 0) + + var ( + header *MessageHeader + typeID int32 + targetID int32 + senderID int32 + serializerID int32 + ) + + for i, tmp := range msg { + switch unwrapped := tmp.(type) { + case *EndpointTerminatedEvent, EndpointTerminatedEvent: + plog.Debug("Handling array wrapped terminate event", log.String("address", state.address), log.Object("msg", unwrapped)) + ctx.Stop(ctx.Self()) + return + } + + rd, _ := tmp.(*remoteDeliver) + + if state.stream == nil { // not connected yet since first connection attempt failed and we are waiting for the retry + if rd.sender != nil { + state.remote.actorSystem.Root.Send(rd.sender, &actor.DeadLetterResponse{Target: rd.target}) + } else { + state.remote.actorSystem.EventStream.Publish(&actor.DeadLetterEvent{Message: rd.message, Sender: rd.sender, PID: rd.target}) + } + continue + } + + if rd.header == nil || rd.header.Length() == 0 { + header = nil + } else { + header = &MessageHeader{ + HeaderData: rd.header.ToMap(), + } + } + + // if the message can be translated to a serialization representation, we do this here + // this only apply to root level messages and never to nested child objects inside the message + message := rd.message + if v, ok := message.(RootSerializable); ok { + message = v.Serialize() + } + + bytes, typeName, err := Serialize(message, serializerID) + if err != nil { + panic(err) + } + typeID, typeNamesArr = addToLookup(typeNames, typeName, typeNamesArr) + targetID, targetNamesArr = addToTargetLookup(targetNames, rd.target, targetNamesArr) + targetRequestID := rd.target.RequestID + + senderID, senderNamesArr = addToSenderLookup(senderNames, rd.sender, senderNamesArr) + senderRequestID := uint32(0) + if rd.sender != nil { + senderRequestID = rd.sender.RequestID + } + + envelopes[i] = &MessageEnvelope{ + MessageHeader: header, + MessageData: bytes, + Sender: senderID, + Target: targetID, + TypeId: typeID, + SerializerId: serializerID, + TargetRequestId: targetRequestID, + SenderRequestId: senderRequestID, + } + } + + err := state.stream.Send(&RemoteMessage{ + MessageType: &RemoteMessage_MessageBatch{ + MessageBatch: &MessageBatch{ + TypeNames: typeNamesArr, + Targets: targetNamesArr, + Senders: senderNamesArr, + Envelopes: envelopes, + }, + }, + }) + if err != nil { + ctx.Stash() + plog.Debug("gRPC Failed to send", log.String("address", state.address), log.Error(err)) + panic("restart it") + } +} + +func addToLookup(m map[string]int32, name string, a []string) (int32, []string) { + max := int32(len(m)) + id, ok := m[name] + if !ok { + m[name] = max + id = max + a = append(a, name) + } + return id, a +} + +func addToTargetLookup(m map[string]int32, pid *actor.PID, arr []*actor.PID) (int32, []*actor.PID) { + max := int32(len(m)) + key := pid.Address + "/" + pid.ID + id, ok := m[key] + if !ok { + c, _ := proto.Clone(pid).(*actor.PID) + c.RequestID = 0 + m[key] = max + id = max + arr = append(arr, c) + } + return id, arr +} + +func addToSenderLookup(m map[string]int32, pid *actor.PID, arr []*actor.PID) (int32, []*actor.PID) { + if pid == nil { + return 0, arr + } + + max := int32(len(m)) + key := pid.Address + "/" + pid.ID + id, ok := m[key] + if !ok { + c, _ := proto.Clone(pid).(*actor.PID) + c.RequestID = 0 + m[key] = max + id = max + arr = append(arr, c) + } + return id + 1, arr +} + +func (state *endpointWriter) Receive(ctx actor.Context) { + switch msg := ctx.Message().(type) { + case *actor.Started: + state.initialize(ctx) + case *actor.Stopped: + plog.Debug("EndpointWriter stopped", log.String("address", state.address)) + state.closeClientConn() + case *actor.Restarting: + plog.Debug("EndpointWriter restarting", log.String("address", state.address)) + state.closeClientConn() + case *EndpointTerminatedEvent: + plog.Info("EndpointWriter received EndpointTerminatedEvent, stopping", log.String("address", state.address)) + ctx.Stop(ctx.Self()) + case *restartAfterConnectFailure: + plog.Debug("EndpointWriter initiating self-restart after failing to connect and a delay", log.String("address", state.address)) + panic(msg.err) + case []interface{}: + state.sendEnvelopes(msg, ctx) + case actor.SystemMessage, actor.AutoReceiveMessage: + // ignore + default: + plog.Error("EndpointWriter received unknown message", log.String("address", state.address), log.TypeOf("type", msg), log.Message(msg)) + } +} + +func (state *endpointWriter) closeClientConn() { + plog.Info("EndpointWriter closing client connection", log.String("address", state.address)) + if state.stream != nil { + err := state.stream.CloseSend() + if err != nil { + plog.Error("EndpointWriter error when closing the stream", log.Error(err)) + } + state.stream = nil + } + if state.conn != nil { + err := state.conn.Close() + if err != nil { + plog.Error("EndpointWriter error when closing the client conn", log.Error(err)) + } + state.conn = nil + } +} diff --git a/remote/endpoint_writer_mailbox.go b/remote/endpoint_writer_mailbox.go new file mode 100644 index 0000000000000000000000000000000000000000..19bcfee397c956a325d8ce68e6c90777a7b2c63e --- /dev/null +++ b/remote/endpoint_writer_mailbox.go @@ -0,0 +1,135 @@ +package remote + +import ( + "runtime" + "sync/atomic" + + "gitee.com/simplexyz/simpleactor-go/actor" + + "gitee.com/simplexyz/simpleactor-go/internal/queue/goring" + "gitee.com/simplexyz/simpleactor-go/internal/queue/mpsc" + "gitee.com/simplexyz/simpleactor-go/log" +) + +const ( + mailboxIdle int32 = iota + mailboxRunning int32 = iota +) + +const ( + mailboxHasNoMessages int32 = iota + mailboxHasMoreMessages int32 = iota +) + +type endpointWriterMailbox struct { + userMailbox *goring.Queue + systemMailbox *mpsc.Queue + schedulerStatus int32 + hasMoreMessages int32 + invoker actor.MessageInvoker + batchSize int + dispatcher actor.Dispatcher + suspended bool +} + +func (m *endpointWriterMailbox) PostUserMessage(message interface{}) { + // batching mailbox only use the message part + m.userMailbox.Push(message) + m.schedule() +} + +func (m *endpointWriterMailbox) PostSystemMessage(message interface{}) { + m.systemMailbox.Push(message) + m.schedule() +} + +func (m *endpointWriterMailbox) RegisterHandlers(invoker actor.MessageInvoker, dispatcher actor.Dispatcher) { + m.invoker = invoker + m.dispatcher = dispatcher +} + +func (m *endpointWriterMailbox) Start() { +} + +func (m *endpointWriterMailbox) schedule() { + atomic.StoreInt32(&m.hasMoreMessages, mailboxHasMoreMessages) // we have more messages to process + if atomic.CompareAndSwapInt32(&m.schedulerStatus, mailboxIdle, mailboxRunning) { + m.dispatcher.Schedule(m.processMessages) + } +} + +func (m *endpointWriterMailbox) processMessages() { + // we are about to start processing messages, we can safely reset the message flag of the mailbox + atomic.StoreInt32(&m.hasMoreMessages, mailboxHasNoMessages) +process: + m.run() + + // set mailbox to idle + atomic.StoreInt32(&m.schedulerStatus, mailboxIdle) + + // check if there are still messages to process (sent after the message loop ended) + if atomic.SwapInt32(&m.hasMoreMessages, mailboxHasNoMessages) == mailboxHasMoreMessages { + // try setting the mailbox back to running + if atomic.CompareAndSwapInt32(&m.schedulerStatus, mailboxIdle, mailboxRunning) { + goto process + } + } +} + +func (m *endpointWriterMailbox) run() { + var msg interface{} + defer func() { + if r := recover(); r != nil { + plog.Info("[ACTOR] Recovering", log.Object("actor", m.invoker), log.Object("reason", r), log.Stack()) + m.invoker.EscalateFailure(r, msg) + } + }() + + for { + // keep processing system messages until queue is empty + if msg = m.systemMailbox.Pop(); msg != nil { + switch msg.(type) { + case *actor.SuspendMailbox: + m.suspended = true + case *actor.ResumeMailbox: + m.suspended = false + default: + m.invoker.InvokeSystemMessage(msg) + } + + continue + } + + // didn't process a system message, so break until we are resumed + if m.suspended { + return + } + + var ok bool + if msg, ok = m.userMailbox.PopMany(int64(m.batchSize)); ok { + m.invoker.InvokeUserMessage(msg) + } else { + return + } + + runtime.Gosched() + } +} + +func (m *endpointWriterMailbox) UserMessageCount() int { + return int(m.userMailbox.Length()) +} + +func endpointWriterMailboxProducer(batchSize, initialSize int) actor.MailboxProducer { + return func() actor.Mailbox { + userMailbox := goring.New(int64(initialSize)) + systemMailbox := mpsc.New() + return &endpointWriterMailbox{ + userMailbox: userMailbox, + systemMailbox: systemMailbox, + hasMoreMessages: mailboxHasNoMessages, + schedulerStatus: mailboxIdle, + batchSize: batchSize, + } + } +} diff --git a/remote/errors.go b/remote/errors.go new file mode 100644 index 0000000000000000000000000000000000000000..e34314466499fcb910126ccc463cf557c5a41d2d --- /dev/null +++ b/remote/errors.go @@ -0,0 +1,25 @@ +package remote + +var ( + ErrUnAvailable = &ResponseError{ResponseStatusCodeUNAVAILABLE} + ErrTimeout = &ResponseError{ResponseStatusCodeTIMEOUT} + ErrProcessNameAlreadyExist = &ResponseError{ResponseStatusCodePROCESSNAMEALREADYEXIST} + ErrDeadLetter = &ResponseError{ResponseStatusCodeDeadLetter} + ErrUnknownError = &ResponseError{ResponseStatusCodeERROR} +) + +// ResponseError is an error type. +// e.g.: +// +// var err = &ResponseError{1} +type ResponseError struct { + Code ResponseStatusCode +} + +func (r *ResponseError) Error() string { + if r == nil { + return "nil" + } + + return r.Code.String() +} diff --git a/remote/json_serializer.go b/remote/json_serializer.go new file mode 100644 index 0000000000000000000000000000000000000000..86221828b9fa300ca49599a11213a51580f4993a --- /dev/null +++ b/remote/json_serializer.go @@ -0,0 +1,74 @@ +package remote + +import ( + "bytes" + "fmt" + "reflect" + + "github.com/gogo/protobuf/jsonpb" + "github.com/gogo/protobuf/proto" +) + +type jsonSerializer struct { + jsonpb.Marshaler + jsonpb.Unmarshaler +} + +func newJsonSerializer() Serializer { + return &jsonSerializer{ + Marshaler: jsonpb.Marshaler{}, + Unmarshaler: jsonpb.Unmarshaler{ + AllowUnknownFields: true, + }, + } +} + +func (j *jsonSerializer) Serialize(msg interface{}) ([]byte, error) { + if message, ok := msg.(*JsonMessage); ok { + return []byte(message.Json), nil + } else if message, ok := msg.(proto.Message); ok { + + str, err := j.Marshaler.MarshalToString(message) + if err != nil { + return nil, err + } + + return []byte(str), nil + } + return nil, fmt.Errorf("msg must be proto.Message") +} + +func (j *jsonSerializer) Deserialize(typeName string, b []byte) (interface{}, error) { + protoType := proto.MessageType(typeName) + if protoType == nil { + m := &JsonMessage{ + TypeName: typeName, + Json: string(b), + } + return m, nil + } + t := protoType.Elem() + + intPtr := reflect.New(t) + instance, ok := intPtr.Interface().(proto.Message) + if ok { + r := bytes.NewReader(b) + j.Unmarshaler.Unmarshal(r, instance) + + return instance, nil + } + + return nil, fmt.Errorf("msg must be proto.Message") +} + +func (j *jsonSerializer) GetTypeName(msg interface{}) (string, error) { + if message, ok := msg.(*JsonMessage); ok { + return message.TypeName, nil + } else if message, ok := msg.(proto.Message); ok { + typeName := proto.MessageName(message) + + return typeName, nil + } + + return "", fmt.Errorf("msg must be proto.Message") +} diff --git a/remote/kind.go b/remote/kind.go new file mode 100644 index 0000000000000000000000000000000000000000..49512961144aa8ae0f6a3aa3ea1a90ba58ff4c4c --- /dev/null +++ b/remote/kind.go @@ -0,0 +1,17 @@ +package remote + +import "gitee.com/simplexyz/simpleactor-go/actor" + +// Kind is the configuration for a kind +type Kind struct { + Kind string + Props *actor.Props +} + +// NewKind creates a new kind configuration +func NewKind(kind string, props *actor.Props) *Kind { + return &Kind{ + Kind: kind, + Props: props, + } +} diff --git a/remote/log.go b/remote/log.go new file mode 100644 index 0000000000000000000000000000000000000000..735676394f9a2b2a7d78f6f2731484c9175f099c --- /dev/null +++ b/remote/log.go @@ -0,0 +1,14 @@ +package remote + +import ( + "gitee.com/simplexyz/simpleactor-go/log" +) + +var plog = log.New(log.DebugLevel, "[REMOTE]") + +// SetLogLevel sets the log level for the logger. +// +// SetLogLevel is safe to call concurrently +func SetLogLevel(level log.Level) { + plog.SetLevel(level) +} diff --git a/remote/messages.go b/remote/messages.go new file mode 100644 index 0000000000000000000000000000000000000000..de7a01e61ee6c5337be4bac246828e07c21323c7 --- /dev/null +++ b/remote/messages.go @@ -0,0 +1,55 @@ +package remote + +import "gitee.com/simplexyz/simpleactor-go/actor" + +type EndpointTerminatedEvent struct { + Address string +} + +type EndpointConnectedEvent struct { + Address string +} + +type remoteWatch struct { + Watcher *actor.PID + Watchee *actor.PID +} + +type remoteUnwatch struct { + Watcher *actor.PID + Watchee *actor.PID +} + +type remoteDeliver struct { + header actor.ReadonlyMessageHeader + message interface{} + target *actor.PID + sender *actor.PID + serializerID int32 +} + +type remoteTerminate struct { + Watcher *actor.PID + Watchee *actor.PID +} + +type JsonMessage struct { + TypeName string + Json string +} + +var stopMessage interface{} = &actor.Stop{} + +var ( + ActorPidRespErr interface{} = &ActorPidResponse{StatusCode: ResponseStatusCodeERROR.ToInt32()} + ActorPidRespTimeout interface{} = &ActorPidResponse{StatusCode: ResponseStatusCodeTIMEOUT.ToInt32()} + ActorPidRespUnavailable interface{} = &ActorPidResponse{StatusCode: ResponseStatusCodeUNAVAILABLE.ToInt32()} +) + +type ( + // Ping is message sent by the actor system to probe an actor is started. + Ping struct{} + + // Pong is response for ping. + Pong struct{} +) diff --git a/remote/proto_serializer.go b/remote/proto_serializer.go new file mode 100644 index 0000000000000000000000000000000000000000..a85ade95078282ab832455b51797e56737cc0003 --- /dev/null +++ b/remote/proto_serializer.go @@ -0,0 +1,45 @@ +package remote + +import ( + "fmt" + + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/reflect/protoreflect" + "google.golang.org/protobuf/reflect/protoregistry" +) + +type protoSerializer struct{} + +func newProtoSerializer() *protoSerializer { + return &protoSerializer{} +} + +func (p *protoSerializer) Serialize(msg interface{}) ([]byte, error) { + if message, ok := msg.(proto.Message); ok { + bytes, err := proto.Marshal(message) + if err != nil { + return nil, err + } + + return bytes, nil + } + return nil, fmt.Errorf("msg must be proto.Message") +} + +func (p *protoSerializer) Deserialize(typeName string, bytes []byte) (interface{}, error) { + n, _ := protoregistry.GlobalTypes.FindMessageByName(protoreflect.FullName(typeName)) + + pm := n.New().Interface() + + err := proto.Unmarshal(bytes, pm) + return pm, err +} + +func (protoSerializer) GetTypeName(msg interface{}) (string, error) { + if message, ok := msg.(proto.Message); ok { + typeName := proto.MessageName(message) + + return string(typeName), nil + } + return "", fmt.Errorf("msg must be proto.Message") +} diff --git a/remote/remote.pb.go b/remote/remote.pb.go new file mode 100644 index 0000000000000000000000000000000000000000..afa715489141cf490a7bbf4305b8e16e821156a3 --- /dev/null +++ b/remote/remote.pb.go @@ -0,0 +1,1407 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc v3.19.1 +// source: remote.proto + +package remote + +import ( + actor "gitee.com/simplexyz/simpleactor-go/actor" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type ListProcessesMatchType int32 + +const ( + ListProcessesMatchType_MatchPartOfString ListProcessesMatchType = 0 + ListProcessesMatchType_MatchExactString ListProcessesMatchType = 1 + ListProcessesMatchType_MatchRegex ListProcessesMatchType = 2 +) + +// Enum value maps for ListProcessesMatchType. +var ( + ListProcessesMatchType_name = map[int32]string{ + 0: "MatchPartOfString", + 1: "MatchExactString", + 2: "MatchRegex", + } + ListProcessesMatchType_value = map[string]int32{ + "MatchPartOfString": 0, + "MatchExactString": 1, + "MatchRegex": 2, + } +) + +func (x ListProcessesMatchType) Enum() *ListProcessesMatchType { + p := new(ListProcessesMatchType) + *p = x + return p +} + +func (x ListProcessesMatchType) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (ListProcessesMatchType) Descriptor() protoreflect.EnumDescriptor { + return file_remote_proto_enumTypes[0].Descriptor() +} + +func (ListProcessesMatchType) Type() protoreflect.EnumType { + return &file_remote_proto_enumTypes[0] +} + +func (x ListProcessesMatchType) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use ListProcessesMatchType.Descriptor instead. +func (ListProcessesMatchType) EnumDescriptor() ([]byte, []int) { + return file_remote_proto_rawDescGZIP(), []int{0} +} + +type RemoteMessage struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Types that are assignable to MessageType: + // *RemoteMessage_MessageBatch + // *RemoteMessage_ConnectRequest + // *RemoteMessage_ConnectResponse + // *RemoteMessage_DisconnectRequest + MessageType isRemoteMessage_MessageType `protobuf_oneof:"message_type"` +} + +func (x *RemoteMessage) Reset() { + *x = RemoteMessage{} + if protoimpl.UnsafeEnabled { + mi := &file_remote_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RemoteMessage) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RemoteMessage) ProtoMessage() {} + +func (x *RemoteMessage) ProtoReflect() protoreflect.Message { + mi := &file_remote_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RemoteMessage.ProtoReflect.Descriptor instead. +func (*RemoteMessage) Descriptor() ([]byte, []int) { + return file_remote_proto_rawDescGZIP(), []int{0} +} + +func (m *RemoteMessage) GetMessageType() isRemoteMessage_MessageType { + if m != nil { + return m.MessageType + } + return nil +} + +func (x *RemoteMessage) GetMessageBatch() *MessageBatch { + if x, ok := x.GetMessageType().(*RemoteMessage_MessageBatch); ok { + return x.MessageBatch + } + return nil +} + +func (x *RemoteMessage) GetConnectRequest() *ConnectRequest { + if x, ok := x.GetMessageType().(*RemoteMessage_ConnectRequest); ok { + return x.ConnectRequest + } + return nil +} + +func (x *RemoteMessage) GetConnectResponse() *ConnectResponse { + if x, ok := x.GetMessageType().(*RemoteMessage_ConnectResponse); ok { + return x.ConnectResponse + } + return nil +} + +func (x *RemoteMessage) GetDisconnectRequest() *DisconnectRequest { + if x, ok := x.GetMessageType().(*RemoteMessage_DisconnectRequest); ok { + return x.DisconnectRequest + } + return nil +} + +type isRemoteMessage_MessageType interface { + isRemoteMessage_MessageType() +} + +type RemoteMessage_MessageBatch struct { + MessageBatch *MessageBatch `protobuf:"bytes,1,opt,name=message_batch,json=messageBatch,proto3,oneof"` +} + +type RemoteMessage_ConnectRequest struct { + ConnectRequest *ConnectRequest `protobuf:"bytes,2,opt,name=connect_request,json=connectRequest,proto3,oneof"` +} + +type RemoteMessage_ConnectResponse struct { + ConnectResponse *ConnectResponse `protobuf:"bytes,3,opt,name=connect_response,json=connectResponse,proto3,oneof"` +} + +type RemoteMessage_DisconnectRequest struct { + DisconnectRequest *DisconnectRequest `protobuf:"bytes,4,opt,name=disconnect_request,json=disconnectRequest,proto3,oneof"` +} + +func (*RemoteMessage_MessageBatch) isRemoteMessage_MessageType() {} + +func (*RemoteMessage_ConnectRequest) isRemoteMessage_MessageType() {} + +func (*RemoteMessage_ConnectResponse) isRemoteMessage_MessageType() {} + +func (*RemoteMessage_DisconnectRequest) isRemoteMessage_MessageType() {} + +type MessageBatch struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + TypeNames []string `protobuf:"bytes,1,rep,name=type_names,json=typeNames,proto3" json:"type_names,omitempty"` + Targets []*actor.PID `protobuf:"bytes,2,rep,name=targets,proto3" json:"targets,omitempty"` + Envelopes []*MessageEnvelope `protobuf:"bytes,3,rep,name=envelopes,proto3" json:"envelopes,omitempty"` + Senders []*actor.PID `protobuf:"bytes,4,rep,name=senders,proto3" json:"senders,omitempty"` +} + +func (x *MessageBatch) Reset() { + *x = MessageBatch{} + if protoimpl.UnsafeEnabled { + mi := &file_remote_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *MessageBatch) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MessageBatch) ProtoMessage() {} + +func (x *MessageBatch) ProtoReflect() protoreflect.Message { + mi := &file_remote_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use MessageBatch.ProtoReflect.Descriptor instead. +func (*MessageBatch) Descriptor() ([]byte, []int) { + return file_remote_proto_rawDescGZIP(), []int{1} +} + +func (x *MessageBatch) GetTypeNames() []string { + if x != nil { + return x.TypeNames + } + return nil +} + +func (x *MessageBatch) GetTargets() []*actor.PID { + if x != nil { + return x.Targets + } + return nil +} + +func (x *MessageBatch) GetEnvelopes() []*MessageEnvelope { + if x != nil { + return x.Envelopes + } + return nil +} + +func (x *MessageBatch) GetSenders() []*actor.PID { + if x != nil { + return x.Senders + } + return nil +} + +type MessageEnvelope struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + TypeId int32 `protobuf:"varint,1,opt,name=type_id,json=typeId,proto3" json:"type_id,omitempty"` + MessageData []byte `protobuf:"bytes,2,opt,name=message_data,json=messageData,proto3" json:"message_data,omitempty"` + Target int32 `protobuf:"varint,3,opt,name=target,proto3" json:"target,omitempty"` + Sender int32 `protobuf:"varint,4,opt,name=sender,proto3" json:"sender,omitempty"` + SerializerId int32 `protobuf:"varint,5,opt,name=serializer_id,json=serializerId,proto3" json:"serializer_id,omitempty"` + MessageHeader *MessageHeader `protobuf:"bytes,6,opt,name=message_header,json=messageHeader,proto3" json:"message_header,omitempty"` + TargetRequestId uint32 `protobuf:"varint,7,opt,name=target_request_id,json=targetRequestId,proto3" json:"target_request_id,omitempty"` + SenderRequestId uint32 `protobuf:"varint,8,opt,name=sender_request_id,json=senderRequestId,proto3" json:"sender_request_id,omitempty"` +} + +func (x *MessageEnvelope) Reset() { + *x = MessageEnvelope{} + if protoimpl.UnsafeEnabled { + mi := &file_remote_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *MessageEnvelope) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MessageEnvelope) ProtoMessage() {} + +func (x *MessageEnvelope) ProtoReflect() protoreflect.Message { + mi := &file_remote_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use MessageEnvelope.ProtoReflect.Descriptor instead. +func (*MessageEnvelope) Descriptor() ([]byte, []int) { + return file_remote_proto_rawDescGZIP(), []int{2} +} + +func (x *MessageEnvelope) GetTypeId() int32 { + if x != nil { + return x.TypeId + } + return 0 +} + +func (x *MessageEnvelope) GetMessageData() []byte { + if x != nil { + return x.MessageData + } + return nil +} + +func (x *MessageEnvelope) GetTarget() int32 { + if x != nil { + return x.Target + } + return 0 +} + +func (x *MessageEnvelope) GetSender() int32 { + if x != nil { + return x.Sender + } + return 0 +} + +func (x *MessageEnvelope) GetSerializerId() int32 { + if x != nil { + return x.SerializerId + } + return 0 +} + +func (x *MessageEnvelope) GetMessageHeader() *MessageHeader { + if x != nil { + return x.MessageHeader + } + return nil +} + +func (x *MessageEnvelope) GetTargetRequestId() uint32 { + if x != nil { + return x.TargetRequestId + } + return 0 +} + +func (x *MessageEnvelope) GetSenderRequestId() uint32 { + if x != nil { + return x.SenderRequestId + } + return 0 +} + +type MessageHeader struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + HeaderData map[string]string `protobuf:"bytes,1,rep,name=header_data,json=headerData,proto3" json:"header_data,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (x *MessageHeader) Reset() { + *x = MessageHeader{} + if protoimpl.UnsafeEnabled { + mi := &file_remote_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *MessageHeader) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MessageHeader) ProtoMessage() {} + +func (x *MessageHeader) ProtoReflect() protoreflect.Message { + mi := &file_remote_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use MessageHeader.ProtoReflect.Descriptor instead. +func (*MessageHeader) Descriptor() ([]byte, []int) { + return file_remote_proto_rawDescGZIP(), []int{3} +} + +func (x *MessageHeader) GetHeaderData() map[string]string { + if x != nil { + return x.HeaderData + } + return nil +} + +type ActorPidRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Kind string `protobuf:"bytes,2,opt,name=kind,proto3" json:"kind,omitempty"` +} + +func (x *ActorPidRequest) Reset() { + *x = ActorPidRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_remote_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ActorPidRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ActorPidRequest) ProtoMessage() {} + +func (x *ActorPidRequest) ProtoReflect() protoreflect.Message { + mi := &file_remote_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ActorPidRequest.ProtoReflect.Descriptor instead. +func (*ActorPidRequest) Descriptor() ([]byte, []int) { + return file_remote_proto_rawDescGZIP(), []int{4} +} + +func (x *ActorPidRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *ActorPidRequest) GetKind() string { + if x != nil { + return x.Kind + } + return "" +} + +type ActorPidResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Pid *actor.PID `protobuf:"bytes,1,opt,name=pid,proto3" json:"pid,omitempty"` + StatusCode int32 `protobuf:"varint,2,opt,name=status_code,json=statusCode,proto3" json:"status_code,omitempty"` +} + +func (x *ActorPidResponse) Reset() { + *x = ActorPidResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_remote_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ActorPidResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ActorPidResponse) ProtoMessage() {} + +func (x *ActorPidResponse) ProtoReflect() protoreflect.Message { + mi := &file_remote_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ActorPidResponse.ProtoReflect.Descriptor instead. +func (*ActorPidResponse) Descriptor() ([]byte, []int) { + return file_remote_proto_rawDescGZIP(), []int{5} +} + +func (x *ActorPidResponse) GetPid() *actor.PID { + if x != nil { + return x.Pid + } + return nil +} + +func (x *ActorPidResponse) GetStatusCode() int32 { + if x != nil { + return x.StatusCode + } + return 0 +} + +type ConnectRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Types that are assignable to ConnectionType: + // *ConnectRequest_ClientConnection + // *ConnectRequest_ServerConnection + ConnectionType isConnectRequest_ConnectionType `protobuf_oneof:"connection_type"` +} + +func (x *ConnectRequest) Reset() { + *x = ConnectRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_remote_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ConnectRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ConnectRequest) ProtoMessage() {} + +func (x *ConnectRequest) ProtoReflect() protoreflect.Message { + mi := &file_remote_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ConnectRequest.ProtoReflect.Descriptor instead. +func (*ConnectRequest) Descriptor() ([]byte, []int) { + return file_remote_proto_rawDescGZIP(), []int{6} +} + +func (m *ConnectRequest) GetConnectionType() isConnectRequest_ConnectionType { + if m != nil { + return m.ConnectionType + } + return nil +} + +func (x *ConnectRequest) GetClientConnection() *ClientConnection { + if x, ok := x.GetConnectionType().(*ConnectRequest_ClientConnection); ok { + return x.ClientConnection + } + return nil +} + +func (x *ConnectRequest) GetServerConnection() *ServerConnection { + if x, ok := x.GetConnectionType().(*ConnectRequest_ServerConnection); ok { + return x.ServerConnection + } + return nil +} + +type isConnectRequest_ConnectionType interface { + isConnectRequest_ConnectionType() +} + +type ConnectRequest_ClientConnection struct { + ClientConnection *ClientConnection `protobuf:"bytes,1,opt,name=client_connection,json=clientConnection,proto3,oneof"` +} + +type ConnectRequest_ServerConnection struct { + ServerConnection *ServerConnection `protobuf:"bytes,2,opt,name=server_connection,json=serverConnection,proto3,oneof"` +} + +func (*ConnectRequest_ClientConnection) isConnectRequest_ConnectionType() {} + +func (*ConnectRequest_ServerConnection) isConnectRequest_ConnectionType() {} + +type DisconnectRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *DisconnectRequest) Reset() { + *x = DisconnectRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_remote_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DisconnectRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DisconnectRequest) ProtoMessage() {} + +func (x *DisconnectRequest) ProtoReflect() protoreflect.Message { + mi := &file_remote_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DisconnectRequest.ProtoReflect.Descriptor instead. +func (*DisconnectRequest) Descriptor() ([]byte, []int) { + return file_remote_proto_rawDescGZIP(), []int{7} +} + +type ClientConnection struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + SystemId string `protobuf:"bytes,1,opt,name=SystemId,proto3" json:"SystemId,omitempty"` +} + +func (x *ClientConnection) Reset() { + *x = ClientConnection{} + if protoimpl.UnsafeEnabled { + mi := &file_remote_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ClientConnection) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ClientConnection) ProtoMessage() {} + +func (x *ClientConnection) ProtoReflect() protoreflect.Message { + mi := &file_remote_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ClientConnection.ProtoReflect.Descriptor instead. +func (*ClientConnection) Descriptor() ([]byte, []int) { + return file_remote_proto_rawDescGZIP(), []int{8} +} + +func (x *ClientConnection) GetSystemId() string { + if x != nil { + return x.SystemId + } + return "" +} + +type ServerConnection struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + SystemId string `protobuf:"bytes,1,opt,name=SystemId,proto3" json:"SystemId,omitempty"` + Address string `protobuf:"bytes,2,opt,name=Address,proto3" json:"Address,omitempty"` +} + +func (x *ServerConnection) Reset() { + *x = ServerConnection{} + if protoimpl.UnsafeEnabled { + mi := &file_remote_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ServerConnection) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ServerConnection) ProtoMessage() {} + +func (x *ServerConnection) ProtoReflect() protoreflect.Message { + mi := &file_remote_proto_msgTypes[9] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ServerConnection.ProtoReflect.Descriptor instead. +func (*ServerConnection) Descriptor() ([]byte, []int) { + return file_remote_proto_rawDescGZIP(), []int{9} +} + +func (x *ServerConnection) GetSystemId() string { + if x != nil { + return x.SystemId + } + return "" +} + +func (x *ServerConnection) GetAddress() string { + if x != nil { + return x.Address + } + return "" +} + +type ConnectResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + MemberId string `protobuf:"bytes,2,opt,name=member_id,json=memberId,proto3" json:"member_id,omitempty"` + Blocked bool `protobuf:"varint,3,opt,name=blocked,proto3" json:"blocked,omitempty"` +} + +func (x *ConnectResponse) Reset() { + *x = ConnectResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_remote_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ConnectResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ConnectResponse) ProtoMessage() {} + +func (x *ConnectResponse) ProtoReflect() protoreflect.Message { + mi := &file_remote_proto_msgTypes[10] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ConnectResponse.ProtoReflect.Descriptor instead. +func (*ConnectResponse) Descriptor() ([]byte, []int) { + return file_remote_proto_rawDescGZIP(), []int{10} +} + +func (x *ConnectResponse) GetMemberId() string { + if x != nil { + return x.MemberId + } + return "" +} + +func (x *ConnectResponse) GetBlocked() bool { + if x != nil { + return x.Blocked + } + return false +} + +type ListProcessesRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Pattern string `protobuf:"bytes,1,opt,name=pattern,proto3" json:"pattern,omitempty"` + Type ListProcessesMatchType `protobuf:"varint,2,opt,name=type,proto3,enum=remote.ListProcessesMatchType" json:"type,omitempty"` +} + +func (x *ListProcessesRequest) Reset() { + *x = ListProcessesRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_remote_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListProcessesRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListProcessesRequest) ProtoMessage() {} + +func (x *ListProcessesRequest) ProtoReflect() protoreflect.Message { + mi := &file_remote_proto_msgTypes[11] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListProcessesRequest.ProtoReflect.Descriptor instead. +func (*ListProcessesRequest) Descriptor() ([]byte, []int) { + return file_remote_proto_rawDescGZIP(), []int{11} +} + +func (x *ListProcessesRequest) GetPattern() string { + if x != nil { + return x.Pattern + } + return "" +} + +func (x *ListProcessesRequest) GetType() ListProcessesMatchType { + if x != nil { + return x.Type + } + return ListProcessesMatchType_MatchPartOfString +} + +type ListProcessesResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Pids []*actor.PID `protobuf:"bytes,1,rep,name=pids,proto3" json:"pids,omitempty"` +} + +func (x *ListProcessesResponse) Reset() { + *x = ListProcessesResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_remote_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListProcessesResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListProcessesResponse) ProtoMessage() {} + +func (x *ListProcessesResponse) ProtoReflect() protoreflect.Message { + mi := &file_remote_proto_msgTypes[12] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListProcessesResponse.ProtoReflect.Descriptor instead. +func (*ListProcessesResponse) Descriptor() ([]byte, []int) { + return file_remote_proto_rawDescGZIP(), []int{12} +} + +func (x *ListProcessesResponse) GetPids() []*actor.PID { + if x != nil { + return x.Pids + } + return nil +} + +type GetProcessDiagnosticsRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Pid *actor.PID `protobuf:"bytes,1,opt,name=pid,proto3" json:"pid,omitempty"` +} + +func (x *GetProcessDiagnosticsRequest) Reset() { + *x = GetProcessDiagnosticsRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_remote_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetProcessDiagnosticsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetProcessDiagnosticsRequest) ProtoMessage() {} + +func (x *GetProcessDiagnosticsRequest) ProtoReflect() protoreflect.Message { + mi := &file_remote_proto_msgTypes[13] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetProcessDiagnosticsRequest.ProtoReflect.Descriptor instead. +func (*GetProcessDiagnosticsRequest) Descriptor() ([]byte, []int) { + return file_remote_proto_rawDescGZIP(), []int{13} +} + +func (x *GetProcessDiagnosticsRequest) GetPid() *actor.PID { + if x != nil { + return x.Pid + } + return nil +} + +type GetProcessDiagnosticsResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + DiagnosticsString string `protobuf:"bytes,1,opt,name=diagnostics_string,json=diagnosticsString,proto3" json:"diagnostics_string,omitempty"` +} + +func (x *GetProcessDiagnosticsResponse) Reset() { + *x = GetProcessDiagnosticsResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_remote_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetProcessDiagnosticsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetProcessDiagnosticsResponse) ProtoMessage() {} + +func (x *GetProcessDiagnosticsResponse) ProtoReflect() protoreflect.Message { + mi := &file_remote_proto_msgTypes[14] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetProcessDiagnosticsResponse.ProtoReflect.Descriptor instead. +func (*GetProcessDiagnosticsResponse) Descriptor() ([]byte, []int) { + return file_remote_proto_rawDescGZIP(), []int{14} +} + +func (x *GetProcessDiagnosticsResponse) GetDiagnosticsString() string { + if x != nil { + return x.DiagnosticsString + } + return "" +} + +var File_remote_proto protoreflect.FileDescriptor + +var file_remote_proto_rawDesc = []byte{ + 0x0a, 0x0c, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06, + 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x1a, 0x0b, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x22, 0xb1, 0x02, 0x0a, 0x0d, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x4d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x3b, 0x0a, 0x0d, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x5f, 0x62, 0x61, 0x74, 0x63, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x72, + 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x42, 0x61, 0x74, + 0x63, 0x68, 0x48, 0x00, 0x52, 0x0c, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x42, 0x61, 0x74, + 0x63, 0x68, 0x12, 0x41, 0x0a, 0x0f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x5f, 0x72, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x72, 0x65, + 0x6d, 0x6f, 0x74, 0x65, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x0e, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x44, 0x0a, 0x10, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, + 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x17, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x00, 0x52, 0x0f, 0x63, 0x6f, 0x6e, 0x6e, + 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4a, 0x0a, 0x12, 0x64, + 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, + 0x2e, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x48, 0x00, 0x52, 0x11, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x42, 0x0e, 0x0a, 0x0c, 0x6d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x22, 0xb0, 0x01, 0x0a, 0x0c, 0x4d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x42, 0x61, 0x74, 0x63, 0x68, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x79, 0x70, 0x65, + 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x74, 0x79, + 0x70, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x24, 0x0a, 0x07, 0x74, 0x61, 0x72, 0x67, 0x65, + 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x61, 0x63, 0x74, 0x6f, 0x72, + 0x2e, 0x50, 0x49, 0x44, 0x52, 0x07, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x12, 0x35, 0x0a, + 0x09, 0x65, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x17, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x52, 0x09, 0x65, 0x6e, 0x76, 0x65, 0x6c, + 0x6f, 0x70, 0x65, 0x73, 0x12, 0x24, 0x0a, 0x07, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x73, 0x18, + 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x2e, 0x50, 0x49, + 0x44, 0x52, 0x07, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x73, 0x22, 0xb8, 0x02, 0x0a, 0x0f, 0x4d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x12, 0x17, + 0x0a, 0x07, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, + 0x06, 0x74, 0x79, 0x70, 0x65, 0x49, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x6d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x6d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x44, 0x61, 0x74, 0x61, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x61, + 0x72, 0x67, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x74, 0x61, 0x72, 0x67, + 0x65, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x06, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x65, + 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x0c, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x72, 0x49, 0x64, 0x12, + 0x3c, 0x0a, 0x0e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, + 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, + 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x0d, + 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x2a, 0x0a, + 0x11, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, + 0x69, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x2a, 0x0a, 0x11, 0x73, 0x65, 0x6e, + 0x64, 0x65, 0x72, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x08, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0f, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x49, 0x64, 0x22, 0x96, 0x01, 0x0a, 0x0d, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x46, 0x0a, 0x0b, 0x68, 0x65, 0x61, 0x64, 0x65, + 0x72, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x72, + 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x65, 0x61, + 0x64, 0x65, 0x72, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x44, 0x61, 0x74, 0x61, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x52, 0x0a, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x44, 0x61, 0x74, 0x61, 0x1a, + 0x3d, 0x0a, 0x0f, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x44, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x39, + 0x0a, 0x0f, 0x41, 0x63, 0x74, 0x6f, 0x72, 0x50, 0x69, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x22, 0x51, 0x0a, 0x10, 0x41, 0x63, 0x74, + 0x6f, 0x72, 0x50, 0x69, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, + 0x03, 0x70, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x61, 0x63, 0x74, + 0x6f, 0x72, 0x2e, 0x50, 0x49, 0x44, 0x52, 0x03, 0x70, 0x69, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x73, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x43, 0x6f, 0x64, 0x65, 0x22, 0xb5, 0x01, 0x0a, + 0x0e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x47, 0x0a, 0x11, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x72, 0x65, 0x6d, + 0x6f, 0x74, 0x65, 0x2e, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x10, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x6f, + 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x47, 0x0a, 0x11, 0x73, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x2e, 0x53, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, + 0x10, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x42, 0x11, 0x0a, 0x0f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, + 0x74, 0x79, 0x70, 0x65, 0x22, 0x13, 0x0a, 0x11, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, + 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x2e, 0x0a, 0x10, 0x43, 0x6c, 0x69, + 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, + 0x08, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x49, 0x64, 0x22, 0x48, 0x0a, 0x10, 0x53, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, + 0x08, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x41, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x41, 0x64, 0x64, 0x72, + 0x65, 0x73, 0x73, 0x22, 0x48, 0x0a, 0x0f, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, + 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6d, 0x65, 0x6d, 0x62, 0x65, + 0x72, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x22, 0x64, 0x0a, + 0x14, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x12, + 0x32, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1e, 0x2e, + 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x63, 0x65, + 0x73, 0x73, 0x65, 0x73, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, + 0x79, 0x70, 0x65, 0x22, 0x37, 0x0a, 0x15, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x63, 0x65, + 0x73, 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1e, 0x0a, 0x04, + 0x70, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x61, 0x63, 0x74, + 0x6f, 0x72, 0x2e, 0x50, 0x49, 0x44, 0x52, 0x04, 0x70, 0x69, 0x64, 0x73, 0x22, 0x3c, 0x0a, 0x1c, + 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x44, 0x69, 0x61, 0x67, 0x6e, 0x6f, + 0x73, 0x74, 0x69, 0x63, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x03, + 0x70, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x61, 0x63, 0x74, 0x6f, + 0x72, 0x2e, 0x50, 0x49, 0x44, 0x52, 0x03, 0x70, 0x69, 0x64, 0x22, 0x4e, 0x0a, 0x1d, 0x47, 0x65, + 0x74, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x44, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, + 0x69, 0x63, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2d, 0x0a, 0x12, 0x64, + 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x5f, 0x73, 0x74, 0x72, 0x69, 0x6e, + 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, + 0x74, 0x69, 0x63, 0x73, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2a, 0x55, 0x0a, 0x16, 0x4c, 0x69, + 0x73, 0x74, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x73, 0x4d, 0x61, 0x74, 0x63, 0x68, + 0x54, 0x79, 0x70, 0x65, 0x12, 0x15, 0x0a, 0x11, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x50, 0x61, 0x72, + 0x74, 0x4f, 0x66, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x10, 0x00, 0x12, 0x14, 0x0a, 0x10, 0x4d, + 0x61, 0x74, 0x63, 0x68, 0x45, 0x78, 0x61, 0x63, 0x74, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x10, + 0x01, 0x12, 0x0e, 0x0a, 0x0a, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x67, 0x65, 0x78, 0x10, + 0x02, 0x32, 0x81, 0x02, 0x0a, 0x08, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x3d, + 0x0a, 0x07, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x12, 0x15, 0x2e, 0x72, 0x65, 0x6d, 0x6f, + 0x74, 0x65, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x1a, 0x15, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, + 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x28, 0x01, 0x30, 0x01, 0x12, 0x4e, 0x0a, + 0x0d, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x73, 0x12, 0x1c, + 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x63, + 0x65, 0x73, 0x73, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x72, + 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, + 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x66, 0x0a, + 0x15, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x44, 0x69, 0x61, 0x67, 0x6e, + 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x12, 0x24, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x2e, + 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x44, 0x69, 0x61, 0x67, 0x6e, 0x6f, + 0x73, 0x74, 0x69, 0x63, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x72, + 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, + 0x44, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x2b, 0x5a, 0x29, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x73, 0x79, 0x6e, 0x6b, 0x72, 0x6f, 0x6e, 0x2f, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x2d, 0x67, 0x6f, 0x2f, 0x72, 0x65, 0x6d, 0x6f, + 0x74, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_remote_proto_rawDescOnce sync.Once + file_remote_proto_rawDescData = file_remote_proto_rawDesc +) + +func file_remote_proto_rawDescGZIP() []byte { + file_remote_proto_rawDescOnce.Do(func() { + file_remote_proto_rawDescData = protoimpl.X.CompressGZIP(file_remote_proto_rawDescData) + }) + return file_remote_proto_rawDescData +} + +var file_remote_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_remote_proto_msgTypes = make([]protoimpl.MessageInfo, 16) +var file_remote_proto_goTypes = []interface{}{ + (ListProcessesMatchType)(0), // 0: remote.ListProcessesMatchType + (*RemoteMessage)(nil), // 1: remote.RemoteMessage + (*MessageBatch)(nil), // 2: remote.MessageBatch + (*MessageEnvelope)(nil), // 3: remote.MessageEnvelope + (*MessageHeader)(nil), // 4: remote.MessageHeader + (*ActorPidRequest)(nil), // 5: remote.ActorPidRequest + (*ActorPidResponse)(nil), // 6: remote.ActorPidResponse + (*ConnectRequest)(nil), // 7: remote.ConnectRequest + (*DisconnectRequest)(nil), // 8: remote.DisconnectRequest + (*ClientConnection)(nil), // 9: remote.ClientConnection + (*ServerConnection)(nil), // 10: remote.ServerConnection + (*ConnectResponse)(nil), // 11: remote.ConnectResponse + (*ListProcessesRequest)(nil), // 12: remote.ListProcessesRequest + (*ListProcessesResponse)(nil), // 13: remote.ListProcessesResponse + (*GetProcessDiagnosticsRequest)(nil), // 14: remote.GetProcessDiagnosticsRequest + (*GetProcessDiagnosticsResponse)(nil), // 15: remote.GetProcessDiagnosticsResponse + nil, // 16: remote.MessageHeader.HeaderDataEntry + (*actor.PID)(nil), // 17: actor.PID +} +var file_remote_proto_depIdxs = []int32{ + 2, // 0: remote.RemoteMessage.message_batch:type_name -> remote.MessageBatch + 7, // 1: remote.RemoteMessage.connect_request:type_name -> remote.ConnectRequest + 11, // 2: remote.RemoteMessage.connect_response:type_name -> remote.ConnectResponse + 8, // 3: remote.RemoteMessage.disconnect_request:type_name -> remote.DisconnectRequest + 17, // 4: remote.MessageBatch.targets:type_name -> actor.PID + 3, // 5: remote.MessageBatch.envelopes:type_name -> remote.MessageEnvelope + 17, // 6: remote.MessageBatch.senders:type_name -> actor.PID + 4, // 7: remote.MessageEnvelope.message_header:type_name -> remote.MessageHeader + 16, // 8: remote.MessageHeader.header_data:type_name -> remote.MessageHeader.HeaderDataEntry + 17, // 9: remote.ActorPidResponse.pid:type_name -> actor.PID + 9, // 10: remote.ConnectRequest.client_connection:type_name -> remote.ClientConnection + 10, // 11: remote.ConnectRequest.server_connection:type_name -> remote.ServerConnection + 0, // 12: remote.ListProcessesRequest.type:type_name -> remote.ListProcessesMatchType + 17, // 13: remote.ListProcessesResponse.pids:type_name -> actor.PID + 17, // 14: remote.GetProcessDiagnosticsRequest.pid:type_name -> actor.PID + 1, // 15: remote.Remoting.Receive:input_type -> remote.RemoteMessage + 12, // 16: remote.Remoting.ListProcesses:input_type -> remote.ListProcessesRequest + 14, // 17: remote.Remoting.GetProcessDiagnostics:input_type -> remote.GetProcessDiagnosticsRequest + 1, // 18: remote.Remoting.Receive:output_type -> remote.RemoteMessage + 13, // 19: remote.Remoting.ListProcesses:output_type -> remote.ListProcessesResponse + 15, // 20: remote.Remoting.GetProcessDiagnostics:output_type -> remote.GetProcessDiagnosticsResponse + 18, // [18:21] is the sub-list for method output_type + 15, // [15:18] is the sub-list for method input_type + 15, // [15:15] is the sub-list for extension type_name + 15, // [15:15] is the sub-list for extension extendee + 0, // [0:15] is the sub-list for field type_name +} + +func init() { file_remote_proto_init() } +func file_remote_proto_init() { + if File_remote_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_remote_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RemoteMessage); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_remote_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*MessageBatch); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_remote_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*MessageEnvelope); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_remote_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*MessageHeader); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_remote_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ActorPidRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_remote_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ActorPidResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_remote_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ConnectRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_remote_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DisconnectRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_remote_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ClientConnection); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_remote_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ServerConnection); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_remote_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ConnectResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_remote_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListProcessesRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_remote_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListProcessesResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_remote_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetProcessDiagnosticsRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_remote_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetProcessDiagnosticsResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + file_remote_proto_msgTypes[0].OneofWrappers = []interface{}{ + (*RemoteMessage_MessageBatch)(nil), + (*RemoteMessage_ConnectRequest)(nil), + (*RemoteMessage_ConnectResponse)(nil), + (*RemoteMessage_DisconnectRequest)(nil), + } + file_remote_proto_msgTypes[6].OneofWrappers = []interface{}{ + (*ConnectRequest_ClientConnection)(nil), + (*ConnectRequest_ServerConnection)(nil), + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_remote_proto_rawDesc, + NumEnums: 1, + NumMessages: 16, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_remote_proto_goTypes, + DependencyIndexes: file_remote_proto_depIdxs, + EnumInfos: file_remote_proto_enumTypes, + MessageInfos: file_remote_proto_msgTypes, + }.Build() + File_remote_proto = out.File + file_remote_proto_rawDesc = nil + file_remote_proto_goTypes = nil + file_remote_proto_depIdxs = nil +} diff --git a/remote/remote.proto b/remote/remote.proto new file mode 100644 index 0000000000000000000000000000000000000000..394dcc402bfe2ba7dad29d24d3b7e415542bf05f --- /dev/null +++ b/remote/remote.proto @@ -0,0 +1,100 @@ +syntax = "proto3"; +package remote; +option go_package = "/gitee.com/simplexyz/simpleactor-go/remote"; +import "actor.proto"; + + +message RemoteMessage { + oneof message_type { + MessageBatch message_batch = 1; + ConnectRequest connect_request = 2; + ConnectResponse connect_response = 3; + DisconnectRequest disconnect_request = 4; + } +} + +message MessageBatch { + repeated string type_names = 1; + repeated actor.PID targets = 2; + repeated MessageEnvelope envelopes = 3; + repeated actor.PID senders = 4; +} + +message MessageEnvelope { + int32 type_id = 1; + bytes message_data = 2; + int32 target = 3; + int32 sender = 4; + int32 serializer_id = 5; + MessageHeader message_header = 6; + uint32 target_request_id = 7; + uint32 sender_request_id = 8; +} + +message MessageHeader { + map header_data = 1; +} + +message ActorPidRequest { + string name = 1; + string kind = 2; +} + +message ActorPidResponse { + actor.PID pid = 1; + int32 status_code = 2; +} + +message ConnectRequest { + oneof connection_type { + ClientConnection client_connection = 1; + ServerConnection server_connection = 2; + } +} + +message DisconnectRequest { + +} + +message ClientConnection { + string SystemId = 1; +} + +message ServerConnection { + string SystemId = 1; + string Address = 2; +} + +message ConnectResponse { + string member_id = 2; + bool blocked = 3; +} + +service Remoting { + rpc Receive (stream RemoteMessage) returns (stream RemoteMessage) {} + rpc ListProcesses(ListProcessesRequest) returns (ListProcessesResponse) {} + rpc GetProcessDiagnostics(GetProcessDiagnosticsRequest) returns (GetProcessDiagnosticsResponse) {} +} + +message ListProcessesRequest { + string pattern = 1; + ListProcessesMatchType type = 2; +} + +enum ListProcessesMatchType { + MatchPartOfString = 0; + MatchExactString = 1; + MatchRegex = 2; +} + +message ListProcessesResponse { + repeated actor.PID pids = 1; +} + +message GetProcessDiagnosticsRequest { + actor.PID pid = 1; +} + +message GetProcessDiagnosticsResponse { + string diagnostics_string= 1; +} \ No newline at end of file diff --git a/remote/remote_grpc.pb.go b/remote/remote_grpc.pb.go new file mode 100644 index 0000000000000000000000000000000000000000..ade00fc60c16780c490019e7c57772d363614d2d --- /dev/null +++ b/remote/remote_grpc.pb.go @@ -0,0 +1,210 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.2.0 +// - protoc v3.19.1 +// source: remote.proto + +package remote + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +// RemotingClient is the client API for Remoting service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type RemotingClient interface { + Receive(ctx context.Context, opts ...grpc.CallOption) (Remoting_ReceiveClient, error) + ListProcesses(ctx context.Context, in *ListProcessesRequest, opts ...grpc.CallOption) (*ListProcessesResponse, error) + GetProcessDiagnostics(ctx context.Context, in *GetProcessDiagnosticsRequest, opts ...grpc.CallOption) (*GetProcessDiagnosticsResponse, error) +} + +type remotingClient struct { + cc grpc.ClientConnInterface +} + +func NewRemotingClient(cc grpc.ClientConnInterface) RemotingClient { + return &remotingClient{cc} +} + +func (c *remotingClient) Receive(ctx context.Context, opts ...grpc.CallOption) (Remoting_ReceiveClient, error) { + stream, err := c.cc.NewStream(ctx, &Remoting_ServiceDesc.Streams[0], "/remote.Remoting/Receive", opts...) + if err != nil { + return nil, err + } + x := &remotingReceiveClient{stream} + return x, nil +} + +type Remoting_ReceiveClient interface { + Send(*RemoteMessage) error + Recv() (*RemoteMessage, error) + grpc.ClientStream +} + +type remotingReceiveClient struct { + grpc.ClientStream +} + +func (x *remotingReceiveClient) Send(m *RemoteMessage) error { + return x.ClientStream.SendMsg(m) +} + +func (x *remotingReceiveClient) Recv() (*RemoteMessage, error) { + m := new(RemoteMessage) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +func (c *remotingClient) ListProcesses(ctx context.Context, in *ListProcessesRequest, opts ...grpc.CallOption) (*ListProcessesResponse, error) { + out := new(ListProcessesResponse) + err := c.cc.Invoke(ctx, "/remote.Remoting/ListProcesses", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *remotingClient) GetProcessDiagnostics(ctx context.Context, in *GetProcessDiagnosticsRequest, opts ...grpc.CallOption) (*GetProcessDiagnosticsResponse, error) { + out := new(GetProcessDiagnosticsResponse) + err := c.cc.Invoke(ctx, "/remote.Remoting/GetProcessDiagnostics", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// RemotingServer is the server API for Remoting service. +// All implementations must embed UnimplementedRemotingServer +// for forward compatibility +type RemotingServer interface { + Receive(Remoting_ReceiveServer) error + ListProcesses(context.Context, *ListProcessesRequest) (*ListProcessesResponse, error) + GetProcessDiagnostics(context.Context, *GetProcessDiagnosticsRequest) (*GetProcessDiagnosticsResponse, error) + mustEmbedUnimplementedRemotingServer() +} + +// UnimplementedRemotingServer must be embedded to have forward compatible implementations. +type UnimplementedRemotingServer struct { +} + +func (UnimplementedRemotingServer) Receive(Remoting_ReceiveServer) error { + return status.Errorf(codes.Unimplemented, "method Receive not implemented") +} +func (UnimplementedRemotingServer) ListProcesses(context.Context, *ListProcessesRequest) (*ListProcessesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListProcesses not implemented") +} +func (UnimplementedRemotingServer) GetProcessDiagnostics(context.Context, *GetProcessDiagnosticsRequest) (*GetProcessDiagnosticsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetProcessDiagnostics not implemented") +} +func (UnimplementedRemotingServer) mustEmbedUnimplementedRemotingServer() {} + +// UnsafeRemotingServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to RemotingServer will +// result in compilation errors. +type UnsafeRemotingServer interface { + mustEmbedUnimplementedRemotingServer() +} + +func RegisterRemotingServer(s grpc.ServiceRegistrar, srv RemotingServer) { + s.RegisterService(&Remoting_ServiceDesc, srv) +} + +func _Remoting_Receive_Handler(srv interface{}, stream grpc.ServerStream) error { + return srv.(RemotingServer).Receive(&remotingReceiveServer{stream}) +} + +type Remoting_ReceiveServer interface { + Send(*RemoteMessage) error + Recv() (*RemoteMessage, error) + grpc.ServerStream +} + +type remotingReceiveServer struct { + grpc.ServerStream +} + +func (x *remotingReceiveServer) Send(m *RemoteMessage) error { + return x.ServerStream.SendMsg(m) +} + +func (x *remotingReceiveServer) Recv() (*RemoteMessage, error) { + m := new(RemoteMessage) + if err := x.ServerStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +func _Remoting_ListProcesses_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListProcessesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(RemotingServer).ListProcesses(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/remote.Remoting/ListProcesses", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(RemotingServer).ListProcesses(ctx, req.(*ListProcessesRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Remoting_GetProcessDiagnostics_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetProcessDiagnosticsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(RemotingServer).GetProcessDiagnostics(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/remote.Remoting/GetProcessDiagnostics", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(RemotingServer).GetProcessDiagnostics(ctx, req.(*GetProcessDiagnosticsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// Remoting_ServiceDesc is the grpc.ServiceDesc for Remoting service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var Remoting_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "remote.Remoting", + HandlerType: (*RemotingServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "ListProcesses", + Handler: _Remoting_ListProcesses_Handler, + }, + { + MethodName: "GetProcessDiagnostics", + Handler: _Remoting_GetProcessDiagnostics_Handler, + }, + }, + Streams: []grpc.StreamDesc{ + { + StreamName: "Receive", + Handler: _Remoting_Receive_Handler, + ServerStreams: true, + ClientStreams: true, + }, + }, + Metadata: "remote.proto", +} diff --git a/remote/remote_handler.go b/remote/remote_handler.go new file mode 100644 index 0000000000000000000000000000000000000000..a4816e928c4736927244e48a8288bae6f8399925 --- /dev/null +++ b/remote/remote_handler.go @@ -0,0 +1,13 @@ +package remote + +import "gitee.com/simplexyz/simpleactor-go/actor" + +// func remoteHandler(pid *actor.PID) (actor.Process, bool) { +// ref := newProcess(pid, nil) +// return ref, true +// } + +func (r *Remote) remoteHandler(pid *actor.PID) (actor.Process, bool) { + ref := newProcess(pid, r) + return ref, true +} diff --git a/remote/remote_process.go b/remote/remote_process.go new file mode 100644 index 0000000000000000000000000000000000000000..b5f3c5ad787c11642fe0207217268bdda4494199 --- /dev/null +++ b/remote/remote_process.go @@ -0,0 +1,50 @@ +package remote + +import ( + "gitee.com/simplexyz/simpleactor-go/actor" +) + +type process struct { + pid *actor.PID + remote *Remote +} + +func newProcess(pid *actor.PID, r *Remote) actor.Process { + return &process{ + pid: pid, + remote: r, + } +} + +var _ actor.Process = &process{} + +func (ref *process) SendUserMessage(pid *actor.PID, message interface{}) { + header, msg, sender := actor.UnwrapEnvelope(message) + ref.remote.SendMessage(pid, header, msg, sender, -1) +} + +func (ref *process) SendSystemMessage(pid *actor.PID, message interface{}) { + // intercept any Watch messages and direct them to the endpoint manager + switch msg := message.(type) { + case *actor.Watch: + rw := &remoteWatch{ + Watcher: msg.Watcher, + Watchee: pid, + } + // endpointManager.remoteWatch(rw) + ref.remote.edpManager.remoteWatch(rw) + case *actor.Unwatch: + ruw := &remoteUnwatch{ + Watcher: msg.Watcher, + Watchee: pid, + } + // endpointManager.remoteUnwatch(ruw) + ref.remote.edpManager.remoteUnwatch(ruw) + default: + ref.remote.SendMessage(pid, nil, message, nil, -1) + } +} + +func (ref *process) Stop(pid *actor.PID) { + ref.SendSystemMessage(pid, stopMessage) +} diff --git a/remote/response_status_code.go b/remote/response_status_code.go new file mode 100644 index 0000000000000000000000000000000000000000..89042dd61fe7e9bb6277e9624e2ba32f1a11ced9 --- /dev/null +++ b/remote/response_status_code.go @@ -0,0 +1,58 @@ +package remote + +import "strconv" + +type ResponseStatusCode int32 + +const ( + ResponseStatusCodeOK ResponseStatusCode = iota + ResponseStatusCodeUNAVAILABLE + ResponseStatusCodeTIMEOUT + ResponseStatusCodePROCESSNAMEALREADYEXIST + ResponseStatusCodeERROR + ResponseStatusCodeDeadLetter + ResponseStatusCodeMAX // just a boundary. +) + +var responseNames [ResponseStatusCodeMAX]string + +func init() { + responseNames[ResponseStatusCodeOK] = "ResponseStatusCodeOK" + responseNames[ResponseStatusCodeUNAVAILABLE] = "ResponseStatusCodeUNAVAILABLE" + responseNames[ResponseStatusCodeTIMEOUT] = "ResponseStatusCodeTIMEOUT" + responseNames[ResponseStatusCodePROCESSNAMEALREADYEXIST] = "ResponseStatusCodePROCESSNAMEALREADYEXIST" + responseNames[ResponseStatusCodePROCESSNAMEALREADYEXIST] = "ResponseStatusCodePROCESSNAMEALREADYEXIST" + responseNames[ResponseStatusCodeERROR] = "ResponseStatusCodeERROR" + responseNames[ResponseStatusCodeDeadLetter] = "ResponseStatusCodeDeadLetter" +} + +func (c ResponseStatusCode) ToInt32() int32 { + return int32(c) +} + +func (c ResponseStatusCode) String() string { + statusCode := int(c) + if statusCode < 0 || statusCode >= len(responseNames) { + return "ResponseStatusCode-" + strconv.Itoa(int(c)) + } + return responseNames[statusCode] +} + +func (c ResponseStatusCode) AsError() *ResponseError { + switch c { + case ResponseStatusCodeOK: + return nil + case ResponseStatusCodeUNAVAILABLE: + return ErrUnAvailable + case ResponseStatusCodeTIMEOUT: + return ErrTimeout + case ResponseStatusCodePROCESSNAMEALREADYEXIST: + return ErrProcessNameAlreadyExist + case ResponseStatusCodeERROR: + return ErrUnknownError + case ResponseStatusCodeDeadLetter: + return ErrDeadLetter + default: + return &ResponseError{c} + } +} diff --git a/remote/response_status_code_test.go b/remote/response_status_code_test.go new file mode 100644 index 0000000000000000000000000000000000000000..9ea81d445b5aa6cb5b4932efac409d9a4660422a --- /dev/null +++ b/remote/response_status_code_test.go @@ -0,0 +1,27 @@ +package remote + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestStatusCode_String(t *testing.T) { + assert := assert.New(t) + for i := 0; i < int(ResponseStatusCodeMAX); i++ { + code := ResponseStatusCode(i) + assert.NotEmpty(fmt.Sprintf("%s", code)) + } + s := fmt.Sprintf("%s", ResponseStatusCode(100)) + assert.Equal(s, "ResponseStatusCode-100") +} + +func TestStatusCode_Error(t *testing.T) { + assert := assert.New(t) + for i := 0; i < int(ResponseStatusCodeMAX); i++ { + var err error = nil + err = &ResponseError{ResponseStatusCode(i)} + assert.Error(err) + } +} diff --git a/remote/serializer.go b/remote/serializer.go new file mode 100644 index 0000000000000000000000000000000000000000..bfab1ab5e5e2a6a618229d7f472ab5ae3501cbfd --- /dev/null +++ b/remote/serializer.go @@ -0,0 +1,45 @@ +package remote + +var ( + DefaultSerializerID int32 + serializers []Serializer +) + +func init() { + RegisterSerializer(newProtoSerializer()) + RegisterSerializer(newJsonSerializer()) +} + +func RegisterSerializer(serializer Serializer) { + serializers = append(serializers, serializer) +} + +type Serializer interface { + Serialize(msg interface{}) ([]byte, error) + Deserialize(typeName string, bytes []byte) (interface{}, error) + GetTypeName(msg interface{}) (string, error) +} + +func Serialize(message interface{}, serializerID int32) ([]byte, string, error) { + res, err := serializers[serializerID].Serialize(message) + typeName, err := serializers[serializerID].GetTypeName(message) + return res, typeName, err +} + +func Deserialize(message []byte, typeName string, serializerID int32) (interface{}, error) { + return serializers[serializerID].Deserialize(typeName, message) +} + +// RootSerializable is the root level in-process representation of a message +type RootSerializable interface { + // Serialize returns the on-the-wire representation of the message + // Message -> IRootSerialized -> ByteString + Serialize() RootSerialized +} + +// RootSerialized is the root level on-the-wire representation of a message +type RootSerialized interface { + // Deserialize returns the in-process representation of a message + // ByteString -> IRootSerialized -> Message + Deserialize() RootSerializable +} diff --git a/remote/serializer_test.go b/remote/serializer_test.go new file mode 100644 index 0000000000000000000000000000000000000000..9549b16727288aef9d002451a576868b92d27ecb --- /dev/null +++ b/remote/serializer_test.go @@ -0,0 +1,57 @@ +package remote + +import ( + "testing" + + "gitee.com/simplexyz/simpleactor-go/actor" + "github.com/stretchr/testify/assert" +) + +//func TestJsonSerializer_round_trip(t *testing.T) { +// m := &ActorPidRequest{ +// Kind: "abc", +// Name: "def", +// } +// b, typeName, _ := Serialize(m, 1) +// res, err := Deserialize(b, typeName, 1) +// +// assert.Nil(t, err) +// +// var typed = res.(*ActorPidRequest) +// assert.Equal(t, "remote.ActorPidRequest", typeName) +// assert.Equal(t, m, typed) +//} +// +//func TestJsonSerializer_Serialize_PID_raw(t *testing.T) { +// system := actor.NewActorSystem() +// m, _ := system.Root.SpawnNamed(actor.PropsFromFunc(func(ctx actor.Context) {}), "actorpid") +// var ser = jsonpb.Marshaler{} +// res, _ := ser.MarshalToString(m) +// assert.Equal(t, "{\"Address\":\"nonhost\",\"Id\":\"actorpid\"}", res) +//} +// +//func TestJsonSerializer_Serialize_PID(t *testing.T) { +// system := actor.NewActorSystem() +// m := system.NewLocalPID("foo") +// b, typeName, _ := Serialize(m, 1) +// res, err := Deserialize(b, typeName, 1) +// +// assert.Nil(t, err) +// +// var typed = res.(*actor.PID) +// assert.Equal(t, "actor.PID", typeName) +// assert.Equal(t, m, typed) +//} + +func TestProtobufSerializer_Serialize_PID(t *testing.T) { + system := actor.NewActorSystem() + m := system.NewLocalPID("foo") + b, typeName, _ := Serialize(m, 0) + res, err := Deserialize(b, typeName, 0) + + assert.Nil(t, err) + + typed := res.(*actor.PID) + assert.Equal(t, "actor.PID", typeName) + assert.True(t, m.Equal(typed)) +} diff --git a/remote/server.go b/remote/server.go new file mode 100644 index 0000000000000000000000000000000000000000..f3b8258246ea5dd972ba58d59d4bcde9d12218b1 --- /dev/null +++ b/remote/server.go @@ -0,0 +1,125 @@ +package remote + +import ( + "fmt" + "io/ioutil" + "net" + "time" + + "gitee.com/simplexyz/simpleactor-go/extensions" + + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/log" + "google.golang.org/grpc" + "google.golang.org/grpc/grpclog" +) + +var extensionId = extensions.NextExtensionID() + +type Remote struct { + actorSystem *actor.ActorSystem + s *grpc.Server + edpReader *endpointReader + edpManager *endpointManager + config *Config + kinds map[string]*actor.Props + activatorPid *actor.PID + blocklist *BlockList +} + +func NewRemote(actorSystem *actor.ActorSystem, config *Config) *Remote { + r := &Remote{ + actorSystem: actorSystem, + config: config, + kinds: make(map[string]*actor.Props), + blocklist: NewBlockList(), + } + for k, v := range config.Kinds { + r.kinds[k] = v + } + + actorSystem.Extensions.Register(r) + + return r +} + +//goland:noinspection GoUnusedExportedFunction +func GetRemote(actorSystem *actor.ActorSystem) *Remote { + r := actorSystem.Extensions.Get(extensionId) + + return r.(*Remote) +} + +func (r *Remote) ExtensionID() extensions.ExtensionID { + return extensionId +} + +func (r *Remote) BlockList() *BlockList { return r.blocklist } + +// Start the remote server +func (r *Remote) Start() { + grpclog.SetLoggerV2(grpclog.NewLoggerV2(ioutil.Discard, ioutil.Discard, ioutil.Discard)) + lis, err := net.Listen("tcp", r.config.Address()) + if err != nil { + panic(fmt.Errorf("failed to listen: %v", err)) + } + + var address string + if r.config.AdvertisedHost != "" { + address = r.config.AdvertisedHost + } else { + address = lis.Addr().String() + } + + r.actorSystem.ProcessRegistry.RegisterAddressResolver(r.remoteHandler) + r.actorSystem.ProcessRegistry.Address = address + plog.Info("Starting remote with address", log.String("address", address)) + + r.edpManager = newEndpointManager(r) + r.edpManager.start() + + r.s = grpc.NewServer(r.config.ServerOptions...) + r.edpReader = newEndpointReader(r) + RegisterRemotingServer(r.s, r.edpReader) + plog.Info("Starting Proto.Actor server", log.String("address", address)) + go r.s.Serve(lis) +} + +func (r *Remote) Shutdown(graceful bool) { + if graceful { + // TODO: need more graceful + r.edpReader.suspend(true) + r.edpManager.stop() + + // For some reason GRPC doesn't want to stop + // Setup timeout as workaround but need to figure out in the future. + // TODO: grpc not stopping + c := make(chan bool, 1) + go func() { + r.s.GracefulStop() + c <- true + }() + + select { + case <-c: + plog.Info("Stopped Proto.Actor server") + case <-time.After(time.Second * 10): + r.s.Stop() + plog.Info("Stopped Proto.Actor server", log.String("err", "timeout")) + } + } else { + r.s.Stop() + plog.Info("Killed Proto.Actor server") + } +} + +func (r *Remote) SendMessage(pid *actor.PID, header actor.ReadonlyMessageHeader, message interface{}, sender *actor.PID, serializerID int32) { + rd := &remoteDeliver{ + header: header, + message: message, + sender: sender, + target: pid, + serializerID: serializerID, + } + r.edpManager.remoteDeliver(rd) +} diff --git a/remote/server_test.go b/remote/server_test.go new file mode 100644 index 0000000000000000000000000000000000000000..529a508193e36dd2c9ccb95a0e7b6161c42880ba --- /dev/null +++ b/remote/server_test.go @@ -0,0 +1,224 @@ +package remote + +import ( + "sort" + "testing" + + "gitee.com/simplexyz/simpleactor-go/actor" + "github.com/stretchr/testify/assert" +) + +func TestStart(t *testing.T) { + system := actor.NewActorSystem() + config := Configure("localhost", 0) + remote := NewRemote(system, config) + remote.Start() + remote.Shutdown(true) +} + +func TestConfig_WithAdvertisedHost(t *testing.T) { + system := actor.NewActorSystem() + config := Configure("localhost", 0, WithAdvertisedHost("Banana")) + remote := NewRemote(system, config) + remote.Start() + assert.Equal(t, "Banana", system.Address()) + remote.Shutdown(true) +} + +func TestRemote_Register(t *testing.T) { + system := actor.NewActorSystem() + config := Configure("localhost", 0, WithKinds( + NewKind("someKind", actor.PropsFromProducer(nil)), + NewKind("someOther", actor.PropsFromProducer(nil)), + )) + remote := NewRemote(system, config) + + kinds := remote.GetKnownKinds() + assert.Equal(t, 2, len(kinds)) + sort.Strings(kinds) + assert.Equal(t, "someKind", kinds[0]) + assert.Equal(t, "someOther", kinds[1]) +} + +func TestRemote_RegisterViaOptions(t *testing.T) { + system := actor.NewActorSystem() + config := Configure("localhost", 0, + WithKinds( + NewKind("someKind", actor.PropsFromProducer(nil)), + NewKind("someOther", actor.PropsFromProducer(nil)))) + + remote := NewRemote(system, config) + kinds := remote.GetKnownKinds() + assert.Equal(t, 2, len(kinds)) + sort.Strings(kinds) + assert.Equal(t, "someKind", kinds[0]) + assert.Equal(t, "someOther", kinds[1]) +} + +func TestRemote_RegisterViaStruct(t *testing.T) { + system := actor.NewActorSystem() + config := &Config{ + Host: "localhost", + Port: 0, + Kinds: map[string]*actor.Props{ + "someKind": actor.PropsFromProducer(nil), + "someOther": actor.PropsFromProducer(nil), + }, + } + + remote := NewRemote(system, config) + kinds := remote.GetKnownKinds() + assert.Equal(t, 2, len(kinds)) + sort.Strings(kinds) + assert.Equal(t, "someKind", kinds[0]) + assert.Equal(t, "someOther", kinds[1]) +} + +// +//func (suite *ServerTestSuite) TestStart_AdvertisedAddress() { +// // Find available Port +// lis, err := net.Listen("tcp", "127.0.0.1:0") // use :0 to choose available Port +// if err != nil { +// panic(err) +// } +// //address := lis.Addr() +// _ = lis.Close() +// +// AdvertisedHost := "192.0.2.1:1234" +// remote.StartMember() +// +// suite.NotEmpty(system.ProcessRegistry.RemoteHandlers, "AddressResolver should be registered on server start") +// suite.Equal(AdvertisedHost, system.ProcessRegistry.Address, "WithAdvertisedHost should have higher priority") +// suite.NotNil(activatorPid, "Activator actor should be initialized on server start") +// suite.NotNil(endpointManager, "EndpointManager should be initialized on server start") +// suite.Equal(AdvertisedHost, remote.config.AdvertisedHost, "Passed configuration option should be used") +// suite.NotNil(remote.edpReader, "EndpointReader should be initialized on server start") +// suite.NotNil(remote.s, "gRPC server should be started on server start") +//} +// +//func (suite *ServerTestSuite) TestShutdown_Graceful() { +// remote.edpReader = &endpointReader{} +// suite.False(remote.edpReader.suspended, "EndpointReader should not be suspended at beginning") +// +// endpointSupervisor, endpointSupervisorProcess := spawnMockProcess("EndpointSupervisor") +// defer removeMockProcess(endpointSupervisor) +// endpointSupervisorProcess.On("SendSystemMessage", mock.Anything, mock.Anything). +// Run(func(args mock.Arguments) { +// if suite.IsType(&actor.PID{}, args.Get(0)) { +// pid := args.Get(0).(*actor.PID) +// suite.Equal(endpointSupervisor, pid) +// } +// if suite.IsType(&actor.Watch{}, args.Get(1)) { +// watch := args.Get(1).(*actor.Watch) +// system.Root.Send(watch.Watcher, &actor.Terminated{ +// Who: endpointSupervisor, +// AddressTerminated: false, +// }) +// } +// }). +// Once() +// endpointSupervisorProcess.On("Stop", endpointSupervisor).Once() +// +// endpointManager = &endpointManagerValue{ +// connections: &sync.Map{}, +// remote: remote, +// endpointSupervisor: endpointSupervisor, +// endpointSub: system.EventStream.Subscribe(func(evt interface{}) {}), +// } +// +// var activatorProcess *mockProcess +// activatorPid, activatorProcess = spawnMockProcess("activator") +// defer removeMockProcess(activatorPid) +// activatorProcess.On("SendSystemMessage", mock.Anything, mock.Anything). +// Run(func(args mock.Arguments) { +// if suite.IsType(&actor.PID{}, args.Get(0)) { +// pid := args.Get(0).(*actor.PID) +// suite.Equal(activatorPid, pid) +// } +// if suite.IsType(&actor.Watch{}, args.Get(1)) { +// watch := args.Get(1).(*actor.Watch) +// system.Root.Send(watch.Watcher, &actor.Terminated{ +// Who: activatorPid, +// AddressTerminated: false, +// }) +// } +// }). +// Once() +// activatorProcess.On("Stop", activatorPid).Once() +// +// lis, err := net.Listen("tcp", "127.0.0.1:0") // use :0 to choose available Port +// if err != nil { +// panic(err) +// } +// defer lis.Close() +// +// grpcStopped := make(chan struct{}, 1) +// remote.s = grpc.NewServer() +// go func() { +// remote.s.Serve(lis) +// grpcStopped <- struct{}{} +// }() +// +// remote.Shutdown(true) +// +// suite.Nil(endpointManager.endpointSub, "Subscription should reset on shutdown") +// suite.Nil(endpointManager.connections, "Connections should reset on shutdown") +// +// select { +// case <-time.NewTimer(15 * time.Second).C: +// suite.FailNow("gRPC server did not stop") +// case <-grpcStopped: +// // O.K. +// } +// +// endpointSupervisorProcess.AssertExpectations(suite.T()) +//} +// +//func (suite *ServerTestSuite) TestShutdown() { +// remote.edpReader = &endpointReader{} +// suite.False(remote.edpReader.suspended, "EndpointReader should not be suspended at beginning") +// +// endpointSupervisor, endpointSupervisorProcess := spawnMockProcess("EndpointSupervisor") +// defer removeMockProcess(endpointSupervisor) +// +// var activatorProcess *mockProcess +// activatorPid, activatorProcess = spawnMockProcess("activator") +// defer removeMockProcess(activatorPid) +// +// endpointManager = &endpointManagerValue{ +// connections: &sync.Map{}, +// remote: nil, +// endpointSupervisor: endpointSupervisor, +// endpointSub: system.EventStream.Subscribe(func(evt interface{}) {}), +// } +// +// lis, err := net.Listen("tcp", "127.0.0.1:0") // use :0 to choose available Port +// if err != nil { +// panic(err) +// } +// defer lis.Close() +// +// grpcStopped := make(chan struct{}, 1) +// remote.s = grpc.NewServer() +// go func() { +// remote.s.Serve(lis) +// grpcStopped <- struct{}{} +// }() +// +// remote.Shutdown(false) +// +// suite.NotNil(endpointManager.endpointSub, "Subscription should not reset on non-graceful shutdown") +// suite.NotNil(endpointManager.connections, "Connections should not reset on non-graceful shutdown") +// +// select { +// case <-time.NewTimer(1 * time.Second).C: +// suite.FailNow("gRPC server did not stop") +// case <-grpcStopped: +// // O.K. +// } +// +// activatorProcess.AssertNotCalled(suite.T(), "SendSystemMessage", mock.Anything, mock.Anything) +// activatorProcess.AssertExpectations(suite.T()) +// endpointSupervisorProcess.AssertNotCalled(suite.T(), "SendSystemMessage", mock.Anything, mock.Anything) +// endpointSupervisorProcess.AssertExpectations(suite.T()) +//} diff --git a/resources/batman.jpg b/resources/batman.jpg new file mode 100644 index 0000000000000000000000000000000000000000..1fbe6602d93fe2de5629b937a87c7c8b3932339c Binary files /dev/null and b/resources/batman.jpg differ diff --git a/router/broadcast_router.go b/router/broadcast_router.go new file mode 100644 index 0000000000000000000000000000000000000000..38009446c40be457ea5ba592550f110c216b4c22 --- /dev/null +++ b/router/broadcast_router.go @@ -0,0 +1,54 @@ +package router + +import ( + "gitee.com/simplexyz/simpleactor-go/actor" +) + +type broadcastGroupRouter struct { + GroupRouter +} + +type broadcastPoolRouter struct { + PoolRouter +} + +type broadcastRouterState struct { + routees *actor.PIDSet + sender actor.SenderContext +} + +func (state *broadcastRouterState) SetSender(sender actor.SenderContext) { + state.sender = sender +} + +func (state *broadcastRouterState) SetRoutees(routees *actor.PIDSet) { + state.routees = routees +} + +func (state *broadcastRouterState) GetRoutees() *actor.PIDSet { + return state.routees +} + +func (state *broadcastRouterState) RouteMessage(message interface{}) { + state.routees.ForEach(func(i int, pid *actor.PID) { + state.sender.Send(pid, message) + }) +} + +func NewBroadcastPool(size int, opts ...actor.PropsOption) *actor.Props { + return (&actor.Props{}). + Configure(actor.WithSpawnFunc(spawner(&broadcastPoolRouter{PoolRouter{PoolSize: size}}))). + Configure(opts...) +} + +func NewBroadcastGroup(routees ...*actor.PID) *actor.Props { + return (&actor.Props{}).Configure(actor.WithSpawnFunc(spawner(&broadcastGroupRouter{GroupRouter{Routees: actor.NewPIDSet(routees...)}}))) +} + +func (config *broadcastPoolRouter) CreateRouterState() State { + return &broadcastRouterState{} +} + +func (config *broadcastGroupRouter) CreateRouterState() State { + return &broadcastRouterState{} +} diff --git a/router/broadcast_router_test.go b/router/broadcast_router_test.go new file mode 100644 index 0000000000000000000000000000000000000000..a7068885b4bb4804548802707caa62bf5b3b0c76 --- /dev/null +++ b/router/broadcast_router_test.go @@ -0,0 +1,40 @@ +package router + +import ( + "strconv" + "sync" + "testing" + "time" + + "gitee.com/simplexyz/simpleactor-go/actor" +) + +var system = actor.NewActorSystem() + +func TestBroadcastRouterThreadSafe(t *testing.T) { + wg := sync.WaitGroup{} + wg.Add(2) + + props := actor.PropsFromFunc(func(c actor.Context) {}) + + grp := system.Root.Spawn(NewBroadcastGroup()) + go func() { + count := 100 + for i := 0; i < count; i++ { + pid, _ := system.Root.SpawnNamed(props, strconv.Itoa(i)) + system.Root.Send(grp, &AddRoutee{PID: pid}) + time.Sleep(10 * time.Millisecond) + } + wg.Done() + }() + go func() { + count := 100 + for c := 0; c < count; c++ { + system.Root.Send(grp, struct{}{}) + time.Sleep(10 * time.Millisecond) + } + wg.Done() + }() + + wg.Wait() +} diff --git a/router/build.sh b/router/build.sh new file mode 100644 index 0000000000000000000000000000000000000000..4d3f33b53f8514460a573f85351662ed9f21a673 --- /dev/null +++ b/router/build.sh @@ -0,0 +1,2 @@ +protoc -I="../actor" --go_out=. --go_opt=paths=source_relative --proto_path=. routercontracts.proto + diff --git a/router/common_test.go b/router/common_test.go new file mode 100644 index 0000000000000000000000000000000000000000..f6ef88f5c7c1f6c3da6ae661ea383025ae0b7bdb --- /dev/null +++ b/router/common_test.go @@ -0,0 +1,235 @@ +package router + +import ( + "fmt" + "io/ioutil" + "log" + "time" + + "gitee.com/simplexyz/simpleactor-go/ctxext" + + "gitee.com/simplexyz/simpleactor-go/actor" + "github.com/stretchr/testify/mock" +) + +var nilPID *actor.PID + +func init() { + // discard all logging in tests + log.SetOutput(ioutil.Discard) +} + +// mockContext +type mockContext struct { + mock.Mock +} + +// +// Interface: Context +// + +func (m *mockContext) Get(id ctxext.ContextExtensionID) ctxext.ContextExtension { + args := m.Called(id) + return args.Get(0).(ctxext.ContextExtension) +} + +func (m *mockContext) Set(ext ctxext.ContextExtension) { + m.Called(ext) +} + +func (m *mockContext) ActorSystem() *actor.ActorSystem { + args := m.Called() + return args.Get(0).(*actor.ActorSystem) +} + +func (m *mockContext) Parent() *actor.PID { + args := m.Called() + return args.Get(0).(*actor.PID) +} + +func (m *mockContext) Self() *actor.PID { + args := m.Called() + return args.Get(0).(*actor.PID) +} + +func (m *mockContext) Sender() *actor.PID { + args := m.Called() + return args.Get(0).(*actor.PID) +} + +func (m *mockContext) Actor() actor.Actor { + args := m.Called() + return args.Get(0).(actor.Actor) +} + +func (m *mockContext) ReceiveTimeout() time.Duration { + args := m.Called() + return args.Get(0).(time.Duration) +} + +func (m *mockContext) Children() []*actor.PID { + args := m.Called() + return args.Get(0).([]*actor.PID) +} + +func (m *mockContext) Respond(response interface{}) { + m.Called(response) +} + +func (m *mockContext) Stash() { + m.Called() +} + +func (m *mockContext) Watch(pid *actor.PID) { + m.Called(pid) +} + +func (m *mockContext) Unwatch(pid *actor.PID) { + m.Called(pid) +} + +func (m *mockContext) SetReceiveTimeout(d time.Duration) { + m.Called(d) +} + +func (m *mockContext) CancelReceiveTimeout() { + m.Called() +} + +func (m *mockContext) Forward(pid *actor.PID) { + m.Called() +} + +func (m *mockContext) ReenterAfter(f *actor.Future, cont func(res interface{}, err error)) { + m.Called(f, cont) +} + +// +// Interface: SenderContext +// + +func (m *mockContext) Message() interface{} { + args := m.Called() + return args.Get(0) +} + +func (m *mockContext) MessageHeader() actor.ReadonlyMessageHeader { + args := m.Called() + return args.Get(0).(actor.ReadonlyMessageHeader) +} + +func (m *mockContext) Send(pid *actor.PID, message interface{}) { + m.Called() + p, _ := system.ProcessRegistry.Get(pid) + p.SendUserMessage(pid, message) +} + +func (m *mockContext) Request(pid *actor.PID, message interface{}) { + args := m.Called() + p, _ := system.ProcessRegistry.Get(pid) + env := &actor.MessageEnvelope{ + Header: nil, + Message: message, + Sender: args.Get(0).(*actor.PID), + } + p.SendUserMessage(pid, env) +} + +func (m *mockContext) RequestWithCustomSender(pid *actor.PID, message interface{}, sender *actor.PID) { + m.Called() + p, _ := system.ProcessRegistry.Get(pid) + env := &actor.MessageEnvelope{ + Header: nil, + Message: message, + Sender: sender, + } + p.SendUserMessage(pid, env) +} + +func (m *mockContext) RequestFuture(pid *actor.PID, message interface{}, timeout time.Duration) *actor.Future { + args := m.Called() + m.Called() + p, _ := system.ProcessRegistry.Get(pid) + p.SendUserMessage(pid, message) + return args.Get(0).(*actor.Future) +} + +// +// Interface: ReceiverContext +// + +func (m *mockContext) Receive(envelope *actor.MessageEnvelope) { + m.Called(envelope) +} + +// +// Interface: SpawnerContext +// + +func (m *mockContext) Spawn(p *actor.Props) *actor.PID { + args := m.Called(p) + return args.Get(0).(*actor.PID) +} + +func (m *mockContext) SpawnPrefix(p *actor.Props, prefix string) *actor.PID { + args := m.Called(p, prefix) + return args.Get(0).(*actor.PID) +} + +func (m *mockContext) SpawnNamed(p *actor.Props, name string) (*actor.PID, error) { + args := m.Called(p, name) + return args.Get(0).(*actor.PID), args.Get(1).(error) +} + +// +// Interface: StopperContext +// + +func (m *mockContext) Stop(pid *actor.PID) { + m.Called(pid) +} + +func (m *mockContext) StopFuture(pid *actor.PID) *actor.Future { + args := m.Called(pid) + return args.Get(0).(*actor.Future) +} + +func (m *mockContext) Poison(pid *actor.PID) { + m.Called(pid) +} + +func (m *mockContext) PoisonFuture(pid *actor.PID) *actor.Future { + args := m.Called(pid) + return args.Get(0).(*actor.Future) +} + +// mockProcess +type mockProcess struct { + mock.Mock +} + +func spawnMockProcess(name string) (*actor.PID, *mockProcess) { + p := &mockProcess{} + pid, ok := system.ProcessRegistry.Add(p, name) + if !ok { + panic(fmt.Errorf("did not spawn named process '%s'", name)) + } + + return pid, p +} + +func removeMockProcess(pid *actor.PID) { + system.ProcessRegistry.Remove(pid) +} + +func (m *mockProcess) SendUserMessage(pid *actor.PID, message interface{}) { + m.Called(pid, message) +} + +func (m *mockProcess) SendSystemMessage(pid *actor.PID, message interface{}) { + m.Called(pid, message) +} + +func (m *mockProcess) Stop(pid *actor.PID) { + m.Called(pid) +} diff --git a/router/config.go b/router/config.go new file mode 100644 index 0000000000000000000000000000000000000000..f1d51f3562c03dee3942e81f3a750fc05c825083 --- /dev/null +++ b/router/config.go @@ -0,0 +1,102 @@ +package router + +import ( + "sync" + + "gitee.com/simplexyz/simpleactor-go/actor" +) + +type RouterType int + +const ( + GroupRouterType RouterType = iota + PoolRouterType +) + +type RouterConfig interface { + RouterType() RouterType + OnStarted(context actor.Context, props *actor.Props, state State) + CreateRouterState() State +} + +type GroupRouter struct { + Routees *actor.PIDSet +} + +type PoolRouter struct { + PoolSize int +} + +func (config *GroupRouter) OnStarted(context actor.Context, props *actor.Props, state State) { + config.Routees.ForEach(func(i int, pid *actor.PID) { + context.Watch(pid) + }) + state.SetSender(context) + state.SetRoutees(config.Routees) +} + +func (config *GroupRouter) RouterType() RouterType { + return GroupRouterType +} + +func (config *PoolRouter) OnStarted(context actor.Context, props *actor.Props, state State) { + var routees actor.PIDSet + for i := 0; i < config.PoolSize; i++ { + routees.Add(context.Spawn(props)) + } + state.SetSender(context) + state.SetRoutees(&routees) +} + +func (config *PoolRouter) RouterType() RouterType { + return PoolRouterType +} + +func spawner(config RouterConfig) actor.SpawnFunc { + return func(actorSystem *actor.ActorSystem, id string, props *actor.Props, parentContext actor.SpawnerContext) (*actor.PID, error) { + return spawn(actorSystem, id, config, props, parentContext) + } +} + +func spawn(actorSystem *actor.ActorSystem, id string, config RouterConfig, props *actor.Props, parentContext actor.SpawnerContext) (*actor.PID, error) { + ref := &process{ + actorSystem: actorSystem, + } + proxy, absent := actorSystem.ProcessRegistry.Add(ref, id) + if !absent { + return proxy, actor.ErrNameExists + } + + pc := *props + pc.Configure(actor.WithSpawnFunc(nil)) + ref.state = config.CreateRouterState() + + if config.RouterType() == GroupRouterType { + wg := &sync.WaitGroup{} + wg.Add(1) + ref.router, _ = actor.DefaultSpawner(actorSystem, id+"/router", actor.PropsFromProducer(func() actor.Actor { + return &groupRouterActor{ + props: &pc, + config: config, + state: ref.state, + wg: wg, + } + }), parentContext) + wg.Wait() // wait for routerActor to start + } else { + wg := &sync.WaitGroup{} + wg.Add(1) + ref.router, _ = actor.DefaultSpawner(actorSystem, id+"/router", actor.PropsFromProducer(func() actor.Actor { + return &poolRouterActor{ + props: &pc, + config: config, + state: ref.state, + wg: wg, + } + }), parentContext) + wg.Wait() // wait for routerActor to start + } + + ref.parent = parentContext.Self() + return proxy, nil +} diff --git a/router/config_test.go b/router/config_test.go new file mode 100644 index 0000000000000000000000000000000000000000..0547681531e7e7c2672ec6b59a1bf18f7816d60b --- /dev/null +++ b/router/config_test.go @@ -0,0 +1,23 @@ +package router + +import ( + "testing" + + "gitee.com/simplexyz/simpleactor-go/actor" + "github.com/stretchr/testify/assert" +) + +func TestSpawn(t *testing.T) { + pr := &broadcastPoolRouter{PoolRouter{PoolSize: 1}} + pid, err := spawn(system, "foo", pr, actor.PropsFromFunc(func(context actor.Context) {}), system.Root) + assert.NoError(t, err) + + _, exists := system.ProcessRegistry.Get(system.NewLocalPID("foo/router")) + assert.True(t, exists) + + err = system.Root.StopFuture(pid).Wait() + assert.NoError(t, err) + + _, exists = system.ProcessRegistry.Get(system.NewLocalPID("foo/router")) + assert.False(t, exists) +} diff --git a/router/consistent_hash_router.go b/router/consistent_hash_router.go new file mode 100644 index 0000000000000000000000000000000000000000..36fd6913fd9fb3592a548441c66de75627d32d6e --- /dev/null +++ b/router/consistent_hash_router.go @@ -0,0 +1,100 @@ +package router + +import ( + "log" + + "gitee.com/simplexyz/simpleactor-go/actor" + "github.com/serialx/hashring" +) + +type Hasher interface { + Hash() string +} + +type consistentHashGroupRouter struct { + GroupRouter +} + +type consistentHashPoolRouter struct { + PoolRouter +} + +type hashmapContainer struct { + hashring *hashring.HashRing + routeeMap map[string]*actor.PID +} +type consistentHashRouterState struct { + hmc *hashmapContainer + sender actor.SenderContext +} + +func (state *consistentHashRouterState) SetSender(sender actor.SenderContext) { + state.sender = sender +} + +func (state *consistentHashRouterState) SetRoutees(routees *actor.PIDSet) { + // lookup from node name to PID + hmc := hashmapContainer{} + hmc.routeeMap = make(map[string]*actor.PID) + nodes := make([]string, routees.Len()) + routees.ForEach(func(i int, pid *actor.PID) { + nodeName := pid.Address + "@" + pid.Id + nodes[i] = nodeName + hmc.routeeMap[nodeName] = pid + }) + // initialize hashring for mapping message keys to node names + hmc.hashring = hashring.New(nodes) + state.hmc = &hmc +} + +func (state *consistentHashRouterState) GetRoutees() *actor.PIDSet { + var routees actor.PIDSet + hmc := state.hmc + for _, v := range hmc.routeeMap { + routees.Add(v) + } + return &routees +} + +func (state *consistentHashRouterState) RouteMessage(message interface{}) { + _, uwpMsg, _ := actor.UnwrapEnvelope(message) + switch msg := uwpMsg.(type) { + case Hasher: + key := msg.Hash() + hmc := state.hmc + + node, ok := hmc.hashring.GetNode(key) + if !ok { + log.Printf("[ROUTING] Consistent has router failed to derminate routee: %v", key) + return + } + if routee, ok := hmc.routeeMap[node]; ok { + state.sender.Send(routee, message) + } else { + log.Println("[ROUTING] Consistent router failed to resolve node", node) + } + default: + log.Println("[ROUTING] Message must implement router.Hasher", msg) + } +} + +func (state *consistentHashRouterState) InvokeRouterManagementMessage(msg ManagementMessage, sender *actor.PID) { +} + +func NewConsistentHashPool(size int, opts ...actor.PropsOption) *actor.Props { + return (&actor.Props{}). + Configure(actor.WithSpawnFunc(spawner(&consistentHashPoolRouter{PoolRouter{PoolSize: size}}))). + Configure(opts...) +} + +func NewConsistentHashGroup(routees ...*actor.PID) *actor.Props { + return (&actor.Props{}).Configure(actor.WithSpawnFunc(spawner(&consistentHashGroupRouter{GroupRouter{Routees: actor.NewPIDSet(routees...)}}))) +} + +func (config *consistentHashPoolRouter) CreateRouterState() State { + return &consistentHashRouterState{} +} + +func (config *consistentHashGroupRouter) CreateRouterState() State { + return &consistentHashRouterState{} +} diff --git a/router/consistent_hash_router_test.go b/router/consistent_hash_router_test.go new file mode 100644 index 0000000000000000000000000000000000000000..7ca3c478769944d596e1bc83727d7923f52ad7b2 --- /dev/null +++ b/router/consistent_hash_router_test.go @@ -0,0 +1,100 @@ +package router_test + +import ( + "strconv" + "sync" + "sync/atomic" + "testing" + "time" + + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/router" +) + +var system = actor.NewActorSystem() + +type myMessage struct { + i int32 + pid *actor.PID +} + +type getRoutees struct { + pid *actor.PID +} + +func (m *myMessage) Hash() string { + i := atomic.LoadInt32(&m.i) + return strconv.Itoa(int(i)) +} + +var wait sync.WaitGroup + +type ( + routerActor struct{} + tellerActor struct{} + managerActor struct { + set []*actor.PID + rpid *actor.PID + } +) + +func (state *routerActor) Receive(context actor.Context) { + switch msg := context.Message().(type) { + case *myMessage: + // log.Printf("%v got message %d", context.Self(), msg.i) + atomic.AddInt32(&msg.i, 1) + wait.Done() + } +} + +func (state *tellerActor) Receive(context actor.Context) { + switch msg := context.Message().(type) { + case *myMessage: + for i := 0; i < 100; i++ { + context.Send(msg.pid, msg) + time.Sleep(10 * time.Millisecond) + } + } +} + +func (state *managerActor) Receive(context actor.Context) { + switch msg := context.Message().(type) { + case *router.Routees: + state.set = msg.PIDs + for i, v := range state.set { + if i%2 == 0 { + context.Send(state.rpid, &router.RemoveRoutee{PID: v}) + // log.Println(v) + } else { + props := actor.PropsFromProducer(func() actor.Actor { return &routerActor{} }) + pid := context.Spawn(props) + context.Send(state.rpid, &router.AddRoutee{PID: pid}) + // log.Println(v) + } + } + context.Send(context.Self(), &getRoutees{state.rpid}) + case *getRoutees: + state.rpid = msg.pid + context.Request(msg.pid, &router.GetRoutees{}) + } +} + +func TestConcurrency(t *testing.T) { + if testing.Short() { + t.SkipNow() + } + + wait.Add(100 * 1000) + rpid := system.Root.Spawn(router.NewConsistentHashPool(100).Configure(actor.WithProducer(func() actor.Actor { return &routerActor{} }))) + + props := actor.PropsFromProducer(func() actor.Actor { return &tellerActor{} }) + for i := 0; i < 1000; i++ { + pid := system.Root.Spawn(props) + system.Root.Send(pid, &myMessage{int32(i), rpid}) + } + + props = actor.PropsFromProducer(func() actor.Actor { return &managerActor{} }) + pid := system.Root.Spawn(props) + system.Root.Send(pid, &getRoutees{rpid}) + wait.Wait() +} diff --git a/router/messages.go b/router/messages.go new file mode 100644 index 0000000000000000000000000000000000000000..569369c2b16a3a226002e1e854e9892c8a11b233 --- /dev/null +++ b/router/messages.go @@ -0,0 +1,15 @@ +package router + +type ManagementMessage interface { + ManagementMessage() +} + +type BroadcastMessage struct { + Message interface{} +} + +func (*AddRoutee) ManagementMessage() {} +func (*RemoveRoutee) ManagementMessage() {} +func (*GetRoutees) ManagementMessage() {} +func (*AdjustPoolSize) ManagementMessage() {} +func (*BroadcastMessage) ManagementMessage() {} diff --git a/router/process.go b/router/process.go new file mode 100644 index 0000000000000000000000000000000000000000..f4770b668dad9138d3ec04816f2032f24055d1cd --- /dev/null +++ b/router/process.go @@ -0,0 +1,87 @@ +package router + +import ( + "sync" + "sync/atomic" + + "gitee.com/simplexyz/simpleactor-go/actor" +) + +// process serves as a proxy to the router implementation and forwards messages directly to the routee. This +// optimization avoids serializing router messages through an actor +type process struct { + parent *actor.PID + router *actor.PID + state State + mu sync.Mutex + watchers actor.PIDSet + stopping int32 + actorSystem *actor.ActorSystem +} + +var _ actor.Process = &process{} + +func (ref *process) SendUserMessage(pid *actor.PID, message interface{}) { + _, msg, _ := actor.UnwrapEnvelope(message) + if _, ok := msg.(ManagementMessage); !ok { + ref.state.RouteMessage(message) + } else { + r, _ := ref.actorSystem.ProcessRegistry.Get(ref.router) + // Always send the original message to the router actor, + // since if the message is enveloped, the sender need to get a response. + r.SendUserMessage(pid, message) + } +} + +func (ref *process) SendSystemMessage(pid *actor.PID, message interface{}) { + switch msg := message.(type) { + case *actor.Watch: + if atomic.LoadInt32(&ref.stopping) == 1 { + if r, ok := ref.actorSystem.ProcessRegistry.Get(msg.Watcher); ok { + r.SendSystemMessage(msg.Watcher, &actor.Terminated{Who: pid}) + } + return + } + ref.mu.Lock() + ref.watchers.Add(msg.Watcher) + ref.mu.Unlock() + + case *actor.Unwatch: + ref.mu.Lock() + ref.watchers.Remove(msg.Watcher) + ref.mu.Unlock() + + case *actor.Stop: + term := &actor.Terminated{Who: pid} + ref.mu.Lock() + ref.watchers.ForEach(func(_ int, other *actor.PID) { + if !other.Equal(ref.parent) { + if r, ok := ref.actorSystem.ProcessRegistry.Get(other); ok { + r.SendSystemMessage(other, term) + } + } + }) + // Notify parent + if ref.parent != nil { + if r, ok := ref.actorSystem.ProcessRegistry.Get(ref.parent); ok { + r.SendSystemMessage(ref.parent, term) + } + } + ref.mu.Unlock() + + default: + r, _ := ref.actorSystem.ProcessRegistry.Get(ref.router) + r.SendSystemMessage(pid, message) + + } +} + +func (ref *process) Stop(pid *actor.PID) { + if atomic.SwapInt32(&ref.stopping, 1) == 1 { + return + } + + _ = ref.actorSystem.Root.StopFuture(ref.router).Wait() + ref.actorSystem.ProcessRegistry.Remove(pid) + ref.SendSystemMessage(pid, &actor.Stop{}) +} diff --git a/router/process_test.go b/router/process_test.go new file mode 100644 index 0000000000000000000000000000000000000000..f44d11d637adc93ed8f46736f7a5efd2cb6fec04 --- /dev/null +++ b/router/process_test.go @@ -0,0 +1,90 @@ +package router + +import ( + "fmt" + "testing" + "time" + + "gitee.com/simplexyz/simpleactor-go/actor" + "github.com/stretchr/testify/mock" +) + +var ( + _ fmt.Formatter + _ time.Time +) + +// TODO fix this +func __TestRouterSendsUserMessageToChild(t *testing.T) { + child, p := spawnMockProcess("child") + defer removeMockProcess(child) + + p.On("SendUserMessage", mock.Anything, mock.MatchedBy(func(env interface{}) bool { + _, msg, _ := actor.UnwrapEnvelope(env) + return msg.(string) == "hello" + })) + p.On("SendSystemMessage", mock.Anything, mock.Anything) + + s1 := actor.NewPIDSet(child) + + rs := new(testRouterState) + // rs.On("SetSender",) + rs.On("SetRoutees", s1) + rs.On("RouteMessage", mock.MatchedBy(func(env interface{}) bool { + _, msg, _ := actor.UnwrapEnvelope(env) + return msg.(string) == "hello" + }), mock.Anything) + + grc := newGroupRouterConfig(child) + grc.On("CreateRouterState").Return(rs) + + routerPID := system.Root.Spawn((&actor.Props{}).Configure(actor.WithSpawnFunc(spawner(grc)))) + system.Root.Send(routerPID, "hello") + system.Root.RequestWithCustomSender(routerPID, "hello", routerPID) + + mock.AssertExpectationsForObjects(t, p, rs) +} + +type testGroupRouter struct { + GroupRouter + mock.Mock +} + +func newGroupRouterConfig(routees ...*actor.PID) *testGroupRouter { + r := new(testGroupRouter) + r.Routees = actor.NewPIDSet(routees...) + return r +} + +func (m *testGroupRouter) CreateRouterState() State { + args := m.Called() + return args.Get(0).(*testRouterState) +} + +type testRouterState struct { + mock.Mock + routees *actor.PIDSet + sender actor.SenderContext +} + +func (m *testRouterState) SetSender(sender actor.SenderContext) { + m.Called(sender) + m.sender = sender +} + +func (m *testRouterState) SetRoutees(routees *actor.PIDSet) { + m.Called(routees) + m.routees = routees +} + +func (m *testRouterState) RouteMessage(message interface{}) { + m.Called(message) + m.routees.ForEach(func(i int, pid *actor.PID) { + system.Root.Send(pid, message) + }) +} + +func (m *testRouterState) GetRoutees() *actor.PIDSet { + args := m.Called() + return args.Get(0).(*actor.PIDSet) +} diff --git a/router/random_router.go b/router/random_router.go new file mode 100644 index 0000000000000000000000000000000000000000..02a4745534dc6d7095716a30a5c48bef3efd7e10 --- /dev/null +++ b/router/random_router.go @@ -0,0 +1,62 @@ +package router + +import ( + "math/rand" + + "gitee.com/simplexyz/simpleactor-go/actor" +) + +type randomGroupRouter struct { + GroupRouter +} + +type randomPoolRouter struct { + PoolRouter +} + +type randomRouterState struct { + routees *actor.PIDSet + sender actor.SenderContext +} + +func (state *randomRouterState) SetSender(sender actor.SenderContext) { + state.sender = sender +} + +func (state *randomRouterState) SetRoutees(routees *actor.PIDSet) { + state.routees = routees +} + +func (state *randomRouterState) GetRoutees() *actor.PIDSet { + return state.routees +} + +func (state *randomRouterState) RouteMessage(message interface{}) { + pid := randomRoutee(state.routees) + state.sender.Send(pid, message) +} + +func NewRandomPool(size int, opts ...actor.PropsOption) *actor.Props { + return (&actor.Props{}). + Configure(actor.WithSpawnFunc(spawner(&randomPoolRouter{PoolRouter{PoolSize: size}}))). + Configure(opts...) +} + +func NewRandomGroup(routees ...*actor.PID) *actor.Props { + return (&actor.Props{}).Configure(actor.WithSpawnFunc(spawner(&randomGroupRouter{GroupRouter{Routees: actor.NewPIDSet(routees...)}}))) +} + +func (config *randomPoolRouter) CreateRouterState() State { + return &randomRouterState{} +} + +func (config *randomGroupRouter) CreateRouterState() State { + return &randomRouterState{} +} + +func randomRoutee(routees *actor.PIDSet) *actor.PID { + l := routees.Len() + r := rand.Intn(l) + pid := routees.Get(r) + return pid +} diff --git a/router/roundrobin_router.go b/router/roundrobin_router.go new file mode 100644 index 0000000000000000000000000000000000000000..dbce59d34d2c0c737a37755e505ec2ad59d34a0f --- /dev/null +++ b/router/roundrobin_router.go @@ -0,0 +1,67 @@ +package router + +import ( + "sync/atomic" + + "gitee.com/simplexyz/simpleactor-go/actor" +) + +type roundRobinGroupRouter struct { + GroupRouter +} + +type roundRobinPoolRouter struct { + PoolRouter +} + +type roundRobinState struct { + index int32 + routees *actor.PIDSet + sender actor.SenderContext +} + +func (state *roundRobinState) SetSender(sender actor.SenderContext) { + state.sender = sender +} + +func (state *roundRobinState) SetRoutees(routees *actor.PIDSet) { + state.routees = routees +} + +func (state *roundRobinState) GetRoutees() *actor.PIDSet { + return state.routees +} + +func (state *roundRobinState) RouteMessage(message interface{}) { + pid := roundRobinRoutee(&state.index, state.routees) + state.sender.Send(pid, message) +} + +func NewRoundRobinPool(size int, opts ...actor.PropsOption) *actor.Props { + return (&actor.Props{}). + Configure(actor.WithSpawnFunc(spawner(&roundRobinPoolRouter{PoolRouter{PoolSize: size}}))). + Configure(opts...) +} + +func NewRoundRobinGroup(routees ...*actor.PID) *actor.Props { + return (&actor.Props{}).Configure(actor.WithSpawnFunc(spawner(&roundRobinGroupRouter{GroupRouter{Routees: actor.NewPIDSet(routees...)}}))) +} + +func (config *roundRobinPoolRouter) CreateRouterState() State { + return &roundRobinState{} +} + +func (config *roundRobinGroupRouter) CreateRouterState() State { + return &roundRobinState{} +} + +func roundRobinRoutee(index *int32, routees *actor.PIDSet) *actor.PID { + i := int(atomic.AddInt32(index, 1)) + if i < 0 { + *index = 0 + i = 0 + } + mod := routees.Len() + routee := routees.Get(i % mod) + return routee +} diff --git a/router/router.go b/router/router.go new file mode 100644 index 0000000000000000000000000000000000000000..6e0fa9eec59e5197ee988f10b7c525233549c838 --- /dev/null +++ b/router/router.go @@ -0,0 +1,11 @@ +package router + +import "gitee.com/simplexyz/simpleactor-go/actor" + +// A type that satisfies router.Interface can be used as a router +type State interface { + RouteMessage(message interface{}) + SetRoutees(routees *actor.PIDSet) + GetRoutees() *actor.PIDSet + SetSender(sender actor.SenderContext) +} diff --git a/router/routeractor_group.go b/router/routeractor_group.go new file mode 100644 index 0000000000000000000000000000000000000000..1e502b42fed84c496cd7465574422958f77bdb15 --- /dev/null +++ b/router/routeractor_group.go @@ -0,0 +1,57 @@ +package router + +import ( + "sync" + + "gitee.com/simplexyz/simpleactor-go/actor" +) + +type groupRouterActor struct { + props *actor.Props + config RouterConfig + state State + wg *sync.WaitGroup +} + +func (a *groupRouterActor) Receive(context actor.Context) { + switch m := context.Message().(type) { + case *actor.Started: + a.config.OnStarted(context, a.props, a.state) + a.wg.Done() + + case *AddRoutee: + r := a.state.GetRoutees() + if r.Contains(m.PID) { + return + } + context.Watch(m.PID) + r.Add(m.PID) + a.state.SetRoutees(r) + + case *RemoveRoutee: + r := a.state.GetRoutees() + if !r.Contains(m.PID) { + return + } + + context.Unwatch(m.PID) + r.Remove(m.PID) + a.state.SetRoutees(r) + + case *BroadcastMessage: + msg := m.Message + sender := context.Sender() + a.state.GetRoutees().ForEach(func(i int, pid *actor.PID) { + context.RequestWithCustomSender(pid, msg, sender) + }) + + case *GetRoutees: + r := a.state.GetRoutees() + routees := make([]*actor.PID, r.Len()) + r.ForEach(func(i int, pid *actor.PID) { + routees[i] = pid + }) + + context.Respond(&Routees{PIDs: routees}) + } +} diff --git a/router/routeractor_group_test.go b/router/routeractor_group_test.go new file mode 100644 index 0000000000000000000000000000000000000000..9ab0849d36a4ae6ba765abeba74dfa9f7b8126f1 --- /dev/null +++ b/router/routeractor_group_test.go @@ -0,0 +1,90 @@ +package router + +import ( + "testing" + + "gitee.com/simplexyz/simpleactor-go/actor" + "github.com/stretchr/testify/mock" +) + +func TestGroupRouterActor_Receive_AddRoute(t *testing.T) { + state := new(testRouterState) + + a := groupRouterActor{state: state} + + p1 := system.NewLocalPID("p1") + c := new(mockContext) + c.On("Message").Return(&AddRoutee{PID: p1}) + c.On("Watch", p1).Once() + + state.On("GetRoutees").Return(&actor.PIDSet{}) + state.On("SetRoutees", actor.NewPIDSet(p1)).Once() + + a.Receive(c) + mock.AssertExpectationsForObjects(t, state, c) +} + +func TestGroupRouterActor_Receive_AddRoute_NoDuplicates(t *testing.T) { + state := new(testRouterState) + + a := groupRouterActor{state: state} + + p1 := system.NewLocalPID("p1") + c := new(mockContext) + c.On("Message").Return(&AddRoutee{PID: p1}) + + state.On("GetRoutees").Return(actor.NewPIDSet(p1)) + + a.Receive(c) + mock.AssertExpectationsForObjects(t, state, c) +} + +func TestGroupRouterActor_Receive_RemoveRoute(t *testing.T) { + state := new(testRouterState) + + a := groupRouterActor{state: state} + + p1, _ := spawnMockProcess("p1") + defer removeMockProcess(p1) + + p2 := system.NewLocalPID("p2") + c := new(mockContext) + c.On("Message").Return(&RemoveRoutee{PID: p1}) + c.On("Unwatch", p1). + Run(func(args mock.Arguments) { + }).Once() + + state.On("GetRoutees").Return(actor.NewPIDSet(p1, p2)) + state.On("SetRoutees", actor.NewPIDSet(p2)).Once() + + a.Receive(c) + mock.AssertExpectationsForObjects(t, state, c) +} + +func TestGroupRouterActor_Receive_BroadcastMessage(t *testing.T) { + state := new(testRouterState) + a := groupRouterActor{state: state} + + p1 := system.NewLocalPID("p1") + p2 := system.NewLocalPID("p2") + + child := new(mockProcess) + child.On("SendUserMessage", mock.Anything, mock.Anything).Times(2) + + system.ProcessRegistry.Add(child, "p1") + system.ProcessRegistry.Add(child, "p2") + defer func() { + system.ProcessRegistry.Remove(&actor.PID{Id: "p1"}) + system.ProcessRegistry.Remove(&actor.PID{Id: "p2"}) + }() + + c := new(mockContext) + c.On("Message").Return(&BroadcastMessage{"hi"}) + c.On("Sender").Return((*actor.PID)(nil)) + c.On("RequestWithCustomSender").Twice() + + state.On("GetRoutees").Return(actor.NewPIDSet(p1, p2)) + + a.Receive(c) + mock.AssertExpectationsForObjects(t, state, c, child) +} diff --git a/router/routeractor_pool.go b/router/routeractor_pool.go new file mode 100644 index 0000000000000000000000000000000000000000..c4defa76df0a29df48cfc1b310000df76b6d6791 --- /dev/null +++ b/router/routeractor_pool.go @@ -0,0 +1,73 @@ +package router + +import ( + "sync" + "time" + + "gitee.com/simplexyz/simpleactor-go/actor" +) + +type poolRouterActor struct { + props *actor.Props + config RouterConfig + state State + wg *sync.WaitGroup +} + +func (a *poolRouterActor) Receive(context actor.Context) { + switch m := context.Message().(type) { + case *actor.Started: + a.config.OnStarted(context, a.props, a.state) + a.wg.Done() + + case *AddRoutee: + r := a.state.GetRoutees() + if r.Contains(m.PID) { + return + } + context.Watch(m.PID) + r.Add(m.PID) + a.state.SetRoutees(r) + + case *RemoveRoutee: + r := a.state.GetRoutees() + if !r.Contains(m.PID) { + return + } + + context.Unwatch(m.PID) + r.Remove(m.PID) + a.state.SetRoutees(r) + // sleep for 1ms before sending the poison pill + // This is to give some time to the routee actor receive all + // the messages. Specially due to the synchronization conditions in + // consistent hash router, where a copy of hmc can be obtained before + // the update and cause messages routed to a dead routee if there is no + // delay. This is a best effort approach and 1ms seems to be acceptable + // in terms of both delay it cause to the router actor and the time it + // provides for the routee to receive messages before it dies. + time.Sleep(time.Millisecond * 1) + context.Send(m.PID, &actor.PoisonPill{}) + + case *BroadcastMessage: + msg := m.Message + sender := context.Sender() + a.state.GetRoutees().ForEach(func(i int, pid *actor.PID) { + context.RequestWithCustomSender(pid, msg, sender) + }) + + case *GetRoutees: + r := a.state.GetRoutees() + routees := make([]*actor.PID, r.Len()) + r.ForEach(func(i int, pid *actor.PID) { + routees[i] = pid + }) + + context.Respond(&Routees{PIDs: routees}) + case *actor.Terminated: + r := a.state.GetRoutees() + if r.Remove(m.Who) { + a.state.SetRoutees(r) + } + } +} diff --git a/router/routeractor_pool_test.go b/router/routeractor_pool_test.go new file mode 100644 index 0000000000000000000000000000000000000000..1ff22feaa03c9534c04da70b5da0146d82be75f9 --- /dev/null +++ b/router/routeractor_pool_test.go @@ -0,0 +1,91 @@ +package router + +import ( + "testing" + + "gitee.com/simplexyz/simpleactor-go/actor" + "github.com/stretchr/testify/mock" +) + +func TestPoolRouterActor_Receive_AddRoute(t *testing.T) { + state := new(testRouterState) + + a := poolRouterActor{state: state} + + p1 := system.NewLocalPID("p1") + c := new(mockContext) + c.On("Message").Return(&AddRoutee{PID: p1}) + c.On("Watch", p1).Once() + + state.On("GetRoutees").Return(&actor.PIDSet{}) + state.On("SetRoutees", actor.NewPIDSet(p1)).Once() + + a.Receive(c) + mock.AssertExpectationsForObjects(t, state, c) +} + +func TestPoolRouterActor_Receive_AddRoute_NoDuplicates(t *testing.T) { + state := new(testRouterState) + + a := poolRouterActor{state: state} + + p1 := system.NewLocalPID("p1") + c := new(mockContext) + c.On("Message").Return(&AddRoutee{PID: p1}) + + state.On("GetRoutees").Return(actor.NewPIDSet(p1)) + + a.Receive(c) + mock.AssertExpectationsForObjects(t, state, c) +} + +func TestPoolRouterActor_Receive_RemoveRoute(t *testing.T) { + state := new(testRouterState) + + a := poolRouterActor{state: state} + + p1, pr1 := spawnMockProcess("p1") + defer removeMockProcess(p1) + pr1.On("SendUserMessage", p1, &actor.PoisonPill{}).Once() + + p2 := system.NewLocalPID("p2") + c := new(mockContext) + c.On("Message").Return(&RemoveRoutee{PID: p1}) + c.On("Unwatch", p1).Once() + + c.On("Send") + + state.On("GetRoutees").Return(actor.NewPIDSet(p1, p2)) + state.On("SetRoutees", actor.NewPIDSet(p2)).Once() + + a.Receive(c) + mock.AssertExpectationsForObjects(t, state, c) +} + +func TestPoolRouterActor_Receive_BroadcastMessage(t *testing.T) { + state := new(testRouterState) + a := poolRouterActor{state: state} + + p1 := system.NewLocalPID("p1") + p2 := system.NewLocalPID("p2") + + child := new(mockProcess) + child.On("SendUserMessage", mock.Anything, mock.Anything).Times(2) + + system.ProcessRegistry.Add(child, "p1") + system.ProcessRegistry.Add(child, "p2") + defer func() { + system.ProcessRegistry.Remove(&actor.PID{Id: "p1"}) + system.ProcessRegistry.Remove(&actor.PID{Id: "p2"}) + }() + + c := new(mockContext) + c.On("Message").Return(&BroadcastMessage{"hi"}) + c.On("Sender").Return((*actor.PID)(nil)) + c.On("RequestWithCustomSender").Twice() + + state.On("GetRoutees").Return(actor.NewPIDSet(p1, p2)) + + a.Receive(c) + mock.AssertExpectationsForObjects(t, state, c, child) +} diff --git a/router/routercontracts.pb.go b/router/routercontracts.pb.go new file mode 100644 index 0000000000000000000000000000000000000000..febbb8aab36a63d6c0a40f140d7ecbe11ea88189 --- /dev/null +++ b/router/routercontracts.pb.go @@ -0,0 +1,390 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc v3.19.1 +// source: routercontracts.proto + +package router + +import ( + actor "gitee.com/simplexyz/simpleactor-go/actor" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type AddRoutee struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + PID *actor.PID `protobuf:"bytes,1,opt,name=PID,proto3" json:"PID,omitempty"` +} + +func (x *AddRoutee) Reset() { + *x = AddRoutee{} + if protoimpl.UnsafeEnabled { + mi := &file_routercontracts_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AddRoutee) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AddRoutee) ProtoMessage() {} + +func (x *AddRoutee) ProtoReflect() protoreflect.Message { + mi := &file_routercontracts_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AddRoutee.ProtoReflect.Descriptor instead. +func (*AddRoutee) Descriptor() ([]byte, []int) { + return file_routercontracts_proto_rawDescGZIP(), []int{0} +} + +func (x *AddRoutee) GetPID() *actor.PID { + if x != nil { + return x.PID + } + return nil +} + +type RemoveRoutee struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + PID *actor.PID `protobuf:"bytes,1,opt,name=PID,proto3" json:"PID,omitempty"` +} + +func (x *RemoveRoutee) Reset() { + *x = RemoveRoutee{} + if protoimpl.UnsafeEnabled { + mi := &file_routercontracts_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RemoveRoutee) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RemoveRoutee) ProtoMessage() {} + +func (x *RemoveRoutee) ProtoReflect() protoreflect.Message { + mi := &file_routercontracts_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RemoveRoutee.ProtoReflect.Descriptor instead. +func (*RemoveRoutee) Descriptor() ([]byte, []int) { + return file_routercontracts_proto_rawDescGZIP(), []int{1} +} + +func (x *RemoveRoutee) GetPID() *actor.PID { + if x != nil { + return x.PID + } + return nil +} + +type AdjustPoolSize struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Change int32 `protobuf:"varint,1,opt,name=change,proto3" json:"change,omitempty"` +} + +func (x *AdjustPoolSize) Reset() { + *x = AdjustPoolSize{} + if protoimpl.UnsafeEnabled { + mi := &file_routercontracts_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AdjustPoolSize) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AdjustPoolSize) ProtoMessage() {} + +func (x *AdjustPoolSize) ProtoReflect() protoreflect.Message { + mi := &file_routercontracts_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AdjustPoolSize.ProtoReflect.Descriptor instead. +func (*AdjustPoolSize) Descriptor() ([]byte, []int) { + return file_routercontracts_proto_rawDescGZIP(), []int{2} +} + +func (x *AdjustPoolSize) GetChange() int32 { + if x != nil { + return x.Change + } + return 0 +} + +type GetRoutees struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *GetRoutees) Reset() { + *x = GetRoutees{} + if protoimpl.UnsafeEnabled { + mi := &file_routercontracts_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetRoutees) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetRoutees) ProtoMessage() {} + +func (x *GetRoutees) ProtoReflect() protoreflect.Message { + mi := &file_routercontracts_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetRoutees.ProtoReflect.Descriptor instead. +func (*GetRoutees) Descriptor() ([]byte, []int) { + return file_routercontracts_proto_rawDescGZIP(), []int{3} +} + +type Routees struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + PIDs []*actor.PID `protobuf:"bytes,1,rep,name=PIDs,proto3" json:"PIDs,omitempty"` +} + +func (x *Routees) Reset() { + *x = Routees{} + if protoimpl.UnsafeEnabled { + mi := &file_routercontracts_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Routees) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Routees) ProtoMessage() {} + +func (x *Routees) ProtoReflect() protoreflect.Message { + mi := &file_routercontracts_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Routees.ProtoReflect.Descriptor instead. +func (*Routees) Descriptor() ([]byte, []int) { + return file_routercontracts_proto_rawDescGZIP(), []int{4} +} + +func (x *Routees) GetPIDs() []*actor.PID { + if x != nil { + return x.PIDs + } + return nil +} + +var File_routercontracts_proto protoreflect.FileDescriptor + +var file_routercontracts_proto_rawDesc = []byte{ + 0x0a, 0x15, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, + 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x1a, + 0x0b, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x29, 0x0a, 0x09, + 0x41, 0x64, 0x64, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x65, 0x12, 0x1c, 0x0a, 0x03, 0x50, 0x49, 0x44, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x2e, 0x50, + 0x49, 0x44, 0x52, 0x03, 0x50, 0x49, 0x44, 0x22, 0x2c, 0x0a, 0x0c, 0x52, 0x65, 0x6d, 0x6f, 0x76, + 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x65, 0x12, 0x1c, 0x0a, 0x03, 0x50, 0x49, 0x44, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x2e, 0x50, 0x49, 0x44, + 0x52, 0x03, 0x50, 0x49, 0x44, 0x22, 0x28, 0x0a, 0x0e, 0x41, 0x64, 0x6a, 0x75, 0x73, 0x74, 0x50, + 0x6f, 0x6f, 0x6c, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x63, 0x68, 0x61, 0x6e, 0x67, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x22, + 0x0c, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x65, 0x73, 0x22, 0x29, 0x0a, + 0x07, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x65, 0x73, 0x12, 0x1e, 0x0a, 0x04, 0x50, 0x49, 0x44, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x2e, 0x50, + 0x49, 0x44, 0x52, 0x04, 0x50, 0x49, 0x44, 0x73, 0x42, 0x2a, 0x5a, 0x28, 0x67, 0x69, 0x74, 0x68, + 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x73, 0x79, 0x6e, 0x6b, 0x72, 0x6f, 0x6e, 0x2f, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x2d, 0x67, 0x6f, 0x2f, 0x72, 0x6f, + 0x75, 0x74, 0x65, 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_routercontracts_proto_rawDescOnce sync.Once + file_routercontracts_proto_rawDescData = file_routercontracts_proto_rawDesc +) + +func file_routercontracts_proto_rawDescGZIP() []byte { + file_routercontracts_proto_rawDescOnce.Do(func() { + file_routercontracts_proto_rawDescData = protoimpl.X.CompressGZIP(file_routercontracts_proto_rawDescData) + }) + return file_routercontracts_proto_rawDescData +} + +var file_routercontracts_proto_msgTypes = make([]protoimpl.MessageInfo, 5) +var file_routercontracts_proto_goTypes = []interface{}{ + (*AddRoutee)(nil), // 0: router.AddRoutee + (*RemoveRoutee)(nil), // 1: router.RemoveRoutee + (*AdjustPoolSize)(nil), // 2: router.AdjustPoolSize + (*GetRoutees)(nil), // 3: router.GetRoutees + (*Routees)(nil), // 4: router.Routees + (*actor.PID)(nil), // 5: actor.PID +} +var file_routercontracts_proto_depIdxs = []int32{ + 5, // 0: router.AddRoutee.PID:type_name -> actor.PID + 5, // 1: router.RemoveRoutee.PID:type_name -> actor.PID + 5, // 2: router.Routees.PIDs:type_name -> actor.PID + 3, // [3:3] is the sub-list for method output_type + 3, // [3:3] is the sub-list for method input_type + 3, // [3:3] is the sub-list for extension type_name + 3, // [3:3] is the sub-list for extension extendee + 0, // [0:3] is the sub-list for field type_name +} + +func init() { file_routercontracts_proto_init() } +func file_routercontracts_proto_init() { + if File_routercontracts_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_routercontracts_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AddRoutee); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_routercontracts_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RemoveRoutee); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_routercontracts_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AdjustPoolSize); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_routercontracts_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetRoutees); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_routercontracts_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Routees); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_routercontracts_proto_rawDesc, + NumEnums: 0, + NumMessages: 5, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_routercontracts_proto_goTypes, + DependencyIndexes: file_routercontracts_proto_depIdxs, + MessageInfos: file_routercontracts_proto_msgTypes, + }.Build() + File_routercontracts_proto = out.File + file_routercontracts_proto_rawDesc = nil + file_routercontracts_proto_goTypes = nil + file_routercontracts_proto_depIdxs = nil +} diff --git a/router/routercontracts.proto b/router/routercontracts.proto new file mode 100644 index 0000000000000000000000000000000000000000..75a6ee879efe58c0fd76728c0726c65dfdd58eee --- /dev/null +++ b/router/routercontracts.proto @@ -0,0 +1,22 @@ +syntax = "proto3"; +package router; +option go_package = "gitee.com/simplexyz/simpleactor-go/router"; +import "actor.proto"; + +message AddRoutee { + actor.PID PID = 1; +} + +message RemoveRoutee { + actor.PID PID = 1; +} + +message AdjustPoolSize { + int32 change = 1; +} + +message GetRoutees {} + +message Routees { + repeated actor.PID PIDs = 1; +} diff --git a/scheduler/timer.go b/scheduler/timer.go new file mode 100644 index 0000000000000000000000000000000000000000..a958c7acc00100b84dfee78fcb588b55b0fa263c --- /dev/null +++ b/scheduler/timer.go @@ -0,0 +1,108 @@ +package scheduler + +import ( + "runtime" + "sync/atomic" + "time" + + "gitee.com/simplexyz/simpleactor-go/actor" +) + +type CancelFunc func() + +type Stopper interface { + Stop() +} + +const ( + stateInit = iota + stateReady + stateDone +) + +func startTimer(delay, interval time.Duration, fn func()) CancelFunc { + var t *time.Timer + var state int32 + t = time.AfterFunc(delay, func() { + for atomic.LoadInt32(&state) == stateInit { + runtime.Gosched() + } + + if state == stateDone { + return + } + + fn() + t.Reset(interval) + }) + + // ensures t != nil and is required to avoid data race in + // AfterFunc calling t.Reset + atomic.StoreInt32(&state, stateReady) + + return func() { + if atomic.SwapInt32(&state, stateDone) != stateDone { + t.Stop() + } + } +} + +// A scheduler utilizing timers to send messages in the future and at regular intervals. +type TimerScheduler struct { + ctx actor.SenderContext +} + +type timerOptionFunc func(*TimerScheduler) + +// WithContext configures the scheduler to use ctx rather than the default, +// EmptyRootContext. +func WithContext(ctx actor.SenderContext) timerOptionFunc { + return func(s *TimerScheduler) { + s.ctx = ctx + } +} + +// NewTimerScheduler creates a new scheduler using the EmptyRootContext. +// Additional options may be specified to override the default behavior. +func NewTimerScheduler(sender actor.SenderContext, opts ...timerOptionFunc) *TimerScheduler { + s := &TimerScheduler{ctx: sender} + for _, opt := range opts { + opt(s) + } + return s +} + +// SendOnce waits for the duration to elapse and then calls actor.SenderContext.Send to forward the message to pid. +func (s *TimerScheduler) SendOnce(delay time.Duration, pid *actor.PID, message interface{}) CancelFunc { + t := time.AfterFunc(delay, func() { + s.ctx.Send(pid, message) + }) + + return func() { t.Stop() } +} + +// SendRepeatedly waits for the initial duration to elapse and then calls Send to forward the message to pid +// repeatedly for each interval. +func (s *TimerScheduler) SendRepeatedly(initial, interval time.Duration, pid *actor.PID, message interface{}) CancelFunc { + return startTimer(initial, interval, func() { + s.ctx.Send(pid, message) + }) +} + +// RequestOnce waits for the duration to elapse and then calls actor.SenderContext.Request to forward the message to +// pid. +func (s *TimerScheduler) RequestOnce(delay time.Duration, pid *actor.PID, message interface{}) CancelFunc { + t := time.AfterFunc(delay, func() { + s.ctx.Request(pid, message) + }) + + return func() { t.Stop() } +} + +// RequestRepeatedly waits for the initial duration to elapse and then calls Request to forward the message to pid +// repeatedly for each interval. +func (s *TimerScheduler) RequestRepeatedly(delay, interval time.Duration, pid *actor.PID, message interface{}) CancelFunc { + return startTimer(delay, interval, func() { + s.ctx.Request(pid, message) + }) +} diff --git a/scheduler/timer_example_test.go b/scheduler/timer_example_test.go new file mode 100644 index 0000000000000000000000000000000000000000..a8a88d0f0ca97beb0e2d465602113485dc07c052 --- /dev/null +++ b/scheduler/timer_example_test.go @@ -0,0 +1,40 @@ +package scheduler_test + +import ( + "fmt" + "sync" + "time" + + "gitee.com/simplexyz/simpleactor-go/actor" + "gitee.com/simplexyz/simpleactor-go/scheduler" +) + +var system = actor.NewActorSystem() + +// Use the timer scheduler to repeatedly send messages to an actor. +func ExampleTimerScheduler_sendRepeatedly() { + var wg sync.WaitGroup + + wg.Add(2) + + count := 0 + props := actor.PropsFromFunc(func(c actor.Context) { + if v, ok := c.Message().(string); ok { + count++ + fmt.Println(count, v) + wg.Done() + } + }) + + pid := system.Root.Spawn(props) + + s := scheduler.NewTimerScheduler(system.Root) + cancel := s.SendRepeatedly(1*time.Millisecond, 1*time.Millisecond, pid, "Hello") + + wg.Wait() + cancel() + + // Output: + // 1 Hello + // 2 Hello +} diff --git a/scheduler/timer_test.go b/scheduler/timer_test.go new file mode 100644 index 0000000000000000000000000000000000000000..49499bf159001673968eba37e5890321c4fe8d15 --- /dev/null +++ b/scheduler/timer_test.go @@ -0,0 +1,115 @@ +package scheduler + +import ( + "testing" + "time" + + "gitee.com/simplexyz/simpleactor-go/actor" + "github.com/stretchr/testify/assert" +) + +var system = actor.NewActorSystem() + +func TestNewTimerScheduler(t *testing.T) { + newActor := func(t *testing.T, n int) (pid *actor.PID, ch chan struct{}) { + ch = make(chan struct{}, n) + props := actor.PropsFromFunc(func(c actor.Context) { + switch c.Message().(type) { + case string: + select { + case ch <- struct{}{}: + default: + t.Errorf("exceeeded expected count %d", n) + } + } + }) + return system.Root.Spawn(props), ch + } + + // check verifies the number of times ch receives a message matches exp + // and executes once more to ensure no further messages are received + check := func(t *testing.T, ch chan struct{}, cancel CancelFunc, exp int) { + got := 0 + for i := 0; i < exp+1; i++ { + select { + case <-ch: + got++ + if got == exp { + cancel() + } + case <-time.After(3 * time.Millisecond): + if got != exp { + assert.Fail(t, "failed to receive message") + } + } + } + cancel() + assert.Equal(t, exp, got) + } + + t.Run("does", func(t *testing.T) { + t.Run("send once", func(t *testing.T) { + s := NewTimerScheduler(system.Root) + pid, ch := newActor(t, 1) + tok := s.SendOnce(1*time.Millisecond, pid, "hello") + + check(t, ch, tok, 1) + }) + + t.Run("send repeatedly", func(t *testing.T) { + s := NewTimerScheduler(system.Root) + pid, ch := newActor(t, 5) + tok := s.SendRepeatedly(1*time.Millisecond, 1*time.Millisecond, pid, "hello") + check(t, ch, tok, 5) + }) + + t.Run("request once", func(t *testing.T) { + s := NewTimerScheduler(system.Root) + pid, ch := newActor(t, 1) + tok := s.RequestOnce(1*time.Millisecond, pid, "hello") + + check(t, ch, tok, 1) + }) + + t.Run("request repeatedly", func(t *testing.T) { + s := NewTimerScheduler(system.Root) + pid, ch := newActor(t, 5) + tok := s.RequestRepeatedly(1*time.Millisecond, 1*time.Millisecond, pid, "hello") + check(t, ch, tok, 5) + }) + }) + + t.Run("does not", func(t *testing.T) { + t.Run("send once", func(t *testing.T) { + s := NewTimerScheduler(system.Root) + pid, ch := newActor(t, 1) + cancel := s.SendOnce(1*time.Millisecond, pid, "hello") + cancel() + check(t, ch, cancel, 0) + }) + + t.Run("send repeatedly", func(t *testing.T) { + s := NewTimerScheduler(system.Root) + pid, ch := newActor(t, 5) + cancel := s.SendRepeatedly(1*time.Millisecond, 1*time.Millisecond, pid, "hello") + cancel() + check(t, ch, cancel, 0) + }) + + t.Run("request once", func(t *testing.T) { + s := NewTimerScheduler(system.Root) + pid, ch := newActor(t, 1) + cancel := s.RequestOnce(1*time.Millisecond, pid, "hello") + cancel() + check(t, ch, cancel, 0) + }) + + t.Run("request repeatedly", func(t *testing.T) { + s := NewTimerScheduler(system.Root) + pid, ch := newActor(t, 5) + cancel := s.RequestRepeatedly(1*time.Millisecond, 1*time.Millisecond, pid, "hello") + cancel() + check(t, ch, cancel, 0) + }) + }) +} diff --git a/stream/typed.go b/stream/typed.go new file mode 100644 index 0000000000000000000000000000000000000000..7fa3031d6ca34c37b568c0e3653fe1ea30c5ad02 --- /dev/null +++ b/stream/typed.go @@ -0,0 +1,42 @@ +package stream + +import "gitee.com/simplexyz/simpleactor-go/actor" + +type TypedStream[T any] struct { + c chan T + pid *actor.PID + actorSystem *actor.ActorSystem +} + +func (s *TypedStream[T]) C() <-chan T { + return s.c +} + +func (s *TypedStream[T]) PID() *actor.PID { + return s.pid +} + +func (s *TypedStream[T]) Close() { + s.actorSystem.Root.Stop(s.pid) + close(s.c) +} + +func NewTypedStream[T any](actorSystem *actor.ActorSystem) *TypedStream[T] { + c := make(chan T) + + props := actor.PropsFromFunc(func(ctx actor.Context) { + switch msg := ctx.Message().(type) { + case actor.AutoReceiveMessage, actor.SystemMessage: + // ignore terminate + case T: + c <- msg + } + }) + pid := actorSystem.Root.Spawn(props) + + return &TypedStream[T]{ + c: c, + pid: pid, + actorSystem: actorSystem, + } +} diff --git a/stream/typed_test.go b/stream/typed_test.go new file mode 100644 index 0000000000000000000000000000000000000000..c1277a4ce31b0785712bf43d10dfcf93b8136a6e --- /dev/null +++ b/stream/typed_test.go @@ -0,0 +1,22 @@ +package stream + +import ( + "testing" + + "gitee.com/simplexyz/simpleactor-go/actor" + "github.com/stretchr/testify/assert" +) + +func TestReceiveFromTypedStream(t *testing.T) { + system := actor.NewActorSystem() + s := NewTypedStream[string](system) + go func() { + rootContext := system.Root + rootContext.Send(s.PID(), "hello") + rootContext.Send(s.PID(), "you") + }() + res := <-s.C() + res2 := <-s.C() + assert.Equal(t, "hello", res) + assert.Equal(t, "you", res2) +} diff --git a/stream/untyped.go b/stream/untyped.go new file mode 100644 index 0000000000000000000000000000000000000000..16cf6de92e6561824b022e9aa6aab65b3a19baea --- /dev/null +++ b/stream/untyped.go @@ -0,0 +1,42 @@ +package stream + +import "gitee.com/simplexyz/simpleactor-go/actor" + +type UntypedStream struct { + c chan interface{} + pid *actor.PID + actorSystem *actor.ActorSystem +} + +func (s *UntypedStream) C() <-chan interface{} { + return s.c +} + +func (s *UntypedStream) PID() *actor.PID { + return s.pid +} + +func (s *UntypedStream) Close() { + s.actorSystem.Root.Stop(s.pid) + close(s.c) +} + +func NewUntypedStream(actorSystem *actor.ActorSystem) *UntypedStream { + c := make(chan interface{}) + + props := actor.PropsFromFunc(func(ctx actor.Context) { + switch msg := ctx.Message().(type) { + case actor.AutoReceiveMessage, actor.SystemMessage: + // ignore terminate + default: + c <- msg + } + }) + pid := actorSystem.Root.Spawn(props) + + return &UntypedStream{ + c: c, + pid: pid, + actorSystem: actorSystem, + } +} diff --git a/stream/untyped_test.go b/stream/untyped_test.go new file mode 100644 index 0000000000000000000000000000000000000000..df4f6c56a5e5c72c50d4da205c472f546934d760 --- /dev/null +++ b/stream/untyped_test.go @@ -0,0 +1,22 @@ +package stream + +import ( + "testing" + + "gitee.com/simplexyz/simpleactor-go/actor" + "github.com/stretchr/testify/assert" +) + +func TestReceiveFromStream(t *testing.T) { + system := actor.NewActorSystem() + s := NewUntypedStream(system) + go func() { + rootContext := system.Root + rootContext.Send(s.PID(), "hello") + rootContext.Send(s.PID(), "you") + }() + res := <-s.C() + res2 := <-s.C() + assert.Equal(t, "hello", res.(string)) + assert.Equal(t, "you", res2.(string)) +}