An experimental pub/sub client and server project.
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

whole lota lint

+40 -318
-291
.golangci.yml
··· 1 - # This code is licensed under the terms of the MIT license. 2 - 3 - ## Golden config for golangci-lint v1.50.1 4 - # 5 - # This is the best config for golangci-lint based on my experience and opinion. 6 - # It is very strict, but not extremely strict. 7 - # Feel free to adopt and change it for your needs. 8 - # 9 - # @neilotoole: ^^ Well, it's less strict now! 10 - # Based on: https://gist.github.com/maratori/47a4d00457a92aa426dbd48a18776322 11 - 12 - run: 13 - # Timeout for analysis, e.g. 30s, 5m. 14 - # Default: 1m 15 - timeout: 5m 16 - 17 - tests: true 18 - 19 - skip-dirs: 20 - # Skip jcolorenc because big chunks of it are copied from 21 - # another encoder, and it uses a bunch of unsafe etc. 22 - - cli/output/jsonw/internal/jcolorenc 23 - 24 - # go-udiff is an internal fork of a package for which there 25 - # is an open issue. Hopefully we can just rely on the original 26 - # package at some point. 27 - # See: https://github.com/aymanbagabas/go-udiff/issues/5 28 - - cli/diff/internal/go-udiff 29 - 30 - # This package is such a mess, and needs to be rewritten completely. 31 - - cli/output/tablew/internal 32 - 33 - # Non-committed scratch dir 34 - - scratch 35 - 36 - output: 37 - sort-results: true 38 - 39 - # This file contains only configs which differ from defaults. 40 - # All possible options can be found here https://github.com/golangci/golangci-lint/blob/master/.golangci.reference.yml 41 - linters-settings: 42 - cyclop: 43 - # The maximal code complexity to report. 44 - # Default: 10 45 - max-complexity: 50 46 - # The maximal average package complexity. 47 - # If it's higher than 0.0 (float) the check is enabled 48 - # Default: 0.0 49 - package-average: 10.0 50 - 51 - errcheck: 52 - # Report about not checking of errors in type assertions: `a := b.(MyStruct)`. 53 - # Such cases aren't reported by default. 54 - # Default: false 55 - check-type-assertions: true 56 - 57 - exhaustive: 58 - # Program elements to check for exhaustiveness. 59 - # Default: [ switch ] 60 - check: 61 - - switch 62 - - map 63 - 64 - funlen: 65 - # Checks the number of lines in a function. 66 - # If lower than 0, disable the check. 67 - # Default: 60 68 - lines: 150 69 - # Checks the number of statements in a function. 70 - # If lower than 0, disable the check. 71 - # Default: 40 72 - statements: 100 73 - 74 - gocognit: 75 - # Minimal code complexity to report 76 - # Default: 30 (but we recommend 10-20) 77 - min-complexity: 50 78 - 79 - gocritic: 80 - # Settings passed to gocritic. 81 - # The settings key is the name of a supported gocritic checker. 82 - # The list of supported checkers can be find in https://go-critic.github.io/overview. 83 - settings: 84 - captLocal: 85 - # Whether to restrict checker to params only. 86 - # Default: true 87 - paramsOnly: false 88 - underef: 89 - # Whether to skip (*x).method() calls where x is a pointer receiver. 90 - # Default: true 91 - skipRecvDeref: false 92 - 93 - gocyclo: 94 - # Minimal code complexity to report. 95 - # Default: 30 (but we recommend 10-20) 96 - min-complexity: 50 97 - 98 - gofumpt: 99 - # Module path which contains the source code being formatted. 100 - # Default: "" 101 - module-path: github.com/neilotoole/sq 102 - # Choose whether to use the extra rules. 103 - # Default: false 104 - extra-rules: true 105 - 106 - gomnd: 107 - # List of function patterns to exclude from analysis. 108 - # Values always ignored: `time.Date`, 109 - # `strconv.FormatInt`, `strconv.FormatUint`, `strconv.FormatFloat`, 110 - # `strconv.ParseInt`, `strconv.ParseUint`, `strconv.ParseFloat`. 111 - # Default: [] 112 - ignored-functions: 113 - - make 114 - - os.Chmod 115 - - os.Mkdir 116 - - os.MkdirAll 117 - - os.OpenFile 118 - - os.WriteFile 119 - - prometheus.ExponentialBuckets 120 - - prometheus.ExponentialBucketsRange 121 - - prometheus.LinearBuckets 122 - ignored-numbers: 123 - - "2" 124 - - "3" 125 - 126 - gomodguard: 127 - blocked: 128 - # List of blocked modules. 129 - # Default: [] 130 - modules: 131 - - github.com/golang/protobuf: 132 - recommendations: 133 - - google.golang.org/protobuf 134 - reason: "see https://developers.google.com/protocol-buffers/docs/reference/go/faq#modules" 135 - - github.com/satori/go.uuid: 136 - recommendations: 137 - - github.com/google/uuid 138 - reason: "satori's package is not maintained" 139 - - github.com/gofrs/uuid: 140 - recommendations: 141 - - github.com/google/uuid 142 - reason: "gofrs' package is not go module" 143 - 144 - govet: 145 - # Enable all analyzers. 146 - # Default: false 147 - enable-all: true 148 - # Disable analyzers by name. 149 - # Run `go tool vet help` to see all analyzers. 150 - # Default: [] 151 - disable: 152 - - fieldalignment # too strict 153 - # Settings per analyzer. 154 - settings: 155 - shadow: 156 - # Whether to be strict about shadowing; can be noisy. 157 - # Default: false 158 - strict: false 159 - 160 - lll: 161 - # Max line length, lines longer will be reported. 162 - # '\t' is counted as 1 character by default, and can be changed with the tab-width option. 163 - # Default: 120. 164 - line-length: 120 165 - # Tab width in spaces. 166 - # Default: 1 167 - tab-width: 1 168 - 169 - nakedret: 170 - # Make an issue if func has more lines of code than this setting, and it has naked returns. 171 - # Default: 30 172 - max-func-lines: 0 173 - 174 - nestif: 175 - # Minimal complexity of if statements to report. 176 - # Default: 5 177 - min-complexity: 20 178 - 179 - nolintlint: 180 - # Exclude following linters from requiring an explanation. 181 - # Default: [] 182 - allow-no-explanation: [ funlen, gocognit, lll ] 183 - # Enable to require an explanation of nonzero length after each nolint directive. 184 - # Default: false 185 - require-explanation: false 186 - # Enable to require nolint directives to mention the specific linter being suppressed. 187 - # Default: false 188 - require-specific: true 189 - 190 - rowserrcheck: 191 - # database/sql is always checked 192 - # Default: [] 193 - packages: 194 - - github.com/jmoiron/sqlx 195 - 196 - tenv: 197 - # The option `all` will run against whole test files (`_test.go`) regardless of method/function signatures. 198 - # Otherwise, only methods that take `*testing.T`, `*testing.B`, and `testing.TB` as arguments are checked. 199 - # Default: false 200 - all: true 201 - 202 - 203 - linters: 204 - disable-all: true 205 - 206 - enable: 207 - ## enabled by default 208 - - errcheck # checking for unchecked errors, these unchecked errors can be critical bugs in some cases 209 - - gosimple # specializes in simplifying a code 210 - - govet # reports suspicious constructs, such as Printf calls whose arguments do not align with the format string 211 - - ineffassign # detects when assignments to existing variables are not used 212 - - staticcheck # is a go vet on steroids, applying a ton of static analysis checks 213 - - typecheck # like the front-end of a Go compiler, parses and type-checks Go code 214 - - unused # checks for unused constants, variables, functions and types 215 - 216 - 217 - # ## disabled by default 218 - - asasalint # checks for pass []any as any in variadic func(...any) 219 - - asciicheck # checks that your code does not contain non-ASCII identifiers 220 - - bidichk # checks for dangerous unicode character sequences 221 - - bodyclose # checks whether HTTP response body is closed successfully 222 - - cyclop # checks function and package cyclomatic complexity 223 - - dupl # tool for code clone detection 224 - - durationcheck # checks for two durations multiplied together 225 - - errname # checks that sentinel errors are prefixed with the Err and error types are suffixed with the Error 226 - - errorlint # finds code that will cause problems with the error wrapping scheme introduced in Go 1.13 227 - - execinquery # checks query string in Query function which reads your Go src files and warning it finds 228 - - exhaustive # checks exhaustiveness of enum switch statements 229 - - exportloopref # checks for pointers to enclosing loop variables 230 - - forbidigo # forbids identifiers 231 - - funlen # tool for detection of long functions 232 - - gochecknoinits # checks that no init functions are present in Go code 233 - - gocognit # computes and checks the cognitive complexity of functions 234 - - goconst # finds repeated strings that could be replaced by a constant 235 - - gocritic # provides diagnostics that check for bugs, performance and style issues 236 - - gocyclo # computes and checks the cyclomatic complexity of functions 237 - - godot # checks if comments end in a period 238 - - gofumpt 239 - - goimports # in addition to fixing imports, goimports also formats your code in the same style as gofmt 240 - # - gomoddirectives # manages the use of 'replace', 'retract', and 'excludes' directives in go.mod 241 - - gomodguard # allow and block lists linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations 242 - - goprintffuncname # checks that printf-like functions are named with f at the end 243 - - gosec # inspects source code for security problems 244 - - lll # reports long lines 245 - - loggercheck # checks key value pairs for common logger libraries (kitlog,klog,logr,zap) 246 - - makezero # finds slice declarations with non-zero initial length 247 - - nakedret # finds naked returns in functions greater than a specified function length 248 - - nestif # reports deeply nested if statements 249 - - nilerr # finds the code that returns nil even if it checks that the error is not nil 250 - - nilnil # checks that there is no simultaneous return of nil error and an invalid value 251 - - noctx # finds sending http request without context.Context 252 - - nolintlint # reports ill-formed or insufficient nolint directives 253 - - nosprintfhostport # checks for misuse of Sprintf to construct a host with port in a URL 254 - - predeclared # finds code that shadows one of Go's predeclared identifiers 255 - - promlinter # checks Prometheus metrics naming via promlint 256 - - reassign # checks that package variables are not reassigned 257 - - revive # fast, configurable, extensible, flexible, and beautiful linter for Go, drop-in replacement of golint 258 - - stylecheck # is a replacement for golint 259 - - tenv # detects using os.Setenv instead of t.Setenv since Go1.17 260 - - testableexamples # checks if examples are testable (have an expected output) 261 - - tparallel # detects inappropriate usage of t.Parallel() method in your Go test codes 262 - - unconvert # removes unnecessary type conversions 263 - - unparam # reports unused function parameters 264 - - usestdlibvars # detects the possibility to use variables/constants from the Go standard library 265 - - whitespace # detects leading and trailing whitespace 266 - 267 - 268 - issues: 269 - # Maximum count of issues with the same text. 270 - # Set to 0 to disable. 271 - # Default: 3 272 - max-same-issues: 3 273 - 274 - exclude-rules: 275 - - source: "^//\\s*go:generate\\s" 276 - linters: [ lll ] 277 - - source: "(noinspection|TODO)" 278 - linters: [ godot ] 279 - - source: "//noinspection" 280 - linters: [ gocritic ] 281 - - source: "^\\s+if _, ok := err\\.\\([^.]+\\.InternalError\\); ok {" 282 - linters: [ errorlint ] 283 - - path: "_test\\.go" 284 - linters: 285 - - bodyclose 286 - - dupl 287 - - funlen 288 - - goconst 289 - - gosec 290 - - noctx 291 - - wrapcheck
+13 -4
example/main.go
··· 16 16 consumeOnly = flag.Bool("consume-only", false, "just consumes (doesn't start server and doesn't publish)") 17 17 flag.Parse() 18 18 19 - if *consumeOnly == false { 19 + if !*consumeOnly { 20 20 go sendMessages() 21 21 } 22 22 ··· 24 24 if err != nil { 25 25 panic(err) 26 26 } 27 - defer sub.Close() 27 + 28 + defer func() { 29 + _ = sub.Close() 30 + }() 28 31 29 - sub.SubscribeToTopics([]string{"topic a"}) 32 + err = sub.SubscribeToTopics([]string{"topic a"}) 33 + if err != nil { 34 + panic(err) 35 + } 30 36 31 37 consumer := sub.Consume(context.Background()) 32 38 if consumer.Err != nil { ··· 44 50 if err != nil { 45 51 panic(err) 46 52 } 47 - defer publisher.Close() 53 + 54 + defer func() { 55 + _ = publisher.Close() 56 + }() 48 57 49 58 // send some messages 50 59 i := 0
+4 -1
example/server/main.go
··· 14 14 if err != nil { 15 15 log.Fatal(err) 16 16 } 17 - defer srv.Shutdown() 17 + 18 + defer func() { 19 + _ = srv.Shutdown() 20 + }() 18 21 19 22 signals := make(chan os.Signal, 1) 20 23 signal.Notify(signals, syscall.SIGTERM, syscall.SIGINT)
+1 -1
pubsub/subscriber_test.go
··· 23 23 require.NoError(t, err) 24 24 25 25 t.Cleanup(func() { 26 - server.Shutdown() 26 + _ = server.Shutdown() 27 27 }) 28 28 } 29 29
+12 -9
server/server.go
··· 51 51 lis net.Listener 52 52 53 53 mu sync.Mutex 54 - topics map[string]topic 54 + topics map[string]*topic 55 55 } 56 56 57 57 // New creates and starts a new server ··· 63 63 64 64 srv := &Server{ 65 65 lis: lis, 66 - topics: map[string]topic{}, 66 + topics: map[string]*topic{}, 67 67 } 68 68 69 69 go srv.start() ··· 131 131 // TODO: see if there's a way to check if the peers connection has been ended etc 132 132 slog.Error("failed to read action from subscriber", "error", err, "peer", peer.Addr()) 133 133 134 - s.unsubscribePeerFromAllTopics(*peer) 134 + s.unsubscribePeerFromAllTopics(peer) 135 135 136 136 return 137 137 } ··· 218 218 return nil 219 219 } 220 220 221 - s.unsubscribeToTopics(*peer, topics) 221 + s.unsubscribeToTopics(peer, topics) 222 222 writeStatus(Unsubscribed, "", conn) 223 223 224 224 return nil ··· 325 325 s.topics[topicName] = t 326 326 } 327 327 328 - func (s *Server) unsubscribeToTopics(peer peer.Peer, topics []string) { 328 + func (s *Server) unsubscribeToTopics(peer *peer.Peer, topics []string) { 329 329 for _, topic := range topics { 330 330 s.removeSubsciberFromTopic(topic, peer) 331 331 } 332 332 } 333 333 334 - func (s *Server) removeSubsciberFromTopic(topicName string, peer peer.Peer) { 334 + func (s *Server) removeSubsciberFromTopic(topicName string, peer *peer.Peer) { 335 335 s.mu.Lock() 336 336 defer s.mu.Unlock() 337 337 ··· 343 343 delete(t.subscriptions, peer.Addr()) 344 344 } 345 345 346 - func (s *Server) unsubscribePeerFromAllTopics(peer peer.Peer) { 346 + func (s *Server) unsubscribePeerFromAllTopics(peer *peer.Peer) { 347 347 s.mu.Lock() 348 348 defer s.mu.Unlock() 349 349 ··· 357 357 defer s.mu.Unlock() 358 358 359 359 if topic, ok := s.topics[topicName]; ok { 360 - return &topic 360 + return topic 361 361 } 362 362 363 363 return nil ··· 367 367 var action Action 368 368 op := func(conn net.Conn) error { 369 369 if timeout > 0 { 370 - conn.SetReadDeadline(time.Now().Add(timeout)) 370 + err := conn.SetReadDeadline(time.Now().Add(timeout)) 371 + if err != nil { 372 + slog.Error("failed to set connection read deadline", "error", err, "peer", peer.Addr()) 373 + } 371 374 } 372 375 373 376 err := binary.Read(conn, binary.BigEndian, &action)
+8 -2
server/server_test.go
··· 25 25 require.NoError(t, err) 26 26 27 27 t.Cleanup(func() { 28 - srv.Shutdown() 28 + _ = srv.Shutdown() 29 29 }) 30 30 31 31 return srv ··· 33 33 34 34 func createServerWithExistingTopic(t *testing.T, topicName string) *Server { 35 35 srv := createServer(t) 36 - srv.topics[topicName] = topic{ 36 + srv.topics[topicName] = &topic{ 37 37 name: topicName, 38 38 subscriptions: make(map[net.Addr]subscriber), 39 39 } ··· 61 61 62 62 var resp Status 63 63 err = binary.Read(conn, binary.BigEndian, &resp) 64 + require.NoError(t, err) 64 65 65 66 assert.Equal(t, expectedRes, int(resp)) 66 67 ··· 106 107 107 108 var resp Status 108 109 err = binary.Read(conn, binary.BigEndian, &resp) 110 + require.NoError(t, err) 109 111 110 112 assert.Equal(t, expectedRes, int(resp)) 111 113 ··· 160 162 161 163 var resp Status 162 164 err = binary.Read(conn, binary.BigEndian, &resp) 165 + require.NoError(t, err) 163 166 164 167 assert.Equal(t, expectedRes, int(resp)) 165 168 ··· 167 170 168 171 var dataLen uint32 169 172 err = binary.Read(conn, binary.BigEndian, &dataLen) 173 + require.NoError(t, err) 170 174 assert.Equal(t, len(expectedMessage), int(dataLen)) 171 175 172 176 buf := make([]byte, dataLen) ··· 196 200 197 201 var resp Status 198 202 err = binary.Read(publisherConn, binary.BigEndian, &resp) 203 + require.NoError(t, err) 199 204 200 205 assert.Equal(t, expectedRes, int(resp)) 201 206 ··· 203 208 204 209 var dataLen uint32 205 210 err = binary.Read(publisherConn, binary.BigEndian, &dataLen) 211 + require.NoError(t, err) 206 212 assert.Equal(t, len(expectedMessage), int(dataLen)) 207 213 208 214 buf := make([]byte, dataLen)
+2 -10
server/topic.go
··· 21 21 currentOffset int 22 22 } 23 23 24 - func newTopic(name string) topic { 25 - return topic{ 24 + func newTopic(name string) *topic { 25 + return &topic{ 26 26 name: name, 27 27 subscriptions: make(map[net.Addr]subscriber), 28 28 } 29 - } 30 - 31 - func (t *topic) removeSubscriber(addr net.Addr) { 32 - t.mu.Lock() 33 - defer t.mu.Unlock() 34 - 35 - slog.Info("removing subscriber", "peer", addr) 36 - delete(t.subscriptions, addr) 37 29 } 38 30 39 31 func (t *topic) sendMessageToSubscribers(msgData []byte) {