queryservice: new state transition scheme

The previous scheme had some subtle race conditions that could lead to unexpected behavior.
This new scheme addresses all of them. There are still some race conditions to be resolved
with the rowcache invalidator, which will be addressed in the next CL.

- All state transitions require a write lock.
- Any section of code that requires a state not to change during an action will require a read lock.
- No long-running locks.
- Managing NOT_SERVING -> SERVING is straightforward. Since nothing else is allowed to happen during this time, we can make an exception and hold the lock for the entire operation.
- Once we're SERVING, there are two phases that we have to manage when disallowQueries gets called:
  1. Wait for existing transactions to complete.
  2. Wait for existing queries to complete.

Waiting for transactions:
Before we start waiting, we have to ensure that no new transactions can get created. Otherwise, we may introduce a race condition that ends up allowing the creation of a new transaction after we conclude that all existing transactions are completed.
How do we prevent this:
disallowQueries:
 - obtain write lock
 - transition SERVING to SHUTTING_TX
 - release write lock
 - wait for existing transactions to complete
Begin:
 - obtain read lock
 - verify SERVING state
 - create a transaction
 - release read lock
This guarantees that new calls to Begin will not go through when we start waiting for transactions to complete.

Waiting for queries:
disallowQueries:
 - obtain write lock
 - transition SHUTDOWN_TX to SHUTTING_DOWN
 - release write lock
 - wg.Wait()
Execute:
 - obtain read lock
 - verify SERVING or SHUTDOWN_TX
 - wg.Add(1)
 - release read lock
 - execute query
 - wg.Done()
This guarantees that no new queries will be allowed once disallowQueries starts wg.Wait(). When the function returns, we know that all existing queries have completed, we're free to transition state to NOT_SERVING.