github.com/hashicorp/vault/sdk@v0.13.0/physical/inmem/inmem_ha.go (about) 1 // Copyright (c) HashiCorp, Inc. 2 // SPDX-License-Identifier: MPL-2.0 3 4 package inmem 5 6 import ( 7 "fmt" 8 "sync" 9 10 log "github.com/hashicorp/go-hclog" 11 "github.com/hashicorp/vault/sdk/physical" 12 ) 13 14 type InmemHABackend struct { 15 physical.Backend 16 locks map[string]string 17 l *sync.Mutex 18 cond *sync.Cond 19 logger log.Logger 20 } 21 22 type TransactionalInmemHABackend struct { 23 physical.Transactional 24 InmemHABackend 25 } 26 27 // NewInmemHA constructs a new in-memory HA backend. This is only for testing. 28 func NewInmemHA(_ map[string]string, logger log.Logger) (physical.Backend, error) { 29 be, err := NewInmem(nil, logger) 30 if err != nil { 31 return nil, err 32 } 33 34 in := &InmemHABackend{ 35 Backend: be, 36 locks: make(map[string]string), 37 logger: logger, 38 l: new(sync.Mutex), 39 } 40 in.cond = sync.NewCond(in.l) 41 return in, nil 42 } 43 44 func NewTransactionalInmemHA(_ map[string]string, logger log.Logger) (physical.Backend, error) { 45 transInmem, err := NewTransactionalInmem(nil, logger) 46 if err != nil { 47 return nil, err 48 } 49 inmemHA := InmemHABackend{ 50 Backend: transInmem, 51 locks: make(map[string]string), 52 logger: logger, 53 l: new(sync.Mutex), 54 } 55 56 in := &TransactionalInmemHABackend{ 57 InmemHABackend: inmemHA, 58 Transactional: transInmem.(physical.Transactional), 59 } 60 in.cond = sync.NewCond(in.l) 61 return in, nil 62 } 63 64 // LockWith is used for mutual exclusion based on the given key. 65 func (i *InmemHABackend) LockWith(key, value string) (physical.Lock, error) { 66 l := &InmemLock{ 67 in: i, 68 key: key, 69 value: value, 70 } 71 return l, nil 72 } 73 74 // LockMapSize is used in some tests to determine whether this backend has ever 75 // been used for HA purposes rather than simply for storage 76 func (i *InmemHABackend) LockMapSize() int { 77 return len(i.locks) 78 } 79 80 // HAEnabled indicates whether the HA functionality should be exposed. 81 // Currently always returns true. 82 func (i *InmemHABackend) HAEnabled() bool { 83 return true 84 } 85 86 // InmemLock is an in-memory Lock implementation for the HABackend 87 type InmemLock struct { 88 in *InmemHABackend 89 key string 90 value string 91 92 held bool 93 leaderCh chan struct{} 94 l sync.Mutex 95 } 96 97 func (i *InmemLock) Lock(stopCh <-chan struct{}) (<-chan struct{}, error) { 98 i.l.Lock() 99 defer i.l.Unlock() 100 if i.held { 101 return nil, fmt.Errorf("lock already held") 102 } 103 104 // Attempt an async acquisition 105 didLock := make(chan struct{}) 106 releaseCh := make(chan bool, 1) 107 go func() { 108 // Wait to acquire the lock 109 i.in.l.Lock() 110 _, ok := i.in.locks[i.key] 111 for ok { 112 i.in.cond.Wait() 113 _, ok = i.in.locks[i.key] 114 } 115 i.in.locks[i.key] = i.value 116 i.in.l.Unlock() 117 118 // Signal that lock is held 119 close(didLock) 120 121 // Handle an early abort 122 release := <-releaseCh 123 if release { 124 i.in.l.Lock() 125 delete(i.in.locks, i.key) 126 i.in.l.Unlock() 127 i.in.cond.Broadcast() 128 } 129 }() 130 131 // Wait for lock acquisition or shutdown 132 select { 133 case <-didLock: 134 releaseCh <- false 135 case <-stopCh: 136 releaseCh <- true 137 return nil, nil 138 } 139 140 // Create the leader channel 141 i.held = true 142 i.leaderCh = make(chan struct{}) 143 return i.leaderCh, nil 144 } 145 146 func (i *InmemLock) Unlock() error { 147 i.l.Lock() 148 defer i.l.Unlock() 149 150 if !i.held { 151 return nil 152 } 153 154 close(i.leaderCh) 155 i.leaderCh = nil 156 i.held = false 157 158 i.in.l.Lock() 159 delete(i.in.locks, i.key) 160 i.in.l.Unlock() 161 i.in.cond.Broadcast() 162 return nil 163 } 164 165 func (i *InmemLock) Value() (bool, string, error) { 166 i.in.l.Lock() 167 val, ok := i.in.locks[i.key] 168 i.in.l.Unlock() 169 return ok, val, nil 170 }