├── .codecov.yml ├── .excludecoverage ├── .excludefmt ├── .excludelint ├── .excludemetalint ├── .gitignore ├── .gitmodules ├── .metalinter.json ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── Makefile ├── README.md ├── checked ├── bytes.go ├── bytes_test.go ├── checked_mock.go ├── debug.go ├── debug_test.go ├── ref.go ├── ref_test.go └── types.go ├── clock ├── config.go ├── options.go ├── types.go └── types_test.go ├── close ├── close.go └── close_test.go ├── config ├── config.go ├── config_test.go ├── example_test.go ├── hostid │ ├── hostid.go │ └── hostid_test.go ├── listenaddress │ ├── listenaddress.go │ └── listenaddress_test.go ├── pooling.go ├── pooling_test.go └── testdata │ └── conf.yaml ├── context ├── cancel.go ├── cancel_test.go ├── context.go ├── context_test.go ├── finalizeables_arraypool_gen.go ├── options.go ├── pool.go ├── pool_test.go └── types.go ├── debug ├── mutex.go └── mutex_test.go ├── doc.go ├── errors ├── errors.go ├── errors_test.go └── example_test.go ├── generated-source-files.mk ├── generated └── mocks │ └── generate.go ├── generics ├── arraypool │ ├── pool.go │ └── pool_test.go ├── hashmap │ ├── byteskey │ │ ├── map_gen.go │ │ ├── map_pool_test.go │ │ └── new_map.go │ ├── idkey │ │ ├── map_gen.go │ │ ├── map_pool_test.go │ │ └── new_map.go │ ├── map.go │ └── map_test.go └── leakcheckpool │ ├── pool.go │ └── pool_test.go ├── glide.lock ├── glide.yaml ├── hash ├── doc.go └── jump │ ├── doc.go │ ├── example_test.go │ ├── jump.go │ └── jump_test.go ├── ident ├── bytes_id.go ├── id_matcher.go ├── ident_mock.go ├── identifier.go ├── identifier_pool.go ├── identifier_pool_test.go ├── identifier_test.go ├── iterator.go ├── iterator_test.go ├── tag.go ├── tag_arraypool_gen.go ├── tag_iterator.go ├── tag_iterator_matcher.go ├── tag_iterator_matcher_test.go ├── tag_iterator_test.go ├── tag_matcher.go ├── tags_test.go ├── testutil │ ├── tags_convert.go │ └── tags_convert_test.go └── types.go ├── instrument ├── build.go ├── build_test.go ├── config.go ├── extended.go ├── invariant.go ├── invariant_test.go ├── methods.go ├── options.go ├── process.go ├── process_test.go ├── reporter.go ├── sanitize.go └── types.go ├── log ├── config.go ├── config_test.go ├── logger.go └── logger_test.go ├── net ├── example_test.go ├── server.go └── server_test.go ├── opentracing ├── context.go ├── context_test.go ├── tracing.go └── tracing_test.go ├── pool ├── bucketized.go ├── bytes.go ├── bytes_test.go ├── checked_bytes.go ├── checked_bytes_test.go ├── checked_object.go ├── checked_object_test.go ├── config.go ├── config_test.go ├── example_test.go ├── floats.go ├── floats_test.go ├── object.go ├── object_test.go ├── options.go ├── pool_mock.go └── types.go ├── pprof ├── pprof.go └── pprof_test.go ├── process ├── process_linux.go └── process_notlinux.go ├── resource └── types.go ├── retry ├── config.go ├── config_test.go ├── example_test.go ├── options.go ├── retry.go ├── retry_test.go └── types.go ├── sampler ├── sampler.go └── sampler_test.go ├── server ├── config.go ├── config_test.go ├── example_test.go ├── options.go ├── server.go └── server_test.go ├── sync ├── example_test.go ├── options.go ├── pooled_worker_pool.go ├── pooled_worker_pool_test.go ├── types.go ├── worker_pool.go └── worker_pool_test.go ├── tcp └── tcp.go ├── test ├── matcher.go └── reporter.go ├── time ├── duration.go ├── duration_test.go ├── matcher.go ├── matcher_test.go ├── range.go ├── range_iter.go ├── range_iter_test.go ├── range_test.go ├── ranges.go ├── ranges_test.go ├── time.go ├── time_test.go ├── unit.go ├── unit_test.go ├── unix_nano.go └── unix_nano_test.go ├── unsafe ├── bytes.go ├── bytes_test.go ├── string.go └── string_test.go └── watch ├── options.go ├── source.go ├── source_test.go ├── value.go ├── value_test.go ├── watch.go └── watch_test.go /.codecov.yml: -------------------------------------------------------------------------------- 1 | coverage: 2 | precision: 2 3 | round: down 4 | range: "70...100" 5 | 6 | status: 7 | project: 8 | default: on 9 | patch: 10 | default: on 11 | changes: 12 | default: on 13 | 14 | comment: 15 | layout: "header, reach, diff, flags, footer" 16 | behavior: default 17 | require_changes: no 18 | require_base: no 19 | require_head: yes 20 | -------------------------------------------------------------------------------- /.excludecoverage: -------------------------------------------------------------------------------- 1 | _mock.go 2 | generated/ 3 | vendor/ 4 | integration/ 5 | testgen/ 6 | -------------------------------------------------------------------------------- /.excludefmt: -------------------------------------------------------------------------------- 1 | (vendor/) 2 | (proto/) 3 | (gen-go/) 4 | (mocks/) 5 | (.pb.go) -------------------------------------------------------------------------------- /.excludelint: -------------------------------------------------------------------------------- 1 | (vendor/) 2 | (proto/) 3 | (gen-go/) 4 | (mocks/) 5 | (.pb.go) 6 | (_mock.go) 7 | (_gen.go) 8 | -------------------------------------------------------------------------------- /.excludemetalint: -------------------------------------------------------------------------------- 1 | vendor/ 2 | generated/ 3 | _mock.go 4 | _gen.go 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.out 2 | *.test 3 | *.xml 4 | *.swp 5 | .idea/ 6 | .vscode/ 7 | *.iml 8 | *.ipr 9 | *.iws 10 | *.cov 11 | *.html 12 | test.log 13 | 14 | # Build binaries 15 | bin/ 16 | 17 | # Debug binaries 18 | debug.test 19 | 20 | # Glide 21 | vendor/ 22 | 23 | # Generated source 24 | codegen/ 25 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule ".ci"] 2 | path = .ci 3 | url = https://github.com/m3db/ci-scripts.git 4 | -------------------------------------------------------------------------------- /.metalinter.json: -------------------------------------------------------------------------------- 1 | { 2 | "Linters": { 3 | "badtime": { 4 | "Command": "badtime", 5 | "Pattern": "PATH:LINE:COL:MESSAGE" 6 | } 7 | }, 8 | "Enable": 9 | [ "golint" 10 | , "deadcode" 11 | , "varcheck" 12 | , "structcheck" 13 | , "goconst" 14 | , "ineffassign" 15 | , "unconvert" 16 | , "misspell" 17 | , "unparam" 18 | , "megacheck" 19 | , "badtime" ], 20 | "Deadline": "3m", 21 | "EnableGC": true 22 | } 23 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | Changelog 2 | ========= 3 | 4 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Contributing 2 | ============ 3 | 4 | We'd love your help making M3DB great! 5 | 6 | ## Getting Started 7 | 8 | M3DB uses Go vendoring to manage dependencies. 9 | To get started: 10 | 11 | ```bash 12 | git submodule update --init --recursive 13 | make test 14 | ``` 15 | 16 | ## Making A Change 17 | 18 | *Before making any significant changes, please [open an 19 | issue](https://github.com/m3db/m3x/issues).* Discussing your proposed 20 | changes ahead of time will make the contribution process smooth for everyone. 21 | 22 | Once we've discussed your changes and you've got your code ready, make sure 23 | that tests are passing (`make test` or `make cover`) and open your PR! Your 24 | pull request is most likely to be accepted if it: 25 | 26 | * Includes tests for new functionality. 27 | * Follows the guidelines in [Effective 28 | Go](https://golang.org/doc/effective_go.html) and the [Go team's common code 29 | review comments](https://github.com/golang/go/wiki/CodeReviewComments). 30 | * Has a [good commit 31 | message](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html). 32 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SELF_DIR := $(dir $(lastword $(MAKEFILE_LIST))) 2 | include $(SELF_DIR)/.ci/common.mk 3 | include $(SELF_DIR)/generated-source-files.mk 4 | 5 | SHELL=/bin/bash -o pipefail 6 | 7 | html_report := coverage.html 8 | test := .ci/test-cover.sh 9 | convert-test-data := .ci/convert-test-data.sh 10 | coverage_xml := coverage.xml 11 | junit_xml := junit.xml 12 | test_log := test.log 13 | metalint_check := .ci/metalint.sh 14 | metalint_config := .metalinter.json 15 | metalint_exclude := .excludemetalint 16 | license_dir := .ci/uber-licence 17 | license_node_modules := $(license_dir)/node_modules 18 | gopath_prefix := $(GOPATH)/src 19 | vendor_prefix := vendor 20 | auto_gen := .ci/auto-gen.sh 21 | m3x_package := github.com/m3db/m3x 22 | mockgen_package := github.com/golang/mock/mockgen 23 | mocks_output_dir := generated/mocks/mocks 24 | mocks_rules_dir := generated/mocks 25 | 26 | BUILD := $(abspath ./bin) 27 | LINUX_AMD64_ENV := GOOS=linux GOARCH=amd64 CGO_ENABLED=0 28 | 29 | .PHONY: setup 30 | setup: 31 | mkdir -p $(BUILD) 32 | 33 | .PHONY: metalint 34 | metalint: install-metalinter install-linter-badtime 35 | @($(metalint_check) $(metalint_config) $(metalint_exclude) && echo "metalinted successfully!") || (echo "metalinter failed" && exit 1) 36 | 37 | .PHONY: test-internal 38 | test-internal: 39 | @which go-junit-report > /dev/null || go get -u github.com/sectioneight/go-junit-report 40 | $(test) $(coverfile) | tee $(test_log) 41 | 42 | .PHONY: test-xml 43 | test-xml: test-internal 44 | go-junit-report < $(test_log) > $(junit_xml) 45 | gocov convert $(coverfile) | gocov-xml > $(coverage_xml) 46 | @$(convert-test-data) $(coverage_xml) 47 | @rm $(coverfile) &> /dev/null 48 | 49 | .PHONY: test 50 | test: test-internal 51 | gocov convert $(coverfile) | gocov report 52 | 53 | .PHONY: testhtml 54 | testhtml: test-internal 55 | gocov convert $(coverfile) | gocov-html > $(html_report) && open $(html_report) 56 | @rm -f $(test_log) &> /dev/null 57 | 58 | .PHONY: test-ci-unit 59 | test-ci-unit: test-base 60 | $(codecov_push) -f $(coverfile) 61 | 62 | .PHONY: install-license-bin 63 | install-license-bin: 64 | @echo Installing node modules 65 | [ -d $(license_node_modules) ] || ( \ 66 | git submodule update --init --recursive && \ 67 | cd $(license_dir) && npm install \ 68 | ) 69 | 70 | .PHONY: install-mockgen 71 | install-mockgen: 72 | @echo Installing mockgen 73 | @which mockgen >/dev/null || (make install-vendor && \ 74 | rm -rf $(gopath_prefix)/$(mockgen_package) && \ 75 | cp -r $(vendor_prefix)/$(mockgen_package) $(gopath_prefix)/$(mockgen_package) && \ 76 | go install $(mockgen_package) \ 77 | ) 78 | 79 | .PHONY: mock-gen 80 | mock-gen: install-mockgen install-license-bin install-util-mockclean 81 | @echo Generating mocks 82 | PACKAGE=$(m3x_package) $(auto_gen) $(mocks_output_dir) $(mocks_rules_dir) 83 | 84 | .PHONY: all-gen 85 | all-gen: mock-gen genny-all 86 | 87 | .PHONY: clean 88 | clean: 89 | @rm -f *.html *.xml *.out *.test 90 | 91 | .PHONY: all 92 | all: metalint test-ci-unit test-genny-all 93 | @echo Made all successfully 94 | 95 | .DEFAULT_GOAL := all 96 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Migration Warning 2 | ================= 3 | This repository has been migrated to github.com/m3db/m3. It's contents can be found at github.com/m3db/m3/src/x. Follow along there for updates. This repository is marked archived, and will no longer receive any updates. 4 | 5 | # M3X [![GoDoc][doc-img]][doc] [![Build Status][ci-img]][ci] [![Coverage Status][cov-img]][cov] 6 | 7 | Common utility code shared by all M3DB components. 8 | 9 |
10 | 11 | This project is released under the [Apache License, Version 2.0](LICENSE). 12 | 13 | [doc-img]: https://godoc.org/github.com/m3db/m3x?status.svg 14 | [doc]: https://godoc.org/github.com/m3db/m3x 15 | [ci-img]: https://travis-ci.org/m3db/m3x.svg?branch=master 16 | [ci]: https://travis-ci.org/m3db/m3x 17 | [cov-img]: https://coveralls.io/repos/m3db/m3x/badge.svg?branch=master&service=github 18 | [cov]: https://coveralls.io/github/m3db/m3x?branch=master 19 | 20 | # Development 21 | 22 | ## Setup 23 | 24 | 1. Clone the repo into your $GOPATH 25 | 2. Run `git submodule update --init --recursive` 26 | 3. Run `glide install -v` - [Install Glide first if you don't have it](https://github.com/Masterminds/glide) 27 | 4. Run `make test` and make sure everything passes 28 | 5. Write new code and tests 29 | -------------------------------------------------------------------------------- /checked/bytes_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package checked 22 | 23 | import ( 24 | "testing" 25 | 26 | "github.com/stretchr/testify/assert" 27 | ) 28 | 29 | func TestBytes(t *testing.T) { 30 | raw := make([]byte, 3, 5) 31 | copy(raw, []byte{'a', 'b', 'c'}) 32 | 33 | var onFinalize BytesFinalizerFn 34 | finalizer := BytesFinalizerFn(func(finalizing Bytes) { 35 | onFinalize(finalizing) 36 | }) 37 | 38 | b := NewBytes(raw, NewBytesOptions().SetFinalizer(finalizer)) 39 | b.IncRef() 40 | 41 | assert.Equal(t, []byte("abc"), b.Bytes()) 42 | assert.Equal(t, 3, b.Len()) 43 | assert.Equal(t, 5, b.Cap()) 44 | 45 | b.Append('d') 46 | b.AppendAll([]byte{'e', 'f'}) 47 | 48 | assert.Equal(t, []byte("abcdef"), b.Bytes()) 49 | assert.Equal(t, 6, b.Len()) 50 | 51 | b.Resize(4) 52 | assert.Equal(t, []byte("abcd"), b.Bytes()) 53 | assert.Equal(t, 4, b.Len()) 54 | 55 | b.Reset([]byte{'x', 'y', 'z'}) 56 | assert.Equal(t, []byte("xyz"), b.Bytes()) 57 | assert.Equal(t, 3, b.Len()) 58 | 59 | b.DecRef() 60 | 61 | finalizerCalls := 0 62 | onFinalize = func(finalizing Bytes) { 63 | // Ensure closing the ref we created 64 | assert.Equal(t, b, finalizing) 65 | finalizing.IncRef() 66 | assert.Equal(t, []byte("xyz"), finalizing.Bytes()) 67 | finalizing.DecRef() 68 | finalizerCalls++ 69 | } 70 | 71 | b.Finalize() 72 | assert.Equal(t, 1, finalizerCalls) 73 | } 74 | -------------------------------------------------------------------------------- /clock/config.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package clock 22 | 23 | import "time" 24 | 25 | // Configuration configures clock options. 26 | type Configuration struct { 27 | // MaxPositiveSkew is the maximum positive clock skew 28 | // with regard to a reference clock. 29 | MaxPositiveSkew time.Duration `yaml:"maxPositiveSkew"` 30 | 31 | // MaxNegativeSkew is the maximum negative clock skew 32 | // with regard to a reference clock. 33 | MaxNegativeSkew time.Duration `yaml:"maxNegativeSkew"` 34 | } 35 | 36 | // NewOptions creates a new set of options. 37 | func (c Configuration) NewOptions() Options { 38 | opts := NewOptions() 39 | if c.MaxPositiveSkew != 0 { 40 | opts = opts.SetMaxPositiveSkew(c.MaxPositiveSkew) 41 | } 42 | if c.MaxNegativeSkew != 0 { 43 | opts = opts.SetMaxNegativeSkew(c.MaxNegativeSkew) 44 | } 45 | return opts 46 | } 47 | -------------------------------------------------------------------------------- /clock/options.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package clock 22 | 23 | import ( 24 | "time" 25 | ) 26 | 27 | type options struct { 28 | nowFn NowFn 29 | maxPositiveSkew time.Duration 30 | maxNegativeSkew time.Duration 31 | } 32 | 33 | // NewOptions creates new clock options. 34 | func NewOptions() Options { 35 | return &options{ 36 | nowFn: time.Now, 37 | } 38 | } 39 | 40 | func (o *options) SetNowFn(value NowFn) Options { 41 | opts := *o 42 | opts.nowFn = value 43 | return &opts 44 | } 45 | 46 | func (o *options) NowFn() NowFn { 47 | return o.nowFn 48 | } 49 | 50 | func (o *options) SetMaxPositiveSkew(value time.Duration) Options { 51 | opts := *o 52 | opts.maxPositiveSkew = value 53 | return &opts 54 | } 55 | 56 | func (o *options) MaxPositiveSkew() time.Duration { 57 | return o.maxPositiveSkew 58 | } 59 | 60 | func (o *options) SetMaxNegativeSkew(value time.Duration) Options { 61 | opts := *o 62 | opts.maxNegativeSkew = value 63 | return &opts 64 | } 65 | 66 | func (o *options) MaxNegativeSkew() time.Duration { 67 | return o.maxNegativeSkew 68 | } 69 | -------------------------------------------------------------------------------- /clock/types.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | // Package clock implements facilities for working with wall clock time. 22 | package clock 23 | 24 | import ( 25 | "time" 26 | ) 27 | 28 | // NowFn is the function supplied to determine "now". 29 | type NowFn func() time.Time 30 | 31 | // Options represents the options for the clock. 32 | type Options interface { 33 | // SetNowFn sets the NowFn. 34 | SetNowFn(value NowFn) Options 35 | 36 | // NowFn returns the NowFn. 37 | NowFn() NowFn 38 | 39 | // SetMaxPositiveSkew sets the maximum positive clock skew 40 | // with regard to a reference clock. 41 | SetMaxPositiveSkew(value time.Duration) Options 42 | 43 | // MaxPositiveSkew returns the maximum positive clock skew 44 | // with regard to a reference clock. 45 | MaxPositiveSkew() time.Duration 46 | 47 | // SetMaxNegativeSkew sets the maximum negative clock skew 48 | // with regard to a reference clock. 49 | SetMaxNegativeSkew(value time.Duration) Options 50 | 51 | // MaxNegativeSkew returns the maximum negative clock skew 52 | // with regard to a reference clock. 53 | MaxNegativeSkew() time.Duration 54 | } 55 | 56 | // ConditionFn specifies a predicate to check. 57 | type ConditionFn func() bool 58 | 59 | // WaitUntil returns true if the condition specified evaluated to 60 | // true before the timeout, false otherwise. 61 | func WaitUntil(fn ConditionFn, timeout time.Duration) bool { 62 | deadline := time.Now().Add(timeout) 63 | for time.Now().Before(deadline) { 64 | if fn() { 65 | return true 66 | } 67 | time.Sleep(100 * time.Millisecond) 68 | } 69 | return false 70 | } 71 | -------------------------------------------------------------------------------- /clock/types_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package clock 22 | 23 | import ( 24 | "testing" 25 | "time" 26 | 27 | "github.com/stretchr/testify/require" 28 | ) 29 | 30 | func TestAlwaysFalseConditionFn(t *testing.T) { 31 | falseConditionFn := func() bool { return false } 32 | require.False(t, WaitUntil(falseConditionFn, time.Second)) 33 | } 34 | 35 | func TestAlwaysTrueConditionFn(t *testing.T) { 36 | trueConditionFn := func() bool { return true } 37 | require.True(t, WaitUntil(trueConditionFn, time.Second)) 38 | } 39 | 40 | func TestDeadlineExpiredConditionFn(t *testing.T) { 41 | count := 0 42 | delayedConditionFn := func() bool { 43 | count++ 44 | return count > 4 45 | } 46 | require.True(t, WaitUntil(delayedConditionFn, time.Second)) 47 | } 48 | -------------------------------------------------------------------------------- /close/close.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | // Package close provides utilities for closing resources. 22 | package close 23 | 24 | import ( 25 | "errors" 26 | "io" 27 | ) 28 | 29 | var ( 30 | // ErrNotCloseable is returned when trying to close a resource 31 | // that does not conform to a closeable interface. 32 | ErrNotCloseable = errors.New("not a closeable resource") 33 | ) 34 | 35 | // Closer is a resource that can be closed. 36 | type Closer interface { 37 | io.Closer 38 | } 39 | 40 | // SimpleCloser is a resource that can be closed without returning a result. 41 | type SimpleCloser interface { 42 | Close() 43 | } 44 | 45 | // TryClose attempts to close a resource, the resource is expected to 46 | // implement either Closeable or CloseableResult. 47 | func TryClose(r interface{}) error { 48 | if r, ok := r.(Closer); ok { 49 | return r.Close() 50 | } 51 | if r, ok := r.(SimpleCloser); ok { 52 | r.Close() 53 | return nil 54 | } 55 | return ErrNotCloseable 56 | } 57 | -------------------------------------------------------------------------------- /close/close_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package close 22 | 23 | import ( 24 | "errors" 25 | "testing" 26 | 27 | "github.com/stretchr/testify/assert" 28 | ) 29 | 30 | func TestTryClose(t *testing.T) { 31 | tests := []struct { 32 | input interface{} 33 | expectErr bool 34 | }{ 35 | { 36 | input: &closer{returnErr: false}, 37 | expectErr: false, 38 | }, 39 | { 40 | input: &closer{returnErr: true}, 41 | expectErr: true, 42 | }, 43 | { 44 | input: &simpleCloser{}, 45 | expectErr: false, 46 | }, 47 | { 48 | input: &nonCloser{}, 49 | expectErr: true, 50 | }, 51 | } 52 | 53 | for _, test := range tests { 54 | err := TryClose(test.input) 55 | if test.expectErr { 56 | assert.Error(t, err) 57 | continue 58 | } 59 | assert.NoError(t, err) 60 | } 61 | } 62 | 63 | type closer struct { 64 | returnErr bool 65 | } 66 | 67 | func (c *closer) Close() error { 68 | if c.returnErr { 69 | return errors.New("") 70 | } 71 | return nil 72 | } 73 | 74 | type simpleCloser struct{} 75 | 76 | func (c *simpleCloser) Close() {} 77 | 78 | type nonCloser struct{} 79 | -------------------------------------------------------------------------------- /config/config.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | // Package config provides utilities for loading configuration files. 22 | package config 23 | 24 | import ( 25 | "errors" 26 | "io/ioutil" 27 | 28 | validator "gopkg.in/validator.v2" 29 | yaml "gopkg.in/yaml.v2" 30 | ) 31 | 32 | var errNoFilesToLoad = errors.New("attempt to load config with no files") 33 | 34 | // Options is an options set used when parsing config. 35 | type Options struct { 36 | DisableUnmarshalStrict bool 37 | DisableValidate bool 38 | } 39 | 40 | // LoadFile loads a config from a file. 41 | func LoadFile(config interface{}, file string, opts Options) error { 42 | return LoadFiles(config, []string{file}, opts) 43 | } 44 | 45 | // LoadFiles loads a config from list of files. If value for a property is 46 | // present in multiple files, the value from the last file will be applied. 47 | // Validation is done after merging all values. 48 | func LoadFiles(config interface{}, files []string, opts Options) error { 49 | if len(files) == 0 { 50 | return errNoFilesToLoad 51 | } 52 | for _, name := range files { 53 | data, err := ioutil.ReadFile(name) 54 | if err != nil { 55 | return err 56 | } 57 | unmarshal := yaml.UnmarshalStrict 58 | if opts.DisableUnmarshalStrict { 59 | unmarshal = yaml.Unmarshal 60 | } 61 | if err := unmarshal(data, config); err != nil { 62 | return err 63 | } 64 | } 65 | if opts.DisableValidate { 66 | return nil 67 | } 68 | return validator.Validate(config) 69 | } 70 | -------------------------------------------------------------------------------- /config/example_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package config_test 22 | 23 | import ( 24 | "fmt" 25 | "log" 26 | 27 | "github.com/m3db/m3x/config" 28 | ) 29 | 30 | type configuration struct { 31 | ListenAddress string `yaml:"listenAddress" validate:"nonzero"` 32 | } 33 | 34 | func ExampleLoadFile() { 35 | var cfg configuration 36 | file := "testdata/conf.yaml" 37 | if err := config.LoadFile(&cfg, file, config.Options{}); err != nil { 38 | log.Fatal(err) 39 | } 40 | fmt.Printf("listenAddress: %s\n", cfg.ListenAddress) 41 | // Output: listenAddress: 0.0.0.0:8392 42 | } 43 | -------------------------------------------------------------------------------- /config/pooling.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package config 22 | 23 | import "github.com/m3db/m3x/sync" 24 | 25 | const ( 26 | defaultWorkerPoolStaticSize = 4096 27 | defaultGrowKillProbability = 0.001 28 | ) 29 | 30 | // WorkerPoolPolicy specifies the policy for the worker pool. 31 | type WorkerPoolPolicy struct { 32 | // Determines if the worker pool automatically grows to capacity. 33 | GrowOnDemand bool `yaml:"grow"` 34 | 35 | // Size for static pools, initial size for dynamically growing pools. 36 | Size int `yaml:"size"` 37 | 38 | // The number of shards for the pool. 39 | NumShards int64 `yaml:"shards"` 40 | 41 | // The probablility that a worker is killed after completing the task. 42 | KillWorkerProbability float64 `yaml:"killProbability" validate:"min=0.0,max=1.0"` 43 | } 44 | 45 | // Options converts the worker pool policy to options, providing 46 | // the options, as well as the default size for the worker pool. 47 | func (w WorkerPoolPolicy) Options() (sync.PooledWorkerPoolOptions, int) { 48 | opts := sync.NewPooledWorkerPoolOptions() 49 | grow := w.GrowOnDemand 50 | opts = opts.SetGrowOnDemand(grow) 51 | if w.KillWorkerProbability != 0 { 52 | opts = opts.SetKillWorkerProbability(w.KillWorkerProbability) 53 | } else if grow { 54 | // NB: if using a growing pool, default kill probability is too low, causing 55 | // the pool to quickly grow out of control. Use a higher default kill probability 56 | opts = opts.SetKillWorkerProbability(defaultGrowKillProbability) 57 | } 58 | 59 | if w.NumShards != 0 { 60 | opts = opts.SetNumShards(w.NumShards) 61 | } 62 | 63 | if w.Size == 0 { 64 | if grow { 65 | w.Size = int(opts.NumShards()) 66 | } else { 67 | w.Size = defaultWorkerPoolStaticSize 68 | } 69 | } 70 | 71 | return opts, w.Size 72 | } 73 | -------------------------------------------------------------------------------- /config/pooling_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | package config 21 | 22 | import ( 23 | "testing" 24 | 25 | "github.com/m3db/m3x/sync" 26 | "github.com/stretchr/testify/assert" 27 | ) 28 | 29 | func TestWorkerPoolPolicyConvertsToOptionsDefault(t *testing.T) { 30 | wpp := WorkerPoolPolicy{} 31 | opts, size := wpp.Options() 32 | defaultOpts := sync.NewPooledWorkerPoolOptions() 33 | 34 | assert.False(t, opts.GrowOnDemand()) 35 | assert.Equal(t, defaultOpts.NumShards(), opts.NumShards()) 36 | assert.Equal(t, defaultOpts.KillWorkerProbability(), opts.KillWorkerProbability()) 37 | assert.Equal(t, defaultWorkerPoolStaticSize, size) 38 | } 39 | 40 | func TestWorkerPoolPolicyConvertsToOptionsDefaultGrow(t *testing.T) { 41 | wpp := WorkerPoolPolicy{GrowOnDemand: true} 42 | opts, size := wpp.Options() 43 | defaultOpts := sync.NewPooledWorkerPoolOptions() 44 | 45 | assert.True(t, opts.GrowOnDemand()) 46 | assert.Equal(t, defaultOpts.NumShards(), opts.NumShards()) 47 | assert.Equal(t, defaultGrowKillProbability, opts.KillWorkerProbability()) 48 | assert.Equal(t, opts.NumShards(), int64(size)) 49 | } 50 | 51 | func TestWorkerPoolPolicyConvertsToOptions(t *testing.T) { 52 | wpp := WorkerPoolPolicy{ 53 | GrowOnDemand: true, 54 | Size: 100, 55 | NumShards: 200, 56 | KillWorkerProbability: 0.5, 57 | } 58 | opts, size := wpp.Options() 59 | assert.True(t, opts.GrowOnDemand()) 60 | assert.Equal(t, int64(200), opts.NumShards()) 61 | assert.Equal(t, 0.5, opts.KillWorkerProbability()) 62 | assert.Equal(t, 100, size) 63 | 64 | } 65 | -------------------------------------------------------------------------------- /config/testdata/conf.yaml: -------------------------------------------------------------------------------- 1 | listenAddress: "0.0.0.0:8392" 2 | -------------------------------------------------------------------------------- /context/cancel.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package context 22 | 23 | import "sync/atomic" 24 | 25 | type cancellable struct { 26 | cancelled int32 27 | } 28 | 29 | // NewCancellable creates a new cancellable object 30 | func NewCancellable() Cancellable { 31 | return &cancellable{} 32 | } 33 | 34 | func (c *cancellable) IsCancelled() bool { return atomic.LoadInt32(&c.cancelled) == 1 } 35 | func (c *cancellable) Cancel() { atomic.StoreInt32(&c.cancelled, 1) } 36 | func (c *cancellable) Reset() { atomic.StoreInt32(&c.cancelled, 0) } 37 | 38 | // NoOpCancellable is a no-op cancellable 39 | var noOpCancellable Cancellable = cancellableNoOp{} 40 | 41 | type cancellableNoOp struct{} 42 | 43 | // NewNoOpCanncellable returns a no-op cancellable 44 | func NewNoOpCanncellable() Cancellable { return noOpCancellable } 45 | func (c cancellableNoOp) IsCancelled() bool { return false } 46 | func (c cancellableNoOp) Cancel() {} 47 | func (c cancellableNoOp) Reset() {} 48 | -------------------------------------------------------------------------------- /context/cancel_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package context 22 | 23 | import ( 24 | "testing" 25 | 26 | "github.com/stretchr/testify/require" 27 | ) 28 | 29 | func TestCancellable(t *testing.T) { 30 | c := NewCancellable() 31 | require.False(t, c.IsCancelled()) 32 | 33 | c.Cancel() 34 | require.True(t, c.IsCancelled()) 35 | 36 | c.Reset() 37 | require.False(t, c.IsCancelled()) 38 | } 39 | 40 | func TestNoOpCancellable(t *testing.T) { 41 | c := NewNoOpCanncellable() 42 | require.False(t, c.IsCancelled()) 43 | 44 | c.Cancel() 45 | require.False(t, c.IsCancelled()) 46 | 47 | c.Reset() 48 | require.False(t, c.IsCancelled()) 49 | } 50 | -------------------------------------------------------------------------------- /context/options.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package context 22 | 23 | import "github.com/m3db/m3x/pool" 24 | 25 | const ( 26 | defaultInitFinalizersCap = 4 27 | defaultMaxFinalizersCap = 1 << 16 28 | ) 29 | 30 | type opts struct { 31 | contextPoolOpts pool.ObjectPoolOptions 32 | finalizerPoolOpts pool.ObjectPoolOptions 33 | maxPooledFinalizerCap int 34 | initPooledFinalizerCap int 35 | } 36 | 37 | // NewOptions returns a new Options object. 38 | func NewOptions() Options { 39 | return &opts{ 40 | contextPoolOpts: pool.NewObjectPoolOptions(), 41 | finalizerPoolOpts: pool.NewObjectPoolOptions(), 42 | maxPooledFinalizerCap: defaultMaxFinalizersCap, 43 | initPooledFinalizerCap: defaultInitFinalizersCap, 44 | } 45 | } 46 | 47 | func (o *opts) SetContextPoolOptions(po pool.ObjectPoolOptions) Options { 48 | opts := *o 49 | opts.contextPoolOpts = po 50 | return &opts 51 | } 52 | 53 | func (o *opts) ContextPoolOptions() pool.ObjectPoolOptions { 54 | return o.contextPoolOpts 55 | } 56 | 57 | func (o *opts) SetFinalizerPoolOptions(po pool.ObjectPoolOptions) Options { 58 | opts := *o 59 | opts.finalizerPoolOpts = po 60 | return &opts 61 | } 62 | 63 | func (o *opts) FinalizerPoolOptions() pool.ObjectPoolOptions { 64 | return o.finalizerPoolOpts 65 | } 66 | 67 | func (o *opts) SetMaxPooledFinalizerCapacity(max int) Options { 68 | opts := *o 69 | opts.maxPooledFinalizerCap = max 70 | return &opts 71 | } 72 | 73 | func (o *opts) MaxPooledFinalizerCapacity() int { 74 | return o.maxPooledFinalizerCap 75 | } 76 | 77 | func (o *opts) SetInitPooledFinalizerCapacity(init int) Options { 78 | opts := *o 79 | opts.initPooledFinalizerCap = init 80 | return &opts 81 | } 82 | 83 | func (o *opts) InitPooledFinalizerCapacity() int { 84 | return o.initPooledFinalizerCap 85 | } 86 | -------------------------------------------------------------------------------- /context/pool.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package context 22 | 23 | import ( 24 | "github.com/m3db/m3x/pool" 25 | ) 26 | 27 | type poolOfContexts struct { 28 | ctxPool pool.ObjectPool 29 | finalizersPool finalizeablesArrayPool 30 | } 31 | 32 | // NewPool creates a new context pool. 33 | func NewPool(opts Options) Pool { 34 | p := &poolOfContexts{ 35 | ctxPool: pool.NewObjectPool(opts.ContextPoolOptions()), 36 | finalizersPool: newFinalizeablesArrayPool(finalizeablesArrayPoolOpts{ 37 | Capacity: opts.InitPooledFinalizerCapacity(), 38 | MaxCapacity: opts.MaxPooledFinalizerCapacity(), 39 | Options: opts.FinalizerPoolOptions(), 40 | }), 41 | } 42 | 43 | p.finalizersPool.Init() 44 | p.ctxPool.Init(func() interface{} { 45 | return newPooledContext(p) 46 | }) 47 | 48 | return p 49 | } 50 | 51 | func (p *poolOfContexts) Get() Context { 52 | return p.ctxPool.Get().(Context) 53 | } 54 | 55 | func (p *poolOfContexts) Put(context Context) { 56 | p.ctxPool.Put(context) 57 | } 58 | 59 | func (p *poolOfContexts) getFinalizeables() []finalizeable { 60 | return p.finalizersPool.Get() 61 | } 62 | 63 | func (p *poolOfContexts) putFinalizeables(finalizeables []finalizeable) { 64 | p.finalizersPool.Put(finalizeables) 65 | } 66 | -------------------------------------------------------------------------------- /context/pool_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package context 22 | 23 | import ( 24 | "testing" 25 | 26 | "github.com/m3db/m3x/resource" 27 | 28 | "github.com/stretchr/testify/assert" 29 | ) 30 | 31 | func TestContextPool(t *testing.T) { 32 | opts := NewOptions().SetInitPooledFinalizerCapacity(0) 33 | pool := NewPool(opts) 34 | 35 | ctx := pool.Get() 36 | finalizeCalled := false 37 | ctx.RegisterFinalizer(resource.FinalizerFn(func() { 38 | finalizeCalled = true 39 | })) 40 | ctx.BlockingClose() 41 | assert.True(t, finalizeCalled) 42 | } 43 | -------------------------------------------------------------------------------- /debug/mutex_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package debug 22 | 23 | import ( 24 | "strings" 25 | "sync" 26 | "sync/atomic" 27 | "testing" 28 | "time" 29 | 30 | "github.com/stretchr/testify/require" 31 | ) 32 | 33 | type captureIOWriter struct { 34 | callback func(p []byte) 35 | } 36 | 37 | func (b *captureIOWriter) Write(p []byte) (int, error) { 38 | b.callback(p) 39 | return len(p), nil 40 | } 41 | 42 | func TestRWMutexReportEvery(t *testing.T) { 43 | var ( 44 | captured string 45 | wg sync.WaitGroup 46 | doneWg sync.WaitGroup 47 | ) 48 | 49 | doneWg.Add(1) 50 | wg.Add(1) 51 | hasCaptured := false 52 | mutex := &RWMutex{Name: "lock", Writer: &captureIOWriter{callback: func(p []byte) { 53 | if !hasCaptured { 54 | hasCaptured = true 55 | captured = string(p) 56 | wg.Done() 57 | } 58 | }}} 59 | 60 | // Create a writer that holds onto the lock 61 | go func() { 62 | mutex.Lock() 63 | doneWg.Wait() 64 | mutex.Unlock() 65 | }() 66 | 67 | // Wait for writer to appear 68 | if atomic.LoadInt64(&mutex.writers) != 1 { 69 | time.Sleep(time.Millisecond) 70 | } 71 | 72 | // Create a few pending writers and readers 73 | for i := 0; i < 3; i++ { 74 | // nolint: megacheck 75 | go func() { 76 | mutex.Lock() 77 | mutex.Unlock() 78 | }() 79 | } 80 | for i := 0; i < 10; i++ { 81 | // nolint: megacheck 82 | go func() { 83 | mutex.RLock() 84 | mutex.RUnlock() 85 | }() 86 | } 87 | 88 | // Start reporter 89 | reporter := mutex.ReportEvery(time.Millisecond) 90 | 91 | // Wait for report 92 | wg.Wait() 93 | 94 | // Close the reporter 95 | reporter.Close() 96 | 97 | // Unlock the active writer 98 | doneWg.Done() 99 | 100 | // Match that the data is as expected 101 | firstExpect := "debug.RWMutex[lock]: writers 1 (pending 3) readers 10, stack: goroutine" 102 | require.True(t, len(captured) > len(firstExpect)) 103 | require.Equal(t, firstExpect, captured[:len(firstExpect)]) 104 | 105 | // Fuzzy match that the stack looks correct 106 | require.True(t, strings.Contains(captured, "TestRWMutexReportEvery.func")) 107 | } 108 | -------------------------------------------------------------------------------- /doc.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | // Package m3x implements common utility functions and experimental components. 22 | package m3x 23 | -------------------------------------------------------------------------------- /errors/example_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package errors_test 22 | 23 | import ( 24 | "fmt" 25 | "strings" 26 | 27 | "github.com/m3db/m3x/errors" 28 | ) 29 | 30 | func ExampleMultiError() { 31 | multiErr := errors.NewMultiError() 32 | 33 | for i := 0; i < 3; i++ { 34 | // Perform some work which may fail. 35 | err := fmt.Errorf("error %d", i) 36 | 37 | if err != nil { 38 | // Add returns a new MultiError. 39 | multiErr = multiErr.Add(err) 40 | } 41 | } 42 | 43 | if err := multiErr.FinalError(); err != nil { 44 | msg := strings.Replace(err.Error(), "\n", "; ", -1) 45 | fmt.Println(msg) 46 | } 47 | 48 | if err := multiErr.LastError(); err != nil { 49 | fmt.Println(err) 50 | } 51 | 52 | // Output: 53 | // error 0; error 1; error 2 54 | // error 2 55 | } 56 | -------------------------------------------------------------------------------- /generated/mocks/generate.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | //go:generate sh -c "mockgen -package=ident $PACKAGE/ident ID,TagIterator | mockclean -pkg $PACKAGE/ident -out $GOPATH/src/$PACKAGE/ident/ident_mock.go" 22 | //go:generate sh -c "mockgen -package=checked $PACKAGE/checked Bytes | mockclean -pkg $PACKAGE/checked -out $GOPATH/src/$PACKAGE/checked/checked_mock.go" 23 | //go:generate sh -c "mockgen -package=pool $PACKAGE/pool CheckedBytesPool,BytesPool | mockclean -pkg $PACKAGE/pool -out $GOPATH/src/$PACKAGE/pool/pool_mock.go" 24 | 25 | package generated 26 | -------------------------------------------------------------------------------- /generics/arraypool/pool_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package arraypool 22 | 23 | import ( 24 | "testing" 25 | 26 | "github.com/m3db/m3x/pool" 27 | 28 | "github.com/stretchr/testify/require" 29 | ) 30 | 31 | func TestArrayPool(t *testing.T) { 32 | pool := newElemArrayPool(elemArrayPoolOpts{ 33 | Capacity: 10, 34 | Options: pool.NewObjectPoolOptions(), 35 | }) 36 | require.NotNil(t, pool) 37 | 38 | pool.Init() 39 | e := pool.Get() 40 | require.NotNil(t, e) 41 | pool.Put(e) 42 | } 43 | 44 | func TestArrayPoolPut(t *testing.T) { 45 | var original []elemType 46 | finalizeFn := func(arr []elemType) []elemType { 47 | require.Equal(t, original, arr) 48 | return arr 49 | } 50 | pool := newElemArrayPool(elemArrayPoolOpts{ 51 | Capacity: 10, 52 | Options: pool.NewObjectPoolOptions(), 53 | FinalizeFn: finalizeFn, 54 | }) 55 | require.NotNil(t, pool) 56 | pool.Init() 57 | original = pool.Get() 58 | require.NotNil(t, original) 59 | pool.Put(original) 60 | } 61 | 62 | func TestElemArrGrow(t *testing.T) { 63 | var results elemArr 64 | require.Len(t, results, 0) 65 | results = results.grow(10) 66 | require.Len(t, results, 10) 67 | results = results.grow(100) 68 | require.Len(t, results, 100) 69 | } 70 | 71 | func TestElemArrGrowResetsValue(t *testing.T) { 72 | var ( 73 | empty elemType 74 | results elemArr 75 | ) 76 | require.Len(t, results, 0) 77 | results = results.grow(10) 78 | require.Len(t, results, 10) 79 | for _, elem := range results { 80 | require.Equal(t, empty, elem) 81 | } 82 | } 83 | 84 | func TestElemArrGrowPanic(t *testing.T) { 85 | var results elemArr 86 | require.Len(t, results, 0) 87 | results = results.grow(100) 88 | require.Len(t, results, 100) 89 | results = results[:0] 90 | results = results.grow(1) 91 | require.Len(t, results, 1) 92 | } 93 | -------------------------------------------------------------------------------- /generics/hashmap/byteskey/map_pool_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package byteskey 22 | 23 | import ( 24 | "testing" 25 | "unsafe" 26 | 27 | "github.com/m3db/m3x/pool" 28 | 29 | "github.com/golang/mock/gomock" 30 | "github.com/mauricelam/genny/generic" 31 | "github.com/stretchr/testify/require" 32 | ) 33 | 34 | // nolint: structcheck 35 | func TestMapWithPooling(t *testing.T) { 36 | ctrl := gomock.NewController(t) 37 | defer ctrl.Finish() 38 | 39 | key := []byte("foo") 40 | value := generic.Type("a") 41 | 42 | pool := pool.NewMockBytesPool(ctrl) 43 | 44 | m := NewMap(MapOptions{KeyCopyPool: pool}) 45 | 46 | mockPooledSlice := make([]byte, 0, 3) 47 | pool.EXPECT().Get(len(key)).Return(mockPooledSlice) 48 | m.Set(key, value) 49 | require.Equal(t, 1, m.Len()) 50 | 51 | // Now ensure that the key is from the pool and not our original key 52 | for _, entry := range m.Iter() { 53 | type slice struct { 54 | array unsafe.Pointer 55 | len int 56 | cap int 57 | } 58 | 59 | keyBytes := entry.Key() 60 | 61 | rawPooledSlice := (*slice)(unsafe.Pointer(&mockPooledSlice)) 62 | rawKeySlice := (*slice)(unsafe.Pointer(&keyBytes)) 63 | 64 | require.True(t, rawPooledSlice.array == rawKeySlice.array) 65 | } 66 | 67 | // Now delete the key to simulate returning to pool 68 | pool.EXPECT().Put(mockPooledSlice[:3]) 69 | m.Delete(key) 70 | require.Equal(t, 0, m.Len()) 71 | } 72 | -------------------------------------------------------------------------------- /generics/hashmap/byteskey/new_map.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package byteskey 22 | 23 | import ( 24 | "bytes" 25 | 26 | "github.com/m3db/m3x/pool" 27 | 28 | "github.com/cespare/xxhash" 29 | "github.com/mauricelam/genny/generic" 30 | ) 31 | 32 | // MapValue is the generic type that needs to be specified when generating. 33 | type MapValue generic.Type 34 | 35 | // MapOptions provides options used when created the map. 36 | type MapOptions struct { 37 | InitialSize int 38 | KeyCopyPool pool.BytesPool 39 | } 40 | 41 | // NewMap returns a new byte keyed map. 42 | func NewMap(opts MapOptions) *Map { 43 | var ( 44 | copyFn CopyFn 45 | finalizeFn FinalizeFn 46 | ) 47 | if pool := opts.KeyCopyPool; pool == nil { 48 | copyFn = func(k []byte) []byte { 49 | return append([]byte(nil), k...) 50 | } 51 | } else { 52 | copyFn = func(k []byte) []byte { 53 | keyLen := len(k) 54 | pooled := pool.Get(keyLen)[:keyLen] 55 | copy(pooled, k) 56 | return pooled 57 | } 58 | finalizeFn = func(k []byte) { 59 | pool.Put(k) 60 | } 61 | } 62 | return mapAlloc(mapOptions{ 63 | hash: func(k []byte) MapHash { 64 | return MapHash(xxhash.Sum64(k)) 65 | }, 66 | equals: bytes.Equal, 67 | copy: copyFn, 68 | finalize: finalizeFn, 69 | initialSize: opts.InitialSize, 70 | }) 71 | } 72 | -------------------------------------------------------------------------------- /generics/hashmap/idkey/map_pool_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package idkey 22 | 23 | import ( 24 | "testing" 25 | "unsafe" 26 | 27 | "github.com/m3db/m3x/ident" 28 | "github.com/m3db/m3x/pool" 29 | 30 | "github.com/golang/mock/gomock" 31 | "github.com/mauricelam/genny/generic" 32 | "github.com/stretchr/testify/require" 33 | ) 34 | 35 | // nolint: structcheck 36 | func TestMapWithPooling(t *testing.T) { 37 | ctrl := gomock.NewController(t) 38 | defer ctrl.Finish() 39 | 40 | key := ident.StringID("foo") 41 | value := generic.Type("a") 42 | 43 | pool := pool.NewMockBytesPool(ctrl) 44 | 45 | m := NewMap(MapOptions{KeyCopyPool: pool}) 46 | 47 | mockPooledSlice := make([]byte, 0, 3) 48 | pool.EXPECT().Get(len(key.Bytes())).Return(mockPooledSlice) 49 | m.Set(key, value) 50 | require.Equal(t, 1, m.Len()) 51 | 52 | // Now ensure that the key is from the pool and not our original key 53 | for _, entry := range m.Iter() { 54 | type slice struct { 55 | array unsafe.Pointer 56 | len int 57 | cap int 58 | } 59 | 60 | keyBytes := []byte(entry.Key().(ident.BytesID)) 61 | 62 | rawPooledSlice := (*slice)(unsafe.Pointer(&mockPooledSlice)) 63 | rawKeySlice := (*slice)(unsafe.Pointer(&keyBytes)) 64 | 65 | require.True(t, rawPooledSlice.array == rawKeySlice.array) 66 | } 67 | 68 | // Now delete the key to simulate returning to pool 69 | pool.EXPECT().Put(mockPooledSlice[:3]) 70 | m.Delete(key) 71 | require.Equal(t, 0, m.Len()) 72 | } 73 | -------------------------------------------------------------------------------- /generics/hashmap/idkey/new_map.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package idkey 22 | 23 | import ( 24 | "github.com/m3db/m3x/ident" 25 | "github.com/m3db/m3x/pool" 26 | 27 | "github.com/cespare/xxhash" 28 | "github.com/mauricelam/genny/generic" 29 | ) 30 | 31 | // MapValue is the generic type that needs to be specified when generating. 32 | type MapValue generic.Type 33 | 34 | // MapOptions provides options used when created the map. 35 | type MapOptions struct { 36 | InitialSize int 37 | KeyCopyPool pool.BytesPool 38 | } 39 | 40 | // NewMap returns a new byte keyed map. 41 | func NewMap(opts MapOptions) *Map { 42 | var ( 43 | copyFn CopyFn 44 | finalizeFn FinalizeFn 45 | ) 46 | if pool := opts.KeyCopyPool; pool == nil { 47 | copyFn = func(k ident.ID) ident.ID { 48 | return ident.BytesID(append([]byte(nil), k.Bytes()...)) 49 | } 50 | } else { 51 | copyFn = func(k ident.ID) ident.ID { 52 | bytes := k.Bytes() 53 | keyLen := len(bytes) 54 | pooled := pool.Get(keyLen)[:keyLen] 55 | copy(pooled, bytes) 56 | return ident.BytesID(pooled) 57 | } 58 | finalizeFn = func(k ident.ID) { 59 | if slice, ok := k.(ident.BytesID); ok { 60 | pool.Put(slice) 61 | } 62 | } 63 | } 64 | return mapAlloc(mapOptions{ 65 | hash: func(id ident.ID) MapHash { 66 | return MapHash(xxhash.Sum64(id.Bytes())) 67 | }, 68 | equals: func(x, y ident.ID) bool { 69 | return x.Equal(y) 70 | }, 71 | copy: copyFn, 72 | finalize: finalizeFn, 73 | initialSize: opts.InitialSize, 74 | }) 75 | } 76 | -------------------------------------------------------------------------------- /glide.yaml: -------------------------------------------------------------------------------- 1 | package: github.com/m3db/m3x 2 | import: 3 | 4 | - package: github.com/golang/mock 5 | version: ^1.0.0 6 | 7 | - package: github.com/uber-go/tally 8 | version: ^3.3.7 9 | 10 | - package: github.com/uber-go/zap 11 | version: ^1.0.0 12 | 13 | - package: gopkg.in/validator.v2 14 | version: 3e4f037f12a1221a0864cf0dd2e81c452ab22448 15 | repo: https://github.com/go-validator/validator.git 16 | 17 | - package: gopkg.in/yaml.v2 18 | version: 5420a8b6744d3b0345ab293f6fcba19c978f1183 19 | repo: https://github.com/go-yaml/yaml.git 20 | 21 | - package: github.com/apache/thrift 22 | version: 9549b25c77587b29be4e0b5c258221a4ed85d37a 23 | 24 | - package: github.com/uber-go/atomic 25 | version: ^1.0.0 26 | 27 | - package: github.com/spaolacci/murmur3 28 | version: ^1.1.0 29 | 30 | - package: github.com/mauricelam/genny 31 | version: 9d8700bcc567cd22ea2ef42ce5835a9c80296c4a 32 | 33 | - package: github.com/cespare/xxhash 34 | version: 48099fad606eafc26e3a569fad19ff510fff4df6 35 | 36 | - package: github.com/stretchr/testify 37 | version: ^1.2.0 38 | subpackages: 39 | - assert 40 | - require 41 | 42 | - package: github.com/MichaelTJones/pcg 43 | version: df440c6ed7ed8897ac98a408365e5e89c7becf1a 44 | 45 | - package: github.com/google/go-cmp 46 | version: ^0.2 47 | subpackages: 48 | - cmp 49 | 50 | - package: github.com/uber/jaeger-lib 51 | version: ^2.0.0 52 | 53 | - package: github.com/uber/jaeger-client-go 54 | version: ^2.7.0 55 | 56 | testImport: 57 | - package: github.com/fortytw2/leaktest 58 | version: ^1.1.0 59 | -------------------------------------------------------------------------------- /hash/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | // Package hash provides implementation of hash functions. 22 | package hash 23 | -------------------------------------------------------------------------------- /hash/jump/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | /* 22 | 23 | Package jump implements the jump consistent hash algorithm as described in 24 | "A Fast, Minimal Memory, Consistent Hash Algorithm"[1]. As the paper notes: 25 | "In comparison to the algorithm of Karger et al., jump consistent hash 26 | requires no storage, is faster, and does a better job of evenly dividing 27 | the key space among the buckets and of evenly dividing the workload when 28 | the number of buckets changes. Its main limitation is that the buckets 29 | must be numbered sequentially, which makes it more suitable for data 30 | storage applications than for distributed web caching." 31 | 32 | [1] http://arxiv.org/pdf/1406.2294v1.pdf 33 | 34 | */ 35 | package jump 36 | -------------------------------------------------------------------------------- /hash/jump/example_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package jump_test 22 | 23 | import ( 24 | "fmt" 25 | "hash/fnv" 26 | "log" 27 | 28 | "github.com/m3db/m3x/hash/jump" 29 | ) 30 | 31 | func ExampleHash() { 32 | var ( 33 | numBuckets int64 = 10 34 | key = []byte("foo") 35 | hasher = fnv.New64() 36 | ) 37 | 38 | // Create hash of the key using whatever hash function you wish. 39 | if _, err := hasher.Write(key); err != nil { 40 | log.Fatal(err) 41 | } 42 | keyHash := hasher.Sum64() 43 | 44 | // Get which bucket the key is assigned to. 45 | bucket := jump.Hash(keyHash, numBuckets) 46 | 47 | fmt.Printf("Key '%s' is assigned to bucket %d.\n", string(key), bucket) 48 | // Output: Key 'foo' is assigned to bucket 9. 49 | } 50 | -------------------------------------------------------------------------------- /hash/jump/jump.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package jump 22 | 23 | // Hash maps a uint64 to a bucket in the range [0, numBuckets). 24 | func Hash(key uint64, numBuckets int64) int64 { 25 | if numBuckets < 0 { 26 | return -1 27 | } 28 | 29 | var b, j int64 30 | for j < numBuckets { 31 | b = j 32 | key = key*2862933555777941757 + 1 33 | j = int64(float64(b+1) * (float64(int64(1)<<31) / float64((key>>33)+1))) 34 | } 35 | 36 | return b 37 | } 38 | -------------------------------------------------------------------------------- /ident/bytes_id.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package ident 22 | 23 | import ( 24 | "bytes" 25 | ) 26 | 27 | // BytesID is a small utility type to avoid the heavy weight of a true ID 28 | // implementation when using in high throughput places like keys in a map. 29 | type BytesID []byte 30 | 31 | // var declaration to ensure package type BytesID implements ID 32 | var _ ID = BytesID(nil) 33 | 34 | // Bytes returns the underlying byte slice of the bytes ID. 35 | func (v BytesID) Bytes() []byte { 36 | return v 37 | } 38 | 39 | // String returns the bytes ID as a string. 40 | func (v BytesID) String() string { 41 | return string(v) 42 | } 43 | 44 | // Equal returns whether the bytes ID is equal to a given ID. 45 | func (v BytesID) Equal(value ID) bool { 46 | return bytes.Equal(value.Bytes(), v) 47 | } 48 | 49 | // NoFinalize is a no-op for a bytes ID as Finalize is already a no-op. 50 | func (v BytesID) NoFinalize() { 51 | } 52 | 53 | // IsNoFinalize is always true since BytesID is not pooled. 54 | func (v BytesID) IsNoFinalize() bool { 55 | return true 56 | } 57 | 58 | // Finalize is a no-op for a bytes ID as it has no associated pool. 59 | func (v BytesID) Finalize() { 60 | } 61 | -------------------------------------------------------------------------------- /ident/id_matcher.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package ident 22 | 23 | import ( 24 | "fmt" 25 | 26 | "github.com/golang/mock/gomock" 27 | ) 28 | 29 | // IDMatcher is a gomock.Matcher that matches ID 30 | type IDMatcher interface { 31 | gomock.Matcher 32 | } 33 | 34 | // NewIDMatcher returns a new IDMatcher 35 | func NewIDMatcher(id string) IDMatcher { 36 | return &idMatcher{id: StringID(id)} 37 | } 38 | 39 | type idMatcher struct { 40 | id ID 41 | } 42 | 43 | func (m *idMatcher) Matches(x interface{}) bool { 44 | id, ok := x.(ID) 45 | if !ok { 46 | return false 47 | } 48 | return m.id.Equal(id) 49 | } 50 | 51 | func (m *idMatcher) String() string { 52 | return fmt.Sprintf("id %s", m.id.String()) 53 | } 54 | -------------------------------------------------------------------------------- /ident/identifier.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package ident 22 | 23 | import ( 24 | "bytes" 25 | 26 | "github.com/m3db/m3x/checked" 27 | ) 28 | 29 | // BinaryID constructs a new ID based on a binary value. 30 | func BinaryID(v checked.Bytes) ID { 31 | v.IncRef() 32 | return &id{data: v} 33 | } 34 | 35 | // StringID constructs a new ID based on a string value. 36 | func StringID(str string) ID { 37 | return BytesID([]byte(str)) 38 | } 39 | 40 | type id struct { 41 | data checked.Bytes 42 | pool Pool 43 | noFinalize bool 44 | } 45 | 46 | // Bytes directly returns the underlying bytes of an ID, it is not safe 47 | // to hold a reference to this slice and is only valid during the lifetime 48 | // of the the ID itself. 49 | func (v *id) Bytes() []byte { 50 | if v.data == nil { 51 | return nil 52 | } 53 | return v.data.Bytes() 54 | } 55 | 56 | func (v *id) Equal(value ID) bool { 57 | return bytes.Equal(v.Bytes(), value.Bytes()) 58 | } 59 | 60 | func (v *id) NoFinalize() { 61 | v.noFinalize = true 62 | } 63 | 64 | func (v *id) IsNoFinalize() bool { 65 | return v.noFinalize 66 | } 67 | 68 | func (v *id) Finalize() { 69 | if v.noFinalize { 70 | return 71 | } 72 | v.data.DecRef() 73 | v.data.Finalize() 74 | v.data = nil 75 | 76 | if v.pool == nil { 77 | return 78 | } 79 | 80 | v.pool.Put(v) 81 | } 82 | 83 | func (v *id) String() string { 84 | return string(v.Bytes()) 85 | } 86 | -------------------------------------------------------------------------------- /ident/identifier_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package ident 22 | 23 | import ( 24 | "testing" 25 | 26 | "github.com/m3db/m3x/checked" 27 | 28 | "github.com/stretchr/testify/assert" 29 | "github.com/stretchr/testify/require" 30 | ) 31 | 32 | func TestConstructorEquality(t *testing.T) { 33 | a := StringID("abc") 34 | b := BinaryID(checked.NewBytes([]byte{'a', 'b', 'c'}, nil)) 35 | 36 | require.Equal(t, a.String(), "abc") 37 | 38 | assert.True(t, a.Equal(b)) 39 | assert.Equal(t, a.String(), b.String()) 40 | assert.Equal(t, a.Bytes(), b.Bytes()) 41 | } 42 | 43 | func TestNoFinalize(t *testing.T) { 44 | v := StringID("abc") 45 | 46 | checkValid := func() { 47 | require.NotNil(t, v.Bytes()) 48 | assert.True(t, v.Equal(StringID("abc"))) 49 | } 50 | checkValid() 51 | assert.True(t, v.IsNoFinalize()) 52 | 53 | v.NoFinalize() 54 | checkValid() 55 | assert.True(t, v.IsNoFinalize()) 56 | 57 | for i := 0; i < 2; i++ { 58 | v.Finalize() 59 | checkValid() 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /ident/tag.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package ident 22 | 23 | import ( 24 | "github.com/m3db/m3x/checked" 25 | ) 26 | 27 | // BinaryTag constructs a new Tag based on binary values. 28 | func BinaryTag(name checked.Bytes, value checked.Bytes) Tag { 29 | return Tag{ 30 | Name: TagName(BinaryID(name)), 31 | Value: TagValue(BinaryID(value)), 32 | } 33 | } 34 | 35 | // StringTag constructs a new Tag based on string values. 36 | func StringTag(name string, value string) Tag { 37 | return Tag{ 38 | Name: TagName(StringID(name)), 39 | Value: TagValue(StringID(value)), 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /ident/tag_arraypool_gen.go: -------------------------------------------------------------------------------- 1 | // This file was automatically generated by genny. 2 | // Any changes will be lost if this file is regenerated. 3 | // see https://github.com/mauricelam/genny 4 | 5 | package ident 6 | 7 | import ( 8 | "github.com/m3db/m3x/pool" 9 | ) 10 | 11 | // Copyright (c) 2018 Uber Technologies, Inc. 12 | // 13 | // Permission is hereby granted, free of charge, to any person obtaining a copy 14 | // of this software and associated documentation files (the "Software"), to deal 15 | // in the Software without restriction, including without limitation the rights 16 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 17 | // copies of the Software, and to permit persons to whom the Software is 18 | // furnished to do so, subject to the following conditions: 19 | // 20 | // The above copyright notice and this permission notice shall be included in 21 | // all copies or substantial portions of the Software. 22 | // 23 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 29 | // THE SOFTWARE. 30 | 31 | // tagArrayPool provides a pool for tag slices. 32 | type tagArrayPool interface { 33 | // Init initializes the array pool, it needs to be called 34 | // before Get/Put use. 35 | Init() 36 | 37 | // Get returns the a slice from the pool. 38 | Get() []Tag 39 | 40 | // Put returns the provided slice to the pool. 41 | Put(elems []Tag) 42 | } 43 | 44 | type tagFinalizeFn func([]Tag) []Tag 45 | 46 | type tagArrayPoolOpts struct { 47 | Options pool.ObjectPoolOptions 48 | Capacity int 49 | MaxCapacity int 50 | FinalizeFn tagFinalizeFn 51 | } 52 | 53 | type tagArrPool struct { 54 | opts tagArrayPoolOpts 55 | pool pool.ObjectPool 56 | } 57 | 58 | func newTagArrayPool(opts tagArrayPoolOpts) tagArrayPool { 59 | if opts.FinalizeFn == nil { 60 | opts.FinalizeFn = defaultTagFinalizerFn 61 | } 62 | p := pool.NewObjectPool(opts.Options) 63 | return &tagArrPool{opts, p} 64 | } 65 | 66 | func (p *tagArrPool) Init() { 67 | p.pool.Init(func() interface{} { 68 | return make([]Tag, 0, p.opts.Capacity) 69 | }) 70 | } 71 | 72 | func (p *tagArrPool) Get() []Tag { 73 | return p.pool.Get().([]Tag) 74 | } 75 | 76 | func (p *tagArrPool) Put(arr []Tag) { 77 | arr = p.opts.FinalizeFn(arr) 78 | if max := p.opts.MaxCapacity; max > 0 && cap(arr) > max { 79 | return 80 | } 81 | p.pool.Put(arr) 82 | } 83 | 84 | func defaultTagFinalizerFn(elems []Tag) []Tag { 85 | var empty Tag 86 | for i := range elems { 87 | elems[i] = empty 88 | } 89 | elems = elems[:0] 90 | return elems 91 | } 92 | 93 | type tagArr []Tag 94 | 95 | func (elems tagArr) grow(n int) []Tag { 96 | if cap(elems) < n { 97 | elems = make([]Tag, n) 98 | } 99 | elems = elems[:n] 100 | // following compiler optimized memcpy impl 101 | // https://github.com/golang/go/wiki/CompilerOptimizations#optimized-memclr 102 | var empty Tag 103 | for i := range elems { 104 | elems[i] = empty 105 | } 106 | return elems 107 | } 108 | -------------------------------------------------------------------------------- /ident/tag_iterator_matcher.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package ident 22 | 23 | import ( 24 | "fmt" 25 | 26 | "github.com/golang/mock/gomock" 27 | ) 28 | 29 | // TagIterMatcher is a gomock.Matcher that matches TagIterator 30 | type TagIterMatcher interface { 31 | gomock.Matcher 32 | } 33 | 34 | // NewTagIterMatcher returns a new TagIterMatcher 35 | func NewTagIterMatcher(iter TagIterator) TagIterMatcher { 36 | return &tagIterMatcher{iter: iter} 37 | } 38 | 39 | type tagIterMatcher struct { 40 | iter TagIterator 41 | } 42 | 43 | func (m *tagIterMatcher) Matches(x interface{}) bool { 44 | t, ok := x.(TagIterator) 45 | if !ok { 46 | return false 47 | } 48 | // duplicate to ensure the both iterators can be re-used again 49 | obs := t.Duplicate() 50 | exp := m.iter.Duplicate() 51 | for exp.Next() { 52 | if !obs.Next() { 53 | return false 54 | } 55 | obsCurrent := obs.Current() 56 | expCurrent := exp.Current() 57 | if !expCurrent.Equal(obsCurrent) { 58 | return false 59 | } 60 | } 61 | if obs.Next() { 62 | return false 63 | } 64 | if exp.Err() != obs.Err() { 65 | return false 66 | } 67 | return true 68 | } 69 | 70 | func (m *tagIterMatcher) String() string { 71 | return fmt.Sprintf("tagIter %v", m.iter) 72 | } 73 | -------------------------------------------------------------------------------- /ident/tag_iterator_matcher_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package ident_test 22 | 23 | import ( 24 | "fmt" 25 | "testing" 26 | 27 | "github.com/m3db/m3x/ident" 28 | 29 | "github.com/golang/mock/gomock" 30 | "github.com/stretchr/testify/assert" 31 | ) 32 | 33 | func TestTagIteratorMatcher(t *testing.T) { 34 | iter := ident.NewTagsIterator(ident.NewTags( 35 | ident.StringTag("hello", "there"), 36 | ident.StringTag("foo", "bar"))) 37 | matcher := ident.NewTagIterMatcher(iter) 38 | assert.True(t, matcher.Matches(iter)) 39 | } 40 | 41 | func TestTagIteratorMatcherNotMatchingWrongType(t *testing.T) { 42 | matcher := ident.NewTagIterMatcher(ident.EmptyTagIterator) 43 | assert.False(t, matcher.Matches(1)) 44 | } 45 | 46 | func TestTagIteratorMatcherNotMatchingEmpty(t *testing.T) { 47 | iter := ident.MustNewTagStringsIterator("hello", "there") 48 | matcher := ident.NewTagIterMatcher(ident.EmptyTagIterator) 49 | assert.False(t, matcher.Matches(iter)) 50 | } 51 | 52 | func TestTagIteratorMatcherNotMatchingTagName(t *testing.T) { 53 | iter := ident.MustNewTagStringsIterator("hello", "there") 54 | matcher := ident.NewTagIterMatcher(iter) 55 | otherIter := ident.MustNewTagStringsIterator("hello", "other") 56 | assert.True(t, matcher.Matches(iter)) 57 | assert.False(t, matcher.Matches(otherIter)) 58 | } 59 | 60 | func TestTagIteratorMatcherNotMatchingTagValue(t *testing.T) { 61 | iter := ident.MustNewTagStringsIterator("hello", "there") 62 | matcher := ident.NewTagIterMatcher(iter) 63 | otherIter := ident.MustNewTagStringsIterator("fail", "there") 64 | assert.False(t, matcher.Matches(otherIter)) 65 | } 66 | 67 | func TestTagIteratorMatcherErrCase(t *testing.T) { 68 | ctrl := gomock.NewController(t) 69 | defer ctrl.Finish() 70 | 71 | iter := ident.NewMockTagIterator(ctrl) 72 | gomock.InOrder( 73 | iter.EXPECT().Duplicate().Return(iter), 74 | iter.EXPECT().Next().Return(true), 75 | iter.EXPECT().Current().Return(ident.StringTag("a", "b")), 76 | iter.EXPECT().Next().Return(false), 77 | iter.EXPECT().Err().Return(fmt.Errorf("random error")), 78 | ) 79 | mIter := ident.MustNewTagStringsIterator("a", "b") 80 | matcher := ident.NewTagIterMatcher(mIter) 81 | assert.False(t, matcher.Matches(iter)) 82 | } 83 | -------------------------------------------------------------------------------- /ident/tag_matcher.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package ident 22 | 23 | import ( 24 | "fmt" 25 | 26 | "github.com/golang/mock/gomock" 27 | ) 28 | 29 | // TagMatcher is a gomock.Matcher that matches Tag 30 | type TagMatcher interface { 31 | gomock.Matcher 32 | } 33 | 34 | // NewTagMatcher returns a new TagMatcher 35 | func NewTagMatcher(name string, value string) TagMatcher { 36 | return &tagMatcher{tag: StringTag(name, value)} 37 | } 38 | 39 | type tagMatcher struct { 40 | tag Tag 41 | } 42 | 43 | func (m *tagMatcher) Matches(x interface{}) bool { 44 | t, ok := x.(Tag) 45 | if !ok { 46 | return false 47 | } 48 | return m.tag.Name.Equal(t.Name) && m.tag.Value.Equal(t.Value) 49 | } 50 | 51 | func (m *tagMatcher) String() string { 52 | return fmt.Sprintf("tag %s=%s", m.tag.Name.String(), m.tag.Value.String()) 53 | } 54 | -------------------------------------------------------------------------------- /ident/tags_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package ident 22 | 23 | import ( 24 | "testing" 25 | 26 | "github.com/stretchr/testify/require" 27 | ) 28 | 29 | func TestTagsUnequalLength(t *testing.T) { 30 | tagsA := NewTags( 31 | StringTag("hello", "there"), 32 | ) 33 | tagsB := NewTags( 34 | StringTag("foo", "bar"), 35 | StringTag("and", "done"), 36 | ) 37 | 38 | require.False(t, tagsA.Equal(tagsB)) 39 | require.False(t, tagsB.Equal(tagsA)) 40 | } 41 | 42 | func TestTagsUnequalOrder(t *testing.T) { 43 | tagsA := NewTags( 44 | StringTag("foo", "bar"), 45 | StringTag("hello", "there"), 46 | ) 47 | tagsB := NewTags( 48 | StringTag("hello", "there"), 49 | StringTag("foo", "bar"), 50 | ) 51 | 52 | require.False(t, tagsA.Equal(tagsB)) 53 | require.False(t, tagsB.Equal(tagsA)) 54 | } 55 | 56 | func TestTagsEqual(t *testing.T) { 57 | tagsA := NewTags( 58 | StringTag("hello", "there"), 59 | StringTag("foo", "bar"), 60 | ) 61 | tagsB := NewTags( 62 | StringTag("hello", "there"), 63 | StringTag("foo", "bar"), 64 | ) 65 | 66 | require.True(t, tagsA.Equal(tagsB)) 67 | require.True(t, tagsB.Equal(tagsA)) 68 | } 69 | -------------------------------------------------------------------------------- /ident/testutil/tags_convert.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package testutil 22 | 23 | import ( 24 | "github.com/m3db/m3x/checked" 25 | "github.com/m3db/m3x/ident" 26 | ) 27 | 28 | // NewTagsFromTagIterator allocates tags for each tag for a tag iterator, this 29 | // will copy bytes from the iterator. 30 | func NewTagsFromTagIterator(iter ident.TagIterator) (ident.Tags, error) { 31 | defer iter.Close() 32 | 33 | var r ident.Tags 34 | if tagsLen := iter.Remaining(); tagsLen > 0 { 35 | tags := make([]ident.Tag, 0, tagsLen) 36 | for iter.Next() { 37 | curr := iter.Current() 38 | tagName := checked.NewBytes(append([]byte(nil), curr.Name.Bytes()...), nil) 39 | tagValue := checked.NewBytes(append([]byte(nil), curr.Value.Bytes()...), nil) 40 | tags = append(tags, ident.BinaryTag(tagName, tagValue)) 41 | } 42 | r = ident.NewTags(tags...) 43 | } 44 | if err := iter.Err(); err != nil { 45 | return ident.Tags{}, err 46 | } 47 | return r, nil 48 | } 49 | -------------------------------------------------------------------------------- /ident/testutil/tags_convert_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package testutil 22 | 23 | import ( 24 | "errors" 25 | "testing" 26 | 27 | "github.com/m3db/m3x/ident" 28 | 29 | "github.com/golang/mock/gomock" 30 | "github.com/stretchr/testify/assert" 31 | "github.com/stretchr/testify/require" 32 | ) 33 | 34 | func TestNewTagsFromTagIterator(t *testing.T) { 35 | in := ident.NewTags( 36 | ident.StringTag("foo", "bar"), 37 | ident.StringTag("qux", "qaz"), 38 | ) 39 | tags, err := NewTagsFromTagIterator(ident.NewTagsIterator(in)) 40 | require.NoError(t, err) 41 | require.Equal(t, len(in.Values()), len(tags.Values())) 42 | for i, expected := range in.Values() { 43 | actual := tags.Values()[i] 44 | assert.True(t, expected.Equal(actual)) 45 | assert.True(t, expected != actual) // Make sure made a copy 46 | } 47 | } 48 | 49 | func TestNewTagsFromTagIteratorEmptyTagIteratorDoesNotAlloc(t *testing.T) { 50 | tags, err := NewTagsFromTagIterator(ident.EmptyTagIterator) 51 | require.NoError(t, err) 52 | require.Equal(t, ident.Tags{}, tags) 53 | } 54 | 55 | func TestNewTagsFromTagIteratorError(t *testing.T) { 56 | ctrl := gomock.NewController(t) 57 | defer ctrl.Finish() 58 | 59 | expectedErr := errors.New("expected error") 60 | 61 | mockIter := ident.NewMockTagIterator(ctrl) 62 | gomock.InOrder( 63 | mockIter.EXPECT().Remaining().Return(1), 64 | mockIter.EXPECT().Next().Return(true), 65 | mockIter.EXPECT().Current().Return(ident.StringTag("foo", "bar")), 66 | mockIter.EXPECT().Next().Return(false), 67 | mockIter.EXPECT().Err().Return(expectedErr), 68 | mockIter.EXPECT().Close(), 69 | ) 70 | 71 | _, err := NewTagsFromTagIterator(mockIter) 72 | require.Error(t, err) 73 | } 74 | -------------------------------------------------------------------------------- /instrument/process.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package instrument 22 | 23 | import ( 24 | "os" 25 | "time" 26 | 27 | "github.com/m3db/m3x/process" 28 | 29 | "github.com/uber-go/tally" 30 | ) 31 | 32 | type processReporter struct { 33 | baseReporter 34 | 35 | metrics processMetrics 36 | } 37 | 38 | type processMetrics struct { 39 | NumFDs tally.Gauge 40 | NumFDErrors tally.Counter 41 | pid int 42 | } 43 | 44 | func (r *processMetrics) report() { 45 | numFDs, err := process.NumFDs(r.pid) 46 | if err == nil { 47 | r.NumFDs.Update(float64(numFDs)) 48 | } else { 49 | r.NumFDErrors.Inc(1) 50 | } 51 | } 52 | 53 | // NewProcessReporter returns a new reporter that reports process 54 | // metrics, currently just the process file descriptor count. 55 | func NewProcessReporter( 56 | scope tally.Scope, 57 | reportInterval time.Duration, 58 | ) Reporter { 59 | r := new(processReporter) 60 | r.init(reportInterval, r.metrics.report) 61 | 62 | processScope := scope.SubScope("process") 63 | r.metrics.NumFDs = processScope.Gauge("num-fds") 64 | r.metrics.NumFDErrors = processScope.Counter("num-fd-errors") 65 | r.metrics.pid = os.Getpid() 66 | 67 | return r 68 | } 69 | -------------------------------------------------------------------------------- /instrument/process_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package instrument 22 | 23 | import ( 24 | "fmt" 25 | "io/ioutil" 26 | "os" 27 | "testing" 28 | "time" 29 | 30 | "github.com/fortytw2/leaktest" 31 | "github.com/stretchr/testify/assert" 32 | "github.com/stretchr/testify/require" 33 | "github.com/uber-go/tally" 34 | ) 35 | 36 | func TestProcessReporter(t *testing.T) { 37 | defer leaktest.Check(t)() 38 | 39 | scope := tally.NewTestScope("", nil) 40 | 41 | countOpen := 32 42 | closeFds := func() {} 43 | for i := 0; i < countOpen; i++ { 44 | tmpFile, err := ioutil.TempFile("", "example") 45 | if err != nil { 46 | require.FailNow(t, fmt.Sprintf("could not open temp file: %v", err)) 47 | } 48 | 49 | // Explicitly close with closeFds call so we can defer the leaktest 50 | // and it always executes last 51 | currCloseFds := closeFds 52 | closeFds = func() { 53 | currCloseFds() 54 | assert.NoError(t, tmpFile.Close()) 55 | assert.NoError(t, os.Remove(tmpFile.Name())) 56 | } 57 | } 58 | 59 | every := 10 * time.Millisecond 60 | 61 | r := NewProcessReporter(scope, every) 62 | require.NoError(t, r.Start()) 63 | 64 | time.Sleep(2 * every) 65 | 66 | _, ok := scope.Snapshot().Gauges()["process.num-fds+"] 67 | if !ok { 68 | require.FailNow(t, "metric for fds not found after waiting 2x interval") 69 | } 70 | 71 | require.NoError(t, r.Stop()) 72 | 73 | closeFds() 74 | } 75 | 76 | func TestProcessReporterDoesNotOpenMoreThanOnce(t *testing.T) { 77 | r := NewProcessReporter(tally.NoopScope, 10*time.Millisecond) 78 | assert.NoError(t, r.Start()) 79 | assert.Error(t, r.Start()) 80 | assert.NoError(t, r.Stop()) 81 | } 82 | 83 | func TestProcessReporterDoesNotCloseMoreThanOnce(t *testing.T) { 84 | r := NewProcessReporter(tally.NoopScope, 10*time.Millisecond) 85 | assert.NoError(t, r.Start()) 86 | assert.NoError(t, r.Stop()) 87 | assert.Error(t, r.Stop()) 88 | } 89 | 90 | func TestProcessReporterDoesNotOpenWithInvalidReportInterval(t *testing.T) { 91 | r := NewProcessReporter(tally.NoopScope, 0) 92 | assert.Error(t, r.Start()) 93 | } 94 | -------------------------------------------------------------------------------- /instrument/reporter.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package instrument 22 | 23 | import ( 24 | "errors" 25 | "sync" 26 | "time" 27 | ) 28 | 29 | type reporterState int 30 | 31 | const ( 32 | reporterStateNotStarted reporterState = iota 33 | reporterStateStarted 34 | reporterStateStopped 35 | ) 36 | 37 | var ( 38 | errReporterAlreadyStartedOrStopped = errors.New("reporter already started or stopped") 39 | errReporterNotRunning = errors.New("reporter not running") 40 | errReporterReportIntervalInvalid = errors.New("reporter report interval is invalid") 41 | ) 42 | 43 | type baseReporter struct { 44 | sync.Mutex 45 | 46 | state reporterState 47 | reportInterval time.Duration 48 | closeCh chan struct{} 49 | doneCh chan struct{} 50 | 51 | // fn is the only field required to set 52 | fn func() 53 | } 54 | 55 | func (r *baseReporter) init( 56 | reportInterval time.Duration, 57 | fn func(), 58 | ) { 59 | r.reportInterval = reportInterval 60 | r.closeCh = make(chan struct{}) 61 | r.doneCh = make(chan struct{}) 62 | r.fn = fn 63 | } 64 | 65 | func (r *baseReporter) Start() error { 66 | r.Lock() 67 | defer r.Unlock() 68 | 69 | if r.state != reporterStateNotStarted { 70 | return errReporterAlreadyStartedOrStopped 71 | } 72 | 73 | if r.reportInterval <= 0 { 74 | return errReporterReportIntervalInvalid 75 | } 76 | 77 | r.state = reporterStateStarted 78 | 79 | go func() { 80 | ticker := time.NewTicker(r.reportInterval) 81 | defer func() { 82 | ticker.Stop() 83 | r.doneCh <- struct{}{} 84 | }() 85 | 86 | for { 87 | select { 88 | case <-ticker.C: 89 | r.fn() 90 | case <-r.closeCh: 91 | return 92 | } 93 | } 94 | }() 95 | 96 | return nil 97 | } 98 | 99 | func (r *baseReporter) Stop() error { 100 | r.Lock() 101 | defer r.Unlock() 102 | 103 | if r.state != reporterStateStarted { 104 | return errReporterNotRunning 105 | } 106 | 107 | r.state = reporterStateStopped 108 | close(r.closeCh) 109 | <-r.doneCh 110 | 111 | return nil 112 | } 113 | -------------------------------------------------------------------------------- /instrument/types.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | // Package instrument implements functions to make instrumenting code, 22 | // including metrics and logging, easier. 23 | package instrument 24 | 25 | import ( 26 | "github.com/opentracing/opentracing-go" 27 | "time" 28 | 29 | "github.com/m3db/m3x/log" 30 | 31 | "github.com/uber-go/tally" 32 | "go.uber.org/zap" 33 | ) 34 | 35 | // Reporter reports metrics about a component. 36 | type Reporter interface { 37 | // Start starts the reporter. 38 | Start() error 39 | // Stop stops the reporter. 40 | Stop() error 41 | } 42 | 43 | // Options represents the options for instrumentation. 44 | type Options interface { 45 | // SetLogger sets the logger. 46 | SetLogger(value log.Logger) Options 47 | 48 | // Logger returns the logger. 49 | Logger() log.Logger 50 | 51 | // SetZapLogger sets the zap logger 52 | SetZapLogger(value *zap.Logger) Options 53 | 54 | // ZapLogger returns the zap logger 55 | ZapLogger() *zap.Logger 56 | 57 | // SetMetricsScope sets the metrics scope. 58 | SetMetricsScope(value tally.Scope) Options 59 | 60 | // MetricsScope returns the metrics scope. 61 | MetricsScope() tally.Scope 62 | 63 | // Tracer returns the tracer. 64 | Tracer() opentracing.Tracer 65 | 66 | // SetTracer sets the tracer. 67 | SetTracer(tracer opentracing.Tracer) Options 68 | 69 | // SetMetricsSamplingRate sets the metrics sampling rate. 70 | SetMetricsSamplingRate(value float64) Options 71 | 72 | // SetMetricsSamplingRate returns the metrics sampling rate. 73 | MetricsSamplingRate() float64 74 | 75 | // ReportInterval sets the time between reporting metrics within the system. 76 | SetReportInterval(time.Duration) Options 77 | 78 | // GetReportInterval returns the time between reporting metrics within the system. 79 | ReportInterval() time.Duration 80 | } 81 | -------------------------------------------------------------------------------- /log/config.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package log 22 | 23 | import ( 24 | "io" 25 | "os" 26 | ) 27 | 28 | // Configuration defines configuration for logging. 29 | type Configuration struct { 30 | File string `json:"file" yaml:"file"` 31 | Level string `json:"level" yaml:"level"` 32 | Fields map[string]interface{} `json:"fields" yaml:"fields"` 33 | } 34 | 35 | // BuildLogger builds a new Logger based on the configuration. 36 | func (cfg Configuration) BuildLogger() (Logger, error) { 37 | writer := io.Writer(os.Stdout) 38 | 39 | if cfg.File != "" { 40 | fd, err := os.OpenFile(cfg.File, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666) 41 | if err != nil { 42 | return nil, err 43 | } 44 | 45 | writer = io.MultiWriter(writer, fd) 46 | } 47 | 48 | logger := NewLogger(writer) 49 | 50 | if len(cfg.Level) != 0 { 51 | level, err := ParseLevel(cfg.Level) 52 | if err != nil { 53 | return nil, err 54 | } 55 | 56 | logger = NewLevelLogger(logger, level) 57 | } 58 | 59 | if len(cfg.Fields) != 0 { 60 | var fields []Field 61 | for k, v := range cfg.Fields { 62 | fields = append(fields, NewField(k, v)) 63 | } 64 | logger = logger.WithFields(fields...) 65 | } 66 | 67 | return logger, nil 68 | } 69 | -------------------------------------------------------------------------------- /log/config_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package log 22 | 23 | import ( 24 | "io/ioutil" 25 | "os" 26 | "strings" 27 | "testing" 28 | 29 | "github.com/stretchr/testify/assert" 30 | "github.com/stretchr/testify/require" 31 | ) 32 | 33 | func TestLoggingConfiguration(t *testing.T) { 34 | tmpfile, err := ioutil.TempFile("", "logtest") 35 | require.NoError(t, err) 36 | 37 | defer tmpfile.Close() 38 | defer os.Remove(tmpfile.Name()) 39 | 40 | cfg := Configuration{ 41 | Fields: map[string]interface{}{ 42 | "my-field": "my-val", 43 | }, 44 | Level: "error", 45 | File: tmpfile.Name(), 46 | } 47 | 48 | log, err := cfg.BuildLogger() 49 | require.NoError(t, err) 50 | 51 | log.Infof("should not appear") 52 | log.Warnf("should not appear") 53 | log.Errorf("this should be appear") 54 | 55 | b, err := ioutil.ReadAll(tmpfile) 56 | require.NoError(t, err) 57 | 58 | str := string(b) 59 | pieces := strings.Split(str, "[") 60 | assert.True(t, len(pieces) >= 2) 61 | 62 | ts := pieces[0] 63 | assert.EqualValues(t, ts+"[E] this should be appear [{my-field my-val}]\n", str) 64 | } 65 | -------------------------------------------------------------------------------- /log/logger_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package log 22 | 23 | import ( 24 | "testing" 25 | 26 | "github.com/stretchr/testify/require" 27 | ) 28 | 29 | func TestNullLogger(t *testing.T) { 30 | require.NotPanics(t, func() { 31 | NullLogger.WithFields(NewField("key", "value")).Info("msg") 32 | }) 33 | } 34 | -------------------------------------------------------------------------------- /net/example_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package net_test 22 | 23 | import ( 24 | "io" 25 | "log" 26 | "net" 27 | 28 | xnet "github.com/m3db/m3x/net" 29 | "github.com/m3db/m3x/retry" 30 | ) 31 | 32 | func ExampleStartForeverAcceptLoop() { 33 | // Create a new listener. 34 | l, err := net.Listen("tcp", ":0") 35 | if err != nil { 36 | log.Fatal(err) 37 | } 38 | defer l.Close() 39 | 40 | // Start accepting incoming connections. 41 | var ( 42 | opts = retry.NewOptions() 43 | connCh, errCh = xnet.StartAcceptLoop(l, opts) 44 | ) 45 | 46 | for { 47 | select { 48 | case conn := <-connCh: 49 | // Handle the connection in a new goroutine. 50 | go func(c net.Conn) { 51 | io.Copy(c, c) 52 | c.Close() 53 | }(conn) 54 | 55 | case err := <-errCh: 56 | // Only fatal errors are returned on errCh. 57 | log.Fatal(err) 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /net/server.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | // Package net implements functions for running network servers. 22 | package net 23 | 24 | import ( 25 | "net" 26 | 27 | "github.com/m3db/m3x/errors" 28 | "github.com/m3db/m3x/retry" 29 | ) 30 | 31 | // StartAcceptLoop starts an accept loop for the given listener, 32 | // returning accepted connections via a channel while handling 33 | // temporary network errors. Fatal errors are returned via the 34 | // error channel with the listener closed on return. 35 | func StartAcceptLoop(l net.Listener, rOpts retry.Options) (<-chan net.Conn, <-chan error) { 36 | var ( 37 | connCh = make(chan net.Conn) 38 | errCh = make(chan error) 39 | retrier = retry.NewRetrier(rOpts) 40 | ) 41 | 42 | go func() { 43 | defer l.Close() 44 | 45 | for { 46 | var conn net.Conn 47 | if err := retrier.Attempt(func() error { 48 | var connErr error 49 | conn, connErr = l.Accept() 50 | if connErr == nil { 51 | return nil 52 | } 53 | // If the error is a temporary network error, we consider it retryable. 54 | if ne, ok := connErr.(net.Error); ok && ne.Temporary() { 55 | return ne 56 | } 57 | // Otherwise it's a non-retryable error. 58 | return errors.NewNonRetryableError(connErr) 59 | }); err != nil { 60 | close(connCh) 61 | errCh <- err 62 | close(errCh) 63 | return 64 | } 65 | connCh <- conn 66 | } 67 | }() 68 | 69 | return connCh, errCh 70 | } 71 | 72 | // StartForeverAcceptLoop starts an accept loop for the 73 | // given listener that retries forever, returning 74 | // accepted connections via a channel while handling 75 | // temporary network errors. Fatal errors are returned via the 76 | // error channel with the listener closed on return. 77 | func StartForeverAcceptLoop(l net.Listener, rOpts retry.Options) (<-chan net.Conn, <-chan error) { 78 | return StartAcceptLoop(l, rOpts.SetForever(true)) 79 | } 80 | -------------------------------------------------------------------------------- /net/server_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package net 22 | 23 | import ( 24 | "fmt" 25 | "io/ioutil" 26 | "net" 27 | "sort" 28 | "sync" 29 | "testing" 30 | 31 | "github.com/m3db/m3x/retry" 32 | 33 | "github.com/stretchr/testify/assert" 34 | ) 35 | 36 | func TestStartAcceptLoop(t *testing.T) { 37 | var ( 38 | results []string 39 | resultLock sync.Mutex 40 | wgClient sync.WaitGroup 41 | wgServer sync.WaitGroup 42 | numConnections = 10 43 | ) 44 | 45 | l, err := net.Listen("tcp", "127.0.0.1:0") 46 | assert.Nil(t, err) 47 | connCh, errCh := StartForeverAcceptLoop(l, retry.NewOptions()) 48 | 49 | wgServer.Add(1) 50 | go func() { 51 | defer wgServer.Done() 52 | 53 | curr := 0 54 | for conn := range connCh { 55 | fmt.Fprintf(conn, "%d", curr) 56 | conn.Close() 57 | curr++ 58 | } 59 | }() 60 | 61 | for i := 0; i < numConnections; i++ { 62 | wgClient.Add(1) 63 | go func() { 64 | defer wgClient.Done() 65 | 66 | conn, err := net.Dial("tcp", l.Addr().String()) 67 | assert.Nil(t, err) 68 | 69 | data, err := ioutil.ReadAll(conn) 70 | assert.Nil(t, err) 71 | 72 | resultLock.Lock() 73 | results = append(results, string(data)) 74 | resultLock.Unlock() 75 | }() 76 | } 77 | 78 | wgClient.Wait() 79 | 80 | // Close the listener to intentionally cause a server-side connection error 81 | l.Close() 82 | wgServer.Wait() 83 | 84 | sort.Strings(results) 85 | 86 | var expected []string 87 | for i := 0; i < numConnections; i++ { 88 | expected = append(expected, fmt.Sprintf("%d", i)) 89 | } 90 | assert.Equal(t, expected, results) 91 | 92 | // Expect a server-side connection error 93 | err = <-errCh 94 | assert.NotNil(t, err) 95 | } 96 | -------------------------------------------------------------------------------- /opentracing/tracing.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package opentracing 22 | 23 | import ( 24 | "fmt" 25 | "io" 26 | 27 | "github.com/opentracing/opentracing-go" 28 | "github.com/uber-go/tally" 29 | jaegercfg "github.com/uber/jaeger-client-go/config" 30 | jaegerzap "github.com/uber/jaeger-client-go/log/zap" 31 | jaegertally "github.com/uber/jaeger-lib/metrics/tally" 32 | "go.uber.org/zap" 33 | ) 34 | 35 | // Supported tracing backends. Currently only jaeger is supported. 36 | var ( 37 | TracingBackendJaeger = "jaeger" 38 | ) 39 | 40 | // TracingConfiguration configures an opentracing backend for m3query to use. Currently only jaeger is supported. 41 | // Tracing is disabled if no backend is specified. 42 | type TracingConfiguration struct { 43 | Backend string `yaml:"backend"` 44 | Jaeger jaegercfg.Configuration `yaml:"jaeger"` 45 | } 46 | 47 | // NewTracer returns a tracer configured with the configuration provided by this struct. The tracer's concrete 48 | // type is determined by cfg.Backend. Currently only `"jaeger"` is supported. `""` implies 49 | // disabled (NoopTracer). 50 | func (cfg *TracingConfiguration) NewTracer(defaultServiceName string, scope tally.Scope, logger *zap.Logger) (opentracing.Tracer, io.Closer, error) { 51 | if cfg.Backend == "" { 52 | return opentracing.NoopTracer{}, noopCloser{}, nil 53 | } 54 | 55 | if cfg.Backend != TracingBackendJaeger { 56 | return nil, nil, fmt.Errorf("unknown tracing backend: %s. Supported backends are: %s", cfg.Backend, TracingBackendJaeger) 57 | } 58 | 59 | if cfg.Jaeger.ServiceName == "" { 60 | cfg.Jaeger.ServiceName = defaultServiceName 61 | } 62 | 63 | jaegerLog := jaegerzap.NewLogger(logger) 64 | tracer, jaegerCloser, err := cfg.Jaeger.NewTracer( 65 | jaegercfg.Logger(jaegerLog), 66 | jaegercfg.Metrics(jaegertally.Wrap(scope))) 67 | 68 | if err != nil { 69 | return nil, nil, fmt.Errorf("failed to initialize jaeger: %s", err.Error()) 70 | } 71 | 72 | return tracer, jaegerCloser, nil 73 | } 74 | 75 | type noopCloser struct{} 76 | 77 | func (noopCloser) Close() error { 78 | return nil 79 | } 80 | -------------------------------------------------------------------------------- /opentracing/tracing_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package opentracing 22 | 23 | import ( 24 | "io" 25 | "testing" 26 | 27 | "github.com/opentracing/opentracing-go" 28 | "github.com/stretchr/testify/assert" 29 | "github.com/stretchr/testify/require" 30 | "github.com/uber-go/tally" 31 | "github.com/uber/jaeger-client-go" 32 | jaegercfg "github.com/uber/jaeger-client-go/config" 33 | "go.uber.org/zap" 34 | ) 35 | 36 | func TestTracingConfiguration_NewTracer(t *testing.T) { 37 | serviceName := "foo" 38 | doCall := func(cfg *TracingConfiguration) (opentracing.Tracer, io.Closer, error) { 39 | return cfg.NewTracer(serviceName, tally.NoopScope, zap.L()) 40 | } 41 | 42 | t.Run("defaults to noop", func(t *testing.T) { 43 | cfg := TracingConfiguration{} 44 | tr, closer, err := doCall(&cfg) 45 | defer closer.Close() 46 | require.NoError(t, err) 47 | assert.Equal(t, opentracing.NoopTracer{}, tr) 48 | assert.Equal(t, noopCloser{}, closer) 49 | }) 50 | 51 | t.Run("errors on non-jaeger", func(t *testing.T) { 52 | cfg := TracingConfiguration{ 53 | Backend: "someone_else", 54 | } 55 | _, _, err := doCall(&cfg) 56 | require.EqualError(t, err, "unknown tracing backend: someone_else. Supported backends are: jaeger") 57 | }) 58 | 59 | t.Run("initializes jaeger tracer", func(t *testing.T) { 60 | cfg := TracingConfiguration{ 61 | Backend: TracingBackendJaeger, 62 | } 63 | tr, closer, err := doCall(&cfg) 64 | defer closer.Close() 65 | 66 | require.NoError(t, err) 67 | assert.IsType(t, (*jaeger.Tracer)(nil), tr) 68 | }) 69 | 70 | t.Run("sets service name on empty", func(t *testing.T) { 71 | cfg := TracingConfiguration{ 72 | Backend: TracingBackendJaeger, 73 | } 74 | _, closer, err := doCall(&cfg) 75 | defer closer.Close() 76 | 77 | require.NoError(t, err) 78 | assert.Equal(t, serviceName, cfg.Jaeger.ServiceName) 79 | }) 80 | 81 | t.Run("leaves service name on non-empty", func(t *testing.T) { 82 | cfg := TracingConfiguration{ 83 | Backend: TracingBackendJaeger, 84 | Jaeger: jaegercfg.Configuration{ 85 | ServiceName: "other", 86 | }, 87 | } 88 | _, closer, err := doCall(&cfg) 89 | defer closer.Close() 90 | 91 | require.NoError(t, err) 92 | assert.Equal(t, "other", cfg.Jaeger.ServiceName) 93 | }) 94 | } 95 | -------------------------------------------------------------------------------- /pool/bytes.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package pool 22 | 23 | type bytesPool struct { 24 | pool BucketizedObjectPool 25 | } 26 | 27 | // NewBytesPool creates a new bytes pool 28 | func NewBytesPool(sizes []Bucket, opts ObjectPoolOptions) BytesPool { 29 | return &bytesPool{pool: NewBucketizedObjectPool(sizes, opts)} 30 | } 31 | 32 | func (p *bytesPool) Init() { 33 | p.pool.Init(func(capacity int) interface{} { 34 | return make([]byte, 0, capacity) 35 | }) 36 | } 37 | 38 | func (p *bytesPool) Get(capacity int) []byte { 39 | if capacity < 1 { 40 | return nil 41 | } 42 | 43 | return p.pool.Get(capacity).([]byte) 44 | } 45 | 46 | func (p *bytesPool) Put(value []byte) { 47 | value = value[:0] 48 | p.pool.Put(value, cap(value)) 49 | } 50 | 51 | // AppendByte appends a byte to a byte slice getting a new slice from the 52 | // BytesPool if the slice is at capacity 53 | func AppendByte(bytes []byte, b byte, pool BytesPool) []byte { 54 | if len(bytes) == cap(bytes) { 55 | newBytes := pool.Get(cap(bytes) * 2) 56 | n := copy(newBytes[:len(bytes)], bytes) 57 | pool.Put(bytes) 58 | bytes = newBytes[:n] 59 | } 60 | 61 | return append(bytes, b) 62 | } 63 | -------------------------------------------------------------------------------- /pool/bytes_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package pool 22 | 23 | import ( 24 | "testing" 25 | 26 | "github.com/stretchr/testify/assert" 27 | ) 28 | 29 | func TestBytesPool(t *testing.T) { 30 | p := getBytesPool(2, []int{5, 10}) 31 | p.Init() 32 | 33 | assert.Equal(t, []byte(nil), p.Get(0)) 34 | 35 | b1 := p.Get(1) 36 | assert.Equal(t, 0, len(b1)) 37 | assert.Equal(t, 5, cap(b1)) 38 | b1 = append(b1, 'a') 39 | 40 | b2 := p.Get(3) 41 | assert.Equal(t, 0, len(b2)) 42 | assert.Equal(t, 5, cap(b2)) 43 | b2 = append(b1, 'b') 44 | assert.NotEqual(t, b1, b2) 45 | p.Put(b1) 46 | 47 | b3 := p.Get(2) 48 | assert.Equal(t, 0, len(b3)) 49 | assert.Equal(t, 5, cap(b3)) 50 | assert.Equal(t, b1, b3[:1]) 51 | } 52 | 53 | func TestBytesPoolGetLargerThanLargestBucket(t *testing.T) { 54 | p := getBytesPool(2, []int{8}) 55 | p.Init() 56 | 57 | x := p.Get(16) 58 | assert.NotNil(t, x) 59 | assert.Equal(t, 16, cap(x)) 60 | assert.Equal(t, 0, len(x)) 61 | 62 | // Assert not from pool 63 | bucketed := p.pool.(*bucketizedObjectPool) 64 | assert.Equal(t, 1, len(bucketed.buckets)) 65 | assert.Equal(t, 2, len(bucketed.buckets[0].pool.(*objectPool).values)) 66 | } 67 | 68 | func TestAppendByte(t *testing.T) { 69 | p := getBytesPool(1, []int{3, 10}) 70 | p.Init() 71 | vals := []byte{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i'} 72 | 73 | b1 := p.Get(2) 74 | b2 := b1 75 | for _, val := range vals { 76 | b2 = AppendByte(b2, val, p) 77 | } 78 | 79 | assert.Equal(t, b2, vals) 80 | assert.Equal(t, 9, len(b2)) 81 | assert.Equal(t, 10, cap(b2)) 82 | 83 | b3 := p.Get(2) 84 | assert.Equal(t, cap(b1), cap(b3)) 85 | assert.Equal(t, b1[:3], b3[:3]) 86 | } 87 | 88 | func getBytesPool(bucketSizes int, bucketCaps []int) *bytesPool { 89 | buckets := make([]Bucket, len(bucketCaps)) 90 | for i, cap := range bucketCaps { 91 | buckets[i] = Bucket{ 92 | Count: bucketSizes, 93 | Capacity: cap, 94 | } 95 | } 96 | 97 | return NewBytesPool(buckets, nil).(*bytesPool) 98 | } 99 | -------------------------------------------------------------------------------- /pool/checked_bytes.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package pool 22 | 23 | import "github.com/m3db/m3x/checked" 24 | 25 | type checkedBytesPool struct { 26 | bytesPool BytesPool 27 | pool BucketizedObjectPool 28 | } 29 | 30 | // NewBytesPoolFn is a function to construct a new bytes pool 31 | type NewBytesPoolFn func(sizes []Bucket) BytesPool 32 | 33 | // NewCheckedBytesPool creates a new checked bytes pool 34 | func NewCheckedBytesPool( 35 | sizes []Bucket, 36 | opts ObjectPoolOptions, 37 | newBackingBytesPool NewBytesPoolFn, 38 | ) CheckedBytesPool { 39 | return &checkedBytesPool{ 40 | bytesPool: newBackingBytesPool(sizes), 41 | pool: NewBucketizedObjectPool(sizes, opts), 42 | } 43 | } 44 | 45 | func (p *checkedBytesPool) BytesPool() BytesPool { 46 | return p.bytesPool 47 | } 48 | 49 | func (p *checkedBytesPool) Init() { 50 | opts := checked.NewBytesOptions(). 51 | SetFinalizer(p) 52 | 53 | p.bytesPool.Init() 54 | p.pool.Init(func(capacity int) interface{} { 55 | value := p.bytesPool.Get(capacity) 56 | return checked.NewBytes(value, opts) 57 | }) 58 | } 59 | 60 | func (p *checkedBytesPool) Get(capacity int) checked.Bytes { 61 | return p.pool.Get(capacity).(checked.Bytes) 62 | } 63 | 64 | func (p *checkedBytesPool) FinalizeBytes(bytes checked.Bytes) { 65 | bytes.IncRef() 66 | bytes.Resize(0) 67 | capacity := bytes.Cap() 68 | bytes.DecRef() 69 | p.pool.Put(bytes, capacity) 70 | } 71 | 72 | // AppendByteChecked appends a byte to a byte slice getting a new slice from 73 | // the CheckedBytesPool if the slice is at capacity 74 | func AppendByteChecked( 75 | bytes checked.Bytes, 76 | b byte, 77 | pool CheckedBytesPool, 78 | ) ( 79 | result checked.Bytes, 80 | swapped bool, 81 | ) { 82 | orig := bytes 83 | 84 | if bytes.Len() == bytes.Cap() { 85 | newBytes := pool.Get(bytes.Cap() * 2) 86 | 87 | // Inc the ref to read/write to it 88 | newBytes.IncRef() 89 | newBytes.Resize(bytes.Len()) 90 | 91 | copy(newBytes.Bytes(), bytes.Bytes()) 92 | 93 | bytes = newBytes 94 | } 95 | 96 | bytes.Append(b) 97 | 98 | result = bytes 99 | swapped = orig != bytes 100 | 101 | if swapped { 102 | // No longer holding reference from the inc 103 | result.DecRef() 104 | } 105 | 106 | return 107 | } 108 | -------------------------------------------------------------------------------- /pool/checked_object.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package pool 22 | 23 | import "github.com/m3db/m3x/checked" 24 | 25 | type checkedObjectPool struct { 26 | pool ObjectPool 27 | finalizerPool ObjectPool 28 | } 29 | 30 | type checkedObjectFinalizer struct { 31 | value checked.ReadWriteRef 32 | p *checkedObjectPool 33 | } 34 | 35 | func (f *checkedObjectFinalizer) Finalize() { 36 | f.p.pool.Put(f.value) 37 | 38 | finalizerPool := f.p.finalizerPool 39 | f.value = nil 40 | f.p = nil 41 | 42 | finalizerPool.Put(f) 43 | } 44 | 45 | // NewCheckedObjectPool creates a new checked pool 46 | func NewCheckedObjectPool(opts ObjectPoolOptions) CheckedObjectPool { 47 | if opts == nil { 48 | opts = NewObjectPoolOptions() 49 | } 50 | return &checkedObjectPool{ 51 | pool: NewObjectPool(opts), 52 | finalizerPool: NewObjectPool(opts.SetInstrumentOptions(opts.InstrumentOptions(). 53 | SetMetricsScope(opts.InstrumentOptions(). 54 | MetricsScope(). 55 | SubScope("finalizer-pool")))), 56 | } 57 | } 58 | 59 | func (p *checkedObjectPool) Init(alloc CheckedAllocator) { 60 | p.pool.Init(func() interface{} { 61 | v := alloc() 62 | v.TrackObject(v) 63 | return v 64 | }) 65 | p.finalizerPool.Init(func() interface{} { 66 | return &checkedObjectFinalizer{} 67 | }) 68 | } 69 | 70 | func (p *checkedObjectPool) Get() checked.ReadWriteRef { 71 | value := p.pool.Get().(checked.ReadWriteRef) 72 | finalizer := p.finalizerPool.Get().(*checkedObjectFinalizer) 73 | finalizer.value = value 74 | finalizer.p = p 75 | value.SetFinalizer(finalizer) 76 | return value 77 | } 78 | -------------------------------------------------------------------------------- /pool/checked_object_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package pool 22 | 23 | import ( 24 | "testing" 25 | 26 | "github.com/m3db/m3x/checked" 27 | 28 | "github.com/stretchr/testify/assert" 29 | ) 30 | 31 | func TestCheckedObjectPool(t *testing.T) { 32 | type obj struct { 33 | checked.RefCount 34 | x int 35 | } 36 | 37 | opts := NewObjectPoolOptions().SetSize(1) 38 | 39 | p := NewCheckedObjectPool(opts) 40 | p.Init(func() checked.ReadWriteRef { 41 | return &obj{} 42 | }) 43 | 44 | assert.Equal(t, 1, checkedObjectPoolLen(p)) 45 | 46 | o := p.Get().(*obj) 47 | assert.Equal(t, 0, checkedObjectPoolLen(p)) 48 | 49 | o.IncRef() 50 | o.IncWrites() 51 | o.x = 3 52 | o.DecWrites() 53 | o.DecRef() 54 | o.Finalize() 55 | 56 | assert.Equal(t, 1, checkedObjectPoolLen(p)) 57 | 58 | o = p.Get().(*obj) 59 | assert.Equal(t, 0, checkedObjectPoolLen(p)) 60 | 61 | o.IncRef() 62 | o.IncReads() 63 | assert.Equal(t, 3, o.x) 64 | } 65 | 66 | func TestCheckedObjectPoolNoOptions(t *testing.T) { 67 | p := NewCheckedObjectPool(nil) 68 | assert.NotNil(t, p) 69 | } 70 | 71 | func checkedObjectPoolLen(p CheckedObjectPool) int { 72 | return len(p.(*checkedObjectPool).pool.(*objectPool).values) 73 | } 74 | -------------------------------------------------------------------------------- /pool/config_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package pool 22 | 23 | import ( 24 | "testing" 25 | 26 | "github.com/m3db/m3x/instrument" 27 | 28 | "github.com/stretchr/testify/require" 29 | ) 30 | 31 | func TestObjectPoolConfiguration(t *testing.T) { 32 | cfg := ObjectPoolConfiguration{ 33 | Size: 1, 34 | Watermark: WatermarkConfiguration{ 35 | RefillLowWatermark: 0.1, 36 | RefillHighWatermark: 0.5, 37 | }, 38 | } 39 | opts := cfg.NewObjectPoolOptions(instrument.NewOptions()).(*objectPoolOptions) 40 | require.Equal(t, 1, opts.size) 41 | require.Equal(t, 0.1, opts.refillLowWatermark) 42 | require.Equal(t, 0.5, opts.refillHighWatermark) 43 | } 44 | 45 | func TestBucketizedPoolConfiguration(t *testing.T) { 46 | cfg := BucketizedPoolConfiguration{ 47 | Buckets: []BucketConfiguration{ 48 | {Count: 1, Capacity: 10}, 49 | {Count: 2, Capacity: 20}, 50 | }, 51 | Watermark: WatermarkConfiguration{ 52 | RefillLowWatermark: 0.1, 53 | RefillHighWatermark: 0.5, 54 | }, 55 | } 56 | expectedBuckets := []Bucket{ 57 | {Count: 1, Capacity: 10}, 58 | {Count: 2, Capacity: 20}, 59 | } 60 | require.Equal(t, expectedBuckets, cfg.NewBuckets()) 61 | opts := cfg.NewObjectPoolOptions(instrument.NewOptions()).(*objectPoolOptions) 62 | require.Equal(t, 0.1, opts.refillLowWatermark) 63 | require.Equal(t, 0.5, opts.refillHighWatermark) 64 | } 65 | -------------------------------------------------------------------------------- /pool/example_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package pool_test 22 | 23 | import ( 24 | "fmt" 25 | 26 | "github.com/m3db/m3x/pool" 27 | ) 28 | 29 | type exampleObject struct { 30 | a, b, c int64 31 | } 32 | 33 | func (o *exampleObject) reset() { 34 | o.a = 0 35 | o.b = 0 36 | o.c = 0 37 | } 38 | 39 | func ExampleObjectPool() { 40 | opts := pool.NewObjectPoolOptions() 41 | p := pool.NewObjectPool(opts) 42 | p.Init(func() interface{} { 43 | // The Pool's Allocator should generally only return pointer 44 | // types, since a pointer can be put into the return interface 45 | // value without an allocation. 46 | return new(exampleObject) 47 | }) 48 | 49 | // Get an exampleObject from the pool. 50 | o := p.Get().(*exampleObject) 51 | 52 | fmt.Printf("Retrieved struct should have default values: %+v", o) 53 | // Output: Retrieved struct should have default values: &{a:0 b:0 c:0} 54 | 55 | // Use the exampleObject. 56 | _ = o 57 | 58 | // Reset the exampleObject and return it to the pool. 59 | o.reset() 60 | p.Put(o) 61 | } 62 | -------------------------------------------------------------------------------- /pool/floats.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package pool 22 | 23 | type floatsPool struct { 24 | pool BucketizedObjectPool 25 | } 26 | 27 | // NewFloatsPool creates a new floats pool 28 | func NewFloatsPool(sizes []Bucket, opts ObjectPoolOptions) FloatsPool { 29 | return &floatsPool{pool: NewBucketizedObjectPool(sizes, opts)} 30 | } 31 | 32 | func (p *floatsPool) Init() { 33 | p.pool.Init(func(capacity int) interface{} { 34 | return make([]float64, 0, capacity) 35 | }) 36 | } 37 | 38 | func (p *floatsPool) Get(capacity int) []float64 { 39 | return p.pool.Get(capacity).([]float64) 40 | } 41 | 42 | func (p *floatsPool) Put(value []float64) { 43 | value = value[:0] 44 | p.pool.Put(value, cap(value)) 45 | } 46 | -------------------------------------------------------------------------------- /pool/floats_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package pool 22 | 23 | import ( 24 | "testing" 25 | 26 | "github.com/stretchr/testify/assert" 27 | ) 28 | 29 | func TestFloatsPool(t *testing.T) { 30 | p := getFloatsPool(2, []int{5, 10}) 31 | p.Init() 32 | 33 | f1 := p.Get(1) 34 | assert.Equal(t, 0, len(f1)) 35 | assert.Equal(t, 5, cap(f1)) 36 | f1 = append(f1, 1.0) 37 | 38 | f2 := p.Get(3) 39 | assert.Equal(t, 0, len(f2)) 40 | assert.Equal(t, 5, cap(f2)) 41 | f2 = append(f1, 2.0) 42 | assert.NotEqual(t, f1, f2) 43 | p.Put(f1) 44 | 45 | f3 := p.Get(2) 46 | assert.Equal(t, 0, len(f3)) 47 | assert.Equal(t, 5, cap(f3)) 48 | assert.Equal(t, f1, f3[:1]) 49 | } 50 | 51 | // nolint: unparam 52 | func getFloatsPool(bucketSizes int, bucketCaps []int) *floatsPool { 53 | buckets := make([]Bucket, len(bucketCaps)) 54 | for i, cap := range bucketCaps { 55 | buckets[i] = Bucket{ 56 | Count: bucketSizes, 57 | Capacity: cap, 58 | } 59 | } 60 | 61 | return NewFloatsPool(buckets, nil).(*floatsPool) 62 | } 63 | -------------------------------------------------------------------------------- /pool/options.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package pool 22 | 23 | import "github.com/m3db/m3x/instrument" 24 | 25 | const ( 26 | defaultSize = 4096 27 | defaultRefillLowWatermark = 0.0 28 | defaultRefillHighWatermark = 0.0 29 | ) 30 | 31 | type objectPoolOptions struct { 32 | size int 33 | refillLowWatermark float64 34 | refillHighWatermark float64 35 | instrumentOpts instrument.Options 36 | onPoolAccessErrorFn OnPoolAccessErrorFn 37 | } 38 | 39 | // NewObjectPoolOptions creates a new set of object pool options 40 | func NewObjectPoolOptions() ObjectPoolOptions { 41 | return &objectPoolOptions{ 42 | size: defaultSize, 43 | refillLowWatermark: defaultRefillLowWatermark, 44 | refillHighWatermark: defaultRefillHighWatermark, 45 | instrumentOpts: instrument.NewOptions(), 46 | onPoolAccessErrorFn: func(err error) { panic(err) }, 47 | } 48 | } 49 | 50 | func (o *objectPoolOptions) SetSize(value int) ObjectPoolOptions { 51 | opts := *o 52 | opts.size = value 53 | return &opts 54 | } 55 | 56 | func (o *objectPoolOptions) Size() int { 57 | return o.size 58 | } 59 | 60 | func (o *objectPoolOptions) SetRefillLowWatermark(value float64) ObjectPoolOptions { 61 | opts := *o 62 | opts.refillLowWatermark = value 63 | return &opts 64 | } 65 | 66 | func (o *objectPoolOptions) RefillLowWatermark() float64 { 67 | return o.refillLowWatermark 68 | } 69 | 70 | func (o *objectPoolOptions) SetRefillHighWatermark(value float64) ObjectPoolOptions { 71 | opts := *o 72 | opts.refillHighWatermark = value 73 | return &opts 74 | } 75 | 76 | func (o *objectPoolOptions) RefillHighWatermark() float64 { 77 | return o.refillHighWatermark 78 | } 79 | 80 | func (o *objectPoolOptions) SetInstrumentOptions(value instrument.Options) ObjectPoolOptions { 81 | opts := *o 82 | opts.instrumentOpts = value 83 | return &opts 84 | } 85 | 86 | func (o *objectPoolOptions) InstrumentOptions() instrument.Options { 87 | return o.instrumentOpts 88 | } 89 | 90 | func (o *objectPoolOptions) SetOnPoolAccessErrorFn(value OnPoolAccessErrorFn) ObjectPoolOptions { 91 | opts := *o 92 | opts.onPoolAccessErrorFn = value 93 | return &opts 94 | } 95 | 96 | func (o *objectPoolOptions) OnPoolAccessErrorFn() OnPoolAccessErrorFn { 97 | return o.onPoolAccessErrorFn 98 | } 99 | -------------------------------------------------------------------------------- /pprof/pprof.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | // Package pprof provides a function for registering a HTTP handler for pprof 22 | // endpoints. 23 | package pprof 24 | 25 | import ( 26 | "net/http" 27 | "net/http/pprof" 28 | "strings" 29 | ) 30 | 31 | const ( 32 | pprofPath = "/debug/pprof/" 33 | ) 34 | 35 | // RegisterHandler registers the pprof handler with the given http mux. 36 | func RegisterHandler(mux *http.ServeMux) { 37 | mux.Handle(pprofPath, handler()) 38 | } 39 | 40 | func handler() http.Handler { 41 | h := func(w http.ResponseWriter, r *http.Request) { 42 | name := strings.TrimPrefix(r.URL.Path, pprofPath) 43 | switch name { 44 | case "cmdline": 45 | pprof.Cmdline(w, r) 46 | case "profile": 47 | pprof.Profile(w, r) 48 | case "symbol": 49 | pprof.Symbol(w, r) 50 | case "trace": 51 | pprof.Trace(w, r) 52 | default: 53 | pprof.Index(w, r) 54 | } 55 | } 56 | return http.HandlerFunc(h) 57 | } 58 | -------------------------------------------------------------------------------- /pprof/pprof_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package pprof 22 | 23 | import ( 24 | "net/http" 25 | "net/http/httptest" 26 | "testing" 27 | 28 | "github.com/stretchr/testify/require" 29 | ) 30 | 31 | func TestHandler(t *testing.T) { 32 | s := httptest.NewServer(handler()) 33 | url := s.URL + pprofPath 34 | defer s.Close() 35 | 36 | for _, endpoint := range []string{ 37 | "", 38 | "cmdline", 39 | "symbol", 40 | "profile?seconds=1", 41 | "trace?seconds=1", 42 | "goroutine?debug=2", 43 | } { 44 | resp, err := http.Get(url + endpoint) 45 | require.NoError(t, err) 46 | require.Equal(t, http.StatusOK, resp.StatusCode) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /process/process_linux.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | // Package process provides functions for inspecting processes. 22 | package process 23 | 24 | import ( 25 | "fmt" 26 | "os" 27 | ) 28 | 29 | // NumFDs returns the number of file descriptors for a given process. 30 | // This is more efficient than the NumFDs() method in the psutils package 31 | // by avoiding reading the destination of the symlinks in the proc directory. 32 | func NumFDs(pid int) (int, error) { 33 | statPath := fmt.Sprintf("/proc/%d/fd", pid) 34 | d, err := os.Open(statPath) 35 | if err != nil { 36 | return 0, err 37 | } 38 | fnames, err := d.Readdirnames(-1) 39 | d.Close() 40 | return len(fnames), err 41 | } 42 | -------------------------------------------------------------------------------- /process/process_notlinux.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | // +build !linux 22 | 23 | package process 24 | 25 | import ( 26 | "errors" 27 | ) 28 | 29 | var errNotAvailable = errors.New( 30 | "cannot get process file descriptors, only available on linux") 31 | 32 | // NumFDs returns the number of file descriptors for a given process. 33 | func NumFDs(pid int) (int, error) { 34 | return 0, errNotAvailable 35 | } 36 | -------------------------------------------------------------------------------- /resource/types.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | // Package resource describes require for object lifecycle management. 22 | // Both Finalizer and Closer have similar concepts, they both exist so that 23 | // different types can be used for resource cleanup with different method names 24 | // as for some things like iterators, the verb close makes more sense than 25 | // finalize and is more consistent with other types. 26 | package resource 27 | 28 | // Finalizer finalizes a checked resource. 29 | type Finalizer interface { 30 | Finalize() 31 | } 32 | 33 | // FinalizerFn is a function literal that is a finalizer. 34 | type FinalizerFn func() 35 | 36 | // Finalize will call the function literal as a finalizer. 37 | func (fn FinalizerFn) Finalize() { 38 | fn() 39 | } 40 | 41 | // Closer is an object that can be closed. 42 | type Closer interface { 43 | Close() 44 | } 45 | 46 | // CloserFn is a function literal that is a closer. 47 | type CloserFn func() 48 | 49 | // Close will call the function literal as a closer. 50 | func (fn CloserFn) Close() { 51 | fn() 52 | } 53 | -------------------------------------------------------------------------------- /retry/config.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package retry 22 | 23 | import ( 24 | "time" 25 | 26 | "github.com/uber-go/tally" 27 | ) 28 | 29 | // Configuration configures options for retry attempts. 30 | type Configuration struct { 31 | // Initial retry backoff. 32 | InitialBackoff time.Duration `yaml:"initialBackoff" validate:"min=0"` 33 | 34 | // Backoff factor for exponential backoff. 35 | BackoffFactor float64 `yaml:"backoffFactor" validate:"min=0"` 36 | 37 | // Maximum backoff time. 38 | MaxBackoff time.Duration `yaml:"maxBackoff" validate:"min=0"` 39 | 40 | // Maximum number of retry attempts. 41 | MaxRetries int `yaml:"maxRetries"` 42 | 43 | // Whether to retry forever until either the attempt succeeds, 44 | // or the retry condition becomes false. 45 | Forever *bool `yaml:"forever"` 46 | 47 | // Whether jittering is applied during retries. 48 | Jitter *bool `yaml:"jitter"` 49 | } 50 | 51 | // NewOptions creates a new retry options based on the configuration. 52 | func (c Configuration) NewOptions(scope tally.Scope) Options { 53 | opts := NewOptions().SetMetricsScope(scope) 54 | if c.InitialBackoff != 0 { 55 | opts = opts.SetInitialBackoff(c.InitialBackoff) 56 | } 57 | if c.BackoffFactor != 0.0 { 58 | opts = opts.SetBackoffFactor(c.BackoffFactor) 59 | } 60 | if c.MaxBackoff != 0 { 61 | opts = opts.SetMaxBackoff(c.MaxBackoff) 62 | } 63 | if c.MaxRetries != 0 { 64 | opts = opts.SetMaxRetries(c.MaxRetries) 65 | } 66 | if c.Forever != nil { 67 | opts = opts.SetForever(*c.Forever) 68 | } 69 | if c.Jitter != nil { 70 | opts = opts.SetJitter(*c.Jitter) 71 | } 72 | 73 | return opts 74 | } 75 | 76 | // NewRetrier creates a new retrier based on the configuration. 77 | func (c Configuration) NewRetrier(scope tally.Scope) Retrier { 78 | return NewRetrier(c.NewOptions(scope)) 79 | } 80 | -------------------------------------------------------------------------------- /retry/config_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package retry 22 | 23 | import ( 24 | "testing" 25 | "time" 26 | 27 | "github.com/stretchr/testify/require" 28 | "github.com/uber-go/tally" 29 | ) 30 | 31 | func TestRetryConfig(t *testing.T) { 32 | b1 := true 33 | b2 := false 34 | cfg := Configuration{ 35 | InitialBackoff: time.Second, 36 | BackoffFactor: 2.0, 37 | MaxBackoff: time.Minute, 38 | MaxRetries: 3, 39 | Forever: &b1, 40 | Jitter: &b2, 41 | } 42 | retrier := cfg.NewRetrier(tally.NoopScope).(*retrier) 43 | require.Equal(t, time.Second, retrier.initialBackoff) 44 | require.Equal(t, 2.0, retrier.backoffFactor) 45 | require.Equal(t, time.Minute, retrier.maxBackoff) 46 | require.Equal(t, 3, retrier.maxRetries) 47 | require.Equal(t, b1, retrier.forever) 48 | require.Equal(t, b2, retrier.jitter) 49 | } 50 | -------------------------------------------------------------------------------- /retry/example_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package retry_test 22 | 23 | import ( 24 | "context" 25 | "errors" 26 | "fmt" 27 | "log" 28 | 29 | "github.com/m3db/m3x/retry" 30 | ) 31 | 32 | func ExampleRetrier() { 33 | var ( 34 | opts = retry.NewOptions() 35 | retrier = retry.NewRetrier(opts) 36 | context = context.Background() 37 | ) 38 | 39 | continueFn := func(attempt int) bool { 40 | // Check if the context has been canceled. 41 | select { 42 | case <-context.Done(): 43 | return false 44 | default: 45 | return true 46 | } 47 | } 48 | 49 | var attempts int 50 | fn := func() error { 51 | // Perform some work which may fail. 52 | 53 | if attempts++; attempts == 3 { 54 | fmt.Printf("Attempt %v succeeded", attempts) 55 | // Output: Attempt 3 succeeded 56 | 57 | return nil 58 | } 59 | return errors.New("test") 60 | } 61 | 62 | if err := retrier.AttemptWhile(continueFn, fn); err != nil { 63 | log.Fatal(err) 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /sampler/sampler.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package sampler 22 | 23 | import ( 24 | "fmt" 25 | 26 | "go.uber.org/atomic" 27 | ) 28 | 29 | // Sampler samples the requests, out of 100 sample calls, 30 | // 100*sampleRate calls will be sampled. 31 | type Sampler struct { 32 | sampleEvery int32 33 | numTried *atomic.Int32 34 | } 35 | 36 | // NewSampler creates a new sampler with a sample rate. 37 | func NewSampler(sampleRate float64) (*Sampler, error) { 38 | if sampleRate <= 0.0 || sampleRate >= 1.0 { 39 | return nil, fmt.Errorf("invalid sample rate %f", sampleRate) 40 | } 41 | return &Sampler{numTried: atomic.NewInt32(0), sampleEvery: int32(1.0 / sampleRate)}, nil 42 | } 43 | 44 | // Sample returns true when the call is sampled. 45 | func (t *Sampler) Sample() bool { 46 | return (t.numTried.Inc()-1)%t.sampleEvery == 0 47 | } 48 | -------------------------------------------------------------------------------- /sampler/sampler_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package sampler 22 | 23 | import ( 24 | "testing" 25 | 26 | "github.com/stretchr/testify/require" 27 | ) 28 | 29 | func TestInvalidSampleRate(t *testing.T) { 30 | _, err := NewSampler(-1.0) 31 | require.Error(t, err) 32 | 33 | _, err = NewSampler(0.0) 34 | require.Error(t, err) 35 | 36 | _, err = NewSampler(1.0) 37 | require.Error(t, err) 38 | 39 | _, err = NewSampler(2.0) 40 | require.Error(t, err) 41 | } 42 | 43 | func TestSampler(t *testing.T) { 44 | s, err := NewSampler(0.5) 45 | require.NoError(t, err) 46 | require.True(t, s.Sample()) 47 | require.False(t, s.Sample()) 48 | require.True(t, s.Sample()) 49 | require.False(t, s.Sample()) 50 | require.True(t, s.Sample()) 51 | 52 | s, err = NewSampler(0.25) 53 | require.NoError(t, err) 54 | require.True(t, s.Sample()) 55 | require.False(t, s.Sample()) 56 | require.False(t, s.Sample()) 57 | require.False(t, s.Sample()) 58 | require.True(t, s.Sample()) 59 | require.False(t, s.Sample()) 60 | require.False(t, s.Sample()) 61 | require.False(t, s.Sample()) 62 | require.True(t, s.Sample()) 63 | } 64 | -------------------------------------------------------------------------------- /server/config.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package server 22 | 23 | import ( 24 | "time" 25 | 26 | "github.com/m3db/m3x/instrument" 27 | "github.com/m3db/m3x/retry" 28 | ) 29 | 30 | // Configuration configs a server. 31 | type Configuration struct { 32 | // Server listening address. 33 | ListenAddress string `yaml:"listenAddress" validate:"nonzero"` 34 | 35 | // Retry mechanism configuration. 36 | Retry retry.Configuration `yaml:"retry"` 37 | 38 | // Whether keep alives are enabled on connections. 39 | KeepAliveEnabled *bool `yaml:"keepAliveEnabled"` 40 | 41 | // KeepAlive period. 42 | KeepAlivePeriod *time.Duration `yaml:"keepAlivePeriod"` 43 | } 44 | 45 | // NewOptions creates server options. 46 | func (c Configuration) NewOptions(iOpts instrument.Options) Options { 47 | opts := NewOptions(). 48 | SetRetryOptions(c.Retry.NewOptions(iOpts.MetricsScope())). 49 | SetInstrumentOptions(iOpts) 50 | if c.KeepAliveEnabled != nil { 51 | opts = opts.SetTCPConnectionKeepAlive(*c.KeepAliveEnabled) 52 | } 53 | if c.KeepAlivePeriod != nil { 54 | opts = opts.SetTCPConnectionKeepAlivePeriod(*c.KeepAlivePeriod) 55 | } 56 | return opts 57 | } 58 | 59 | // NewServer creates a new server. 60 | func (c Configuration) NewServer(handler Handler, iOpts instrument.Options) Server { 61 | return NewServer(c.ListenAddress, handler, c.NewOptions(iOpts)) 62 | } 63 | -------------------------------------------------------------------------------- /server/config_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package server 22 | 23 | import ( 24 | "testing" 25 | "time" 26 | 27 | "github.com/m3db/m3x/instrument" 28 | 29 | "github.com/stretchr/testify/require" 30 | yaml "gopkg.in/yaml.v2" 31 | ) 32 | 33 | func TestServerConfiguration(t *testing.T) { 34 | str := ` 35 | listenAddress: addr 36 | keepAliveEnabled: true 37 | keepAlivePeriod: 5s 38 | ` 39 | 40 | var cfg Configuration 41 | require.NoError(t, yaml.Unmarshal([]byte(str), &cfg)) 42 | require.Equal(t, "addr", cfg.ListenAddress) 43 | require.True(t, *cfg.KeepAliveEnabled) 44 | require.Equal(t, 5*time.Second, *cfg.KeepAlivePeriod) 45 | 46 | opts := cfg.NewOptions(instrument.NewOptions()) 47 | require.Equal(t, 5*time.Second, opts.TCPConnectionKeepAlivePeriod()) 48 | require.True(t, opts.TCPConnectionKeepAlive()) 49 | 50 | require.NotNil(t, cfg.NewServer(nil, instrument.NewOptions())) 51 | } 52 | -------------------------------------------------------------------------------- /server/example_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package server_test 22 | 23 | import ( 24 | "log" 25 | "net" 26 | 27 | xserver "github.com/m3db/m3x/server" 28 | ) 29 | 30 | type simpleHandler struct{} 31 | 32 | func (h *simpleHandler) Handle(conn net.Conn) { conn.Close() } 33 | func (h *simpleHandler) Close() {} 34 | 35 | func ExampleServer() { 36 | var ( 37 | address = ":0" 38 | handler = &simpleHandler{} 39 | opts = xserver.NewOptions() 40 | ) 41 | 42 | s := xserver.NewServer(address, handler, opts) 43 | 44 | if err := s.ListenAndServe(); err != nil { 45 | log.Fatal(err) 46 | } 47 | 48 | // Block indefintely so server can run. 49 | select {} 50 | } 51 | -------------------------------------------------------------------------------- /sync/example_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package sync_test 22 | 23 | import ( 24 | "fmt" 25 | "log" 26 | "sync" 27 | 28 | xsync "github.com/m3db/m3x/sync" 29 | ) 30 | 31 | type response struct { 32 | a int 33 | } 34 | 35 | func ExampleWorkerPool() { 36 | var ( 37 | wg sync.WaitGroup 38 | workers = xsync.NewWorkerPool(3) 39 | errorCh = make(chan error, 1) 40 | numRequests = 9 41 | responses = make([]response, numRequests) 42 | ) 43 | 44 | wg.Add(numRequests) 45 | workers.Init() 46 | 47 | for i := 0; i < numRequests; i++ { 48 | // Capture loop variable. 49 | i := i 50 | 51 | // Execute request on worker pool. 52 | workers.Go(func() { 53 | defer wg.Done() 54 | 55 | var err error 56 | 57 | // Perform some work which may fail. 58 | resp := response{a: i} 59 | 60 | if err != nil { 61 | // Return the first error that is encountered. 62 | select { 63 | case errorCh <- err: 64 | default: 65 | } 66 | 67 | return 68 | } 69 | 70 | // Can concurrently modify responses since each iteration updates a 71 | // different index. 72 | responses[i] = resp 73 | }) 74 | } 75 | 76 | // Wait for all requests to finish. 77 | wg.Wait() 78 | 79 | close(errorCh) 80 | if err := <-errorCh; err != nil { 81 | log.Fatal(err) 82 | } 83 | 84 | var total int 85 | for _, r := range responses { 86 | total += r.a 87 | } 88 | 89 | fmt.Printf("Total is %v", total) 90 | // Output: Total is 36 91 | } 92 | -------------------------------------------------------------------------------- /sync/worker_pool.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | // Package sync implements synchronization facililites such as worker pools. 22 | package sync 23 | 24 | import ( 25 | "time" 26 | ) 27 | 28 | type workerPool struct { 29 | workCh chan struct{} 30 | } 31 | 32 | // NewWorkerPool creates a new worker pool. 33 | func NewWorkerPool(size int) WorkerPool { 34 | return &workerPool{workCh: make(chan struct{}, size)} 35 | } 36 | 37 | func (p *workerPool) Init() { 38 | for i := 0; i < cap(p.workCh); i++ { 39 | p.workCh <- struct{}{} 40 | } 41 | } 42 | 43 | func (p *workerPool) Go(work Work) { 44 | token := <-p.workCh 45 | go func() { 46 | work() 47 | p.workCh <- token 48 | }() 49 | } 50 | 51 | func (p *workerPool) GoIfAvailable(work Work) bool { 52 | select { 53 | case token := <-p.workCh: 54 | go func() { 55 | work() 56 | p.workCh <- token 57 | }() 58 | return true 59 | default: 60 | return false 61 | } 62 | } 63 | 64 | func (p *workerPool) GoWithTimeout(work Work, timeout time.Duration) bool { 65 | select { 66 | case token := <-p.workCh: 67 | go func() { 68 | work() 69 | p.workCh <- token 70 | }() 71 | return true 72 | case <-time.After(timeout): 73 | return false 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /tcp/tcp.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | // Package tcp implements a tcp listener. 22 | package tcp 23 | 24 | import ( 25 | "net" 26 | "time" 27 | ) 28 | 29 | // NewTCPListener is Listener specifically for TCP 30 | // 31 | // TODO(jeromefroe): Move this into the net package which covers network I/O. 32 | func NewTCPListener(listenAddress string, keepAlivePeriod time.Duration) (net.Listener, error) { 33 | l, err := net.Listen("tcp", listenAddress) 34 | if err != nil { 35 | return nil, err 36 | } 37 | return tcpKeepAliveListener{l.(*net.TCPListener), keepAlivePeriod}, err 38 | } 39 | 40 | // tcpKeepAliveListener sets TCP keep-alive timeouts on accepted 41 | // connections. It's used by ListenAndServe and ListenAndServeTLS so 42 | // dead TCP connections (e.g. closing laptop mid-download) eventually 43 | // go away. This is cargo culted from http/server.go 44 | type tcpKeepAliveListener struct { 45 | *net.TCPListener 46 | keepAlivePeriod time.Duration 47 | } 48 | 49 | func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) { 50 | tc, err := ln.AcceptTCP() 51 | if err != nil { 52 | return 53 | } 54 | tc.SetKeepAlive(true) 55 | tc.SetKeepAlivePeriod(ln.keepAlivePeriod) 56 | return tc, nil 57 | } 58 | -------------------------------------------------------------------------------- /test/matcher.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package test 22 | 23 | import ( 24 | "fmt" 25 | 26 | "github.com/golang/mock/gomock" 27 | "github.com/google/go-cmp/cmp" 28 | ) 29 | 30 | // CmpMatcher returns a new matcher backed by go-cmp/cmp.Equal. 31 | func CmpMatcher(x interface{}, opts ...cmp.Option) gomock.Matcher { 32 | return &matcher{x, opts} 33 | } 34 | 35 | type matcher struct { 36 | expected interface{} 37 | opts cmp.Options 38 | } 39 | 40 | func (m matcher) Matches(x interface{}) bool { 41 | return cmp.Equal(m.expected, x, m.opts...) 42 | } 43 | 44 | func (m matcher) String() string { 45 | return fmt.Sprintf("%v", m.expected) 46 | } 47 | -------------------------------------------------------------------------------- /test/reporter.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | // Package test contains utility methods for testing. 22 | package test 23 | 24 | import ( 25 | "fmt" 26 | "testing" 27 | 28 | "github.com/golang/mock/gomock" 29 | ) 30 | 31 | // Reporter wraps a *testing.T, and provides a more useful failure mode 32 | // when interacting with gomock.Controller. 33 | // 34 | // For example, consider: 35 | // func TestMyThing(t *testing.T) { 36 | // mockCtrl := gomock.NewController(t) 37 | // defer mockCtrl.Finish() 38 | // mockObj := something.NewMockMyInterface(mockCtrl) 39 | // go func() { 40 | // mockObj.SomeMethod(4, "blah") 41 | // } 42 | // } 43 | // 44 | // It hangs without any indication that it's missing an EXPECT() on `mockObj`. 45 | // Providing the Reporter to the gomock.Controller ctor avoids this, and terminates 46 | // with useful feedback. i.e. 47 | // func TestMyThing(t *testing.T) { 48 | // mockCtrl := gomock.NewController(test.Reporter{t}) 49 | // defer mockCtrl.Finish() 50 | // mockObj := something.NewMockMyInterface(mockCtrl) 51 | // go func() { 52 | // mockObj.SomeMethod(4, "blah") // crashes the test now 53 | // } 54 | // } 55 | type Reporter struct { 56 | T *testing.T 57 | } 58 | 59 | // ensure Reporter implements gomock.TestReporter. 60 | var _ gomock.TestReporter = Reporter{} 61 | 62 | // Errorf is equivalent testing.T.Errorf. 63 | func (r Reporter) Errorf(format string, args ...interface{}) { 64 | r.T.Errorf(format, args...) 65 | } 66 | 67 | // Fatalf crashes the program with a panic to allow users to diagnose 68 | // missing expects. 69 | func (r Reporter) Fatalf(format string, args ...interface{}) { 70 | panic(fmt.Sprintf(format, args...)) 71 | } 72 | -------------------------------------------------------------------------------- /time/matcher.go: -------------------------------------------------------------------------------- 1 | package time 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "github.com/golang/mock/gomock" 8 | ) 9 | 10 | // Matcher is a gomock.Matcher that matches time.Time 11 | type Matcher interface { 12 | gomock.Matcher 13 | } 14 | 15 | // NewMatcher returns a new Matcher 16 | func NewMatcher(t time.Time) Matcher { 17 | return &matcher{t: t} 18 | } 19 | 20 | type matcher struct { 21 | t time.Time 22 | } 23 | 24 | func (m *matcher) Matches(x interface{}) bool { 25 | timeStruct, ok := x.(time.Time) 26 | if !ok { 27 | return false 28 | } 29 | return m.t.Equal(timeStruct) 30 | } 31 | 32 | func (m *matcher) String() string { 33 | return fmt.Sprintf("time: %s", m.t.String()) 34 | } 35 | -------------------------------------------------------------------------------- /time/matcher_test.go: -------------------------------------------------------------------------------- 1 | package time 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | "github.com/stretchr/testify/require" 8 | ) 9 | 10 | func TestMatcher(t *testing.T) { 11 | loc, _ := time.LoadLocation("Asia/Shanghai") 12 | t1 := time.Now().UTC() 13 | t2 := t1.In(loc) 14 | 15 | // Make sure t1 and t2 don't == eachother 16 | require.NotEqual(t, t1, t2) 17 | 18 | // Make sure t1 and t2 Match eachother 19 | t1Matcher := NewMatcher(t1) 20 | require.True(t, t1Matcher.Matches(t2)) 21 | 22 | // Make sure the matcher doesn't always return true 23 | require.NotEqual(t, t1Matcher, t2.Add(1*time.Hour)) 24 | require.NotEqual(t, t1Matcher, 10) 25 | } 26 | -------------------------------------------------------------------------------- /time/range_iter.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package time 22 | 23 | import "container/list" 24 | 25 | // RangeIter iterates over a collection of time ranges. 26 | type RangeIter struct { 27 | ranges *list.List 28 | cur *list.Element 29 | } 30 | 31 | func newRangeIter(ranges *list.List) *RangeIter { 32 | return &RangeIter{ranges: ranges} 33 | } 34 | 35 | // Next moves to the next item. 36 | func (it *RangeIter) Next() bool { 37 | if it.ranges == nil { 38 | return false 39 | } 40 | if it.cur == nil { 41 | it.cur = it.ranges.Front() 42 | } else { 43 | it.cur = it.cur.Next() 44 | } 45 | return it.cur != nil 46 | } 47 | 48 | // Value returns the current time range. 49 | func (it *RangeIter) Value() Range { 50 | if it.cur == nil { 51 | return Range{} 52 | } 53 | return it.cur.Value.(Range) 54 | } 55 | -------------------------------------------------------------------------------- /time/range_iter_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package time 22 | 23 | import ( 24 | "container/list" 25 | "testing" 26 | "time" 27 | 28 | "github.com/stretchr/testify/require" 29 | ) 30 | 31 | var ( 32 | testTimeRanges = []Range{ 33 | {Start: testStart, End: testStart.Add(time.Second)}, 34 | {Start: testStart.Add(2 * time.Second), End: testStart.Add(10 * time.Second)}, 35 | {Start: testStart.Add(20 * time.Second), End: testStart.Add(25 * time.Second)}, 36 | } 37 | ) 38 | 39 | func getTestList() *list.List { 40 | l := list.New() 41 | for _, r := range testTimeRanges { 42 | l.PushBack(r) 43 | } 44 | return l 45 | } 46 | 47 | func TestRangeIter(t *testing.T) { 48 | it := newRangeIter(nil) 49 | require.False(t, it.Next()) 50 | 51 | it = newRangeIter(getTestList()) 52 | for i := 0; i < len(testTimeRanges); i++ { 53 | require.True(t, it.Next()) 54 | require.Equal(t, testTimeRanges[i], it.Value()) 55 | } 56 | require.False(t, it.Next()) 57 | 58 | } 59 | -------------------------------------------------------------------------------- /time/time.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | // Package time implement facilities for working with time. 22 | package time 23 | 24 | import "time" 25 | 26 | const ( 27 | nanosPerMillis = int64(time.Millisecond) 28 | ) 29 | 30 | // ToNormalizedTime returns the normalized units of time given a time unit. 31 | func ToNormalizedTime(t time.Time, u time.Duration) int64 { 32 | return t.UnixNano() / u.Nanoseconds() 33 | } 34 | 35 | // FromNormalizedTime returns the time given the normalized time units and the time unit. 36 | func FromNormalizedTime(nt int64, u time.Duration) time.Time { 37 | return time.Unix(0, int64(u/time.Nanosecond)*nt) 38 | } 39 | 40 | // ToNormalizedDuration returns the normalized units of duration given a time unit. 41 | func ToNormalizedDuration(d time.Duration, u time.Duration) int64 { 42 | return int64(d / u) 43 | } 44 | 45 | // FromNormalizedDuration returns the duration given the normalized time duration and a time unit. 46 | func FromNormalizedDuration(nd int64, u time.Duration) time.Duration { 47 | return time.Duration(nd) * u 48 | } 49 | 50 | // ToNanoseconds converts a time to nanoseconds. 51 | func ToNanoseconds(t time.Time) int64 { 52 | return t.UnixNano() 53 | } 54 | 55 | // FromNanoseconds converts nanoseconds to a time. 56 | func FromNanoseconds(nsecs int64) time.Time { 57 | return time.Unix(0, nsecs) 58 | } 59 | 60 | // ToUnixMillis converts a time to milliseconds since Unix epoch 61 | func ToUnixMillis(t time.Time) int64 { 62 | return t.UnixNano() / nanosPerMillis 63 | } 64 | 65 | // FromUnixMillis converts milliseconds since Unix epoch to a time 66 | func FromUnixMillis(ms int64) time.Time { 67 | return time.Unix(0, ms*nanosPerMillis) 68 | } 69 | 70 | // Ceil returns the result of rounding t up to a multiple of d since 71 | // the zero time. 72 | func Ceil(t time.Time, d time.Duration) time.Time { 73 | res := t.Truncate(d) 74 | if res.Before(t) { 75 | res = res.Add(d) 76 | } 77 | return res 78 | } 79 | 80 | // MinTime returns the earlier one of t1 and t2. 81 | func MinTime(t1, t2 time.Time) time.Time { 82 | if t1.Before(t2) { 83 | return t1 84 | } 85 | return t2 86 | } 87 | 88 | // MaxTime returns the later one of t1 and t2. 89 | func MaxTime(t1, t2 time.Time) time.Time { 90 | if t1.After(t2) { 91 | return t1 92 | } 93 | return t2 94 | } 95 | -------------------------------------------------------------------------------- /time/unix_nano.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package time 22 | 23 | import "time" 24 | 25 | // UnixNano is used to indicate that an int64 stores a unix timestamp at 26 | // nanosecond resolution 27 | type UnixNano int64 28 | 29 | // ToTime returns a time.ToTime from a UnixNano 30 | func (u UnixNano) ToTime() time.Time { 31 | return time.Unix(0, int64(u)) 32 | } 33 | 34 | // ToUnixNano returns a UnixNano from a time.Time 35 | func ToUnixNano(t time.Time) UnixNano { 36 | return UnixNano(t.UnixNano()) 37 | } 38 | 39 | // Before reports whether the time instant u is before t. 40 | func (u UnixNano) Before(t UnixNano) bool { 41 | return u < t 42 | } 43 | 44 | // After reports whether the time instant u is after t. 45 | func (u UnixNano) After(t UnixNano) bool { 46 | return u > t 47 | } 48 | 49 | // Equal reports whether the time instant u is equal to t. 50 | func (u UnixNano) Equal(t UnixNano) bool { 51 | return u == t 52 | } 53 | -------------------------------------------------------------------------------- /time/unix_nano_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package time 22 | 23 | import ( 24 | "testing" 25 | "time" 26 | 27 | "github.com/stretchr/testify/require" 28 | ) 29 | 30 | func TestUnixNano(t *testing.T) { 31 | time := time.Unix(0, 1000) 32 | unixNano := ToUnixNano(time) 33 | require.Equal(t, UnixNano(1000), unixNano) 34 | require.Equal(t, time, unixNano.ToTime()) 35 | } 36 | 37 | func TestUnixNanoBefore(t *testing.T) { 38 | t0 := UnixNano(0) 39 | t1 := UnixNano(1) 40 | 41 | require.Equal(t, true, t0.Before(t1)) 42 | require.Equal(t, false, t1.Before(t0)) 43 | require.Equal(t, false, t0.Before(t0)) 44 | } 45 | 46 | func TestUnixNanoAfter(t *testing.T) { 47 | t0 := UnixNano(0) 48 | t1 := UnixNano(1) 49 | 50 | require.Equal(t, false, t0.After(t1)) 51 | require.Equal(t, true, t1.After(t0)) 52 | require.Equal(t, false, t0.After(t0)) 53 | } 54 | 55 | func TestUnixNanoEqual(t *testing.T) { 56 | t0 := UnixNano(0) 57 | t1 := UnixNano(1) 58 | 59 | require.Equal(t, false, t0.Equal(t1)) 60 | require.Equal(t, false, t1.Equal(t0)) 61 | require.Equal(t, true, t0.Equal(t0)) 62 | } 63 | -------------------------------------------------------------------------------- /unsafe/bytes_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package unsafe 22 | 23 | import ( 24 | "bytes" 25 | "testing" 26 | 27 | "github.com/stretchr/testify/require" 28 | ) 29 | 30 | func TestWithStringSmallString(t *testing.T) { 31 | str := []byte("foobarbaz") 32 | validateWithString(t, str) 33 | } 34 | 35 | func TestWithStringLargeString(t *testing.T) { 36 | var buf bytes.Buffer 37 | for i := 0; i < 65536; i++ { 38 | buf.WriteByte(byte(i % 256)) 39 | } 40 | str := buf.Bytes() 41 | validateWithString(t, str) 42 | } 43 | 44 | func TestWithStringAndArgSmallString(t *testing.T) { 45 | str := []byte("foobarbaz") 46 | validateWithStringAndArg(t, str) 47 | } 48 | 49 | func TestWithStringAndArgLargeString(t *testing.T) { 50 | var buf bytes.Buffer 51 | for i := 0; i < 65536; i++ { 52 | buf.WriteByte(byte(i % 256)) 53 | } 54 | str := buf.Bytes() 55 | validateWithStringAndArg(t, str) 56 | } 57 | 58 | var withStringBenchSink string 59 | 60 | func BenchmarkWithString(b *testing.B) { 61 | str := []byte("foobarbaz") 62 | WithString(str, func(s string) { 63 | withStringBenchSink = s 64 | }) 65 | } 66 | 67 | func validateWithString(t *testing.T, b []byte) { 68 | WithString(b, func(str string) { 69 | require.Equal(t, []byte(str), []byte(b)) 70 | require.Equal(t, len(str), len(b)) 71 | }) 72 | } 73 | 74 | func validateWithStringAndArg(t *testing.T, b []byte) { 75 | WithStringAndArg(b, "cat", func(str string, arg interface{}) { 76 | var buf bytes.Buffer 77 | buf.WriteString(str) 78 | buf.WriteString(arg.(string)) 79 | require.Equal(t, string(b)+"cat", buf.String()) 80 | }) 81 | } 82 | -------------------------------------------------------------------------------- /unsafe/string_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package unsafe 22 | 23 | import ( 24 | "bytes" 25 | "testing" 26 | 27 | "github.com/stretchr/testify/require" 28 | ) 29 | 30 | func TestWithBytesSmallString(t *testing.T) { 31 | str := "foobarbaz" 32 | validateWithBytes(t, str) 33 | } 34 | 35 | func TestWithBytesLargeString(t *testing.T) { 36 | var buf bytes.Buffer 37 | for i := 0; i < 65536; i++ { 38 | buf.WriteByte(byte(i % 256)) 39 | } 40 | str := buf.String() 41 | validateWithBytes(t, str) 42 | } 43 | 44 | func TestWithBytesAndArgSmallString(t *testing.T) { 45 | str := "foobarbaz" 46 | validateWithBytesAndArg(t, str) 47 | } 48 | 49 | func TestWithBytesAndArgLargeString(t *testing.T) { 50 | var buf bytes.Buffer 51 | for i := 0; i < 65536; i++ { 52 | buf.WriteByte(byte(i % 256)) 53 | } 54 | str := buf.String() 55 | validateWithBytesAndArg(t, str) 56 | } 57 | 58 | var withBytesBenchSink ImmutableBytes 59 | 60 | func BenchmarkWithBytes(b *testing.B) { 61 | for i := 0; i < b.N; i++ { 62 | WithBytes("foobar", func(b ImmutableBytes) { 63 | withBytesBenchSink = b 64 | }) 65 | } 66 | } 67 | 68 | func validateWithBytes(t *testing.T, str string) { 69 | WithBytes(str, func(b ImmutableBytes) { 70 | require.Equal(t, []byte(str), []byte(b)) 71 | require.Equal(t, len(str), len(b)) 72 | require.Equal(t, len(str), cap(b)) 73 | }) 74 | } 75 | 76 | func validateWithBytesAndArg(t *testing.T, str string) { 77 | WithBytesAndArg(str, "cat", func(data ImmutableBytes, arg interface{}) { 78 | var buf bytes.Buffer 79 | for _, b := range data { 80 | buf.WriteByte(b) 81 | } 82 | buf.WriteString(arg.(string)) 83 | require.Equal(t, str+"cat", buf.String()) 84 | }) 85 | } 86 | -------------------------------------------------------------------------------- /watch/source.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package watch 22 | 23 | import ( 24 | "errors" 25 | "sync" 26 | 27 | "github.com/m3db/m3x/close" 28 | "github.com/m3db/m3x/log" 29 | ) 30 | 31 | // ErrSourceClosed indicates that the Source should be closed. 32 | var ErrSourceClosed = errors.New("source closed") 33 | 34 | // SourceInput provides data for Source, 35 | type SourceInput interface { 36 | // Poll will be called by Source for data. Any backoff/jitter logic should 37 | // be handled here. 38 | Poll() (interface{}, error) 39 | } 40 | 41 | // Source polls data by calling SourcePollFn and notifies its watches on updates. 42 | type Source interface { 43 | close.SimpleCloser 44 | 45 | // Get returns the latest value. 46 | Get() interface{} 47 | 48 | // Watch returns the value and a Watch. 49 | Watch() (interface{}, Watch, error) 50 | } 51 | 52 | // NewSource returns a new Source. 53 | func NewSource(input SourceInput, logger log.Logger) Source { 54 | s := &source{ 55 | input: input, 56 | w: NewWatchable(), 57 | logger: logger, 58 | } 59 | 60 | go s.run() 61 | return s 62 | } 63 | 64 | type source struct { 65 | sync.RWMutex 66 | 67 | input SourceInput 68 | w Watchable 69 | closed bool 70 | logger log.Logger 71 | } 72 | 73 | func (s *source) run() { 74 | for !s.isClosed() { 75 | data, err := s.input.Poll() 76 | if err == ErrSourceClosed { 77 | s.logger.Errorf("watch source upstream is closed") 78 | s.Close() 79 | return 80 | } 81 | if err != nil { 82 | s.logger.Errorf("watch source poll error: %v", err) 83 | continue 84 | } 85 | 86 | if err = s.w.Update(data); err != nil { 87 | s.logger.Errorf("watch source update error: %v", err) 88 | } 89 | } 90 | } 91 | 92 | func (s *source) isClosed() bool { 93 | s.RLock() 94 | defer s.RUnlock() 95 | return s.closed 96 | } 97 | 98 | func (s *source) Close() { 99 | s.Lock() 100 | defer s.Unlock() 101 | if s.closed { 102 | return 103 | } 104 | s.closed = true 105 | s.w.Close() 106 | } 107 | 108 | func (s *source) Get() interface{} { 109 | return s.w.Get() 110 | } 111 | 112 | func (s *source) Watch() (interface{}, Watch, error) { 113 | return s.w.Watch() 114 | } 115 | --------------------------------------------------------------------------------