Fixes a race condition when updating the state store during a snapshot restore.

This commit is contained in:
James Phillips 2017-01-20 06:12:10 -08:00
parent 0891b6c74d
commit a604c438a2
No known key found for this signature in database
GPG Key ID: 77183E682AC5FC11
1 changed files with 16 additions and 1 deletions

View File

@ -5,6 +5,7 @@ import (
"fmt"
"io"
"log"
"sync"
"time"
"github.com/armon/go-metrics"
@ -24,8 +25,15 @@ type consulFSM struct {
logOutput io.Writer
logger *log.Logger
path string
// stateLock is only used to protect outside callers to State() from
// racing with Restore(), which is called by Raft (it puts in a totally
// new state store). Everything else is synchronized by the Raft side,
// so doesn't need to lock this.
stateLock sync.RWMutex
state *state.StateStore
gc *state.TombstoneGC
gc *state.TombstoneGC
}
// consulSnapshot is used to provide a snapshot of the current
@ -60,6 +68,8 @@ func NewFSM(gc *state.TombstoneGC, logOutput io.Writer) (*consulFSM, error) {
// State is used to return a handle to the current state
func (c *consulFSM) State() *state.StateStore {
c.stateLock.RLock()
defer c.stateLock.RUnlock()
return c.state
}
@ -316,7 +326,12 @@ func (c *consulFSM) Restore(old io.ReadCloser) error {
if err != nil {
return err
}
// External code might be calling State(), so we need to synchronize
// here to make sure we swap in the new state store atomically.
c.stateLock.Lock()
c.state = stateNew
c.stateLock.Unlock()
// Set up a new restore transaction
restore := c.state.Restore()