Golang
Published on 10 March 2023
Resources
- https://gobyexample.com/
- Concurrency in Go by Katherine Cox-Buday
- The GOMAXPROCS Lever — Page 83 — faster/quicker concurrency issue reproduction
- “For instance, I worked on one project that had a test suite plagued by race conditions. However it came to be, the team had a handful of packages that had tests that sometimes failed. The infrastructure on which we ran our tests only had four logical CPUs, and so at any one point in time we had four goroutines executing simultaneously. By increasing GOMAXPROCS beyond the number of logical CPUs we had, we were able to trigger the race conditions much more often, and thus get them corrected faster.”
- The GOMAXPROCS Lever — Page 83 — faster/quicker concurrency issue reproduction
- Other todos
- and that error libraries by dave cheney and clones that can store stack trace, also check if errors.Join can remediate it in any way (probably it can’t because it ain’t a panic).
- Also check about the diff. b/w log.Fatal and panic
- continue from 172 replicated requests
- read p queue and broadcast impl.
- watch modeling errors in now cli
- Above two do not mention semaphores, which you can read about in here: https://code-pilot.me/synchronization-patterns-in-go (only read semaphore, skip the rest)
- That being said, buffered channels can act like semaphores (https://gobyexample.com/worker-pools)
- sync.Pool
- While semaphores and buffered channels are good for making worker pools, wherein you too many tasks but want at max only n number of them to be processed concurrently, there’s a similar but different case of resource pools. In a resource pool, you are holding instances of a resource (like DB connections, or instances created after expensive computation, avoiding to reinitialise too much memory again and again so older initialisations have be cleaned up by GC) and you’d pull out one of the resource from the pool and put it back into the pool once you’re done with it. This is much better to do with sync.Pool instead.
- Even sync.Pool doesn’t cover all cases. Via reddit: sync.Pool works really well for i/o buffers and large objects with similar access profiles. For smaller object in pools with high churn rates the CPU overhead can become an issue. For me this has been most noticeable working with frequently modified trees. (Reference)
- Here’s a in detail explanation of how it works https://developer20.com/using-sync-pool/, here’s another one https://www.sobyte.net/post/2022–03/think-in-sync-pool
- Like
useMemo
value in React, items in sync.Pool can be garbage collected- In GC pressure, or if not interacted with pool for long, GC can evict the pool items https://go-review.googlesource.com/c/go/+/166961 so you might have think about
defer close
anddefer cleanup
- This once has an example to use channels to avoid being GC collected https://www.reddit.com/r/golang/comments/2ap67l/when_to_use_syncpool_and_when_not_to/
- In GC pressure, or if not interacted with pool for long, GC can evict the pool items https://go-review.googlesource.com/c/go/+/166961 so you might have think about
- This is a good comment to read https://www.reddit.com/r/golang/comments/vdude2/preventing_syncpool_from_consuming_all_memory/icmt2aa/ (the guy seems smart as well)
- Here’s a typesafe sync pool from somebody https://old.reddit.com/r/golang/comments/123zhi7/githubcomcolegazeropool_zeroallocation_typesafe/
- It’s being torn apart by commenters, I should look for a ticket about Generic support in sync.Pool (or auto inference using return type of New fn). Otherwise roll out your own I guess?
- Option/Result are here, explore them https://github.com/samber/mo
- You also have in OneTab a tab collection about result/option with and without generics, explore that
Gotchas
- For use in generics, comparable and constraints.ordered are different
- Upon closing a channel, it becomes non-blocking. You have to make it nil to for it to not consume any CPU resources. See https://youtu.be/t9bEg2A4jsw. When you assign nil, it becomes (and what you call it as) nil channel
- Few covered in this section (incl. channels) https://fasterthanli.me/articles/lies-we-tell-ourselves-to-keep-using-golang#all-or-nothing-so-let-s-do-nothing
- Don’t use sync.NewCond(&sync.Mutex{}) for broadcasting
- Reasons
- It can only signal that an event has happened, but doesn’t tell what it is; you cannot send data in Broadcast fn
- If your listener (goroutine) is not on cond.Wait(), it can miss the signal that was broadcasted
- They require altogther different syntax which is unintuitive as well
- What to use instead?
- https://github.com/teivah/broadcast
- Use Notify() method to broadcast
- Using 0 in relay.Listener(0) worked just well for me. 0 in
relay.Listener(0)
and inmake(chan type, 0)
means unbuffered channel.make(chan type)
is just a shorthand formake(chan type, 0)
.
- https://github.com/teivah/broadcast
- Reasons