Update split SDK to 6.0.2 to fix sync bug (#17060)

* Update split SDK to 6.0.2 to fix sync bug

* Vendor and tidy
This commit is contained in:
Joram Wilander
2021-03-04 11:16:08 -05:00
committed by GitHub
parent fa2ecad0a9
commit aba00a3cfd
150 changed files with 2500 additions and 1960 deletions

5
go.mod
View File

@@ -18,7 +18,6 @@ require (
github.com/aws/aws-sdk-go v1.36.29 github.com/aws/aws-sdk-go v1.36.29
github.com/blang/semver v3.5.1+incompatible github.com/blang/semver v3.5.1+incompatible
github.com/blevesearch/bleve v1.0.14 github.com/blevesearch/bleve v1.0.14
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect
github.com/cespare/xxhash/v2 v2.1.1 github.com/cespare/xxhash/v2 v2.1.1
github.com/corpix/uarand v0.1.1 // indirect github.com/corpix/uarand v0.1.1 // indirect
github.com/dgrijalva/jwt-go v3.2.0+incompatible github.com/dgrijalva/jwt-go v3.2.0+incompatible
@@ -32,12 +31,10 @@ require (
github.com/getsentry/sentry-go v0.9.0 github.com/getsentry/sentry-go v0.9.0
github.com/glycerine/go-unsnap-stream v0.0.0-20190901134440-81cf024a9e0a // indirect github.com/glycerine/go-unsnap-stream v0.0.0-20190901134440-81cf024a9e0a // indirect
github.com/go-asn1-ber/asn1-ber v1.5.3 // indirect github.com/go-asn1-ber/asn1-ber v1.5.3 // indirect
github.com/go-bindata/go-bindata v3.1.2+incompatible // indirect
github.com/go-redis/redis/v8 v8.4.9 // indirect github.com/go-redis/redis/v8 v8.4.9 // indirect
github.com/go-resty/resty/v2 v2.4.0 // indirect github.com/go-resty/resty/v2 v2.4.0 // indirect
github.com/go-sql-driver/mysql v1.5.0 github.com/go-sql-driver/mysql v1.5.0
github.com/gobwas/ws v1.1.0-rc.2 github.com/gobwas/ws v1.1.0-rc.2
github.com/golang-migrate/migrate v3.5.4+incompatible // indirect
github.com/golang-migrate/migrate/v4 v4.14.1 github.com/golang-migrate/migrate/v4 v4.14.1
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
github.com/golang/snappy v0.0.2 // indirect github.com/golang/snappy v0.0.2 // indirect
@@ -108,7 +105,7 @@ require (
github.com/sirupsen/logrus v1.7.0 github.com/sirupsen/logrus v1.7.0
github.com/smartystreets/assertions v1.0.0 // indirect github.com/smartystreets/assertions v1.0.0 // indirect
github.com/spf13/cobra v1.1.1 github.com/spf13/cobra v1.1.1
github.com/splitio/go-client/v6 v6.0.1 github.com/splitio/go-client/v6 v6.0.2
github.com/stretchr/objx v0.3.0 // indirect github.com/stretchr/objx v0.3.0 // indirect
github.com/stretchr/testify v1.7.0 github.com/stretchr/testify v1.7.0
github.com/throttled/throttled v2.2.5+incompatible github.com/throttled/throttled v2.2.5+incompatible

33
go.sum
View File

@@ -48,6 +48,7 @@ git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGy
git.apache.org/thrift.git v0.12.0/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= git.apache.org/thrift.git v0.12.0/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
github.com/Azure/azure-sdk-for-go v26.5.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/azure-sdk-for-go v26.5.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8=
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
github.com/Azure/go-autorest v11.5.2+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/go-autorest v11.5.2+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
@@ -71,6 +72,7 @@ github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0
github.com/Masterminds/squirrel v1.5.0 h1:JukIZisrUXadA9pl3rMkjhiamxiB0cXiu+HGp/Y8cY8= github.com/Masterminds/squirrel v1.5.0 h1:JukIZisrUXadA9pl3rMkjhiamxiB0cXiu+HGp/Y8cY8=
github.com/Masterminds/squirrel v1.5.0/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10= github.com/Masterminds/squirrel v1.5.0/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10=
github.com/Masterminds/vcs v1.13.0/go.mod h1:N09YCmOQr6RLxC6UNHzuVwAdodYbbnycGHSmwVJjcKA= github.com/Masterminds/vcs v1.13.0/go.mod h1:N09YCmOQr6RLxC6UNHzuVwAdodYbbnycGHSmwVJjcKA=
github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5 h1:ygIc8M6trr62pF5DucadTWGdEB4mEyvzi0e2nbcmcyA=
github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/PaulARoy/azurestoragecache v0.0.0-20170906084534-3c249a3ba788/go.mod h1:lY1dZd8HBzJ10eqKERHn3CU59tfhzcAVb2c0ZhIWSOk= github.com/PaulARoy/azurestoragecache v0.0.0-20170906084534-3c249a3ba788/go.mod h1:lY1dZd8HBzJ10eqKERHn3CU59tfhzcAVb2c0ZhIWSOk=
@@ -205,6 +207,7 @@ github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE
github.com/codegangsta/cli v1.20.0/go.mod h1:/qJNoX69yVSKu5o4jLyXAENLRyk1uhi7zkbQ3slBdOA= github.com/codegangsta/cli v1.20.0/go.mod h1:/qJNoX69yVSKu5o4jLyXAENLRyk1uhi7zkbQ3slBdOA=
github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM=
github.com/containerd/containerd v1.4.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/containerd v1.4.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
github.com/containerd/containerd v1.4.1 h1:pASeJT3R3YyVn+94qEPk0SnU1OQ20Jd/T+SPKy9xehY=
github.com/containerd/containerd v1.4.1/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/containerd v1.4.1/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
@@ -249,15 +252,20 @@ github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUn
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/dhui/dktest v0.3.3 h1:DBuH/9GFaWbDRa42qsut/hbQu+srAQ0rPWnUoiGX7CA=
github.com/dhui/dktest v0.3.3/go.mod h1:EML9sP4sqJELHn4jV7B0TY8oF6077nk83/tz7M56jcQ= github.com/dhui/dktest v0.3.3/go.mod h1:EML9sP4sqJELHn4jV7B0TY8oF6077nk83/tz7M56jcQ=
github.com/die-net/lrucache v0.0.0-20181227122439-19a39ef22a11/go.mod h1:ew0MSjCVDdtGMjF3kzLK9hwdgF5mOE8SbYVF3Rc7mkU= github.com/die-net/lrucache v0.0.0-20181227122439-19a39ef22a11/go.mod h1:ew0MSjCVDdtGMjF3kzLK9hwdgF5mOE8SbYVF3Rc7mkU=
github.com/disintegration/imaging v1.6.0/go.mod h1:xuIt+sRxDFrHS0drzXUlCJthkJ8k7lkkUojDSR247MQ= github.com/disintegration/imaging v1.6.0/go.mod h1:xuIt+sRxDFrHS0drzXUlCJthkJ8k7lkkUojDSR247MQ=
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c= github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4= github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug=
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v17.12.0-ce-rc1.0.20200618181300-9dc6525e6118+incompatible h1:iWPIG7pWIsCwT6ZtHnTUpoVMnete7O/pzd9HFE3+tn8=
github.com/docker/docker v17.12.0-ce-rc1.0.20200618181300-9dc6525e6118+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v17.12.0-ce-rc1.0.20200618181300-9dc6525e6118+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/dsnet/compress v0.0.1 h1:PlZu0n3Tuv04TzpfPbrnI0HW/YwodEXDS+oPKahKF0Q= github.com/dsnet/compress v0.0.1 h1:PlZu0n3Tuv04TzpfPbrnI0HW/YwodEXDS+oPKahKF0Q=
github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5JflhBbQEHo= github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5JflhBbQEHo=
@@ -323,9 +331,6 @@ github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1T
github.com/go-asn1-ber/asn1-ber v1.3.2-0.20191121212151-29be175fc3a3/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= github.com/go-asn1-ber/asn1-ber v1.3.2-0.20191121212151-29be175fc3a3/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
github.com/go-asn1-ber/asn1-ber v1.5.3 h1:u7utq56RUFiynqUzgVMFDymapcOtQ/MZkh3H4QYkxag= github.com/go-asn1-ber/asn1-ber v1.5.3 h1:u7utq56RUFiynqUzgVMFDymapcOtQ/MZkh3H4QYkxag=
github.com/go-asn1-ber/asn1-ber v1.5.3/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= github.com/go-asn1-ber/asn1-ber v1.5.3/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
github.com/go-bindata/go-bindata v1.0.0 h1:DZ34txDXWn1DyWa+vQf7V9ANc2ILTtrEjtlsdJRF26M=
github.com/go-bindata/go-bindata v3.1.2+incompatible h1:5vjJMVhowQdPzjE1LdxyFF7YFTXg5IgGVW4gBr5IbvE=
github.com/go-bindata/go-bindata v3.1.2+incompatible/go.mod h1:xK8Dsgwmeed+BBsSy2XTopBn/8uK2HWuGSnA11C3Joo=
github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98=
github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
@@ -370,10 +375,8 @@ github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFG
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/golang-migrate/migrate v1.3.2 h1:QAlFV1QF9zdkzy/jujlBVkVu+L/+k18cg8tuY1/4JDY=
github.com/golang-migrate/migrate v3.5.4+incompatible h1:R7OzwvCJTCgwapPCiX6DyBiu2czIUMDCB118gFTKTUA=
github.com/golang-migrate/migrate v3.5.4+incompatible/go.mod h1:IsVUlFN5puWOmXrqjgGUfIRIbU7mr8oNBE2tyERd9Wk=
github.com/golang-migrate/migrate/v4 v4.14.1 h1:qmRd/rNGjM1r3Ve5gHd5ZplytrD02UcItYNxJ3iUHHE= github.com/golang-migrate/migrate/v4 v4.14.1 h1:qmRd/rNGjM1r3Ve5gHd5ZplytrD02UcItYNxJ3iUHHE=
github.com/golang-migrate/migrate/v4 v4.14.1/go.mod h1:l7Ks0Au6fYHuUIxUhQ0rcVX1uLlJg54C/VvW7tvxSz0= github.com/golang-migrate/migrate/v4 v4.14.1/go.mod h1:l7Ks0Au6fYHuUIxUhQ0rcVX1uLlJg54C/VvW7tvxSz0=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
@@ -766,6 +769,7 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ= github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ=
github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg= github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg=
@@ -827,7 +831,9 @@ github.com/onsi/gomega v1.10.4/go.mod h1:g/HbgYopi++010VEqkFgJHKC09uJiW9UkXvMUuK
github.com/oov/psd v0.0.0-20201203182240-dad9002861d9 h1:a6xAIHhp7La7zsMnX7RLRokELz540I7oJV56TR50TYg= github.com/oov/psd v0.0.0-20201203182240-dad9002861d9 h1:a6xAIHhp7La7zsMnX7RLRokELz540I7oJV56TR50TYg=
github.com/oov/psd v0.0.0-20201203182240-dad9002861d9/go.mod h1:GHI1bnmAcbp96z6LNfBJvtrjxhaXGkbsk967utPlvL8= github.com/oov/psd v0.0.0-20201203182240-dad9002861d9/go.mod h1:GHI1bnmAcbp96z6LNfBJvtrjxhaXGkbsk967utPlvL8=
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI=
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis=
github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
@@ -1025,12 +1031,12 @@ 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/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
github.com/splitio/go-client/v6 v6.0.1 h1:w8lOuyIw4S2DQQjAIPBKzF+eDngzkQIY8hWcbkWjcxc= github.com/splitio/go-client/v6 v6.0.2 h1:nlEeXaI6bI7c74gkX8kohwqjrkCoTUzMY4QFPsAXKMU=
github.com/splitio/go-client/v6 v6.0.1/go.mod h1:WHjkBtEEJFHOpgqnHSb/X+e4CgzTi1gPNkg+c7w1Izg= github.com/splitio/go-client/v6 v6.0.2/go.mod h1:JbxrPJiIJHdPi5alQx5bszNzNrdJR+er1ktd9RzGtgk=
github.com/splitio/go-split-commons/v2 v2.0.1 h1:fpmdHKgWdcL6ON7hpSUUKTh4Nsv7Kl/9BBE1ZPcmdsw= github.com/splitio/go-split-commons/v3 v3.0.0 h1:TtBvkI6mbTJLPO2YIcIXKzbeHHndml44zhi+guWiY/I=
github.com/splitio/go-split-commons/v2 v2.0.1/go.mod h1:YsmZa0AE9DCTosPXekKBxYHzNXJHpZwID9FSfo5Tnis= github.com/splitio/go-split-commons/v3 v3.0.0/go.mod h1:6YEmYZnfrqV1Fp0vo4JL26zhzsDPD7p/OwFWy1LW2n4=
github.com/splitio/go-toolkit/v3 v3.0.1 h1:/H2wytH9r4GT4FpVmMWe7wUX99Y67b15fSbfIT1lIt8= github.com/splitio/go-toolkit/v4 v4.0.0 h1:L8yEQ9TxzG0zC1xR3zA+KI4IqCwBV0GiIGsBFRtWXJY=
github.com/splitio/go-toolkit/v3 v3.0.1/go.mod h1:HGgawLnM2RlM84zVRbATpPMjF7H6u9CUYG6RlpwOlOk= github.com/splitio/go-toolkit/v4 v4.0.0/go.mod h1:EdIHN0yzB1GTXDYQc0KdKvnjkO/jfUM2YqHVYfhD3Wo=
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf h1:pvbZ0lM0XWPBqUKqFU8cmavspvIl9nulOYwdy6IFRRo= github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf h1:pvbZ0lM0XWPBqUKqFU8cmavspvIl9nulOYwdy6IFRRo=
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf/go.mod h1:RJID2RhlZKId02nZ62WenDCkgHFerpIOmW0iT7GKmXM= github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf/go.mod h1:RJID2RhlZKId02nZ62WenDCkgHFerpIOmW0iT7GKmXM=
github.com/steveyen/gtreap v0.1.0 h1:CjhzTa274PyJLJuMZwIzCO1PfC00oRa8d1Kc78bFXJM= github.com/steveyen/gtreap v0.1.0 h1:CjhzTa274PyJLJuMZwIzCO1PfC00oRa8d1Kc78bFXJM=
@@ -1395,6 +1401,7 @@ golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxb
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 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-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-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e h1:EHBhcS0mlXEAVwNyO2dLfjToGsyY4j24pTs2ScHnX7s=
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -1623,6 +1630,7 @@ gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2/go.mod h1:K4uyk7z7BCEPqu6E+C
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
@@ -1634,6 +1642,7 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3 h1:sXmLre5bzIR6ypkjXCDI3jHPssRhc8KD/Ome589sc3U= honnef.co/go/tools v0.0.1-2020.1.3 h1:sXmLre5bzIR6ypkjXCDI3jHPssRhc8KD/Ome589sc3U=
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.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4 h1:UoveltGrhghAA7ePc+e+QYDHXrBps2PqFZiHkGR/xK8=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
modernc.org/b v1.0.0/go.mod h1:uZWcZfRj1BpYzfN9JTerzlNUnnPsV9O2ZA8JsRcubNg= modernc.org/b v1.0.0/go.mod h1:uZWcZfRj1BpYzfN9JTerzlNUnnPsV9O2ZA8JsRcubNg=
modernc.org/db v1.0.0/go.mod h1:kYD/cO29L/29RM0hXYl4i3+Q5VojL31kTUVpVJDw0s8= modernc.org/db v1.0.0/go.mod h1:kYD/cO29L/29RM0hXYl4i3+Q5VojL31kTUVpVJDw0s8=

View File

@@ -9,11 +9,11 @@ import (
"github.com/splitio/go-client/v6/splitio/engine/evaluator" "github.com/splitio/go-client/v6/splitio/engine/evaluator"
"github.com/splitio/go-client/v6/splitio/engine/evaluator/impressionlabels" "github.com/splitio/go-client/v6/splitio/engine/evaluator/impressionlabels"
impressionlistener "github.com/splitio/go-client/v6/splitio/impressionListener" impressionlistener "github.com/splitio/go-client/v6/splitio/impressionListener"
"github.com/splitio/go-split-commons/v2/dtos" "github.com/splitio/go-split-commons/v3/dtos"
"github.com/splitio/go-split-commons/v2/provisional" "github.com/splitio/go-split-commons/v3/provisional"
"github.com/splitio/go-split-commons/v2/storage" "github.com/splitio/go-split-commons/v3/storage"
"github.com/splitio/go-split-commons/v2/util" "github.com/splitio/go-split-commons/v3/util"
"github.com/splitio/go-toolkit/v3/logging" "github.com/splitio/go-toolkit/v4/logging"
) )
// SplitClient is the entry-point of the split SDK. // SplitClient is the entry-point of the split SDK.

View File

@@ -15,24 +15,24 @@ import (
"github.com/splitio/go-client/v6/splitio/engine" "github.com/splitio/go-client/v6/splitio/engine"
"github.com/splitio/go-client/v6/splitio/engine/evaluator" "github.com/splitio/go-client/v6/splitio/engine/evaluator"
impressionlistener "github.com/splitio/go-client/v6/splitio/impressionListener" impressionlistener "github.com/splitio/go-client/v6/splitio/impressionListener"
config "github.com/splitio/go-split-commons/v2/conf" config "github.com/splitio/go-split-commons/v3/conf"
"github.com/splitio/go-split-commons/v2/dtos" "github.com/splitio/go-split-commons/v3/dtos"
"github.com/splitio/go-split-commons/v2/provisional" "github.com/splitio/go-split-commons/v3/provisional"
"github.com/splitio/go-split-commons/v2/service" "github.com/splitio/go-split-commons/v3/service"
"github.com/splitio/go-split-commons/v2/service/local" "github.com/splitio/go-split-commons/v3/service/local"
"github.com/splitio/go-split-commons/v2/storage" "github.com/splitio/go-split-commons/v3/storage"
"github.com/splitio/go-split-commons/v2/storage/mutexmap" "github.com/splitio/go-split-commons/v3/storage/mutexmap"
"github.com/splitio/go-split-commons/v2/storage/mutexqueue" "github.com/splitio/go-split-commons/v3/storage/mutexqueue"
"github.com/splitio/go-split-commons/v2/storage/redis" "github.com/splitio/go-split-commons/v3/storage/redis"
"github.com/splitio/go-split-commons/v2/synchronizer" "github.com/splitio/go-split-commons/v3/synchronizer"
"github.com/splitio/go-split-commons/v2/synchronizer/worker/event" "github.com/splitio/go-split-commons/v3/synchronizer/worker/event"
"github.com/splitio/go-split-commons/v2/synchronizer/worker/impression" "github.com/splitio/go-split-commons/v3/synchronizer/worker/impression"
"github.com/splitio/go-split-commons/v2/synchronizer/worker/impressionscount" "github.com/splitio/go-split-commons/v3/synchronizer/worker/impressionscount"
"github.com/splitio/go-split-commons/v2/synchronizer/worker/metric" "github.com/splitio/go-split-commons/v3/synchronizer/worker/metric"
"github.com/splitio/go-split-commons/v2/synchronizer/worker/segment" "github.com/splitio/go-split-commons/v3/synchronizer/worker/segment"
"github.com/splitio/go-split-commons/v2/synchronizer/worker/split" "github.com/splitio/go-split-commons/v3/synchronizer/worker/split"
"github.com/splitio/go-split-commons/v2/tasks" "github.com/splitio/go-split-commons/v3/tasks"
"github.com/splitio/go-toolkit/v3/logging" "github.com/splitio/go-toolkit/v4/logging"
) )
const ( const (
@@ -63,7 +63,7 @@ type SplitFactory struct {
cfg *conf.SplitSdkConfig cfg *conf.SplitSdkConfig
impressionListener *impressionlistener.WrapperImpressionListener impressionListener *impressionlistener.WrapperImpressionListener
logger logging.LoggerInterface logger logging.LoggerInterface
syncManager *synchronizer.Manager syncManager synchronizer.Manager
impressionManager provisional.ImpressionManager impressionManager provisional.ImpressionManager
} }
@@ -372,17 +372,11 @@ func setupLocalhostFactory(
splitPeriod := cfg.TaskPeriods.SplitSync splitPeriod := cfg.TaskPeriods.SplitSync
readyChannel := make(chan int, 1) readyChannel := make(chan int, 1)
splitAPI := &service.SplitAPI{SplitFetcher: local.NewFileSplitFetcher(cfg.SplitFile, logger)}
syncManager, err := synchronizer.NewSynchronizerManager( syncManager, err := synchronizer.NewSynchronizerManager(
synchronizer.NewLocal( synchronizer.NewLocal(splitPeriod, splitAPI, splitStorage, logger),
splitPeriod,
&service.SplitAPI{
SplitFetcher: local.NewFileSplitFetcher(cfg.SplitFile, logger),
},
splitStorage,
logger,
),
logger, logger,
config.AdvancedConfig{}, config.AdvancedConfig{StreamingEnabled: false},
nil, nil,
splitStorage, splitStorage,
readyChannel, readyChannel,

View File

@@ -5,7 +5,7 @@ import (
"sync" "sync"
"github.com/splitio/go-client/v6/splitio/conf" "github.com/splitio/go-client/v6/splitio/conf"
"github.com/splitio/go-toolkit/v3/logging" "github.com/splitio/go-toolkit/v4/logging"
) )
// factoryInstances factory tracker instantiations // factoryInstances factory tracker instantiations

View File

@@ -9,9 +9,9 @@ import (
"strings" "strings"
"github.com/splitio/go-client/v6/splitio/engine/evaluator/impressionlabels" "github.com/splitio/go-client/v6/splitio/engine/evaluator/impressionlabels"
"github.com/splitio/go-split-commons/v2/storage" "github.com/splitio/go-split-commons/v3/storage"
"github.com/splitio/go-toolkit/v3/datastructures/set" "github.com/splitio/go-toolkit/v4/datastructures/set"
"github.com/splitio/go-toolkit/v3/logging" "github.com/splitio/go-toolkit/v4/logging"
) )
// InputValidation struct is responsible for cheking any input of treatment and // InputValidation struct is responsible for cheking any input of treatment and

View File

@@ -3,9 +3,9 @@ package client
import ( import (
"fmt" "fmt"
"github.com/splitio/go-split-commons/v2/dtos" "github.com/splitio/go-split-commons/v3/dtos"
"github.com/splitio/go-split-commons/v2/storage" "github.com/splitio/go-split-commons/v3/storage"
"github.com/splitio/go-toolkit/v3/logging" "github.com/splitio/go-toolkit/v4/logging"
) )
// SplitManager provides information of the currently stored splits // SplitManager provides information of the currently stored splits

View File

@@ -10,10 +10,10 @@ import (
"strings" "strings"
impressionlistener "github.com/splitio/go-client/v6/splitio/impressionListener" impressionlistener "github.com/splitio/go-client/v6/splitio/impressionListener"
"github.com/splitio/go-split-commons/v2/conf" "github.com/splitio/go-split-commons/v3/conf"
"github.com/splitio/go-toolkit/v3/datastructures/set" "github.com/splitio/go-toolkit/v4/datastructures/set"
"github.com/splitio/go-toolkit/v3/logging" "github.com/splitio/go-toolkit/v4/logging"
"github.com/splitio/go-toolkit/v3/nethelpers" "github.com/splitio/go-toolkit/v4/nethelpers"
) )
const ( const (

View File

@@ -3,7 +3,7 @@ package conf
import ( import (
"strings" "strings"
"github.com/splitio/go-split-commons/v2/conf" "github.com/splitio/go-split-commons/v3/conf"
) )
// NormalizeSDKConf compares against SDK Config to set defaults // NormalizeSDKConf compares against SDK Config to set defaults

View File

@@ -7,7 +7,7 @@ import (
"github.com/splitio/go-client/v6/splitio/engine/evaluator/impressionlabels" "github.com/splitio/go-client/v6/splitio/engine/evaluator/impressionlabels"
"github.com/splitio/go-client/v6/splitio/engine/grammar" "github.com/splitio/go-client/v6/splitio/engine/grammar"
"github.com/splitio/go-client/v6/splitio/engine/hash" "github.com/splitio/go-client/v6/splitio/engine/hash"
"github.com/splitio/go-toolkit/v3/logging" "github.com/splitio/go-toolkit/v4/logging"
) )
// Engine struct is responsible for cheking if any of the conditions of the split matches, // Engine struct is responsible for cheking if any of the conditions of the split matches,

View File

@@ -7,11 +7,11 @@ import (
"github.com/splitio/go-client/v6/splitio/engine" "github.com/splitio/go-client/v6/splitio/engine"
"github.com/splitio/go-client/v6/splitio/engine/evaluator/impressionlabels" "github.com/splitio/go-client/v6/splitio/engine/evaluator/impressionlabels"
"github.com/splitio/go-client/v6/splitio/engine/grammar" "github.com/splitio/go-client/v6/splitio/engine/grammar"
"github.com/splitio/go-split-commons/v2/dtos" "github.com/splitio/go-split-commons/v3/dtos"
"github.com/splitio/go-split-commons/v2/storage" "github.com/splitio/go-split-commons/v3/storage"
"github.com/splitio/go-toolkit/v3/injection" "github.com/splitio/go-toolkit/v4/injection"
"github.com/splitio/go-toolkit/v3/logging" "github.com/splitio/go-toolkit/v4/logging"
) )
const ( const (

View File

@@ -2,9 +2,9 @@ package grammar
import ( import (
"github.com/splitio/go-client/v6/splitio/engine/grammar/matchers" "github.com/splitio/go-client/v6/splitio/engine/grammar/matchers"
"github.com/splitio/go-split-commons/v2/dtos" "github.com/splitio/go-split-commons/v3/dtos"
"github.com/splitio/go-toolkit/v3/injection" "github.com/splitio/go-toolkit/v4/injection"
"github.com/splitio/go-toolkit/v3/logging" "github.com/splitio/go-toolkit/v4/logging"
) )
// Condition struct with added logic that wraps around a DTO // Condition struct with added logic that wraps around a DTO

View File

@@ -2,7 +2,7 @@ package matchers
import ( import (
"fmt" "fmt"
"github.com/splitio/go-toolkit/v3/datastructures/set" "github.com/splitio/go-toolkit/v4/datastructures/set"
"reflect" "reflect"
) )

View File

@@ -1,7 +1,7 @@
package matchers package matchers
import ( import (
"github.com/splitio/go-toolkit/v3/datastructures/set" "github.com/splitio/go-toolkit/v4/datastructures/set"
) )
// ContainsAnyOfSetMatcher matches if the set supplied to the getTreatment is a superset of the one in the split // ContainsAnyOfSetMatcher matches if the set supplied to the getTreatment is a superset of the one in the split

View File

@@ -1,7 +1,7 @@
package matchers package matchers
import ( import (
"github.com/splitio/go-toolkit/v3/datastructures/set" "github.com/splitio/go-toolkit/v4/datastructures/set"
) )
// EqualToSetMatcher matches if the set supplied to the getTreatment is equal to the one in the split // EqualToSetMatcher matches if the set supplied to the getTreatment is equal to the one in the split

View File

@@ -3,7 +3,7 @@ package matchers
import ( import (
"fmt" "fmt"
"github.com/splitio/go-split-commons/v2/storage" "github.com/splitio/go-split-commons/v3/storage"
) )
// InSegmentMatcher matches if the key passed is in the segment which the matcher was constructed with // InSegmentMatcher matches if the key passed is in the segment which the matcher was constructed with

View File

@@ -4,9 +4,9 @@ import (
"errors" "errors"
"fmt" "fmt"
"github.com/splitio/go-split-commons/v2/dtos" "github.com/splitio/go-split-commons/v3/dtos"
"github.com/splitio/go-toolkit/v3/injection" "github.com/splitio/go-toolkit/v4/injection"
"github.com/splitio/go-toolkit/v3/logging" "github.com/splitio/go-toolkit/v4/logging"
) )
const ( const (

View File

@@ -1,7 +1,7 @@
package matchers package matchers
import ( import (
"github.com/splitio/go-toolkit/v3/datastructures/set" "github.com/splitio/go-toolkit/v4/datastructures/set"
) )
// PartOfSetMatcher matches if the set supplied to the getTreatment is a subset of the one in the split // PartOfSetMatcher matches if the set supplied to the getTreatment is a subset of the one in the split

View File

@@ -1,7 +1,7 @@
package matchers package matchers
import ( import (
"github.com/splitio/go-toolkit/v3/datastructures/set" "github.com/splitio/go-toolkit/v4/datastructures/set"
) )
// WhitelistMatcher matches if the key received is present in the matcher's whitelist // WhitelistMatcher matches if the key received is present in the matcher's whitelist

View File

@@ -1,9 +1,9 @@
package grammar package grammar
import ( import (
"github.com/splitio/go-split-commons/v2/dtos" "github.com/splitio/go-split-commons/v3/dtos"
"github.com/splitio/go-toolkit/v3/injection" "github.com/splitio/go-toolkit/v4/injection"
"github.com/splitio/go-toolkit/v3/logging" "github.com/splitio/go-toolkit/v4/logging"
) )
// Split struct with added logic that wraps around a DTO // Split struct with added logic that wraps around a DTO

View File

@@ -1,7 +1,7 @@
package impressionlistener package impressionlistener
import ( import (
"github.com/splitio/go-split-commons/v2/dtos" "github.com/splitio/go-split-commons/v3/dtos"
) )
// ILObject struct to map entire data for listener // ILObject struct to map entire data for listener

View File

@@ -1,4 +1,4 @@
package splitio package splitio
// Version contains a string with the split sdk version // Version contains a string with the split sdk version
const Version = "6.0.1" const Version = "6.0.2"

View File

@@ -1,47 +0,0 @@
package push
import (
"fmt"
"github.com/splitio/go-toolkit/v3/common"
)
// IncomingEvent struct to process every kind of notification that comes from streaming
type IncomingEvent struct {
id *string
timestamp *int64
encoding *string
data *string
name *string
clientID *string
event string
channel *string
message *string
code *int
statusCode *int
href *string
}
func (i *IncomingEvent) String() string {
return fmt.Sprintf(`Incoming event [id="%s", ts=%d, enc="%s", data="%s", name="%s", client="%s", `+
`event="%s", channel="%s", code=%d, status=%d]`,
common.StringFromRef(i.id),
common.Int64FromRef(i.timestamp),
common.StringFromRef(i.encoding),
common.StringFromRef(i.data),
common.StringFromRef(i.name),
common.StringFromRef(i.clientID),
i.event,
common.StringFromRef(i.channel),
common.IntFromRef(i.code),
common.IntFromRef(i.statusCode))
}
// Metrics dto
type Metrics struct {
Publishers int `json:"publishers"`
}
// Occupancy dto
type Occupancy struct {
Data Metrics `json:"metrics"`
}

View File

@@ -1,89 +0,0 @@
package push
import (
"encoding/json"
"fmt"
"github.com/splitio/go-split-commons/v2/dtos"
"github.com/splitio/go-toolkit/v3/logging"
)
// EventHandler struct
type EventHandler struct {
keeper *Keeper
parser *NotificationParser
processor *Processor
logger logging.LoggerInterface
}
// NewEventHandler builds new EventHandler
func NewEventHandler(keeper *Keeper, parser *NotificationParser, processor *Processor, logger logging.LoggerInterface) *EventHandler {
return &EventHandler{
keeper: keeper,
parser: parser,
processor: processor,
logger: logger,
}
}
func (e *EventHandler) wrapOccupancy(incomingEvent IncomingEvent) *Occupancy {
if incomingEvent.data == nil {
return nil
}
var occupancy *Occupancy
err := json.Unmarshal([]byte(*incomingEvent.data), &occupancy)
if err != nil {
return nil
}
return occupancy
}
func (e *EventHandler) wrapUpdateEvent(incomingEvent IncomingEvent) *dtos.IncomingNotification {
if incomingEvent.data == nil {
return nil
}
var incomingNotification *dtos.IncomingNotification
err := json.Unmarshal([]byte(*incomingEvent.data), &incomingNotification)
if err != nil {
e.logger.Error("cannot parse data as IncomingNotification type")
return nil
}
incomingNotification.Channel = *incomingEvent.channel
return incomingNotification
}
// HandleIncomingMessage handles incoming message from streaming
func (e *EventHandler) HandleIncomingMessage(event map[string]interface{}) {
incomingEvent := e.parser.Parse(event)
switch incomingEvent.event {
case update:
e.logger.Debug("Update event received")
incomingNotification := e.wrapUpdateEvent(incomingEvent)
if incomingNotification == nil {
e.logger.Debug("Skipping incoming notification...")
return
}
e.logger.Debug("Incoming Notification:", incomingNotification)
err := e.processor.Process(*incomingNotification)
if err != nil {
e.logger.Debug("Could not process notification", err.Error())
return
}
case occupancy:
e.logger.Debug("Presence event received")
occupancy := e.wrapOccupancy(incomingEvent)
if occupancy == nil || incomingEvent.channel == nil {
e.logger.Debug("Skipping occupancy...")
return
}
e.keeper.UpdateManagers(*incomingEvent.channel, occupancy.Data.Publishers)
return
case errorType: // TODO: Update this when logic is fully defined
e.logger.Error(fmt.Sprintf("Error received: %+v", incomingEvent))
default:
e.logger.Debug(fmt.Sprintf("Unexpected incomingEvent: %+v", incomingEvent))
e.logger.Error("Unexpected type of event received")
}
}

View File

@@ -1,10 +0,0 @@
package push
// Manager interface for Push Manager
type Manager interface {
Start()
Stop()
StartWorkers()
StopWorkers()
IsRunning() bool
}

View File

@@ -1,98 +0,0 @@
package push
import (
"strings"
"sync"
)
const (
// PublisherNotPresent there are no publishers sending data
PublisherNotPresent = iota
// PublisherAvailable there are publishers running
PublisherAvailable
)
const (
prefix = "[?occupancy=metrics.publishers]"
)
// last struct for storing the last notification
type last struct {
manager string
timestamp int64
mutex *sync.RWMutex
}
// Keeper struct
type Keeper struct {
managers map[string]int
activeRegion string
last last
publishers chan<- int
mutex *sync.RWMutex
}
// NewKeeper creates new keeper
func NewKeeper(publishers chan int) *Keeper {
last := last{
mutex: &sync.RWMutex{},
}
return &Keeper{
managers: make(map[string]int),
activeRegion: "us-east-1",
mutex: &sync.RWMutex{},
publishers: publishers,
last: last,
}
}
func (k *Keeper) cleanManagerPrefix(manager string) string {
return strings.Replace(manager, prefix, "", -1)
}
// Publishers returns the quantity of publishers for a particular manager
func (k *Keeper) Publishers(manager string) *int {
k.mutex.RLock()
defer k.mutex.RUnlock()
publisher, ok := k.managers[manager]
if ok {
return &publisher
}
return nil
}
// UpdateManagers updates current manager count
func (k *Keeper) UpdateManagers(manager string, publishers int) {
parsedManager := k.cleanManagerPrefix(manager)
k.mutex.Lock()
defer k.mutex.Unlock()
k.managers[parsedManager] = publishers
isAvailable := false
for _, publishers := range k.managers {
if publishers > 0 {
isAvailable = true
break
}
}
if !isAvailable {
k.publishers <- PublisherNotPresent
return
}
k.publishers <- PublisherAvailable
}
// LastNotification return the latest notification saved
func (k *Keeper) LastNotification() (string, int64) {
k.last.mutex.RLock()
defer k.last.mutex.RUnlock()
return k.last.manager, k.last.timestamp
}
// UpdateLastNotification updates last message received
func (k *Keeper) UpdateLastNotification(manager string, timestamp int64) {
k.last.mutex.Lock()
defer k.last.mutex.Unlock()
k.last.manager = k.cleanManagerPrefix(manager)
k.last.timestamp = timestamp
}

View File

@@ -1,64 +0,0 @@
package push
import (
"github.com/splitio/go-toolkit/v3/common"
"github.com/splitio/go-toolkit/v3/logging"
)
const (
update = "update"
errorType = "error"
occupancy = "[meta]occupancy"
)
// NotificationParser struct
type NotificationParser struct {
logger logging.LoggerInterface
}
// NewNotificationParser creates notifcation parser
func NewNotificationParser(logger logging.LoggerInterface) *NotificationParser {
return &NotificationParser{
logger: logger,
}
}
// Parse parses incoming event from streaming
func (n *NotificationParser) Parse(event map[string]interface{}) IncomingEvent {
incomingEvent := IncomingEvent{
id: common.AsStringOrNil(event["id"]),
encoding: common.AsStringOrNil(event["encoding"]),
data: common.AsStringOrNil(event["data"]),
name: common.AsStringOrNil(event["name"]),
clientID: common.AsStringOrNil(event["clientId"]),
channel: common.AsStringOrNil(event["channel"]),
message: common.AsStringOrNil(event["message"]),
href: common.AsStringOrNil(event["href"]),
}
timestamp := common.AsFloat64OrNil(event["timestamp"])
if timestamp != nil {
incomingEvent.timestamp = common.Int64Ref(int64(*timestamp))
}
code := common.AsFloat64OrNil(event["code"])
if code != nil {
incomingEvent.code = common.IntRef(int(*code))
}
statusCode := common.AsFloat64OrNil(event["statusCode"])
if statusCode != nil {
incomingEvent.statusCode = common.IntRef(int(*statusCode))
}
if incomingEvent.code != nil && incomingEvent.statusCode != nil {
incomingEvent.event = errorType
return incomingEvent
}
if incomingEvent.name != nil && *incomingEvent.name == occupancy {
incomingEvent.event = occupancy
return incomingEvent
}
incomingEvent.event = update
return incomingEvent
}

View File

@@ -1,112 +0,0 @@
package push
import (
"errors"
"fmt"
"github.com/splitio/go-split-commons/v2/dtos"
"github.com/splitio/go-split-commons/v2/storage"
"github.com/splitio/go-toolkit/v3/logging"
)
const (
segmentQueueCheck = 5000
splitQueueCheck = 5000
streamingPausedType = "STREAMING_PAUSED"
streamingResumedType = "STREAMING_RESUMED"
streamingDisabledType = "STREAMING_DISABLED"
)
const (
// StreamingPaused The SDK should stop processing incoming UPDATE-type events
streamingPaused = iota
// StreamingResumed The SDK should resume processing UPDATE-type events (if not already)
streamingResumed
// StreamingDisabled The SDK should disable streaming completely and dont try to reconnect until the SDK is re-instantiated
streamingDisabled
)
// Processor struct for notification processor
type Processor struct {
segmentQueue chan dtos.SegmentChangeNotification
splitQueue chan dtos.SplitChangeNotification
splitStorage storage.SplitStorageProducer
controlStatus chan<- int
logger logging.LoggerInterface
}
// NewProcessor creates new processor
func NewProcessor(segmentQueue chan dtos.SegmentChangeNotification, splitQueue chan dtos.SplitChangeNotification, splitStorage storage.SplitStorageProducer, logger logging.LoggerInterface, controlStatus chan int) (*Processor, error) {
if cap(segmentQueue) < segmentQueueCheck {
return nil, errors.New("Small size of segmentQueue")
}
if cap(splitQueue) < splitQueueCheck {
return nil, errors.New("Small size of splitQueue")
}
if cap(controlStatus) < 1 {
return nil, errors.New("Small size for control chan")
}
return &Processor{
segmentQueue: segmentQueue,
splitQueue: splitQueue,
splitStorage: splitStorage,
controlStatus: controlStatus,
logger: logger,
}, nil
}
// Process takes an incoming notification and generates appropriate notifications for it.
func (p *Processor) Process(i dtos.IncomingNotification) error {
switch i.Type {
case dtos.SplitUpdate:
if i.ChangeNumber == nil {
return errors.New("ChangeNumber could not be nil, discarded")
}
splitUpdate := dtos.NewSplitChangeNotification(i.Channel, *i.ChangeNumber)
p.splitQueue <- splitUpdate
case dtos.SegmentUpdate:
if i.ChangeNumber == nil {
return errors.New("ChangeNumber could not be nil, discarded")
}
if i.SegmentName == nil {
return errors.New("SegmentName could not be nil, discarded")
}
segmentUpdate := dtos.NewSegmentChangeNotification(i.Channel, *i.ChangeNumber, *i.SegmentName)
p.segmentQueue <- segmentUpdate
case dtos.SplitKill:
if i.ChangeNumber == nil {
return errors.New("ChangeNumber could not be nil, discarded")
}
if i.SplitName == nil {
return errors.New("SplitName could not be nil, discarded")
}
if i.DefaultTreatment == nil {
return errors.New("DefaultTreatment could not be nil, discarded")
}
splitUpdate := dtos.NewSplitChangeNotification(i.Channel, *i.ChangeNumber)
p.splitStorage.KillLocally(*i.SplitName, *i.DefaultTreatment, *i.ChangeNumber)
p.splitQueue <- splitUpdate
case dtos.Control:
if i.ControlType == nil {
return errors.New("ControlType could not be nil, discarded")
}
control := dtos.NewControlNotification(i.Channel, *i.ControlType)
switch control.ControlType {
case streamingDisabledType:
p.logger.Debug("Received notification for disabling streaming")
p.controlStatus <- streamingDisabled
case streamingPausedType:
p.logger.Debug("Received notification for pausing streaming")
p.controlStatus <- streamingPaused
case streamingResumedType:
p.logger.Debug("Received notification for resuming streaming")
p.controlStatus <- streamingResumed
default:
p.logger.Debug(fmt.Sprintf("%s Unexpected type of Control Notification", control.ControlType))
}
default:
return fmt.Errorf("Unknown IncomingNotification type: %T", i)
}
return nil
}

View File

@@ -1,370 +0,0 @@
package push
import (
"errors"
"fmt"
"net/http"
"sync/atomic"
"time"
"github.com/splitio/go-split-commons/v2/conf"
"github.com/splitio/go-split-commons/v2/dtos"
"github.com/splitio/go-split-commons/v2/service"
"github.com/splitio/go-split-commons/v2/service/api/sse"
"github.com/splitio/go-split-commons/v2/storage"
"github.com/splitio/go-toolkit/v3/common"
"github.com/splitio/go-toolkit/v3/logging"
sseStatus "github.com/splitio/go-toolkit/v3/sse"
)
const (
resetTimer = 120
maxPeriod = 30 * time.Minute
)
const (
// Ready represents ready
Ready = iota
// PushIsDown there are no publishers for streaming
PushIsDown
// PushIsUp there are publishers presents
PushIsUp
// BackoffAuth backoff is running for authentication
BackoffAuth
// BackoffSSE backoff is running for connecting to stream
BackoffSSE
// TokenExpiration flag to restart push services
TokenExpiration
// StreamingPaused flag for pausing streaming
StreamingPaused
// StreamingResumed flag for resuming streaming
StreamingResumed
// StreamingDisabled flag for disabling streaming
StreamingDisabled
// Reconnect flag to reconnect
Reconnect
// NonRetriableError represents an error that will force switching to polling
NonRetriableError
)
// PushManager struct for managing push services
type PushManager struct {
authClient service.AuthClient
sseClient *sse.StreamingClient
segmentWorker *SegmentUpdateWorker
splitWorker *SplitUpdateWorker
eventHandler *EventHandler
managerStatus chan<- int
streamingStatus chan int
publishers chan int
logger logging.LoggerInterface
cancelAuthBackoff chan struct{}
cancelSSEBackoff chan struct{}
cancelTokenExpiration chan struct{}
cancelStreamingWatcher chan struct{}
control chan int
status atomic.Value
}
// NewPushManager creates new PushManager
func NewPushManager(
logger logging.LoggerInterface,
synchronizeSegmentHandler func(segmentName string, till *int64) error,
synchronizeSplitsHandler func(till *int64) error,
splitStorage storage.SplitStorage,
config *conf.AdvancedConfig,
managerStatus chan int,
authClient service.AuthClient,
) (Manager, error) {
splitQueue := make(chan dtos.SplitChangeNotification, config.SplitUpdateQueueSize)
segmentQueue := make(chan dtos.SegmentChangeNotification, config.SegmentUpdateQueueSize)
control := make(chan int, 1)
processor, err := NewProcessor(segmentQueue, splitQueue, splitStorage, logger, control)
if err != nil {
return nil, err
}
parser := NewNotificationParser(logger)
if parser == nil {
return nil, errors.New("Could not instantiate NotificationParser")
}
publishers := make(chan int, 1000)
keeper := NewKeeper(publishers)
if keeper == nil {
return nil, errors.New("Could not instantiate Keeper")
}
eventHandler := NewEventHandler(keeper, parser, processor, logger)
segmentWorker, err := NewSegmentUpdateWorker(segmentQueue, synchronizeSegmentHandler, logger)
if err != nil {
return nil, err
}
splitWorker, err := NewSplitUpdateWorker(splitQueue, synchronizeSplitsHandler, logger)
if err != nil {
return nil, err
}
streamingStatus := make(chan int, 1000)
status := atomic.Value{}
status.Store(Ready)
return &PushManager{
authClient: authClient,
sseClient: sse.NewStreamingClient(config, streamingStatus, logger),
segmentWorker: segmentWorker,
splitWorker: splitWorker,
managerStatus: managerStatus,
streamingStatus: streamingStatus,
eventHandler: eventHandler,
publishers: publishers,
logger: logger,
cancelAuthBackoff: make(chan struct{}, 1),
cancelSSEBackoff: make(chan struct{}, 1),
cancelTokenExpiration: make(chan struct{}, 1),
cancelStreamingWatcher: make(chan struct{}, 1),
control: control,
status: status,
}, nil
}
func (p *PushManager) cancelStreaming() {
p.logger.Error("Error, switching to polling")
p.managerStatus <- NonRetriableError
}
func (p *PushManager) performAuthentication(errResult chan error) *dtos.Token {
select {
case <-p.cancelAuthBackoff:
// Discarding previous msg
default:
}
tokenResult := make(chan *dtos.Token, 1)
cancelAuthBackoff := common.WithBackoffCancelling(1*time.Second, maxPeriod, func() bool {
token, err := p.authClient.Authenticate()
if err != nil {
errType, ok := err.(dtos.HTTPError)
if ok && errType.Code >= http.StatusInternalServerError {
p.managerStatus <- BackoffAuth
return false // It will continue retrying
}
errResult <- errors.New("Error authenticating")
return true
}
tokenResult <- token
return true // Result is OK, Stopping Here, no more backoff
})
defer cancelAuthBackoff()
select {
case token := <-tokenResult:
if !token.PushEnabled {
return nil
}
return token
case err := <-errResult:
p.logger.Error(err.Error())
return nil
case <-p.cancelAuthBackoff:
return nil
}
}
func (p *PushManager) connectToStreaming(errResult chan error, token string, channels []string) error {
select {
case <-p.cancelSSEBackoff:
// Discarding previous msg
default:
}
sseResult := make(chan struct{}, 1)
cancelSSEBackoff := common.WithBackoffCancelling(1*time.Second, maxPeriod, func() bool {
p.sseClient.ConnectStreaming(token, channels, p.eventHandler.HandleIncomingMessage)
status := <-p.streamingStatus
switch status {
case sseStatus.OK:
sseResult <- struct{}{}
return true
case sseStatus.ErrorInternal:
p.managerStatus <- BackoffSSE
return false // It will continue retrying
default:
errResult <- errors.New("Error connecting streaming")
return true
}
})
defer cancelSSEBackoff()
select {
case <-sseResult:
return nil
case err := <-errResult:
p.logger.Error(err.Error())
return err
case <-p.cancelSSEBackoff:
return nil
}
}
func (p *PushManager) fetchStreamingToken(errResult chan error) (string, []string, error) {
token := p.performAuthentication(errResult)
if token == nil {
return "", []string{}, errors.New("Could not perform authentication")
}
channels, err := token.ChannelList()
if err != nil {
return "", []string{}, errors.New("Could not perform authentication")
}
nextTokenExpiration, err := token.CalculateNextTokenExpiration()
if err != nil {
return "", []string{}, errors.New("Could not perform authentication")
}
go func() {
// Create timeout timer for calculating next token expiration
idleDuration := nextTokenExpiration
tokenExpirationTimer := time.NewTimer(idleDuration)
defer tokenExpirationTimer.Stop()
select {
case <-tokenExpirationTimer.C: // Timedout
p.logger.Info("Token expired")
p.managerStatus <- TokenExpiration
return
case <-p.cancelTokenExpiration:
return
}
}()
return token.Token, channels, nil
}
func (p *PushManager) streamingStatusWatcher() {
for {
select {
case status := <-p.streamingStatus: // Streaming SSE Status
switch status {
case sseStatus.ErrorKeepAlive: // On ConnectionTimedOut -> Reconnect
fallthrough
case sseStatus.ErrorInternal: // On Error >= 500 -> Reconnect
fallthrough
case sseStatus.ErrorReadingStream: // On IOF -> Reconnect
p.managerStatus <- Reconnect
default: // Whatever other errors -> Send Error to disconnect
p.cancelStreaming()
}
case publisherStatus := <-p.publishers: // Publisher Available/Not Available
switch publisherStatus {
case PublisherNotPresent:
if p.status.Load().(int) != StreamingPaused {
p.managerStatus <- PushIsDown
}
case PublisherAvailable:
if p.status.Load().(int) != StreamingPaused {
p.managerStatus <- PushIsUp
}
default:
p.logger.Debug(fmt.Sprintf("Unexpected publisher status received %d", publisherStatus))
}
case controlStatus := <-p.control:
switch controlStatus {
case streamingPaused:
p.logger.Debug("Received Pause Streaming Notification")
if p.status.Load().(int) != StreamingPaused {
p.logger.Info("Sending Pause Streaming")
p.status.Store(StreamingPaused)
p.managerStatus <- PushIsDown
}
case streamingResumed:
p.logger.Debug("Received Resume Streaming Notification")
if p.status.Load().(int) == StreamingPaused {
p.status.Store(StreamingResumed)
publishersAvailable := p.eventHandler.keeper.Publishers("control_pri")
if publishersAvailable != nil && *publishersAvailable > 0 {
p.logger.Info("Sending Resume Streaming")
p.managerStatus <- PushIsUp
}
}
case streamingDisabled:
p.logger.Info("Received Streaming Disabled Notification")
p.managerStatus <- StreamingDisabled
default:
p.logger.Debug(fmt.Sprintf("Unexpected control status received %d", controlStatus))
}
case <-p.cancelStreamingWatcher: // Stopping Watcher
return
}
}
}
func (p *PushManager) drainStatus() {
select {
case <-p.cancelStreamingWatcher: // Discarding previous msg
default:
}
select {
case <-p.cancelTokenExpiration: // Discarding previous token expiration
default:
}
}
// Start push services
func (p *PushManager) Start() {
if p.IsRunning() {
p.logger.Info("PushManager is already running, skipping Start")
return
}
p.drainStatus()
// errResult listener for fetching token and connecting to SSE
errResult := make(chan error, 1)
token, channels, err := p.fetchStreamingToken(errResult)
if err != nil {
p.cancelStreaming()
return
}
err = p.connectToStreaming(errResult, token, channels)
if err != nil {
p.cancelStreaming()
return
}
// Everything is good, starting workers
p.splitWorker.Start()
p.segmentWorker.Start()
// Sending Ready
p.managerStatus <- Ready
// Starting streaming status watcher, it will listen 1) errors in SSE, 2) publishers changes, 3) stop
go p.streamingStatusWatcher()
}
// Stop push services
func (p *PushManager) Stop() {
p.logger.Info("Stopping Push Services")
p.cancelAuthBackoff <- struct{}{}
p.cancelSSEBackoff <- struct{}{}
p.cancelTokenExpiration <- struct{}{}
p.cancelStreamingWatcher <- struct{}{}
if p.sseClient.IsRunning() {
p.sseClient.StopStreaming(true)
}
p.StopWorkers()
}
// IsRunning returns true if the services are running
func (p *PushManager) IsRunning() bool {
return p.sseClient.IsRunning() || p.splitWorker.IsRunning() || p.segmentWorker.IsRunning()
}
// StopWorkers stops workers
func (p *PushManager) StopWorkers() {
if p.splitWorker.IsRunning() {
p.splitWorker.Stop()
}
if p.segmentWorker.IsRunning() {
p.segmentWorker.Stop()
}
}
// StartWorkers starts workers
func (p *PushManager) StartWorkers() {
if !p.splitWorker.IsRunning() {
p.splitWorker.Start()
}
if !p.segmentWorker.IsRunning() {
p.segmentWorker.Start()
}
}

View File

@@ -1,76 +0,0 @@
package push
import (
"errors"
"fmt"
"sync"
"sync/atomic"
"github.com/splitio/go-split-commons/v2/dtos"
"github.com/splitio/go-toolkit/v3/logging"
)
// SegmentUpdateWorker struct
type SegmentUpdateWorker struct {
activeGoroutines *sync.WaitGroup
segmentQueue chan dtos.SegmentChangeNotification
handler func(segmentName string, till *int64) error
logger logging.LoggerInterface
stop chan struct{}
running atomic.Value
}
// NewSegmentUpdateWorker creates SegmentUpdateWorker
func NewSegmentUpdateWorker(segmentQueue chan dtos.SegmentChangeNotification, handler func(segmentName string, till *int64) error, logger logging.LoggerInterface) (*SegmentUpdateWorker, error) {
if cap(segmentQueue) < 5000 {
return nil, errors.New("")
}
running := atomic.Value{}
running.Store(false)
return &SegmentUpdateWorker{
segmentQueue: segmentQueue,
handler: handler,
logger: logger,
stop: make(chan struct{}, 1),
running: running,
}, nil
}
// Start starts worker
func (s *SegmentUpdateWorker) Start() {
s.logger.Debug("Started SegmentUpdateWorker")
if s.IsRunning() {
s.logger.Debug("Segment worker is already running")
return
}
s.running.Store(true)
go func() {
for {
select {
case segmentUpdate := <-s.segmentQueue:
s.logger.Debug("Received Segment update and proceding to perform fetch")
s.logger.Debug(fmt.Sprintf("SegmentName: %s\nChangeNumber: %d", segmentUpdate.SegmentName, &segmentUpdate.ChangeNumber))
err := s.handler(segmentUpdate.SegmentName, &segmentUpdate.ChangeNumber)
if err != nil {
s.logger.Error(err)
}
case <-s.stop:
return
}
}
}()
}
// Stop stops worker
func (s *SegmentUpdateWorker) Stop() {
if s.IsRunning() {
s.stop <- struct{}{}
s.running.Store(false)
}
}
// IsRunning indicates if worker is running or not
func (s *SegmentUpdateWorker) IsRunning() bool {
return s.running.Load().(bool)
}

View File

@@ -1,77 +0,0 @@
package push
import (
"errors"
"fmt"
"sync"
"sync/atomic"
"github.com/splitio/go-split-commons/v2/dtos"
"github.com/splitio/go-toolkit/v3/logging"
)
// SplitUpdateWorker struct
type SplitUpdateWorker struct {
activeGoroutines *sync.WaitGroup
splitQueue chan dtos.SplitChangeNotification
handler func(till *int64) error
logger logging.LoggerInterface
stop chan struct{}
running atomic.Value
}
// NewSplitUpdateWorker creates SplitUpdateWorker
func NewSplitUpdateWorker(splitQueue chan dtos.SplitChangeNotification, handler func(till *int64) error, logger logging.LoggerInterface) (*SplitUpdateWorker, error) {
if cap(splitQueue) < 5000 {
return nil, errors.New("")
}
running := atomic.Value{}
running.Store(false)
return &SplitUpdateWorker{
activeGoroutines: &sync.WaitGroup{},
splitQueue: splitQueue,
handler: handler,
logger: logger,
running: running,
stop: make(chan struct{}, 1),
}, nil
}
// Start starts worker
func (s *SplitUpdateWorker) Start() {
s.logger.Debug("Started SplitUpdateWorker")
if s.IsRunning() {
s.logger.Info("Split worker is already running")
return
}
s.running.Store(true)
go func() {
for {
select {
case splitUpdate := <-s.splitQueue:
s.logger.Debug("Received Split update and proceding to perform fetch")
s.logger.Debug(fmt.Sprintf("ChangeNumber: %d", splitUpdate.ChangeNumber))
err := s.handler(&splitUpdate.ChangeNumber)
if err != nil {
s.logger.Error(err)
}
case <-s.stop:
return
}
}
}()
}
// Stop stops worker
func (s *SplitUpdateWorker) Stop() {
if s.IsRunning() {
s.stop <- struct{}{}
s.running.Store(false)
}
}
// IsRunning indicates if worker is running or not
func (s *SplitUpdateWorker) IsRunning() bool {
return s.running.Load().(bool)
}

View File

@@ -1,127 +0,0 @@
package sse
import (
"strings"
"sync"
"sync/atomic"
"github.com/splitio/go-split-commons/v2/conf"
"github.com/splitio/go-toolkit/v3/logging"
"github.com/splitio/go-toolkit/v3/sse"
)
const (
version = "1.1"
keepAlive = 120
)
// StreamingClient struct
type StreamingClient struct {
mutex *sync.RWMutex
sseClient *sse.SSEClient
sseStatus chan int
streamingStatus chan<- int
running atomic.Value
logger logging.LoggerInterface
stopped chan struct{}
}
// NewStreamingClient creates new SSE Client
func NewStreamingClient(cfg *conf.AdvancedConfig, streamingStatus chan int, logger logging.LoggerInterface) *StreamingClient {
sseStatus := make(chan int, 1)
sseClient, _ := sse.NewSSEClient(cfg.StreamingServiceURL, sseStatus, keepAlive, logger)
running := atomic.Value{}
running.Store(false)
return &StreamingClient{
mutex: &sync.RWMutex{},
sseClient: sseClient,
sseStatus: sseStatus,
streamingStatus: streamingStatus,
logger: logger,
running: running,
stopped: make(chan struct{}, 1),
}
}
// ConnectStreaming connects to streaming
func (s *StreamingClient) ConnectStreaming(token string, channelList []string, handleIncomingMessage func(e map[string]interface{})) {
params := make(map[string]string)
params["channels"] = strings.Join(append(channelList), ",")
params["accessToken"] = token
params["v"] = version
httpHandlerExited := make(chan struct{}, 1)
go func() {
s.sseClient.Do(params, handleIncomingMessage)
httpHandlerExited <- struct{}{}
}()
// Consume remaining message in completion signaling channel if any:
select {
case <-s.stopped:
default:
}
select {
case <-s.sseStatus:
default:
}
go func() {
defer func() { // When this goroutine exits, StopStreaming is freed
select {
case s.stopped <- struct{}{}:
default:
}
}()
for {
select {
case <-httpHandlerExited:
return
case status := <-s.sseStatus:
switch status {
case sse.OK:
s.logger.Info("SSE OK")
s.running.Store(true)
s.streamingStatus <- sse.OK
case sse.ErrorConnectToStreaming:
s.logger.Error("Error connecting to streaming")
s.streamingStatus <- sse.ErrorConnectToStreaming
case sse.ErrorKeepAlive:
s.logger.Error("Connection timed out")
s.streamingStatus <- sse.ErrorKeepAlive
case sse.ErrorOnClientCreation:
s.logger.Error("Could not create client for streaming")
s.streamingStatus <- sse.ErrorOnClientCreation
case sse.ErrorReadingStream:
s.logger.Error("Error reading streaming buffer")
s.streamingStatus <- sse.ErrorReadingStream
case sse.ErrorRequestPerformed:
s.logger.Error("Error performing request when connect to stream service")
s.streamingStatus <- sse.ErrorRequestPerformed
case sse.ErrorInternal:
s.logger.Error("Internal Error when connect to stream service")
s.streamingStatus <- sse.ErrorInternal
default:
s.logger.Error("Unexpected error occured with streaming")
s.streamingStatus <- sse.ErrorUnexpected
}
}
}
}()
}
// StopStreaming stops streaming
func (s *StreamingClient) StopStreaming(blocking bool) {
s.sseClient.Shutdown()
s.logger.Info("Stopped streaming")
s.running.Store(false)
if blocking {
<-s.stopped
}
}
// IsRunning returns true if it's running
func (s *StreamingClient) IsRunning() bool {
return s.running.Load().(bool)
}

View File

@@ -1,12 +0,0 @@
package synchronizer
// Synchronizer interface for syncing data to and from splits servers
type Synchronizer interface {
SyncAll() error
SynchronizeSplits(till *int64) error
SynchronizeSegment(segmentName string, till *int64) error
StartPeriodicFetching()
StopPeriodicFetching()
StartPeriodicDataRecording()
StopPeriodicDataRecording()
}

View File

@@ -1,184 +0,0 @@
package synchronizer
import (
"errors"
"sync/atomic"
"github.com/splitio/go-split-commons/v2/conf"
"github.com/splitio/go-split-commons/v2/push"
"github.com/splitio/go-split-commons/v2/service"
"github.com/splitio/go-split-commons/v2/storage"
"github.com/splitio/go-toolkit/v3/logging"
)
const (
// Ready represents ready
Ready = iota
// StreamingReady ready
StreamingReady
// Error represents some error in SSE streaming
Error
)
const (
// Idle flags
Idle = iota
// Streaming flags
Streaming
// Polling flags
Polling
)
// Manager struct
type Manager struct {
synchronizer Synchronizer
logger logging.LoggerInterface
config conf.AdvancedConfig
pushManager push.Manager
managerStatus chan int
streamingStatus chan int
status atomic.Value
}
// NewSynchronizerManager creates new sync manager
func NewSynchronizerManager(
synchronizer Synchronizer,
logger logging.LoggerInterface,
config conf.AdvancedConfig,
authClient service.AuthClient,
splitStorage storage.SplitStorage,
managerStatus chan int,
) (*Manager, error) {
if managerStatus == nil || cap(managerStatus) < 1 {
return nil, errors.New("Status channel cannot be nil nor having capacity")
}
status := atomic.Value{}
status.Store(Idle)
manager := &Manager{
synchronizer: synchronizer,
logger: logger,
config: config,
managerStatus: managerStatus,
status: status,
}
if config.StreamingEnabled {
streamingStatus := make(chan int, 1000)
pushManager, err := push.NewPushManager(logger, synchronizer.SynchronizeSegment, synchronizer.SynchronizeSplits, splitStorage, &config, streamingStatus, authClient)
if err != nil {
return nil, err
}
manager.pushManager = pushManager
manager.streamingStatus = streamingStatus
}
return manager, nil
}
func (s *Manager) startPolling() {
s.status.Store(Polling)
s.pushManager.StopWorkers()
s.synchronizer.StartPeriodicFetching()
}
// IsRunning returns true if is in Streaming or Polling
func (s *Manager) IsRunning() bool {
return s.status.Load().(int) != Idle
}
// Start starts synchronization through Split
func (s *Manager) Start() {
if s.IsRunning() {
s.logger.Info("Manager is already running, skipping start")
return
}
select {
case <-s.managerStatus:
// Discarding previous status before starting
default:
}
err := s.synchronizer.SyncAll()
if err != nil {
s.managerStatus <- Error
return
}
s.logger.Debug("SyncAll Ready")
s.managerStatus <- Ready
s.synchronizer.StartPeriodicDataRecording()
if s.config.StreamingEnabled {
s.logger.Info("Start Streaming")
go s.pushManager.Start()
// Listens Streaming Status
for {
status := <-s.streamingStatus
switch status {
// Backoff is running -> start polling until auth is ok
case push.BackoffAuth:
fallthrough
// Backoff is running -> start polling until sse is connected
case push.BackoffSSE:
if s.status.Load().(int) != Polling {
s.logger.Info("Start periodic polling due backoff")
s.startPolling()
}
// SSE Streaming and workers are ready
case push.Ready:
// If Ready comes eventually when Backoff is done and polling is running
if s.status.Load().(int) == Polling {
s.synchronizer.StopPeriodicFetching()
}
s.logger.Info("SSE Streaming is ready")
s.status.Store(Streaming)
go s.synchronizer.SyncAll()
case push.StreamingDisabled:
fallthrough
// NonRetriableError occurs and it will switch to polling
case push.NonRetriableError:
s.pushManager.Stop()
s.logger.Info("Start periodic polling in Streaming")
s.startPolling()
return
// Publisher sends that there is no Notification Managers available
case push.PushIsDown:
// If streaming is already running, proceeding to stop workers
// and keeping SSE running
if s.status.Load().(int) == Streaming {
s.logger.Info("Start periodic polling in Streaming")
s.startPolling()
}
// Publisher sends that there are at least one Notification Manager available
case push.PushIsUp:
// If streaming is not already running, proceeding to start workers
if s.status.Load().(int) != Streaming {
s.logger.Info("Stop periodic polling")
s.pushManager.StartWorkers()
s.synchronizer.StopPeriodicFetching()
s.status.Store(Streaming)
go s.synchronizer.SyncAll()
}
// Reconnect received due error in streaming -> reconnecting
case push.Reconnect:
fallthrough
// Token expired -> reconnecting
case push.TokenExpiration:
s.pushManager.Stop()
go s.pushManager.Start()
}
}
} else {
s.logger.Info("Start periodic polling")
s.synchronizer.StartPeriodicFetching()
s.status.Store(Polling)
}
}
// Stop stop synchronizaation through Split
func (s *Manager) Stop() {
s.logger.Info("STOPPING MANAGER TASKS")
if s.pushManager != nil && s.pushManager.IsRunning() {
s.pushManager.Stop()
}
s.synchronizer.StopPeriodicFetching()
s.synchronizer.StopPeriodicDataRecording()
s.status.Store(Idle)
}

View File

@@ -1,8 +0,0 @@
package segment
// SegmentFetcher interface
type SegmentFetcher interface {
SynchronizeSegment(name string, till *int64) error
SynchronizeSegments() error
SegmentNames() []interface{}
}

View File

@@ -1,6 +0,0 @@
package split
// SplitFetcher interface
type SplitFetcher interface {
SynchronizeSplits(till *int64) error
}

View File

@@ -1,5 +1,6 @@
package dtos package dtos
/*
const ( const (
// SplitUpdate used when split is updated // SplitUpdate used when split is updated
SplitUpdate = "SPLIT_UPDATE" SplitUpdate = "SPLIT_UPDATE"
@@ -147,3 +148,4 @@ func NewSplitKillNotification(channelName string, changeNumber int64, defaultTre
SplitName: splitName, SplitName: splitName,
} }
} }
*/

View File

@@ -3,7 +3,7 @@ package provisional
import ( import (
"sync" "sync"
"github.com/splitio/go-split-commons/v2/util" "github.com/splitio/go-split-commons/v3/util"
) )
// Key struct for mapping each key to an amount // Key struct for mapping each key to an amount

View File

@@ -4,8 +4,8 @@ import (
"fmt" "fmt"
"strings" "strings"
"github.com/splitio/go-split-commons/v2/dtos" "github.com/splitio/go-split-commons/v3/dtos"
"github.com/splitio/go-toolkit/v3/provisional/hashing" "github.com/splitio/go-toolkit/v4/provisional/hashing"
) )
const hashKeyTemplate = "%s:%s:%s:%s:%d" const hashKeyTemplate = "%s:%s:%s:%s:%d"

View File

@@ -3,9 +3,9 @@ package provisional
import ( import (
"time" "time"
"github.com/splitio/go-split-commons/v2/conf" "github.com/splitio/go-split-commons/v3/conf"
"github.com/splitio/go-split-commons/v2/dtos" "github.com/splitio/go-split-commons/v3/dtos"
"github.com/splitio/go-split-commons/v2/util" "github.com/splitio/go-split-commons/v3/util"
) )
const lastSeenCacheSize = 500000 // cache up to 500k impression hashes const lastSeenCacheSize = 500000 // cache up to 500k impression hashes

View File

@@ -4,8 +4,8 @@ import (
"fmt" "fmt"
"sync" "sync"
"github.com/splitio/go-split-commons/v2/dtos" "github.com/splitio/go-split-commons/v3/dtos"
"github.com/splitio/go-toolkit/v3/provisional/int64cache" "github.com/splitio/go-toolkit/v4/provisional/int64cache"
) )
// ImpressionObserver is used to check wether an impression has been previously seen // ImpressionObserver is used to check wether an impression has been previously seen

View File

@@ -0,0 +1,13 @@
package push
// Borrowed synchronizer interface to break circular dependencies
type synchronizerInterface interface {
SyncAll(requestNoCache bool) error
SynchronizeSplits(till *int64, requestNoCache bool) error
LocalKill(splitName string, defaultTreatment string, changeNumber int64)
SynchronizeSegment(segmentName string, till *int64, requestNoCache bool) error
StartPeriodicFetching()
StopPeriodicFetching()
StartPeriodicDataRecording()
StopPeriodicDataRecording()
}

View File

@@ -0,0 +1,14 @@
package push
const (
workerStatusIdle = iota
workerStatusRunning
workerStatusShuttingDown
)
const (
pushManagerStatusIdle = iota
pushManagerStatusInitializing
pushManagerStatusRunning
pushManagerStatusShuttingDown
)

View File

@@ -0,0 +1,237 @@
package push
import (
"errors"
"fmt"
"net/http"
"sync"
"time"
"github.com/splitio/go-split-commons/v3/conf"
"github.com/splitio/go-split-commons/v3/dtos"
"github.com/splitio/go-split-commons/v3/service"
"github.com/splitio/go-split-commons/v3/service/api/sse"
"github.com/splitio/go-toolkit/v4/common"
"github.com/splitio/go-toolkit/v4/logging"
"github.com/splitio/go-toolkit/v4/struct/traits/lifecycle"
)
// Status update contants that will be propagated to the push manager's user
const (
StatusUp = iota
StatusDown
StatusRetryableError
StatusNonRetryableError
)
// ErrAlreadyRunning is the error to be returned when .Start() is called on an already running instance
var ErrAlreadyRunning = errors.New("push manager already running")
// ErrNotRunning is the error to be returned when .Stop() is called on a non-running instance
var ErrNotRunning = errors.New("push manager not running")
// Manager interface contains public methods for push manager
type Manager interface {
Start() error
Stop() error
StopWorkers()
StartWorkers()
}
// ManagerImpl implements the manager interface
type ManagerImpl struct {
parser NotificationParser
sseClient sse.StreamingClient
authAPI service.AuthClient
processor Processor
statusTracker StatusTracker
feedback FeedbackLoop
nextRefresh *time.Timer
refreshTokenMutex sync.Mutex
/*
running *gtSync.AtomicBool
status int32
shutdownWaiter chan struct{}
*/
lifecycle lifecycle.Manager
logger logging.LoggerInterface
}
// FeedbackLoop is a type alias for the type of chan that must be supplied for push status tobe propagated
type FeedbackLoop = chan<- int64
// NewManager constructs a new push manager
func NewManager(
logger logging.LoggerInterface,
synchronizer synchronizerInterface,
cfg *conf.AdvancedConfig,
feedbackLoop chan<- int64,
authAPI service.AuthClient,
) (*ManagerImpl, error) {
processor, err := NewProcessor(cfg.SplitUpdateQueueSize, cfg.SegmentUpdateQueueSize, synchronizer, logger)
if err != nil {
return nil, fmt.Errorf("error instantiating processor: %w", err)
}
statusTracker := NewStatusTracker(logger)
parser := &NotificationParserImpl{
logger: logger,
onSplitUpdate: processor.ProcessSplitChangeUpdate,
onSplitKill: processor.ProcessSplitKillUpdate,
onSegmentUpdate: processor.ProcessSegmentChangeUpdate,
onControlUpdate: statusTracker.HandleControl,
onOccupancyMesage: statusTracker.HandleOccupancy,
onAblyError: statusTracker.HandleAblyError,
}
manager := &ManagerImpl{
authAPI: authAPI,
sseClient: sse.NewStreamingClient(cfg, logger),
statusTracker: statusTracker,
feedback: feedbackLoop,
processor: processor,
parser: parser,
logger: logger,
}
manager.lifecycle.Setup()
return manager, nil
}
// Start initiates the authentication flow and if successful initiates a connection
func (m *ManagerImpl) Start() error {
if !m.lifecycle.BeginInitialization() {
return ErrAlreadyRunning
}
m.triggerConnectionFlow()
return nil
}
// Stop method stops the sse client and it's status monitoring goroutine
func (m *ManagerImpl) Stop() error {
if !m.lifecycle.BeginShutdown() {
return ErrNotRunning
}
m.statusTracker.NotifySSEShutdownExpected()
m.withRefreshTokenLock(func() {
if m.nextRefresh != nil {
m.nextRefresh.Stop()
}
})
m.StopWorkers()
m.sseClient.StopStreaming()
m.lifecycle.AwaitShutdownComplete()
return nil
}
// StartWorkers start the splits & segments workers
func (m *ManagerImpl) StartWorkers() {
m.processor.StartWorkers()
}
// StopWorkers stops the splits & segments workers
func (m *ManagerImpl) StopWorkers() {
m.processor.StopWorkers()
}
func (m *ManagerImpl) performAuthentication() (*dtos.Token, *int64) {
token, err := m.authAPI.Authenticate()
if err != nil {
if errType, ok := err.(dtos.HTTPError); ok {
if errType.Code >= http.StatusInternalServerError {
m.logger.Error(fmt.Sprintf("Error authenticating: %s", err.Error()))
return nil, common.Int64Ref(StatusRetryableError)
}
return nil, common.Int64Ref(StatusNonRetryableError) // 400, 401, etc
}
// Not an HTTP eerror, most likely a tcp/bad connection. Should retry
return nil, common.Int64Ref(StatusRetryableError)
}
if !token.PushEnabled {
return nil, common.Int64Ref(StatusNonRetryableError)
}
return token, nil
}
func (m *ManagerImpl) eventHandler(e sse.IncomingMessage) {
newStatus, err := m.parser.ParseAndForward(e)
if newStatus != nil {
m.feedback <- *newStatus
} else if err != nil {
m.logger.Error("error parsing message: ", err)
m.logger.Debug("failed message: ", e)
m.feedback <- StatusRetryableError
}
}
func (m *ManagerImpl) triggerConnectionFlow() {
token, status := m.performAuthentication()
if status != nil {
m.lifecycle.AbnormalShutdown()
defer m.lifecycle.ShutdownComplete()
m.feedback <- *status
return
}
tokenList, err := token.ChannelList()
if err != nil {
m.logger.Error("error parsing channel list: ", err)
m.lifecycle.AbnormalShutdown()
defer m.lifecycle.ShutdownComplete()
m.feedback <- StatusRetryableError
return
}
m.statusTracker.Reset()
sseStatus := make(chan int, 100)
m.sseClient.ConnectStreaming(token.Token, sseStatus, tokenList, m.eventHandler)
go func() {
defer m.lifecycle.ShutdownComplete()
if !m.lifecycle.InitializationComplete() {
return
}
for {
message := <-sseStatus
switch message {
case sse.StatusFirstEventOk:
when, err := token.CalculateNextTokenExpiration()
if err != nil || when <= 0 {
m.logger.Warning("Failed to calculate next token expiration time. Defaulting to 50 minutes")
when = 50 * time.Minute
}
m.withRefreshTokenLock(func() {
m.nextRefresh = time.AfterFunc(when, func() {
m.logger.Info("Refreshing SSE auth token.")
m.Stop()
m.Start()
})
})
m.feedback <- StatusUp
case sse.StatusConnectionFailed:
m.lifecycle.AbnormalShutdown()
m.logger.Error("SSE Connection failed")
m.feedback <- StatusRetryableError
return
case sse.StatusDisconnected:
m.logger.Debug("propagating sse disconnection event")
status := m.statusTracker.HandleDisconnection()
if status != nil { // connection ended unexpectedly
m.lifecycle.AbnormalShutdown()
m.feedback <- *status
}
return
case sse.StatusUnderlyingClientInUse:
m.lifecycle.AbnormalShutdown()
m.logger.Error("unexpected error in streaming. Switching to polling")
m.feedback <- StatusNonRetryableError
return
}
}
}()
}
func (m *ManagerImpl) withRefreshTokenLock(f func()) {
m.refreshTokenMutex.Lock()
defer m.refreshTokenMutex.Unlock()
f()
}

View File

@@ -0,0 +1,399 @@
package push
import (
"encoding/json"
"errors"
"fmt"
"strings"
"github.com/splitio/go-split-commons/v3/service/api/sse"
"github.com/splitio/go-toolkit/v4/logging"
)
// SSE event type constants
const (
SSEEventTypeSync = "sync"
SSEEventTypeMessage = "message"
SSEEventTypeError = "error"
)
// Message type constants
const (
MessageTypeUpdate = iota
MessageTypeControl
MessageTypeOccupancy
)
// Update type constants
const (
UpdateTypeSplitChange = "SPLIT_UPDATE"
UpdateTypeSplitKill = "SPLIT_KILL"
UpdateTypeSegmentChange = "SEGMENT_UPDATE"
UpdateTypeContol = "CONTROL"
)
// Control type constants
const (
ControlTypeStreamingEnabled = "STREAMING_ENABLED"
ControlTypeStreamingPaused = "STREAMING_PAUSED"
ControlTypeStreamingDisabled = "STREAMING_DISABLED"
)
const (
occupancuName = "[meta]occupancy"
occupancyPrefix = "[?occupancy=metrics.publishers]"
)
// ErrEmptyEvent indicates an event without message and event fields
var ErrEmptyEvent = errors.New("empty incoming event")
// NotificationParser interface
type NotificationParser interface {
ParseAndForward(sse.IncomingMessage) (*int64, error)
}
// NotificationParserImpl implementas the NotificationParser interface
type NotificationParserImpl struct {
logger logging.LoggerInterface
onSplitUpdate func(*SplitChangeUpdate) error
onSplitKill func(*SplitKillUpdate) error
onSegmentUpdate func(*SegmentChangeUpdate) error
onControlUpdate func(*ControlUpdate) *int64
onOccupancyMesage func(*OccupancyMessage) *int64
onAblyError func(*AblyError) *int64
}
// ParseAndForward accepts an incoming RAW event and returns a properly parsed & typed event
func (p *NotificationParserImpl) ParseAndForward(raw sse.IncomingMessage) (*int64, error) {
if raw.Event() == "" {
if raw.ID() == "" {
return nil, ErrEmptyEvent
}
// If it has ID its a sync event, which we're not using not. Ignore.
p.logger.Debug("Ignoring sync event")
return nil, nil
}
data := genericData{}
err := json.Unmarshal([]byte(raw.Data()), &data)
if err != nil {
return nil, fmt.Errorf("error parsing JSON: %w", err)
}
switch raw.Event() {
case SSEEventTypeError:
return p.parseError(&data)
case SSEEventTypeMessage:
return p.parseMessage(&data)
}
return nil, nil
}
func (p *NotificationParserImpl) parseError(data *genericData) (*int64, error) {
return p.onAblyError(&AblyError{
code: data.Code,
statusCode: data.StatusCode,
message: data.Message,
href: data.Href,
timestamp: data.Timestamp,
}), nil
}
func (p *NotificationParserImpl) parseMessage(data *genericData) (*int64, error) {
var nested genericMessageData
err := json.Unmarshal([]byte(data.Data), &nested)
if err != nil {
return nil, fmt.Errorf("error parsing message nested json data: %w", err)
}
if data.Name == occupancuName {
return p.onOccupancyMesage(&OccupancyMessage{
BaseMessage: BaseMessage{
timestamp: data.Timestamp,
channel: data.Channel,
},
publishers: nested.Metrics.Publishers,
}), nil
}
return p.parseUpdate(data, &nested)
}
func (p *NotificationParserImpl) parseUpdate(data *genericData, nested *genericMessageData) (*int64, error) {
if data == nil || nested == nil {
return nil, errors.New("parseUpdate: data cannot be nil")
}
base := BaseUpdate{
BaseMessage: BaseMessage{timestamp: data.Timestamp, channel: data.Channel},
changeNumber: nested.ChangeNumber,
}
switch nested.Type {
case UpdateTypeSplitChange:
return nil, p.onSplitUpdate(&SplitChangeUpdate{BaseUpdate: base})
case UpdateTypeSplitKill:
return nil, p.onSplitKill(&SplitKillUpdate{BaseUpdate: base, splitName: nested.SplitName, defaultTreatment: nested.DefaultTreatment})
case UpdateTypeSegmentChange:
return nil, p.onSegmentUpdate(&SegmentChangeUpdate{BaseUpdate: base, segmentName: nested.SegmentName})
case UpdateTypeContol:
return p.onControlUpdate(&ControlUpdate{BaseMessage: base.BaseMessage, controlType: nested.ControlType}), nil
default:
// TODO: log full event in debug mode
return nil, fmt.Errorf("invalid update type: %s", nested.Type)
}
}
// Event basic interface
type Event interface {
fmt.Stringer
EventType() string
Timestamp() int64
}
// SSESyncEvent represents an SSE Sync event with only id (used for resuming connections)
type SSESyncEvent struct {
id string
timestamp int64
}
// EventType always returns SSEEventTypeSync for SSESyncEvents
func (e *SSESyncEvent) EventType() string { return SSEEventTypeSync }
// Timestamp returns the timestamp of the event parsing
func (e *SSESyncEvent) Timestamp() int64 { return e.timestamp }
// String returns the string represenation of the event
func (e *SSESyncEvent) String() string {
return fmt.Sprintf("SSESync(id=%s,timestamp=%d)", e.id, e.timestamp)
}
// AblyError struct
type AblyError struct {
code int
statusCode int
message string
href string
timestamp int64
}
// EventType always returns SSEEventTypeError for AblyError
func (a *AblyError) EventType() string { return SSEEventTypeError }
// Code returns the error code
func (a *AblyError) Code() int { return a.code }
// StatusCode returns the status code
func (a *AblyError) StatusCode() int { return a.statusCode }
// Message returns the error message
func (a *AblyError) Message() string { return a.message }
// Href returns the documentation link
func (a *AblyError) Href() string { return a.href }
// Timestamp returns the error timestamp
func (a *AblyError) Timestamp() int64 { return a.timestamp }
// IsRetryable returns whether the error is recoverable via a push subsystem restart
func (a *AblyError) IsRetryable() bool { return a.code >= 40140 && a.code <= 40149 }
// String returns the string representation of the ably error
func (a *AblyError) String() string {
return fmt.Sprintf("AblyError(code=%d,statusCode=%d,message=%s,timestamp=%d,isRetryable=%t)",
a.code, a.statusCode, a.message, a.timestamp, a.IsRetryable())
}
// Message basic interface
type Message interface {
Event
MessageType() int64
Channel() string
}
// BaseMessage contains the basic message-specific fields and methods
type BaseMessage struct {
timestamp int64
channel string
}
// EventType always returns SSEEventTypeMessage for BaseMessage and embedding types
func (m *BaseMessage) EventType() string { return SSEEventTypeMessage }
// Timestamp returns the timestamp of the message reception
func (m *BaseMessage) Timestamp() int64 { return m.timestamp }
// Channel returns which channel the message was received in
func (m *BaseMessage) Channel() string { return m.channel }
// OccupancyMessage contains fields & methods related to ocupancy messages
type OccupancyMessage struct {
BaseMessage
publishers int64
}
// MessageType always returns MessageTypeOccupancy for Occupancy messages
func (o *OccupancyMessage) MessageType() int64 { return MessageTypeOccupancy }
// ChannelWithoutPrefix returns the original channel namem without the metadata prefix
func (o *OccupancyMessage) ChannelWithoutPrefix() string {
return strings.Replace(o.Channel(), occupancyPrefix, "", 1)
}
// Publishers returbs the amount of publishers in the current channel
func (o *OccupancyMessage) Publishers() int64 {
return o.publishers
}
// Strings returns the string representation of an occupancy message
func (o *OccupancyMessage) String() string {
return fmt.Sprintf("Occupancy(channel=%s,publishers=%d,timestamp=%d)",
o.Channel(), o.publishers, o.Timestamp())
}
// Update basic interface
type Update interface {
Message
UpdateType() string
ChangeNumber() int64
}
// BaseUpdate contains fields & methods related to update-based messages
type BaseUpdate struct {
BaseMessage
changeNumber int64
}
// MessageType alwats returns MessageType for Update messages
func (b *BaseUpdate) MessageType() int64 { return MessageTypeUpdate }
// ChangeNumber returns the changeNumber of the update
func (b *BaseUpdate) ChangeNumber() int64 { return b.changeNumber }
// SplitChangeUpdate represents a SplitChange notification generated in the split servers
type SplitChangeUpdate struct {
BaseUpdate
}
// UpdateType always returns UpdateTypeSplitChange for SplitKillUpdate messages
func (u *SplitChangeUpdate) UpdateType() string { return UpdateTypeSplitChange }
// String returns the String representation of a split change notification
func (u *SplitChangeUpdate) String() string {
return fmt.Sprintf("SplitChange(channel=%s,changeNumber=%d,timestamp=%d)",
u.Channel(), u.ChangeNumber(), u.Timestamp())
}
// SplitKillUpdate represents a SplitKill notification generated in the split servers
type SplitKillUpdate struct {
BaseUpdate
splitName string
defaultTreatment string
}
// UpdateType always returns UpdateTypeSplitKill for SplitKillUpdate messages
func (u *SplitKillUpdate) UpdateType() string { return UpdateTypeSplitKill }
// SplitName returns the name of the killed split
func (u *SplitKillUpdate) SplitName() string { return u.splitName }
// DefaultTreatment returns the last default treatment seen in the split servers for this split
func (u *SplitKillUpdate) DefaultTreatment() string { return u.defaultTreatment }
// ToSplitChangeUpdate Maps this kill notification to a split change one
func (u *SplitKillUpdate) ToSplitChangeUpdate() *SplitChangeUpdate {
return &SplitChangeUpdate{BaseUpdate: u.BaseUpdate}
}
// String returns the string representation of this update
func (u *SplitKillUpdate) String() string {
return fmt.Sprintf("SplitKill(channel=%s,changeNumber=%d,splitName=%s,defaultTreatment=%s,timestamp=%d)",
u.Channel(), u.ChangeNumber(), u.SplitName(), u.DefaultTreatment(), u.Timestamp())
}
// SegmentChangeUpdate represents a segment change notification generated in the split servers.
type SegmentChangeUpdate struct {
BaseUpdate
segmentName string
}
// UpdateType is always UpdateTypeSegmentChange for Segmet Updates
func (u *SegmentChangeUpdate) UpdateType() string { return UpdateTypeSegmentChange }
// SegmentName returns the name of the updated segment
func (u *SegmentChangeUpdate) SegmentName() string { return u.segmentName }
// String returns the string representation of a segment update notification
func (u *SegmentChangeUpdate) String() string {
return fmt.Sprintf("SegmentChange(channel=%s,changeNumber=%d,segmentName=%s,timestamp=%d",
u.Channel(), u.ChangeNumber(), u.segmentName, u.Timestamp())
}
// ControlUpdate represents a control notification generated by the split push subsystem
type ControlUpdate struct {
BaseMessage
controlType string
}
// MessageType always returns MessageTypeControl for Control messages
func (u *ControlUpdate) MessageType() int64 { return MessageTypeControl }
// ControlType returns the type of control notification received
func (u *ControlUpdate) ControlType() string { return u.controlType }
// String returns a string representation of this notification
func (u *ControlUpdate) String() string {
return fmt.Sprintf("Control(channel=%s,type=%s,timestamp=%d)",
u.Channel(), u.controlType, u.Timestamp())
}
type genericData struct {
// Error associated data
Code int `json:"code"`
StatusCode int `json:"statusCode"`
Message string `json:"message"`
Href string `json:"href"`
ClientID string `json:"clientId"`
ID string `json:"id"`
Name string `json:"name"`
Timestamp int64 `json:"timestamp"`
Encoding string `json:"encoding"`
Channel string `json:"channel"`
Data string `json:"data"`
//"id":"tO4rXGE4CX:0:0","timestamp":1612897630627,"encoding":"json","channel":"[?occupancy=metrics.publishers]control_sec","data":"{\"metrics\":{\"publishers\":0}}","name":"[meta]occupancy"}
}
type metrics struct {
Publishers int64 `json:"publishers"`
}
type genericMessageData struct {
Metrics metrics `json:"metrics"`
Type string `json:"type"`
ChangeNumber int64 `json:"changeNumber"`
SplitName string `json:"splitName"`
DefaultTreatment string `json:"defaultTreatment"`
SegmentName string `json:"segmentName"`
ControlType string `json:"controlType"`
// {\"type\":\"SPLIT_UPDATE\",\"changeNumber\":1612909342671}"}
}
// Compile-type assertions of interface requirements
var _ Event = &AblyError{}
var _ Message = &OccupancyMessage{}
var _ Message = &SplitChangeUpdate{}
var _ Message = &SplitKillUpdate{}
var _ Message = &SegmentChangeUpdate{}
var _ Message = &ControlUpdate{}
var _ Update = &SplitChangeUpdate{}
var _ Update = &SplitKillUpdate{}
var _ Update = &SegmentChangeUpdate{}

View File

@@ -0,0 +1,107 @@
package push
import (
"errors"
"fmt"
"github.com/splitio/go-toolkit/v4/logging"
)
const (
splitQueueMinSize = 5000
segmentQueueMinSize = 5000
)
// Processor provides the interface for an update-message processor
type Processor interface {
ProcessSplitChangeUpdate(update *SplitChangeUpdate) error
ProcessSplitKillUpdate(update *SplitKillUpdate) error
ProcessSegmentChangeUpdate(update *SegmentChangeUpdate) error
StartWorkers()
StopWorkers()
}
// ProcessorImpl struct for notification processor
type ProcessorImpl struct {
segmentQueue chan SegmentChangeUpdate
splitQueue chan SplitChangeUpdate
splitWorker *SplitUpdateWorker
segmentWorker *SegmentUpdateWorker
synchronizer synchronizerInterface
logger logging.LoggerInterface
}
// NewProcessor creates new processor
func NewProcessor(
splitQueueSize int64,
segmentQueueSize int64,
synchronizer synchronizerInterface,
logger logging.LoggerInterface,
) (*ProcessorImpl, error) {
if segmentQueueSize < segmentQueueMinSize {
return nil, errors.New("Small size of segmentQueue")
}
if splitQueueSize < splitQueueMinSize {
return nil, errors.New("Small size of splitQueue")
}
splitQueue := make(chan SplitChangeUpdate, splitQueueSize)
splitWorker, err := NewSplitUpdateWorker(splitQueue, synchronizer, logger)
if err != nil {
return nil, fmt.Errorf("error instantiating split worker: %w", err)
}
segmentQueue := make(chan SegmentChangeUpdate, segmentQueueSize)
segmentWorker, err := NewSegmentUpdateWorker(segmentQueue, synchronizer, logger)
if err != nil {
return nil, fmt.Errorf("error instantiating split worker: %w", err)
}
return &ProcessorImpl{
splitQueue: splitQueue,
splitWorker: splitWorker,
segmentQueue: segmentQueue,
segmentWorker: segmentWorker,
synchronizer: synchronizer,
logger: logger,
}, nil
}
// ProcessSplitChangeUpdate accepts a split change notifications and schedules a fetch
func (p *ProcessorImpl) ProcessSplitChangeUpdate(update *SplitChangeUpdate) error {
if update == nil {
return errors.New("split change update cannot be nil")
}
p.splitQueue <- *update
return nil
}
// ProcessSplitKillUpdate accepts a split kill notification, issues a local kill and schedules a fetch
func (p *ProcessorImpl) ProcessSplitKillUpdate(update *SplitKillUpdate) error {
if update == nil {
return errors.New("split change update cannot be nil")
}
p.synchronizer.LocalKill(update.SplitName(), update.DefaultTreatment(), update.ChangeNumber())
return p.ProcessSplitChangeUpdate(update.ToSplitChangeUpdate())
}
// ProcessSegmentChangeUpdate accepts a segment change notification and schedules a fetch
func (p *ProcessorImpl) ProcessSegmentChangeUpdate(update *SegmentChangeUpdate) error {
if update == nil {
return errors.New("split change update cannot be nil")
}
p.segmentQueue <- *update
return nil
}
// StartWorkers enables split & segments workers
func (p *ProcessorImpl) StartWorkers() {
p.splitWorker.Start()
p.segmentWorker.Start()
}
// StopWorkers pauses split & segments workers
func (p *ProcessorImpl) StopWorkers() {
p.splitWorker.Stop()
p.segmentWorker.Stop()
}

View File

@@ -0,0 +1,82 @@
package push
import (
"errors"
"fmt"
"sync/atomic"
"github.com/splitio/go-toolkit/v4/common"
"github.com/splitio/go-toolkit/v4/logging"
"github.com/splitio/go-toolkit/v4/struct/traits/lifecycle"
)
// SegmentUpdateWorker struct
type SegmentUpdateWorker struct {
segmentQueue chan SegmentChangeUpdate
sync synchronizerInterface
logger logging.LoggerInterface
lifecycle lifecycle.Manager
}
// NewSegmentUpdateWorker creates SegmentUpdateWorker
func NewSegmentUpdateWorker(
segmentQueue chan SegmentChangeUpdate,
synchronizer synchronizerInterface,
logger logging.LoggerInterface,
) (*SegmentUpdateWorker, error) {
if cap(segmentQueue) < 5000 {
return nil, errors.New("")
}
running := atomic.Value{}
running.Store(false)
worker := &SegmentUpdateWorker{
segmentQueue: segmentQueue,
sync: synchronizer,
logger: logger,
}
worker.lifecycle.Setup()
return worker, nil
}
// Start starts worker
func (s *SegmentUpdateWorker) Start() {
if !s.lifecycle.BeginInitialization() {
s.logger.Info("Segment worker is already running")
return
}
go func() {
if !s.lifecycle.InitializationComplete() {
return
}
defer s.lifecycle.ShutdownComplete()
for {
select {
case segmentUpdate := <-s.segmentQueue:
s.logger.Debug("Received Segment update and proceding to perform fetch")
s.logger.Debug(fmt.Sprintf("SegmentName: %s\nChangeNumber: %d", segmentUpdate.SegmentName(), segmentUpdate.ChangeNumber()))
err := s.sync.SynchronizeSegment(segmentUpdate.SegmentName(), common.Int64Ref(segmentUpdate.ChangeNumber()), true)
if err != nil {
s.logger.Error(err)
}
case <-s.lifecycle.ShutdownRequested():
return
}
}
}()
}
// Stop stops worker
func (s *SegmentUpdateWorker) Stop() {
if !s.lifecycle.BeginShutdown() {
s.logger.Debug("Split worker not runnning. Ignoring.")
return
}
s.lifecycle.AwaitShutdownComplete()
}
// IsRunning indicates if worker is running or not
func (s *SegmentUpdateWorker) IsRunning() bool {
return s.lifecycle.IsRunning()
}

View File

@@ -0,0 +1,80 @@
package push
import (
"errors"
"fmt"
"github.com/splitio/go-toolkit/v4/common"
"github.com/splitio/go-toolkit/v4/logging"
"github.com/splitio/go-toolkit/v4/struct/traits/lifecycle"
)
// SplitUpdateWorker struct
type SplitUpdateWorker struct {
splitQueue chan SplitChangeUpdate
sync synchronizerInterface
logger logging.LoggerInterface
lifecycle lifecycle.Manager
}
// NewSplitUpdateWorker creates SplitUpdateWorker
func NewSplitUpdateWorker(
splitQueue chan SplitChangeUpdate,
synchronizer synchronizerInterface,
logger logging.LoggerInterface,
) (*SplitUpdateWorker, error) {
if cap(splitQueue) < 5000 {
return nil, errors.New("")
}
worker := &SplitUpdateWorker{
splitQueue: splitQueue,
sync: synchronizer,
logger: logger,
}
worker.lifecycle.Setup()
return worker, nil
}
// Start starts worker
func (s *SplitUpdateWorker) Start() {
if !s.lifecycle.BeginInitialization() {
s.logger.Info("Split worker is already running")
return
}
s.logger.Debug("Started SplitUpdateWorker")
go func() {
defer s.lifecycle.ShutdownComplete()
if !s.lifecycle.InitializationComplete() {
return
}
for {
select {
case splitUpdate := <-s.splitQueue:
s.logger.Debug("Received Split update and proceding to perform fetch")
s.logger.Debug(fmt.Sprintf("ChangeNumber: %d", splitUpdate.ChangeNumber()))
err := s.sync.SynchronizeSplits(common.Int64Ref(splitUpdate.ChangeNumber()), true)
if err != nil {
s.logger.Error(err)
}
case <-s.lifecycle.ShutdownRequested():
return
}
}
}()
}
// Stop stops worker
func (s *SplitUpdateWorker) Stop() {
if !s.lifecycle.BeginShutdown() {
s.logger.Debug("Split worker not runnning. Ignoring.")
return
}
s.lifecycle.AwaitShutdownComplete()
}
// IsRunning indicates if worker is running or not
func (s *SplitUpdateWorker) IsRunning() bool {
return s.lifecycle.IsRunning()
}

View File

@@ -0,0 +1,158 @@
package push
import (
"fmt"
"sync"
"github.com/splitio/go-toolkit/v4/common"
"github.com/splitio/go-toolkit/v4/logging"
)
// StatusTracker keeps track of the status of the push subsystem and generates appropriate status change notifications.
type StatusTracker interface {
HandleOccupancy(*OccupancyMessage) *int64
HandleControl(*ControlUpdate) *int64
HandleAblyError(*AblyError) *int64
HandleDisconnection() *int64
NotifySSEShutdownExpected()
Reset()
}
// StatusTrackerImpl is a concrete implementation of the StatusTracker interface
type StatusTrackerImpl struct {
logger logging.LoggerInterface
mutex sync.Mutex
occupancy map[string]int64
lastControlTimestamp int64
lastOccupancyTimestamp int64
lastControlMessage string
lastStatusPropagated int64
shutdownExpected bool
}
// NotifySSEShutdownExpected should be called when we are forcefully closing the SSE client
func (p *StatusTrackerImpl) NotifySSEShutdownExpected() {
p.mutex.Lock()
defer p.mutex.Unlock()
p.shutdownExpected = true
}
// Reset should be called on initialization and when the a new connection is being established (to start from scratch)
func (p *StatusTrackerImpl) Reset() {
p.mutex.Lock()
defer p.mutex.Unlock()
p.occupancy = map[string]int64{"control_pri": 2, "control_sec": 2}
p.lastControlMessage = ControlTypeStreamingEnabled
p.lastStatusPropagated = StatusUp
p.shutdownExpected = false
}
// HandleOccupancy should be called for every occupancy notification received
func (p *StatusTrackerImpl) HandleOccupancy(message *OccupancyMessage) (newStatus *int64) {
p.mutex.Lock()
defer p.mutex.Unlock()
if p.shutdownExpected {
return nil // we don't care about occupancy if we're disconnecting
}
channel := message.ChannelWithoutPrefix()
if _, ok := p.occupancy[channel]; !ok {
p.logger.Warning(fmt.Sprintf("received occupancy on non-registered channel '%s'. Ignoring", channel))
return nil
}
p.lastOccupancyTimestamp = message.Timestamp()
p.occupancy[channel] = message.Publishers()
return p.updateStatus()
}
// HandleAblyError should be called whenever an ably error is received
func (p *StatusTrackerImpl) HandleAblyError(errorEvent *AblyError) (newStatus *int64) {
p.mutex.Lock()
defer p.mutex.Unlock()
if p.shutdownExpected {
return nil // we don't care about occupancy if we're disconnecting
}
// Regardless of whether the error is retryable or not, we're going to close the connection
p.shutdownExpected = true
if errorEvent.IsRetryable() {
p.logger.Info("Received retryable error message. Restarting SSE connection with backoff")
return p.propagateStatus(StatusRetryableError)
}
p.logger.Info("Received non-retryable error message. Disabling streaming")
return p.propagateStatus(StatusNonRetryableError)
}
// HandleControl should be called whenever a control notification is received
func (p *StatusTrackerImpl) HandleControl(controlUpdate *ControlUpdate) *int64 {
p.mutex.Lock()
defer p.mutex.Unlock()
if p.shutdownExpected {
return nil // we don't care about occupancy if we're disconnecting
}
if p.lastControlTimestamp > controlUpdate.timestamp {
p.logger.Warning("Received an old control update. Ignoring")
return nil
}
p.lastControlMessage = controlUpdate.controlType
p.lastControlTimestamp = controlUpdate.timestamp
return p.updateStatus()
}
// HandleDisconnection should be called whenver the SSE client gets disconnected
func (p *StatusTrackerImpl) HandleDisconnection() *int64 {
p.mutex.Lock()
defer p.mutex.Unlock()
if !p.shutdownExpected {
return p.propagateStatus(StatusRetryableError)
}
return nil
}
// NewStatusTracker returns a new StatusTracker
func NewStatusTracker(logger logging.LoggerInterface) *StatusTrackerImpl {
tracker := &StatusTrackerImpl{logger: logger}
tracker.Reset()
return tracker
}
func (p *StatusTrackerImpl) occupancyOk() bool {
for _, v := range p.occupancy {
if v > 0 {
return true
}
}
return false
}
func (p *StatusTrackerImpl) updateStatus() *int64 {
if p.lastStatusPropagated == StatusUp {
if !p.occupancyOk() || p.lastControlMessage == ControlTypeStreamingPaused {
return p.propagateStatus(StatusDown)
}
if p.lastControlMessage == ControlTypeStreamingDisabled {
return p.propagateStatus(StatusNonRetryableError)
}
}
if p.lastStatusPropagated == StatusDown {
if p.occupancyOk() && p.lastControlMessage == ControlTypeStreamingEnabled {
return p.propagateStatus(StatusUp)
}
if p.lastControlMessage == ControlTypeStreamingDisabled {
return p.propagateStatus(StatusNonRetryableError)
}
}
return nil
}
func (p *StatusTrackerImpl) propagateStatus(newStatus int64) *int64 {
p.lastStatusPropagated = newStatus
return common.Int64Ref(newStatus)
}
var _ StatusTracker = &StatusTrackerImpl{}

View File

@@ -3,9 +3,9 @@ package api
import ( import (
"encoding/json" "encoding/json"
"github.com/splitio/go-split-commons/v2/conf" "github.com/splitio/go-split-commons/v3/conf"
"github.com/splitio/go-split-commons/v2/dtos" "github.com/splitio/go-split-commons/v3/dtos"
"github.com/splitio/go-toolkit/v3/logging" "github.com/splitio/go-toolkit/v4/logging"
) )
// AuthAPIClient struct is responsible for authenticating client for push services // AuthAPIClient struct is responsible for authenticating client for push services
@@ -24,7 +24,7 @@ func NewAuthAPIClient(apikey string, cfg conf.AdvancedConfig, logger logging.Log
// Authenticate performs authentication for push services // Authenticate performs authentication for push services
func (a *AuthAPIClient) Authenticate() (*dtos.Token, error) { func (a *AuthAPIClient) Authenticate() (*dtos.Token, error) {
raw, err := a.client.Get("/api/auth") raw, err := a.client.Get("/api/auth", map[string]string{CacheControlHeader: CacheControlNoCache})
if err != nil { if err != nil {
a.logger.Error("Error while authenticating for streaming", err) a.logger.Error("Error while authenticating for streaming", err)
return nil, err return nil, err

View File

@@ -9,14 +9,20 @@ import (
"net/http" "net/http"
"time" "time"
"github.com/splitio/go-split-commons/v2/conf" "github.com/splitio/go-split-commons/v3/conf"
"github.com/splitio/go-split-commons/v2/dtos" "github.com/splitio/go-split-commons/v3/dtos"
"github.com/splitio/go-toolkit/v3/logging" "github.com/splitio/go-toolkit/v4/logging"
)
// Cache control header constants
const (
CacheControlHeader = "Cache-Control"
CacheControlNoCache = "no-cache"
) )
// Client interface for HTTPClient // Client interface for HTTPClient
type Client interface { type Client interface {
Get(service string) ([]byte, error) Get(service string, headers map[string]string) ([]byte, error)
Post(service string, body []byte, headers map[string]string) error Post(service string, body []byte, headers map[string]string) error
} }
@@ -51,7 +57,7 @@ func NewHTTPClient(
} }
// Get method is a get call to an url // Get method is a get call to an url
func (c *HTTPClient) Get(service string) ([]byte, error) { func (c *HTTPClient) Get(service string, headers map[string]string) ([]byte, error) {
serviceURL := c.url + service serviceURL := c.url + service
c.logger.Debug("[GET] ", serviceURL) c.logger.Debug("[GET] ", serviceURL)
req, _ := http.NewRequest("GET", serviceURL, nil) req, _ := http.NewRequest("GET", serviceURL, nil)
@@ -60,15 +66,18 @@ func (c *HTTPClient) Get(service string) ([]byte, error) {
c.logger.Debug("Authorization [ApiKey]: ", logging.ObfuscateAPIKey(authorization)) c.logger.Debug("Authorization [ApiKey]: ", logging.ObfuscateAPIKey(authorization))
req.Header.Add("Accept-Encoding", "gzip") req.Header.Add("Accept-Encoding", "gzip")
req.Header.Add("Content-Type", "application/json") req.Header.Add("Content-Type", "application/json")
req.Header.Add("SplitSDKVersion", c.metadata.SDKVersion)
req.Header.Add("SplitSDKMachineName", c.metadata.MachineName)
req.Header.Add("SplitSDKMachineIP", c.metadata.MachineIP)
for headerName, headerValue := range headers {
req.Header.Add(headerName, headerValue)
}
c.logger.Debug(fmt.Sprintf("Headers: %v", req.Header)) c.logger.Debug(fmt.Sprintf("Headers: %v", req.Header))
req.Header.Add("Authorization", "Bearer "+authorization) req.Header.Add("Authorization", "Bearer "+authorization)
req.Header.Add("SplitSDKVersion", c.metadata.SDKVersion)
req.Header.Add("SplitSDKMachineName", c.metadata.MachineName)
req.Header.Add("SplitSDKMachineIP", c.metadata.MachineIP)
resp, err := c.httpClient.Do(req) resp, err := c.httpClient.Do(req)
if err != nil { if err != nil {
c.logger.Error("Error requesting data to API: ", req.URL.String(), err.Error()) c.logger.Error("Error requesting data to API: ", req.URL.String(), err.Error())
@@ -80,7 +89,10 @@ func (c *HTTPClient) Get(service string) ([]byte, error) {
var reader io.ReadCloser var reader io.ReadCloser
switch resp.Header.Get("Content-Encoding") { switch resp.Header.Get("Content-Encoding") {
case "gzip": case "gzip":
reader, _ = gzip.NewReader(resp.Body) reader, err = gzip.NewReader(resp.Body)
if err != nil {
return nil, fmt.Errorf("error parsing gzip resopnse body: %w", err)
}
defer reader.Close() defer reader.Close()
default: default:
reader = resp.Body reader = resp.Body

View File

@@ -5,9 +5,9 @@ import (
"encoding/json" "encoding/json"
"strconv" "strconv"
"github.com/splitio/go-split-commons/v2/conf" "github.com/splitio/go-split-commons/v3/conf"
"github.com/splitio/go-split-commons/v2/dtos" "github.com/splitio/go-split-commons/v3/dtos"
"github.com/splitio/go-toolkit/v3/logging" "github.com/splitio/go-toolkit/v4/logging"
) )
type httpFetcherBase struct { type httpFetcherBase struct {
@@ -15,7 +15,7 @@ type httpFetcherBase struct {
logger logging.LoggerInterface logger logging.LoggerInterface
} }
func (h *httpFetcherBase) fetchRaw(url string, since int64) ([]byte, error) { func (h *httpFetcherBase) fetchRaw(url string, since int64, requestNoCache bool) ([]byte, error) {
var bufferQuery bytes.Buffer var bufferQuery bytes.Buffer
bufferQuery.WriteString(url) bufferQuery.WriteString(url)
@@ -23,7 +23,12 @@ func (h *httpFetcherBase) fetchRaw(url string, since int64) ([]byte, error) {
bufferQuery.WriteString("?since=") bufferQuery.WriteString("?since=")
bufferQuery.WriteString(strconv.FormatInt(since, 10)) bufferQuery.WriteString(strconv.FormatInt(since, 10))
} }
data, err := h.client.Get(bufferQuery.String())
var extraHeaders map[string]string
if requestNoCache {
extraHeaders = map[string]string{CacheControlHeader: CacheControlNoCache}
}
data, err := h.client.Get(bufferQuery.String(), extraHeaders)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -51,8 +56,8 @@ func NewHTTPSplitFetcher(
} }
// Fetch makes an http call to the split backend and returns the list of updated splits // Fetch makes an http call to the split backend and returns the list of updated splits
func (f *HTTPSplitFetcher) Fetch(since int64) (*dtos.SplitChangesDTO, error) { func (f *HTTPSplitFetcher) Fetch(since int64, requestNoCache bool) (*dtos.SplitChangesDTO, error) {
data, err := f.fetchRaw("/splitChanges", since) data, err := f.fetchRaw("/splitChanges", since, requestNoCache)
if err != nil { if err != nil {
f.logger.Error("Error fetching split changes ", err) f.logger.Error("Error fetching split changes ", err)
return nil, err return nil, err
@@ -89,12 +94,12 @@ func NewHTTPSegmentFetcher(
} }
// Fetch issues a GET request to the split backend and returns the contents of a particular segment // Fetch issues a GET request to the split backend and returns the contents of a particular segment
func (f *HTTPSegmentFetcher) Fetch(segmentName string, since int64) (*dtos.SegmentChangesDTO, error) { func (f *HTTPSegmentFetcher) Fetch(segmentName string, since int64, requestNoCache bool) (*dtos.SegmentChangesDTO, error) {
var bufferQuery bytes.Buffer var bufferQuery bytes.Buffer
bufferQuery.WriteString("/segmentChanges/") bufferQuery.WriteString("/segmentChanges/")
bufferQuery.WriteString(segmentName) bufferQuery.WriteString(segmentName)
data, err := f.fetchRaw(bufferQuery.String(), since) data, err := f.fetchRaw(bufferQuery.String(), since, requestNoCache)
if err != nil { if err != nil {
f.logger.Error(err.Error()) f.logger.Error(err.Error())
return nil, err return nil, err

View File

@@ -3,9 +3,9 @@ package api
import ( import (
"encoding/json" "encoding/json"
"github.com/splitio/go-split-commons/v2/conf" "github.com/splitio/go-split-commons/v3/conf"
"github.com/splitio/go-split-commons/v2/dtos" "github.com/splitio/go-split-commons/v3/dtos"
"github.com/splitio/go-toolkit/v3/logging" "github.com/splitio/go-toolkit/v4/logging"
) )
type httpRecorderBase struct { type httpRecorderBase struct {

View File

@@ -0,0 +1,123 @@
package sse
import (
"errors"
"strings"
"github.com/splitio/go-split-commons/v3/conf"
"github.com/splitio/go-toolkit/v4/logging"
"github.com/splitio/go-toolkit/v4/sse"
"github.com/splitio/go-toolkit/v4/struct/traits/lifecycle"
gtSync "github.com/splitio/go-toolkit/v4/sync"
)
const (
version = "1.1"
keepAlive = 70
)
// StreamingClient interface
type StreamingClient interface {
ConnectStreaming(token string, streamingStatus chan int, channelList []string, handleIncomingMessage func(IncomingMessage))
StopStreaming()
IsRunning() bool
}
// StreamingClientImpl struct
type StreamingClientImpl struct {
sseClient *sse.Client
logger logging.LoggerInterface
lifecycle lifecycle.Manager
}
// Status constants
const (
StatusConnectionFailed = iota
StatusUnderlyingClientInUse
StatusFirstEventOk
StatusDisconnected
)
// IncomingMessage is an alias of sse.RawEvent
type IncomingMessage = sse.RawEvent
// NewStreamingClient creates new SSE Client
func NewStreamingClient(cfg *conf.AdvancedConfig, logger logging.LoggerInterface) *StreamingClientImpl {
sseClient, _ := sse.NewClient(cfg.StreamingServiceURL, keepAlive, logger)
client := &StreamingClientImpl{
sseClient: sseClient,
logger: logger,
}
client.lifecycle.Setup()
return client
}
// ConnectStreaming connects to streaming
func (s *StreamingClientImpl) ConnectStreaming(token string, streamingStatus chan int, channelList []string, handleIncomingMessage func(IncomingMessage)) {
if !s.lifecycle.BeginInitialization() {
s.logger.Info("Connection is already in process/running. Ignoring")
return
}
params := make(map[string]string)
params["channels"] = strings.Join(append(channelList), ",")
params["accessToken"] = token
params["v"] = version
go func() {
defer s.lifecycle.ShutdownComplete()
if !s.lifecycle.InitializationComplete() {
return
}
firstEventReceived := gtSync.NewAtomicBool(false)
out := s.sseClient.Do(params, func(m IncomingMessage) {
if firstEventReceived.TestAndSet() && !m.IsError() {
streamingStatus <- StatusFirstEventOk
}
handleIncomingMessage(m)
})
if out == nil { // all good
streamingStatus <- StatusDisconnected
return
}
// Something didn'g go as expected
s.lifecycle.AbnormalShutdown()
asConnectionFailedError := &sse.ErrConnectionFailed{}
if errors.As(out, &asConnectionFailedError) {
streamingStatus <- StatusConnectionFailed
return
}
switch out {
case sse.ErrNotIdle:
// If this happens we have a bug
streamingStatus <- StatusUnderlyingClientInUse
case sse.ErrReadingStream:
streamingStatus <- StatusDisconnected
case sse.ErrTimeout:
streamingStatus <- StatusDisconnected
default:
}
}()
}
// StopStreaming stops streaming
func (s *StreamingClientImpl) StopStreaming() {
if !s.lifecycle.BeginShutdown() {
s.logger.Info("SSE client wrapper not running. Ignoring")
return
}
s.sseClient.Shutdown(true)
s.lifecycle.AwaitShutdownComplete()
s.logger.Info("Stopped streaming")
}
// IsRunning returns true if the client is running
func (s *StreamingClientImpl) IsRunning() bool {
return s.lifecycle.IsRunning()
}

View File

@@ -1,7 +1,7 @@
package service package service
import ( import (
"github.com/splitio/go-split-commons/v2/dtos" "github.com/splitio/go-split-commons/v3/dtos"
) )
// AuthClient inteface to be implemneted by AuthClient // AuthClient inteface to be implemneted by AuthClient
@@ -11,12 +11,12 @@ type AuthClient interface {
// SplitFetcher interface to be implemented by Split Fetchers // SplitFetcher interface to be implemented by Split Fetchers
type SplitFetcher interface { type SplitFetcher interface {
Fetch(changeNumber int64) (*dtos.SplitChangesDTO, error) Fetch(changeNumber int64, requstNoCache bool) (*dtos.SplitChangesDTO, error)
} }
// SegmentFetcher interface to be implemented by Split Fetchers // SegmentFetcher interface to be implemented by Split Fetchers
type SegmentFetcher interface { type SegmentFetcher interface {
Fetch(name string, changeNumber int64) (*dtos.SegmentChangesDTO, error) Fetch(name string, changeNumber int64, requestNoCace bool) (*dtos.SegmentChangesDTO, error)
} }
// ImpressionsRecorder interface to be implemented by Impressions loggers // ImpressionsRecorder interface to be implemented by Impressions loggers

View File

@@ -8,8 +8,9 @@ import (
"runtime/debug" "runtime/debug"
"strings" "strings"
"github.com/splitio/go-split-commons/v2/dtos" "github.com/splitio/go-split-commons/v3/dtos"
"github.com/splitio/go-toolkit/v3/logging" "github.com/splitio/go-split-commons/v3/service"
"github.com/splitio/go-toolkit/v4/logging"
yaml "gopkg.in/yaml.v2" yaml "gopkg.in/yaml.v2"
) )
@@ -222,7 +223,7 @@ func parseSplitsYAML(data string) (d []dtos.SplitDTO) {
} }
// Fetch parses the file and returns the appropriate structures // Fetch parses the file and returns the appropriate structures
func (s *FileSplitFetcher) Fetch(changeNumber int64) (*dtos.SplitChangesDTO, error) { func (s *FileSplitFetcher) Fetch(changeNumber int64, _ bool) (*dtos.SplitChangesDTO, error) {
fileContents, err := ioutil.ReadFile(s.splitFile) fileContents, err := ioutil.ReadFile(s.splitFile)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -256,3 +257,5 @@ func (s *FileSplitFetcher) Fetch(changeNumber int64) (*dtos.SplitChangesDTO, err
Till: till, Till: till,
}, nil }, nil
} }
var _ service.SplitFetcher = &FileSplitFetcher{}

View File

@@ -1,10 +1,10 @@
package service package service
import ( import (
"github.com/splitio/go-split-commons/v2/conf" "github.com/splitio/go-split-commons/v3/conf"
"github.com/splitio/go-split-commons/v2/dtos" "github.com/splitio/go-split-commons/v3/dtos"
"github.com/splitio/go-split-commons/v2/service/api" "github.com/splitio/go-split-commons/v3/service/api"
"github.com/splitio/go-toolkit/v3/logging" "github.com/splitio/go-toolkit/v4/logging"
) )
// SplitAPI struct for fetchers and recorders // SplitAPI struct for fetchers and recorders

View File

@@ -1,8 +1,8 @@
package storage package storage
import ( import (
"github.com/splitio/go-split-commons/v2/dtos" "github.com/splitio/go-split-commons/v3/dtos"
"github.com/splitio/go-toolkit/v3/datastructures/set" "github.com/splitio/go-toolkit/v4/datastructures/set"
) )
// SplitStorageProducer should be implemented by structs that offer writing splits in storage // SplitStorageProducer should be implemented by structs that offer writing splits in storage

View File

@@ -4,7 +4,7 @@ import (
"errors" "errors"
"strings" "strings"
"github.com/splitio/go-toolkit/v3/logging" "github.com/splitio/go-toolkit/v4/logging"
) )
// MetricWrapper struct // MetricWrapper struct

View File

@@ -1,6 +1,6 @@
package mocks package mocks
import "github.com/splitio/go-split-commons/v2/dtos" import "github.com/splitio/go-split-commons/v3/dtos"
// MockEventStorage is a mocked implementation of Event Storage // MockEventStorage is a mocked implementation of Event Storage
type MockEventStorage struct { type MockEventStorage struct {

View File

@@ -1,6 +1,6 @@
package mocks package mocks
import "github.com/splitio/go-split-commons/v2/dtos" import "github.com/splitio/go-split-commons/v3/dtos"
// MockImpressionStorage is a mocked implementation of Impression Storage // MockImpressionStorage is a mocked implementation of Impression Storage
type MockImpressionStorage struct { type MockImpressionStorage struct {

View File

@@ -1,6 +1,6 @@
package mocks package mocks
import "github.com/splitio/go-split-commons/v2/dtos" import "github.com/splitio/go-split-commons/v3/dtos"
// MockMetricStorage is a mocked implementation of Metric Storage // MockMetricStorage is a mocked implementation of Metric Storage
type MockMetricStorage struct { type MockMetricStorage struct {

View File

@@ -1,6 +1,6 @@
package mocks package mocks
import "github.com/splitio/go-toolkit/v3/datastructures/set" import "github.com/splitio/go-toolkit/v4/datastructures/set"
// MockSegmentStorage is a mocked implementation of Segment Storage // MockSegmentStorage is a mocked implementation of Segment Storage
type MockSegmentStorage struct { type MockSegmentStorage struct {

View File

@@ -1,8 +1,8 @@
package mocks package mocks
import ( import (
"github.com/splitio/go-split-commons/v2/dtos" "github.com/splitio/go-split-commons/v3/dtos"
"github.com/splitio/go-toolkit/v3/datastructures/set" "github.com/splitio/go-toolkit/v4/datastructures/set"
) )
// MockSplitStorage is a mocked implementation of Split Storage // MockSplitStorage is a mocked implementation of Split Storage

View File

@@ -3,7 +3,7 @@ package mutexmap
import ( import (
"sync" "sync"
"github.com/splitio/go-split-commons/v2/dtos" "github.com/splitio/go-split-commons/v3/dtos"
) )
// MMMetricsStorage contains an in-memory implementation of Metrics storage // MMMetricsStorage contains an in-memory implementation of Metrics storage

View File

@@ -4,7 +4,7 @@ import (
"fmt" "fmt"
"sync" "sync"
"github.com/splitio/go-toolkit/v3/datastructures/set" "github.com/splitio/go-toolkit/v4/datastructures/set"
) )
// MMSegmentStorage contains is an in-memory implementation of segment storage // MMSegmentStorage contains is an in-memory implementation of segment storage

View File

@@ -3,8 +3,8 @@ package mutexmap
import ( import (
"sync" "sync"
"github.com/splitio/go-split-commons/v2/dtos" "github.com/splitio/go-split-commons/v3/dtos"
"github.com/splitio/go-toolkit/v3/datastructures/set" "github.com/splitio/go-toolkit/v4/datastructures/set"
) )
// MMSplitStorage struct contains is an in-memory implementation of split storage // MMSplitStorage struct contains is an in-memory implementation of split storage

View File

@@ -5,8 +5,8 @@ import (
"fmt" "fmt"
"sync" "sync"
"github.com/splitio/go-split-commons/v2/dtos" "github.com/splitio/go-split-commons/v3/dtos"
"github.com/splitio/go-toolkit/v3/logging" "github.com/splitio/go-toolkit/v4/logging"
) )
// MaxAccumulatedBytes is the maximum size to accumulate in events before flush (in bytes) // MaxAccumulatedBytes is the maximum size to accumulate in events before flush (in bytes)

View File

@@ -4,8 +4,8 @@ import (
"container/list" "container/list"
"sync" "sync"
"github.com/splitio/go-split-commons/v2/dtos" "github.com/splitio/go-split-commons/v3/dtos"
"github.com/splitio/go-toolkit/v3/logging" "github.com/splitio/go-toolkit/v4/logging"
) )
// NewMQImpressionsStorage returns an instance of MQEventsStorage // NewMQImpressionsStorage returns an instance of MQEventsStorage

View File

@@ -5,10 +5,10 @@ import (
"math" "math"
"sync" "sync"
"github.com/splitio/go-split-commons/v2/dtos" "github.com/splitio/go-split-commons/v3/dtos"
"github.com/splitio/go-toolkit/v3/logging" "github.com/splitio/go-toolkit/v4/logging"
"github.com/splitio/go-toolkit/v3/queuecache" "github.com/splitio/go-toolkit/v4/queuecache"
"github.com/splitio/go-toolkit/v3/redis" "github.com/splitio/go-toolkit/v4/redis"
) )
// EventsStorage redis implementation of EventsStorage interface // EventsStorage redis implementation of EventsStorage interface

View File

@@ -5,9 +5,9 @@ import (
"sync" "sync"
"time" "time"
"github.com/splitio/go-split-commons/v2/dtos" "github.com/splitio/go-split-commons/v3/dtos"
"github.com/splitio/go-toolkit/v3/logging" "github.com/splitio/go-toolkit/v4/logging"
"github.com/splitio/go-toolkit/v3/redis" "github.com/splitio/go-toolkit/v4/redis"
) )
const impressionsTTLRefresh = time.Duration(3600) * time.Second const impressionsTTLRefresh = time.Duration(3600) * time.Second

View File

@@ -7,9 +7,9 @@ import (
"strings" "strings"
"sync" "sync"
"github.com/splitio/go-split-commons/v2/dtos" "github.com/splitio/go-split-commons/v3/dtos"
"github.com/splitio/go-toolkit/v3/logging" "github.com/splitio/go-toolkit/v4/logging"
"github.com/splitio/go-toolkit/v3/redis" "github.com/splitio/go-toolkit/v4/redis"
) )
// MetricsStorage is a redis-based implementation of split storage // MetricsStorage is a redis-based implementation of split storage

View File

@@ -4,8 +4,8 @@ import (
"errors" "errors"
"strings" "strings"
"github.com/splitio/go-toolkit/v3/logging" "github.com/splitio/go-toolkit/v4/logging"
"github.com/splitio/go-toolkit/v3/redis" "github.com/splitio/go-toolkit/v4/redis"
) )
// ErrorHashNotPresent constant // ErrorHashNotPresent constant

View File

@@ -6,10 +6,10 @@ import (
"strings" "strings"
"time" "time"
"github.com/splitio/go-split-commons/v2/conf" "github.com/splitio/go-split-commons/v3/conf"
"github.com/splitio/go-toolkit/v3/logging" "github.com/splitio/go-toolkit/v4/logging"
"github.com/splitio/go-toolkit/v3/redis" "github.com/splitio/go-toolkit/v4/redis"
"github.com/splitio/go-toolkit/v3/redis/helpers" "github.com/splitio/go-toolkit/v4/redis/helpers"
) )
// NewRedisClient returns a new Prefixed Redis Client // NewRedisClient returns a new Prefixed Redis Client

View File

@@ -6,9 +6,9 @@ import (
"strings" "strings"
"sync" "sync"
"github.com/splitio/go-toolkit/v3/datastructures/set" "github.com/splitio/go-toolkit/v4/datastructures/set"
"github.com/splitio/go-toolkit/v3/logging" "github.com/splitio/go-toolkit/v4/logging"
"github.com/splitio/go-toolkit/v3/redis" "github.com/splitio/go-toolkit/v4/redis"
) )
// SegmentStorage is a redis implementation of a storage for segments // SegmentStorage is a redis implementation of a storage for segments

View File

@@ -8,10 +8,10 @@ import (
"strings" "strings"
"sync" "sync"
"github.com/splitio/go-split-commons/v2/dtos" "github.com/splitio/go-split-commons/v3/dtos"
"github.com/splitio/go-toolkit/v3/datastructures/set" "github.com/splitio/go-toolkit/v4/datastructures/set"
"github.com/splitio/go-toolkit/v3/logging" "github.com/splitio/go-toolkit/v4/logging"
"github.com/splitio/go-toolkit/v3/redis" "github.com/splitio/go-toolkit/v4/redis"
) )
// SplitStorage is a redis-based implementation of split storage // SplitStorage is a redis-based implementation of split storage

View File

@@ -0,0 +1,13 @@
package synchronizer
// Synchronizer interface for syncing data to and from splits servers
type Synchronizer interface {
SyncAll(requestNoCache bool) error
SynchronizeSplits(till *int64, requestNoCache bool) error
LocalKill(splitName string, defaultTreatment string, changeNumber int64)
SynchronizeSegment(segmentName string, till *int64, requestNoCache bool) error
StartPeriodicFetching()
StopPeriodicFetching()
StartPeriodicDataRecording()
StopPeriodicDataRecording()
}

View File

@@ -1,13 +1,13 @@
package synchronizer package synchronizer
import ( import (
"github.com/splitio/go-split-commons/v2/dtos" "github.com/splitio/go-split-commons/v3/dtos"
"github.com/splitio/go-split-commons/v2/service" "github.com/splitio/go-split-commons/v3/service"
"github.com/splitio/go-split-commons/v2/storage" "github.com/splitio/go-split-commons/v3/storage"
storageMock "github.com/splitio/go-split-commons/v2/storage/mocks" storageMock "github.com/splitio/go-split-commons/v3/storage/mocks"
"github.com/splitio/go-split-commons/v2/synchronizer/worker/split" "github.com/splitio/go-split-commons/v3/synchronizer/worker/split"
"github.com/splitio/go-split-commons/v2/tasks" "github.com/splitio/go-split-commons/v3/tasks"
"github.com/splitio/go-toolkit/v3/logging" "github.com/splitio/go-toolkit/v4/logging"
) )
// Local implements Local Synchronizer // Local implements Local Synchronizer
@@ -47,8 +47,9 @@ func NewLocal(
} }
// SyncAll syncs splits and segments // SyncAll syncs splits and segments
func (s *Local) SyncAll() error { func (s *Local) SyncAll(requestNoCache bool) error {
return s.workers.SplitFetcher.SynchronizeSplits(nil) _, err := s.workers.SplitFetcher.SynchronizeSplits(nil, requestNoCache)
return err
} }
// StartPeriodicFetching starts periodic fetchers tasks // StartPeriodicFetching starts periodic fetchers tasks
@@ -70,11 +71,16 @@ func (s *Local) StopPeriodicDataRecording() {
} }
// SynchronizeSplits syncs splits // SynchronizeSplits syncs splits
func (s *Local) SynchronizeSplits(till *int64) error { func (s *Local) SynchronizeSplits(till *int64, requestNoCache bool) error {
return s.workers.SplitFetcher.SynchronizeSplits(till) _, err := s.workers.SplitFetcher.SynchronizeSplits(nil, requestNoCache)
return err
} }
// SynchronizeSegment syncs segment // SynchronizeSegment syncs segment
func (s *Local) SynchronizeSegment(name string, till *int64) error { func (s *Local) SynchronizeSegment(name string, till *int64, _ bool) error {
return nil return nil
} }
// LocalKill does nothing
func (s *Local) LocalKill(splitName string, defaultTreatment string, changeNumber int64) {
}

View File

@@ -0,0 +1,207 @@
package synchronizer
import (
"errors"
"sync/atomic"
"time"
"github.com/splitio/go-split-commons/v3/conf"
"github.com/splitio/go-split-commons/v3/push"
"github.com/splitio/go-split-commons/v3/service"
"github.com/splitio/go-split-commons/v3/storage"
"github.com/splitio/go-toolkit/v4/backoff"
"github.com/splitio/go-toolkit/v4/logging"
"github.com/splitio/go-toolkit/v4/struct/traits/lifecycle"
)
const (
// Ready represents ready
Ready = iota
// StreamingReady ready
StreamingReady
// Error represents some error in SSE streaming
Error
)
// Operation mode constants
const (
Streaming = iota
Polling
)
// Manager interface
type Manager interface {
Start()
Stop()
IsRunning() bool
}
// ManagerImpl struct
type ManagerImpl struct {
synchronizer Synchronizer
logger logging.LoggerInterface
config conf.AdvancedConfig
pushManager push.Manager
managerStatus chan int
streamingStatus chan int64
operationMode int32
lifecycle lifecycle.Manager
backoff backoff.Interface
}
// NewSynchronizerManager creates new sync manager
func NewSynchronizerManager(
synchronizer Synchronizer,
logger logging.LoggerInterface,
config conf.AdvancedConfig,
authClient service.AuthClient,
splitStorage storage.SplitStorage,
managerStatus chan int,
) (*ManagerImpl, error) {
if managerStatus == nil || cap(managerStatus) < 1 {
return nil, errors.New("Status channel cannot be nil nor having capacity")
}
manager := &ManagerImpl{
backoff: backoff.New(),
synchronizer: synchronizer,
logger: logger,
config: config,
managerStatus: managerStatus,
}
manager.lifecycle.Setup()
if config.StreamingEnabled {
streamingStatus := make(chan int64, 1000)
pushManager, err := push.NewManager(logger, synchronizer, &config, streamingStatus, authClient)
if err != nil {
return nil, err
}
manager.pushManager = pushManager
manager.streamingStatus = streamingStatus
}
return manager, nil
}
// IsRunning returns true if is in Streaming or Polling
func (s *ManagerImpl) IsRunning() bool {
return s.lifecycle.IsRunning()
}
// Start starts synchronization through Split
func (s *ManagerImpl) Start() {
if !s.lifecycle.BeginInitialization() {
s.logger.Info("Manager is already running, skipping start")
return
}
// It's safe to drain the channel here, since it's guaranteed that the manager status is "starting"
// push manager is still stopped
for len(s.managerStatus) > 0 {
<-s.managerStatus
}
err := s.synchronizer.SyncAll(false)
if err != nil {
defer s.lifecycle.ShutdownComplete()
s.managerStatus <- Error
return
}
if !s.lifecycle.InitializationComplete() {
defer s.lifecycle.ShutdownComplete()
return
}
s.logger.Debug("SyncAll Ready")
s.managerStatus <- Ready
s.synchronizer.StartPeriodicDataRecording()
if !s.config.StreamingEnabled {
s.logger.Info("SDK initialized in polling mode")
s.startPolling()
go func() { // create a goroutine that stops everything (the same way the streaming status watcher would)
<-s.lifecycle.ShutdownRequested()
s.stop()
}()
return
}
// Start streaming
s.logger.Info("SDK Initialized in streaming mode")
s.pushManager.Start()
go s.pushStatusWatcher()
}
func (s *ManagerImpl) stop() {
if s.pushManager != nil {
s.pushManager.Stop()
}
s.synchronizer.StopPeriodicFetching()
s.synchronizer.StopPeriodicDataRecording()
s.lifecycle.ShutdownComplete()
}
// Stop stop synchronizaation through Split
func (s *ManagerImpl) Stop() {
if !s.lifecycle.BeginShutdown() {
s.logger.Info("sync manager not yet running, skipping shutdown.")
return
}
s.logger.Info("Stopping all synchronization tasks")
s.lifecycle.AwaitShutdownComplete()
}
func (s *ManagerImpl) pushStatusWatcher() {
defer s.stop()
for {
select {
case <-s.lifecycle.ShutdownRequested():
return
case status := <-s.streamingStatus:
switch status {
case push.StatusUp:
s.stopPolling()
s.logger.Info("streaming up and running")
s.enableStreaming()
s.synchronizer.SyncAll(true)
case push.StatusDown:
s.logger.Info("streaming down, switchin to polling")
s.synchronizer.SyncAll(false)
s.pauseStreaming()
s.startPolling()
case push.StatusRetryableError:
howLong := s.backoff.Next()
s.logger.Error("retryable error in streaming subsystem. Switching to polling and retrying in ", howLong, " seconds")
s.pushManager.Stop()
s.synchronizer.SyncAll(false)
s.startPolling()
time.Sleep(howLong)
s.pushManager.Start()
case push.StatusNonRetryableError:
s.logger.Error("non retryable error in streaming subsystem. Switching to polling until next SDK initialization")
s.pushManager.Stop()
s.synchronizer.SyncAll(false)
s.startPolling()
}
}
}
}
func (s *ManagerImpl) startPolling() {
atomic.StoreInt32(&s.operationMode, Polling)
s.synchronizer.StartPeriodicFetching()
}
func (s *ManagerImpl) stopPolling() {
s.synchronizer.StopPeriodicFetching()
}
func (s *ManagerImpl) pauseStreaming() {
s.pushManager.StartWorkers()
}
func (s *ManagerImpl) enableStreaming() {
s.pushManager.StartWorkers()
atomic.StoreInt32(&s.operationMode, Streaming)
s.backoff.Reset()
}

View File

@@ -1,16 +1,16 @@
package synchronizer package synchronizer
import ( import (
"github.com/splitio/go-split-commons/v2/conf" "github.com/splitio/go-split-commons/v3/conf"
"github.com/splitio/go-split-commons/v2/synchronizer/worker/event" "github.com/splitio/go-split-commons/v3/synchronizer/worker/event"
"github.com/splitio/go-split-commons/v2/synchronizer/worker/impression" "github.com/splitio/go-split-commons/v3/synchronizer/worker/impression"
"github.com/splitio/go-split-commons/v2/synchronizer/worker/impressionscount" "github.com/splitio/go-split-commons/v3/synchronizer/worker/impressionscount"
"github.com/splitio/go-split-commons/v2/synchronizer/worker/metric" "github.com/splitio/go-split-commons/v3/synchronizer/worker/metric"
"github.com/splitio/go-split-commons/v2/synchronizer/worker/segment" "github.com/splitio/go-split-commons/v3/synchronizer/worker/segment"
"github.com/splitio/go-split-commons/v2/synchronizer/worker/split" "github.com/splitio/go-split-commons/v3/synchronizer/worker/split"
"github.com/splitio/go-split-commons/v2/tasks" "github.com/splitio/go-split-commons/v3/tasks"
"github.com/splitio/go-toolkit/v3/asynctask" "github.com/splitio/go-toolkit/v4/asynctask"
"github.com/splitio/go-toolkit/v3/logging" "github.com/splitio/go-toolkit/v4/logging"
) )
// SplitTasks struct for tasks // SplitTasks struct for tasks
@@ -25,8 +25,8 @@ type SplitTasks struct {
// Workers struct for workers // Workers struct for workers
type Workers struct { type Workers struct {
SplitFetcher split.SplitFetcher SplitFetcher split.Updater
SegmentFetcher segment.SegmentFetcher SegmentFetcher segment.Updater
TelemetryRecorder metric.MetricRecorder TelemetryRecorder metric.MetricRecorder
ImpressionRecorder impression.ImpressionRecorder ImpressionRecorder impression.ImpressionRecorder
EventRecorder event.EventRecorder EventRecorder event.EventRecorder
@@ -83,12 +83,12 @@ func (s *SynchronizerImpl) dataFlusher() {
} }
// SyncAll syncs splits and segments // SyncAll syncs splits and segments
func (s *SynchronizerImpl) SyncAll() error { func (s *SynchronizerImpl) SyncAll(requestNoCache bool) error {
err := s.workers.SplitFetcher.SynchronizeSplits(nil) _, err := s.workers.SplitFetcher.SynchronizeSplits(nil, requestNoCache)
if err != nil { if err != nil {
return err return err
} }
return s.workers.SegmentFetcher.SynchronizeSegments() return s.workers.SegmentFetcher.SynchronizeSegments(requestNoCache)
} }
// StartPeriodicFetching starts periodic fetchers tasks // StartPeriodicFetching starts periodic fetchers tasks
@@ -148,11 +148,32 @@ func (s *SynchronizerImpl) StopPeriodicDataRecording() {
} }
// SynchronizeSplits syncs splits // SynchronizeSplits syncs splits
func (s *SynchronizerImpl) SynchronizeSplits(till *int64) error { func (s *SynchronizerImpl) SynchronizeSplits(till *int64, requstNoCache bool) error {
return s.workers.SplitFetcher.SynchronizeSplits(till) referencedSegments, err := s.workers.SplitFetcher.SynchronizeSplits(till, requstNoCache)
for _, segment := range s.filterCachedSegments(referencedSegments) {
go s.SynchronizeSegment(segment, nil, true) // send segment to workerpool (queue is bypassed)
}
return err
}
func (s *SynchronizerImpl) filterCachedSegments(segmentsReferenced []string) []string {
toRet := make([]string, 0, len(segmentsReferenced))
for _, name := range segmentsReferenced {
if !s.workers.SegmentFetcher.IsSegmentCached(name) {
toRet = append(toRet, name)
}
}
return toRet
}
// LocalKill locally kills a split
func (s *SynchronizerImpl) LocalKill(splitName string, defaultTreatment string, changeNumber int64) {
s.workers.SplitFetcher.LocalKill(splitName, defaultTreatment, changeNumber)
} }
// SynchronizeSegment syncs segment // SynchronizeSegment syncs segment
func (s *SynchronizerImpl) SynchronizeSegment(name string, till *int64) error { func (s *SynchronizerImpl) SynchronizeSegment(name string, till *int64, requstNoCache bool) error {
return s.workers.SegmentFetcher.SynchronizeSegment(name, till) return s.workers.SegmentFetcher.SynchronizeSegment(name, till, requstNoCache)
} }
var _ Synchronizer = &SynchronizerImpl{}

View File

@@ -2,13 +2,14 @@ package event
import ( import (
"errors" "errors"
"strconv"
"time" "time"
"github.com/splitio/go-split-commons/v2/dtos" "github.com/splitio/go-split-commons/v3/dtos"
"github.com/splitio/go-split-commons/v2/service" "github.com/splitio/go-split-commons/v3/service"
"github.com/splitio/go-split-commons/v2/storage" "github.com/splitio/go-split-commons/v3/storage"
"github.com/splitio/go-split-commons/v2/util" "github.com/splitio/go-split-commons/v3/util"
"github.com/splitio/go-toolkit/v3/logging" "github.com/splitio/go-toolkit/v4/logging"
) )
// RecorderSingle struct for event sync // RecorderSingle struct for event sync
@@ -54,7 +55,7 @@ func (e *RecorderSingle) SynchronizeEvents(bulkSize int64) error {
err = e.eventRecorder.Record(queuedEvents, e.metadata) err = e.eventRecorder.Record(queuedEvents, e.metadata)
if err != nil { if err != nil {
if httpError, ok := err.(*dtos.HTTPError); ok { if httpError, ok := err.(*dtos.HTTPError); ok {
e.metricsWrapper.StoreCounters(storage.PostEventsCounter, string(httpError.Code)) e.metricsWrapper.StoreCounters(storage.PostEventsCounter, strconv.Itoa(httpError.Code))
} }
return err return err
} }

View File

@@ -2,14 +2,15 @@ package impression
import ( import (
"errors" "errors"
"strconv"
"time" "time"
"github.com/splitio/go-split-commons/v2/conf" "github.com/splitio/go-split-commons/v3/conf"
"github.com/splitio/go-split-commons/v2/dtos" "github.com/splitio/go-split-commons/v3/dtos"
"github.com/splitio/go-split-commons/v2/service" "github.com/splitio/go-split-commons/v3/service"
"github.com/splitio/go-split-commons/v2/storage" "github.com/splitio/go-split-commons/v3/storage"
"github.com/splitio/go-split-commons/v2/util" "github.com/splitio/go-split-commons/v3/util"
"github.com/splitio/go-toolkit/v3/logging" "github.com/splitio/go-toolkit/v4/logging"
) )
const ( const (
@@ -95,7 +96,7 @@ func (i *RecorderSingle) SynchronizeImpressions(bulkSize int64) error {
err = i.impressionRecorder.Record(bulkImpressions, i.metadata, map[string]string{splitSDKImpressionsMode: i.mode}) err = i.impressionRecorder.Record(bulkImpressions, i.metadata, map[string]string{splitSDKImpressionsMode: i.mode})
if err != nil { if err != nil {
if httpError, ok := err.(*dtos.HTTPError); ok { if httpError, ok := err.(*dtos.HTTPError); ok {
i.metricsWrapper.StoreCounters(storage.TestImpressionsCounter, string(httpError.Code)) i.metricsWrapper.StoreCounters(storage.TestImpressionsCounter, strconv.Itoa(httpError.Code))
} }
return err return err
} }

View File

@@ -1,10 +1,10 @@
package impressionscount package impressionscount
import ( import (
"github.com/splitio/go-split-commons/v2/dtos" "github.com/splitio/go-split-commons/v3/dtos"
"github.com/splitio/go-split-commons/v2/provisional" "github.com/splitio/go-split-commons/v3/provisional"
"github.com/splitio/go-split-commons/v2/service" "github.com/splitio/go-split-commons/v3/service"
"github.com/splitio/go-toolkit/v3/logging" "github.com/splitio/go-toolkit/v4/logging"
) )
// RecorderSingle struct for impressionsCount sync // RecorderSingle struct for impressionsCount sync

Some files were not shown because too many files have changed in this diff Show More