diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..ac393d7 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,47 @@ +name: Go build test + +on: + pull_request: + branches: + - master + schedule: + - cron: '0 20 * * Sat' + push: + + +env: + GLOBAL_ENV: global_env_value + DAY_OF_WEEK: ThursDay + +jobs: + build: + + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Set up Go + uses: actions/setup-go@v4 # caching is enabled by default + with: + go-version-file: 'go.mod' + cache-dependency-path: go.sum + # go-version: '1.19.x' + # go-version: 'stable' + + - name: Build ${{ github.ref_name }} + run: go build -v ./... + + - name: Test + run: go test -v ./... + + - name: Use variables + if: ${{ env.DAY_OF_WEEK == 'ThursDay' }} + # if [[ env.DAY_OF_WEEK == 'ThursDay' ]]; then + # fi + run: | + echo "repository variable : $REPOSITORY_VAR" + echo "secret var : $REPOSITORY_SECRET" + echo "global env : $GLOBAL_ENV" + env: + REPOSITORY_VAR: ${{ vars.REPOSITORY_VAR }} + REPOSITORY_SECRET: ${{ secrets.SECRET_TEST_NAME }} diff --git a/.gitignore b/.gitignore index 8b13789..3a0733e 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ +cover.out diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..e82e15d --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,102 @@ +linters-settings: + dupl: + threshold: 100 + funlen: + lines: 100 + statements: 50 + goconst: + min-len: 2 + min-occurrences: 2 + gocritic: + settings: + hugeParam: + sizeThreshold: 100 + enabled-tags: + - diagnostic + - experimental + - opinionated + - performance + - style + disabled-checks: + - dupImport # https://github.com/go-critic/go-critic/issues/845 + - ifElseChain + - octalLiteral + - whyNoLint + - wrapperFunc + - unnecessaryBlock + gocyclo: + min-complexity: 15 + goimports: + local-prefixes: github.com/monacohq/ncw-x,github.com/monacohq/ncw_nft + golint: + min-confidence: 0.8 + gomnd: + settings: + mnd: + # don't include the "operation" and "assign" + checks: + - argument + - case + - condition + - return + lll: + line-length: 140 + maligned: + suggest-new: true + misspell: + locale: US +linters: + # please, do not use `enable-all`: it's deprecated and will be removed soon. + # inverted configuration with `enable-all` and `disable` is not scalable during updates of golangci-lint + disable-all: true + enable: + - depguard + - dogsled + - dupl + - errcheck + - exportloopref + - funlen + - gochecknoinits + - goconst + - gocritic + - gocyclo + - gofmt + - goimports + - gomnd + - goprintffuncname + - gosec + - govet + - ineffassign + - lll + - misspell + - nakedret + - revive + - typecheck + - unconvert + - unused + - whitespace + - dupword + - unparam + - unconvert + +issues: + # Excluding configuration per-path, per-linter, per-text and per-source + exclude-rules: + - linters: + - gomnd + source: "\\.New.*()" + + - linters: + - gomnd + source: "strconv\\..*()" + + - linters: + - gomnd + source: " make()" + +run: + skip-dirs: + - docs + - tmp + timeout: 3m + allow-parallel-runners: true diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..02713da --- /dev/null +++ b/Makefile @@ -0,0 +1,32 @@ +GO ?= go +GOFMT ?= gofmt "-s" +GO_ENV ?= local +VERSION ?= $(shell git describe --tags --always || git rev-parse --short HEAD) +IMAGE_TAG ?= $(GO_ENV)-$(VERSION) +APP_TAG ?= .APP_TAG + +GOFILES := $(shell find . -name "*.go" -type f -not -path "./vendor/*") + +.PHONY: fmt +fmt: + $(GOFMT) -w $(GOFILES) + +.PHONY: lint lint_install lint_check +lint: lint_install lint_check + @$(GOBIN)/golangci-lint version + @$(GOBIN)/golangci-lint run + +LINTVERSION ?= v1.50.1 +GOBIN = $(HOME)/go/bin +LINTINSTALL = $(GO) install github.com/golangci/golangci-lint/cmd/golangci-lint@$(LINTVERSION) +lint_install: + @if [ ! -d $(GOBIN) ] ; then mkdir -p $(GOBIN) ; else echo "GOBIN: $(GOBIN)" ; fi + @if [ ! -f $(GOBIN)/golangci-lint ] ; then GOBIN=$(GOBIN) $(LINTINSTALL); else echo "golangci-lint exists" ; fi + +CURRENT_VERSION := `$(GOBIN)/golangci-lint version | sed -nre 's/^[^0-9]*(([0-9]+\.)*[0-9]+).*/\1/p'` +lint_check: + @if [ "$(shell echo v$(CURRENT_VERSION))" != "$(LINTVERSION)" ] ; then GOBIN=$(GOBIN) $(LINTINSTALL) ; fi + +.PHONY: test +test: + $(GO) test -race -v -p 4 -race -cover -coverprofile=cover.out ./... diff --git a/benchmark_timewheel_test.go b/benchmark_timewheel_test.go index a20c255..bdae98b 100644 --- a/benchmark_timewheel_test.go +++ b/benchmark_timewheel_test.go @@ -11,7 +11,7 @@ import ( func BenchmarkTimeWheelTest(b *testing.B) { const delay = 10 * time.Millisecond num := int32(0) - tw, _ := NewTimeWheel(10*time.Millisecond, 3600) + tw, _ := NewTimeWheel(delay, 3600) tw.activate() defer tw.stop() f := func() { @@ -22,6 +22,26 @@ func BenchmarkTimeWheelTest(b *testing.B) { for i := 0; i < b.N; i++ { tw.addTimer(delay, f, false) } - time.Sleep(100 * time.Millisecond) + time.Sleep(3 * delay) + require.EqualValues(b, b.N, atomic.LoadInt32(&num)) +} + +func BenchmarkTimeWheelParallelTest(b *testing.B) { + const delay = 10 * time.Millisecond + num := int32(0) + tw, _ := NewTimeWheel(delay, 3600) + tw.activate() + defer tw.stop() + f := func() { + atomic.AddInt32(&num, 1) + } + b.ReportAllocs() + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + tw.addTimer(delay, f, false) + } + }) + time.Sleep(3 * delay) require.EqualValues(b, b.N, atomic.LoadInt32(&num)) } diff --git a/no_copy.go b/no_copy.go new file mode 100644 index 0000000..a1cf13f --- /dev/null +++ b/no_copy.go @@ -0,0 +1,6 @@ +package timewheel + +type noCopy struct{} //nolint: unused + +func (*noCopy) Lock() {} //nolint: unused +func (*noCopy) Unlock() {} //nolint: unused diff --git a/timewheel.go b/timewheel.go index cf41f2a..29e6df1 100644 --- a/timewheel.go +++ b/timewheel.go @@ -17,7 +17,7 @@ type CallBack func() // based on https://github.com/ouqiang/timewheel type TimeWheel struct { - noCopy noCopy + noCopy noCopy // nolint: unused interval time.Duration // time interval to moving forward ticker *time.Ticker @@ -185,7 +185,7 @@ func (tw *TimeWheel) addTask(task *Task) { } } -func (tw *TimeWheel) getPositionAndCircle(d time.Duration) (pos int64, circle int64) { +func (tw *TimeWheel) getPositionAndCircle(d time.Duration) (pos, circle int64) { delayNanoseconds := d.Nanoseconds() intervalNanoseconds := tw.interval.Nanoseconds() circle = delayNanoseconds / intervalNanoseconds / tw.slotNum @@ -238,8 +238,3 @@ func RemoveTimer(key string) { func Stop() { tw.stop() } - -type noCopy struct{} - -func (*noCopy) Lock() {} -func (*noCopy) Unlock() {} diff --git a/timewheel_test.go b/timewheel_test.go index 9823564..d12eca2 100644 --- a/timewheel_test.go +++ b/timewheel_test.go @@ -71,7 +71,6 @@ func TestTimeWheelRemove(t *testing.T) { tw.addTimer(delay, wfun, false) <-testChan - }) }