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 b7b42d718a
commit e4b88324b3
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" "fmt"
"io" "io"
"log" "log"
"sync"
"time" "time"
"github.com/armon/go-metrics" "github.com/armon/go-metrics"
@ -24,8 +25,15 @@ type consulFSM struct {
logOutput io.Writer logOutput io.Writer
logger *log.Logger logger *log.Logger
path string 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 state *state.StateStore
gc *state.TombstoneGC
gc *state.TombstoneGC
} }
// consulSnapshot is used to provide a snapshot of the current // 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 // State is used to return a handle to the current state
func (c *consulFSM) State() *state.StateStore { func (c *consulFSM) State() *state.StateStore {
c.stateLock.RLock()
defer c.stateLock.RUnlock()
return c.state return c.state
} }
@ -316,7 +326,12 @@ func (c *consulFSM) Restore(old io.ReadCloser) error {
if err != nil { if err != nil {
return err 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.state = stateNew
c.stateLock.Unlock()
// Set up a new restore transaction // Set up a new restore transaction
restore := c.state.Restore() restore := c.state.Restore()