diff --git a/Gopkg.lock b/Gopkg.lock index 164b8c6..7ad96fe 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -3,46 +3,54 @@ [[projects]] branch = "master" - digest = "1:6716c9fe6333591128e72848f246fc01dc72240e1e64185d8b4e124e7280b35d" + digest = "1:9cac35beaf0b218e41036e92ec8f664146227a6549a3bd307bb7b223cb40313b" name = "github.com/AndreasBriese/bbloom" packages = ["."] - pruneopts = "UT" - revision = "e2d15f34fcf99d5dbb871c820ec73f710fca9815" + pruneopts = "" + revision = "46b345b51c9667fcbaad862a370d73bd7aa802b6" [[projects]] - digest = "1:e92f5581902c345eb4ceffdcd4a854fb8f73cf436d47d837d1ec98ef1fe0a214" + digest = "1:6146fda730c18186631e91e818d995e759e7cbe27644d6871ccd469f6865c686" name = "github.com/StackExchange/wmi" packages = ["."] - pruneopts = "UT" - revision = "5d049714c4a64225c3c79a7cf7d02f7fb5b96338" - version = "1.0.0" + pruneopts = "" + revision = "cbe66965904dbe8a6cd589e2298e5d8b986bd7dd" + version = "1.1.0" [[projects]] - digest = "1:dbd3a713434b6f32d9459b1e6786ad58cec128470b58555cdc7b3b7314a1706f" + digest = "1:3b186fcaa1c0b374249e76601cd4d5c8c7f0712f8ad419f2e8288c7b9e7ed520" name = "github.com/aead/serpent" packages = ["."] - pruneopts = "UT" + pruneopts = "" revision = "fba169763ea663f7496376e5cdf709e4c7504704" version = "v0.1" [[projects]] branch = "master" - digest = "1:3474fd67b98ac29b6fb7b164306063e9fd380b061a2fb9ba80f6e0a6444102bb" + digest = "1:baf770c4efa1883bb5e444614e85b8028bbad33913aca290a43298f65d9df485" name = "github.com/bluele/gcache" packages = ["."] - pruneopts = "UT" + pruneopts = "" revision = "bc40bd6538339bd4be8cb0b48fc33fdf012ddb6e" [[projects]] - digest = "1:ffe9824d294da03b391f44e1ae8281281b4afc1bdaa9588c9097785e3af10cec" + digest = "1:d25acc7560ed91f825cb9b01a1e945bb1117cdcaba19077137e2d9ffc9cf6d05" + name = "github.com/cespare/xxhash" + packages = ["."] + pruneopts = "" + revision = "d7df74196a9e781ede915320c11c378c1b2f3a1f" + version = "v2.1.1" + +[[projects]] + digest = "1:0deddd908b6b4b768cfc272c16ee61e7088a60f7fe2f06c547bd3d8e1f8b8e77" name = "github.com/davecgh/go-spew" packages = ["spew"] - pruneopts = "UT" + pruneopts = "" revision = "8991bc29aa16c548c550c7ff78260e27b9ab7c73" version = "v1.1.1" [[projects]] - digest = "1:6be8582a4f52ba2851d8a039eb9c3a3b90334b2820563d71e97de35580da128e" + digest = "1:238e2deec0c24dca94461b2f72fe05eba40063b653677e60901af56a6fd5148b" name = "github.com/dgraph-io/badger" packages = [ ".", @@ -50,76 +58,85 @@ "pb", "skl", "table", + "trie", "y", ] - pruneopts = "UT" - revision = "2fa005c9d4bf695277ab5214c1fbce3735b9562a" - version = "v1.6.0" + pruneopts = "" + revision = "8760a5018cd670a5cecc79a7e927ecaf47acc434" + version = "v1.6.1" [[projects]] - branch = "master" - digest = "1:6e8109ce247a59ab1eeb5330166c12735f6590de99c9647b6162d11518d32c9a" - name = "github.com/dgryski/go-farm" - packages = ["."] - pruneopts = "UT" - revision = "6a90982ecee230ff6cba02d5bd386acc030be9d3" + digest = "1:1d9a7f60f0ff31ba3336a9c1349c212e7076845b537c053a1703a26730145167" + name = "github.com/dgraph-io/ristretto" + packages = ["z"] + pruneopts = "" + revision = "dbc185e050f48c5190a78b4a07829d9c10afca21" + version = "v0.0.2" [[projects]] - digest = "1:6f9339c912bbdda81302633ad7e99a28dfa5a639c864061f1929510a9a64aa74" + digest = "1:f1a75a8e00244e5ea77ff274baa9559eb877437b240ee7b278f3fc560d9f08bf" name = "github.com/dustin/go-humanize" packages = ["."] - pruneopts = "UT" + pruneopts = "" revision = "9f541cc9db5d55bce703bd99987c9d5cb8eea45e" version = "v1.0.0" [[projects]] - digest = "1:440028f55cb322d8cb5b9d5ebec298a00b7d74690a658fe6b1c0c0b44341bfae" + digest = "1:b6581f9180e0f2d5549280d71819ab951db9d511478c87daca95669589d505c0" name = "github.com/go-ole/go-ole" packages = [ ".", "oleutil", ] - pruneopts = "UT" + pruneopts = "" revision = "97b6244175ae18ea6eef668034fd6565847501c9" version = "v1.2.4" [[projects]] - digest = "1:573ca21d3669500ff845bdebee890eb7fc7f0f50c59f2132f2a0c6b03d85086a" - name = "github.com/golang/protobuf" - packages = ["proto"] - pruneopts = "UT" - revision = "6c65a5562fc06764971b7c5d05c76c75e84bdbf7" - version = "v1.3.2" + digest = "1:fd608a9f543f30dd5a086b2cdd0bce71b65bca62798ff7ce21348806cdf1412d" + name = "github.com/gofrs/uuid" + packages = ["."] + pruneopts = "" + revision = "abfe1881e60ef34074c1b8d8c63b42565c356ed6" + version = "v3.3.0" [[projects]] - digest = "1:c3388642e07731a240e14f4bc7207df59cfcc009447c657b9de87fec072d07e3" + digest = "1:02c7a8570f619bdb5620e8f6a16407455c8e63433d8a9e829f618813dc7f89a5" + name = "github.com/golang/protobuf" + packages = ["proto"] + pruneopts = "" + revision = "d04d7b157bb510b1e0c10132224b616ac0e26b17" + version = "v1.4.2" + +[[projects]] + digest = "1:20dc576ad8f98fe64777c62f090a9b37dd67c62b23fe42b429c2c41936aa8a9c" name = "github.com/google/renameio" packages = ["."] - pruneopts = "UT" + pruneopts = "" revision = "f0e32980c006571efd537032e5f9cd8c1a92819e" version = "v0.1.0" [[projects]] - digest = "1:cbec35fe4d5a4fba369a656a8cd65e244ea2c743007d8f6c1ccb132acf9d1296" + digest = "1:fbab76ba211c99fcd45a481a32530efc229f3510fd94889f361dcaf13ff05fe0" name = "github.com/gorilla/mux" packages = ["."] - pruneopts = "UT" - revision = "00bdffe0f3c77e27d2cf6f5c70232a2d3e4d9c15" - version = "v1.7.3" + pruneopts = "" + revision = "75dcda0896e109a2a22c9315bca3bb21b87b2ba5" + version = "v1.7.4" [[projects]] - digest = "1:7b5c6e2eeaa9ae5907c391a91c132abfd5c9e8a784a341b5625e750c67e6825d" + digest = "1:5122b0b5a933bd0e16b887aa7844933c8a09fb5ba9b5f5651253cb5336c6462a" name = "github.com/gorilla/websocket" packages = ["."] - pruneopts = "UT" - revision = "66b9c49e59c6c48f0ffce28c2d8b8a5678502c6d" - version = "v1.4.0" + pruneopts = "" + revision = "b65e62901fc1c0d968042419e74789f6af455eb9" + version = "v1.4.2" [[projects]] - digest = "1:88e0b0baeb9072f0a4afbcf12dda615fc8be001d1802357538591155998da21b" + digest = "1:2f0c811248aeb64978037b357178b1593372439146bda860cb16f2c80785ea93" name = "github.com/hashicorp/go-version" packages = ["."] - pruneopts = "UT" + pruneopts = "" revision = "ac23dc3fea5d1a983c43f6a0f6e2c13f0195d8bd" version = "v1.2.0" @@ -127,66 +144,36 @@ digest = "1:870d441fe217b8e689d7949fef6e43efbc787e50f200cb1e70dbca9204a1d6be" name = "github.com/inconshreveable/mousetrap" packages = ["."] - pruneopts = "UT" + pruneopts = "" revision = "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75" version = "v1.0" [[projects]] - branch = "master" - digest = "1:7e8b852581596acce37bcb939a05d7d5ff27156045b50057e659e299c16fc1ca" - name = "github.com/mmcloughlin/avo" - packages = [ - "attr", - "build", - "buildtags", - "gotypes", - "internal/prnt", - "internal/stack", - "ir", - "operand", - "pass", - "printer", - "reg", - "src", - "x86", - ] - pruneopts = "UT" - revision = "bb615f61ce85790a1667efc145c66e917cce1a39" - -[[projects]] - digest = "1:cf31692c14422fa27c83a05292eb5cbe0fb2775972e8f1f8446a71549bd8980b" + digest = "1:c45802472e0c06928cd997661f2af610accd85217023b1d5f6331bebce0671d3" name = "github.com/pkg/errors" packages = ["."] - pruneopts = "UT" - revision = "ba968bfe8b2f7e042a574c888954fccecfa385b4" - version = "v0.8.1" + pruneopts = "" + revision = "614d223910a179a466c1767a985424175c39b465" + version = "v0.9.1" [[projects]] - digest = "1:274f67cb6fed9588ea2521ecdac05a6d62a8c51c074c1fccc6a49a40ba80e925" - name = "github.com/satori/go.uuid" - packages = ["."] - pruneopts = "UT" - revision = "f58768cc1a7a7e77a3bd49e98cdd21419399b6a3" - version = "v1.2.0" - -[[projects]] - branch = "master" - digest = "1:f1ee4af7c43f206d87f13644636e3710a05e499a084a32ec2cc7d8aa25cef1aa" + digest = "1:90da107a52bdacf25384ae6fc2889df9cf3c956ecde1561825b13ba70a8283b5" name = "github.com/seehuhn/fortuna" packages = ["."] - pruneopts = "UT" - revision = "f376bbcd33f446d4431a641aead7851fcbeafa08" + pruneopts = "" + revision = "6f13c9c4f925cf5dd33c070a2f0fbe596385917b" + version = "v1.0.1" [[projects]] - branch = "master" - digest = "1:c671ecdf2ce51ffaf895169874d074960b3f1297f9bdae49fcb645116d936700" + digest = "1:bf9a02016247817c7ca7dc0d42ee608ffdef20de1474f6d676cfd80a64068a38" name = "github.com/seehuhn/sha256d" packages = ["."] - pruneopts = "UT" - revision = "e9f377c596061894b7f9ee69aab61e62c3ccc13e" + pruneopts = "" + revision = "4a65999787a5902349359436a10df5fe59a10a64" + version = "v1.0.0" [[projects]] - digest = "1:7ebe52387a847d276a9b9e7b379b2e3e4d536843ad840a9c4426a0152e6b3d54" + digest = "1:16f319cf21ddf49f27b3a2093d68316840dc25ec5c2a0a431a4a4fc01ea707e2" name = "github.com/shirou/gopsutil" packages = [ "cpu", @@ -196,121 +183,132 @@ "net", "process", ] - pruneopts = "UT" - revision = "d80c43f9c984a48783daf22f4bd9278006ae483a" - version = "v2.19.7" + pruneopts = "" + revision = "a81cf97fce2300934e6c625b9917103346c26ba3" + version = "v2.20.4" [[projects]] - branch = "master" - digest = "1:99c6a6dab47067c9b898e8c8b13d130c6ab4ffbcc4b7cc6236c2cd0b1e344f5b" - name = "github.com/shirou/w32" - packages = ["."] - pruneopts = "UT" - revision = "bb4de0191aa41b5507caa14b0650cdbddcd9280b" - -[[projects]] - digest = "1:e096613fb7cf34743d49af87d197663cfccd61876e2219853005a57baedfa562" + digest = "1:bff75d4f1a2d2c4b8f4b46ff5ac230b80b5fa49276f615900cba09fe4c97e66e" name = "github.com/spf13/cobra" packages = ["."] - pruneopts = "UT" - revision = "f2b07da1e2c38d5f12845a4f607e2e1018cbb1f5" - version = "v0.0.5" + pruneopts = "" + revision = "a684a6d7f5e37385d954dd3b5a14fc6912c6ab9d" + version = "v1.0.0" [[projects]] - digest = "1:524b71991fc7d9246cc7dc2d9e0886ccb97648091c63e30eef619e6862c955dd" + digest = "1:688428eeb1ca80d92599eb3254bdf91b51d7e232fead3a73844c1f201a281e51" name = "github.com/spf13/pflag" packages = ["."] - pruneopts = "UT" + pruneopts = "" revision = "2e9d26c8c37aae03e3f9d4e90b7116f5accb7cab" version = "v1.0.5" [[projects]] branch = "master" - digest = "1:93d6687fc19da8a35c7352d72117a6acd2072dfb7e9bfd65646227bf2a913b2a" + digest = "1:86e6712cfd4070a2120c03fcec41cfcbbc51813504a74e28d74479edfaf669ee" name = "github.com/tevino/abool" packages = ["."] - pruneopts = "UT" + pruneopts = "" revision = "9b9efcf221b50905aab9bbabd3daed56dc10f339" [[projects]] - digest = "1:46aea6ffe39c3d95c13640c2515236ed3c5cdffc3c78c6c0ed4edec2caf7a0dc" + digest = "1:029181b60f6ea672544d102f81d7d9508a45a70e17ce8a649535959e0249d56b" name = "github.com/tidwall/gjson" packages = ["."] - pruneopts = "UT" - revision = "c5e72cdf74dff23857243dd662c465b810891c21" - version = "v1.3.2" + pruneopts = "" + revision = "f042915ca17de35980544c91ab2c8ceb73b682f2" + version = "v1.6.0" [[projects]] - digest = "1:8453ddbed197809ee8ca28b06bd04e127bec9912deb4ba451fea7a1eca578328" + digest = "1:72511ec1089fee111c995492d1d390a38ac7ab888aabdb1188985f2a1719c599" name = "github.com/tidwall/match" packages = ["."] - pruneopts = "UT" + pruneopts = "" revision = "33827db735fff6510490d69a8622612558a557ed" version = "v1.0.1" [[projects]] - digest = "1:ddfe0a54e5f9b29536a6d7b2defa376f2cb2b6e4234d676d7ff214d5b097cb50" + digest = "1:3d4deb9e8160077721a79fc1b5a6ce27016c98f9f8e631090d5af8f50120ddf0" name = "github.com/tidwall/pretty" packages = ["."] - pruneopts = "UT" - revision = "1166b9ac2b65e46a43d8618d30d1554f4652d49b" - version = "v1.0.0" + pruneopts = "" + revision = "b2475501f89994f7ea30b3c94ba86b49079961fe" + version = "v1.0.1" [[projects]] - digest = "1:b70c951ba6fdeecfbd50dabe95aa5e1b973866ae9abbece46ad60348112214f2" + digest = "1:b749bf81c4c595c1218489d2fa4d90e4a953a5fd11420722a2f57c0c5beecb92" name = "github.com/tidwall/sjson" packages = ["."] - pruneopts = "UT" - revision = "25fb082a20e29e83fb7b7ef5f5919166aad1f084" - version = "v1.0.4" + pruneopts = "" + revision = "11cb24d8421de3e1bb5c5efb066a03037150568d" + version = "v1.1.1" [[projects]] - digest = "1:f2ac2c724fc8214bb7b9dd6d4f5b7a983152051f5133320f228557182263cb94" + digest = "1:2a591e844f6019284ded9f5095e53764c3d69bb8e7d1cf2b318f1b7e25864508" name = "go.etcd.io/bbolt" packages = ["."] - pruneopts = "UT" - revision = "a0458a2b35708eef59eb5f620ceb3cd1c01a824d" - version = "v1.3.3" + pruneopts = "" + revision = "68cc10a767ea1c6b9e8dcb9847317ff192d6d974" + version = "v1.3.4" [[projects]] branch = "master" - digest = "1:e0efd295a10a5703f556a97b7f90f9b3a02ee5080b26ee5374d100a6131aea68" + digest = "1:305d718b88fcd3b251b910416367de49af1e7944a9a17efabedab5f0ba7745de" name = "golang.org/x/net" packages = [ "internal/timeseries", "trace", ] - pruneopts = "UT" - revision = "74dc4d7220e7acc4e100824340f3e66577424772" + pruneopts = "" + revision = "0ba52f642ac2f9371a88bfdde41f4b4e195a37c0" [[projects]] branch = "master" - digest = "1:d52ccbb025caf0a2166e0204ed934567b8d0dfdd23a09d947f1f040708888a0a" + digest = "1:bf837d996e7dfe7b819cbe53c8c9733e93228577f0561e43996b9ef0ea8a68a9" name = "golang.org/x/sys" packages = [ + "internal/unsafeheader", "unix", "windows", ] - pruneopts = "UT" - revision = "fde4db37ae7ad8191b03d30d27f258b5291ae4e3" + pruneopts = "" + revision = "05986578812163b26672dabd9b425240ae2bb0ad" [[projects]] - branch = "master" - digest = "1:b3e64b43fe77039813b4f33100e4ef2e17960309f3c6c9ab120f5c55de747992" - name = "golang.org/x/tools" + digest = "1:f3bbd8ea54cde834a67fc50f27cbdf35eb950225953fb304891be068ba82d163" + name = "google.golang.org/protobuf" packages = [ - "go/ast/astutil", - "go/gcexportdata", - "go/internal/gcimporter", - "go/internal/packagesdriver", - "go/packages", - "go/types/typeutil", - "internal/fastwalk", - "internal/gopathwalk", - "internal/semver", + "encoding/prototext", + "encoding/protowire", + "internal/descfmt", + "internal/descopts", + "internal/detrand", + "internal/encoding/defval", + "internal/encoding/messageset", + "internal/encoding/tag", + "internal/encoding/text", + "internal/errors", + "internal/fieldnum", + "internal/fieldsort", + "internal/filedesc", + "internal/filetype", + "internal/flags", + "internal/genname", + "internal/impl", + "internal/mapsort", + "internal/pragma", + "internal/set", + "internal/strs", + "internal/version", + "proto", + "reflect/protoreflect", + "reflect/protoregistry", + "runtime/protoiface", + "runtime/protoimpl", ] - pruneopts = "UT" - revision = "caa95bb40b630f80d344d1f710f7e39be971d3e8" + pruneopts = "" + revision = "d165be301fb1e13390ad453281ded24385fd8ebc" + version = "v1.23.0" [solve-meta] analyzer-name = "dep" @@ -320,11 +318,11 @@ "github.com/bluele/gcache", "github.com/davecgh/go-spew/spew", "github.com/dgraph-io/badger", + "github.com/gofrs/uuid", "github.com/google/renameio", "github.com/gorilla/mux", "github.com/gorilla/websocket", "github.com/hashicorp/go-version", - "github.com/satori/go.uuid", "github.com/seehuhn/fortuna", "github.com/shirou/gopsutil/host", "github.com/spf13/cobra", diff --git a/Gopkg.toml b/Gopkg.toml index e5fae40..d988731 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -23,72 +23,3 @@ # non-go = false # go-tests = true # unused-packages = true - - -[[constraint]] - name = "github.com/aead/serpent" - version = "0.1.0" - -[[constraint]] - branch = "master" - name = "github.com/bluele/gcache" - -[[constraint]] - name = "github.com/davecgh/go-spew" - version = "1.1.1" - -[[constraint]] - name = "github.com/dgraph-io/badger" - version = "1.5.4" - -[[constraint]] - name = "github.com/google/renameio" - version = "0.1.0" - -[[constraint]] - name = "github.com/gorilla/mux" - version = "1.7.3" - -[[constraint]] - name = "github.com/gorilla/websocket" - version = "1.4.0" - -[[constraint]] - branch = "master" - name = "github.com/seehuhn/fortuna" - -[[constraint]] - branch = "master" - name = "github.com/tevino/abool" - -[[constraint]] - name = "github.com/tidwall/gjson" - version = "1.2.1" - -[[constraint]] - name = "github.com/tidwall/sjson" - version = "1.0.4" - -[[constraint]] - name = "go.etcd.io/bbolt" - version = "1.3.2" - -[[constraint]] - branch = "master" - name = "golang.org/x/sys" - -[prune] - go-tests = true - unused-packages = true - -[[constraint]] - name = "github.com/satori/go.uuid" - version = "1.2.0" - -[[constraint]] - name = "github.com/shirou/gopsutil" - version = "2.19.6" - -[[constraint]] - name = "github.com/hashicorp/go-version" - version = "1.2.0" diff --git a/notifications/notification.go b/notifications/notification.go index 08778a4..78a8790 100644 --- a/notifications/notification.go +++ b/notifications/notification.go @@ -8,8 +8,7 @@ import ( "github.com/safing/portbase/database" "github.com/safing/portbase/database/record" "github.com/safing/portbase/log" - - uuid "github.com/satori/go.uuid" + "github.com/safing/portbase/utils" ) // Notification types @@ -96,7 +95,7 @@ func notify(nType uint8, id string, msg string, actions ...Action) *Notification } if id == "" { - id = uuid.NewV4().String() + id = utils.DerivedInstanceUUID(msg).String() } n := Notification{ @@ -121,7 +120,7 @@ func (n *Notification) Save() *Notification { n.Created = time.Now().Unix() } if n.GUID == "" { - n.GUID = uuid.NewV4().String() + n.GUID = utils.RandomUUID(n.ID).String() } // make ack notification if there are no defined actions diff --git a/utils/onceagain.go b/utils/onceagain.go new file mode 100644 index 0000000..0d34622 --- /dev/null +++ b/utils/onceagain.go @@ -0,0 +1,78 @@ +package utils + +// This file is forked from https://github.com/golang/go/blob/bc593eac2dc63d979a575eccb16c7369a5ff81e0/src/sync/once.go. + +import ( + "sync" + "sync/atomic" +) + +// OnceAgain is an object that will perform only one action "in flight". It's basically the same as sync.Once, but is automatically reused when the function was executed and everyone who waited has left. +type OnceAgain struct { + // done indicates whether the action has been performed. + // It is first in the struct because it is used in the hot path. + // The hot path is inlined at every call site. + // Placing done first allows more compact instructions on some architectures (amd64/x86), + // and fewer instructions (to calculate offset) on other architectures. + done uint32 + + // Number of waiters waiting for the function to finish. The last waiter resets done. + waiters int32 + + m sync.Mutex +} + +// Do calls the function f if and only if Do is being called for the +// first time for this instance of Once. In other words, given +// var once Once +// if once.Do(f) is called multiple times, only the first call will invoke f, +// even if f has a different value in each invocation. A new instance of +// Once is required for each function to execute. +// +// Do is intended for initialization that must be run exactly once. Since f +// is niladic, it may be necessary to use a function literal to capture the +// arguments to a function to be invoked by Do: +// config.once.Do(func() { config.init(filename) }) +// +// Because no call to Do returns until the one call to f returns, if f causes +// Do to be called, it will deadlock. +// +// If f panics, Do considers it to have returned; future calls of Do return +// without calling f. +// +func (o *OnceAgain) Do(f func()) { + // Note: Here is an incorrect implementation of Do: + // + // if atomic.CompareAndSwapUint32(&o.done, 0, 1) { + // f() + // } + // + // Do guarantees that when it returns, f has finished. + // This implementation would not implement that guarantee: + // given two simultaneous calls, the winner of the cas would + // call f, and the second would return immediately, without + // waiting for the first's call to f to complete. + // This is why the slow path falls back to a mutex, and why + // the atomic.StoreUint32 must be delayed until after f returns. + + if atomic.LoadUint32(&o.done) == 0 { + // Outlined slow-path to allow inlining of the fast-path. + o.doSlow(f) + } +} + +func (o *OnceAgain) doSlow(f func()) { + atomic.AddInt32(&o.waiters, 1) + defer func() { + if atomic.AddInt32(&o.waiters, -1) == 0 { + atomic.StoreUint32(&o.done, 0) // reset + } + }() + + o.m.Lock() + defer o.m.Unlock() + if o.done == 0 { + defer atomic.StoreUint32(&o.done, 1) + f() + } +} diff --git a/utils/onceagain_test.go b/utils/onceagain_test.go new file mode 100644 index 0000000..06fc6ec --- /dev/null +++ b/utils/onceagain_test.go @@ -0,0 +1,55 @@ +package utils + +import ( + "sync" + "sync/atomic" + "testing" + "time" + + "github.com/tevino/abool" +) + +func TestOnceAgain(t *testing.T) { + + oa := OnceAgain{} + executed := abool.New() + var testWg sync.WaitGroup + + // basic + for i := 0; i < 10; i++ { + testWg.Add(100) + for i := 0; i < 100; i++ { + go func() { + oa.Do(func() { + if !executed.SetToIf(false, true) { + t.Errorf("concurrent execution!") + } + time.Sleep(10 * time.Millisecond) + }) + testWg.Done() + }() + } + testWg.Wait() + executed.UnSet() // reset check + } + + // streaming + var execs uint32 + testWg.Add(100) + for i := 0; i < 100; i++ { + go func() { + oa.Do(func() { + atomic.AddUint32(&execs, 1) + time.Sleep(10 * time.Millisecond) + }) + testWg.Done() + }() + + time.Sleep(1 * time.Millisecond) + } + + testWg.Wait() + if execs >= 20 { + t.Errorf("unexpected high exec count: %d", execs) + } +} diff --git a/utils/uuid.go b/utils/uuid.go new file mode 100644 index 0000000..8d185c4 --- /dev/null +++ b/utils/uuid.go @@ -0,0 +1,45 @@ +package utils + +import ( + "encoding/binary" + "time" + + "github.com/gofrs/uuid" +) + +var ( + constantUUID = uuid.Must(uuid.FromString("e8dba9f7-21e2-4c82-96cb-6586922c6422")) + instanceUUID = RandomUUID("instance") +) + +// RandomUUID returns a new random UUID with optionally provided ns +func RandomUUID(ns string) uuid.UUID { + randUUID, err := uuid.NewV4() + switch { + case err != nil: + // fallback + // should practically never happen + return uuid.NewV5(uuidFromTime(), ns) + case ns != "": + // mix ns into the UUID + return uuid.NewV5(randUUID, ns) + default: + return randUUID + } +} + +// DerivedUUID returns a new UUID that is derived from the input only, and therefore is always reproducible. +func DerivedUUID(input string) uuid.UUID { + return uuid.NewV5(constantUUID, input) +} + +// DerivedInstanceUUID returns a new UUID that is derived from the input, but is unique per instance (execution) and therefore is only reproducible with the same process. +func DerivedInstanceUUID(input string) uuid.UUID { + return uuid.NewV5(instanceUUID, input) +} + +func uuidFromTime() uuid.UUID { + var timeUUID uuid.UUID + binary.LittleEndian.PutUint64(timeUUID[:], uint64(time.Now().UnixNano())) + return timeUUID +} diff --git a/utils/uuid_test.go b/utils/uuid_test.go new file mode 100644 index 0000000..fff8825 --- /dev/null +++ b/utils/uuid_test.go @@ -0,0 +1,69 @@ +package utils + +import ( + "testing" + "time" + + "github.com/gofrs/uuid" +) + +func TestUUID(t *testing.T) { + // check randomness + a := RandomUUID("") + a2 := RandomUUID("") + if a.String() == a2.String() { + t.Error("should not match") + } + + // check with input + b := RandomUUID("b") + b2 := RandomUUID("b") + if b.String() == b2.String() { + t.Error("should not match") + } + + // check with long input + c := RandomUUID("TG8UkxS+4rVrDxHtDAaNab1CBpygzmX1g5mJA37jbQ5q2uE4rVrDxHtDAaNab1CBpygzmX1g5mJA37jbQ5q2uE") + c2 := RandomUUID("TG8UkxS+4rVrDxHtDAaNab1CBpygzmX1g5mJA37jbQ5q2uE4rVrDxHtDAaNab1CBpygzmX1g5mJA37jbQ5q2uE") + if c.String() == c2.String() { + t.Error("should not match") + } + + // check for nanosecond precision + d := uuidFromTime() + time.Sleep(2 * time.Nanosecond) + d2 := uuidFromTime() + if d.String() == d2.String() { + t.Error("should not match") + } + + // check mixing + timeUUID := uuidFromTime() + e := uuid.NewV5(timeUUID, "e") + e2 := uuid.NewV5(timeUUID, "e2") + if e.String() == e2.String() { + t.Error("should not match") + } + + // check deriving + f := DerivedUUID("f") + f2 := DerivedUUID("f") + f3 := DerivedUUID("f3") + if f.String() != f2.String() { + t.Error("should match") + } + if f.String() == f3.String() { + t.Error("should not match") + } + + // check instance deriving + g := DerivedInstanceUUID("g") + g2 := DerivedInstanceUUID("g") + g3 := DerivedInstanceUUID("g3") + if g.String() != g2.String() { + t.Error("should match") + } + if g.String() == g3.String() { + t.Error("should not match") + } +}