SockJS for Go II (lessons learned)
It’s been a while since implementing SockJS library for Go. Since then I learned couple of new things about the language and runtime. So here I’ll try to summarise what it was.
Ad: Concurrent Safe Data Structure of ConnectionsIt's always a problem with shared data. I was choosing between using locks or channels and in the end I decided to go with channels (see sessions.go). However this approach has some disadvantages. First of all it does not distinguish between read operations and write operations. And in fact what we can have is concurrent reads. We don't need to serialize read operations into channel. I've concluded that in this case it is better to use sync.RWLock. So I've changed the implementation to use sync.RLock()/RUnlock() for read operations and sync.Lock()/Unlock() for write operations (see sessions.go).
Go Routines LeakingIt is easy to leak go routines. At first I did not realize that, but I started to worry after seeing memory consumption going up over time. After sending SIGQUIT signal (or Ctrl+\ on Linux) to the running process all was clear. Too many zombie routines hanging around. To demonstrate the problem have a look at this trivial example:
You can run it here. In the output you’ll see 2 routines. Notice routine 2 in “chan send” state. It is trying to send to the channel but that operation is never going to happen. And that is exactly what happened in my routines here and here. One of the solution is to make channel “buffered”. In this specific case it is enough to set buffer length to 1:
ch := make(chan bool, 1)
Other option would be to close the channel but that has different consequences (you can try it yourself and see what happens).