Runs supplied fn with current primary client. If primary client changes, fn is restarted. When fn finishes (with or without error), this method returns given error value.
(origCtx context.Context, fn func(newCtx context.Context, primary kvclient) error)
| 245 | // Runs supplied fn with current primary client. If primary client changes, fn is restarted. |
| 246 | // When fn finishes (with or without error), this method returns given error value. |
| 247 | func (m *MultiClient) runWithPrimaryClient(origCtx context.Context, fn func(newCtx context.Context, primary kvclient) error) error { |
| 248 | cancelFn := context.CancelFunc(nil) |
| 249 | cancelFnID := 0 |
| 250 | |
| 251 | cleanup := func() { |
| 252 | if cancelFn != nil { |
| 253 | cancelFn() |
| 254 | } |
| 255 | if cancelFnID > 0 { |
| 256 | m.unregisterCancelFn(cancelFnID) |
| 257 | } |
| 258 | } |
| 259 | |
| 260 | defer cleanup() |
| 261 | |
| 262 | // This only loops if switchover to a new primary backend happens while calling 'fn', which is very rare. |
| 263 | for { |
| 264 | cleanup() |
| 265 | pid, kv := m.getPrimaryClient() |
| 266 | |
| 267 | var cancelCtx context.Context |
| 268 | cancelCtx, cancelFn = context.WithCancel(origCtx) |
| 269 | cancelFnID = m.registerCancelFn(pid, cancelFn) |
| 270 | |
| 271 | err := fn(cancelCtx, kv) |
| 272 | |
| 273 | if err == nil { |
| 274 | return nil |
| 275 | } |
| 276 | |
| 277 | if cancelCtx.Err() == context.Canceled && origCtx.Err() == nil { |
| 278 | // our context was cancelled, but outer context is not done yet. retry |
| 279 | continue |
| 280 | } |
| 281 | |
| 282 | return err |
| 283 | } |
| 284 | } |
| 285 | |
| 286 | // List is a part of the kv.Client interface. |
| 287 | func (m *MultiClient) List(ctx context.Context, prefix string) ([]string, error) { |
no test coverage detected