Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(873)

Side by Side Diff: common/lazyslot/lazyslot.go

Issue 1748933002: Retry RPC deadlines when fetching GAE settings. (Closed) Base URL: https://chromium.googlesource.com/external/github.com/luci/luci-go@simplify-lazyslot
Patch Set: move retry to settings.go Created 4 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | server/settings/settings.go » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 // Package lazyslot implements a caching scheme for globally shared objects that 5 // Package lazyslot implements a caching scheme for globally shared objects that
6 // take significant time to refresh. 6 // take significant time to refresh.
7 // 7 //
8 // The defining property of the implementation is that only one goroutine will 8 // The defining property of the implementation is that only one goroutine will
9 // block when refreshing such object, while all others will use a slightly stale 9 // block when refreshing such object, while all others will use a slightly stale
10 // cached copy. 10 // cached copy.
(...skipping 21 matching lines...) Expand all
32 // If it returns no errors, it MUST return non-nil Value.Value or Slot.Get will 32 // If it returns no errors, it MUST return non-nil Value.Value or Slot.Get will
33 // panic. 33 // panic.
34 type Fetcher func(c context.Context, prev Value) (Value, error) 34 type Fetcher func(c context.Context, prev Value) (Value, error)
35 35
36 // Slot holds a cached Value and refreshes it when it expires. 36 // Slot holds a cached Value and refreshes it when it expires.
37 // 37 //
38 // Only one goroutine will be busy refreshing, all others will see a slightly 38 // Only one goroutine will be busy refreshing, all others will see a slightly
39 // stale copy of the value during the refresh. 39 // stale copy of the value during the refresh.
40 type Slot struct { 40 type Slot struct {
41 Fetcher Fetcher // used to actually load the value on demand 41 Fetcher Fetcher // used to actually load the value on demand
42 » Timeout time.Duration // how long to allow to fetch, 5 sec by default. 42 » Timeout time.Duration // how long to allow to fetch, 15 sec by default.
43 43
44 lock sync.Mutex // protects the guts below 44 lock sync.Mutex // protects the guts below
45 current *Value // currently known value or nil if not fetched 45 current *Value // currently known value or nil if not fetched
46 currentFetcherCtx context.Context // non-nil if some goroutine is fetchi ng now 46 currentFetcherCtx context.Context // non-nil if some goroutine is fetchi ng now
47 } 47 }
48 48
49 // Get returns stored value if it is still fresh. 49 // Get returns stored value if it is still fresh.
50 // 50 //
51 // It may return slightly stale copy if some other goroutine is fetching a new 51 // It may return slightly stale copy if some other goroutine is fetching a new
52 // copy now. If there's no cached copy at all, blocks until it is retrieved. 52 // copy now. If there's no cached copy at all, blocks until it is retrieved.
(...skipping 20 matching lines...) Expand all
73 if s.current != nil && now.Before(s.current.Expiration) { 73 if s.current != nil && now.Before(s.current.Expiration) {
74 result = *s.current 74 result = *s.current
75 done = true 75 done = true
76 return 76 return
77 } 77 }
78 78
79 // Fetching the value for the first time ever? Do it under the l ock because 79 // Fetching the value for the first time ever? Do it under the l ock because
80 // there's nothing to return yet. All goroutines would have to w ait for this 80 // there's nothing to return yet. All goroutines would have to w ait for this
81 // initial fetch to complete. They'll all block on s.lock.Lock() above. 81 // initial fetch to complete. They'll all block on s.lock.Lock() above.
82 if s.current == nil { 82 if s.current == nil {
83 » » » result, err = doFetch(c, s.Fetcher, Value{}) 83 » » » result, err = doFetch(s.makeFetcherCtx(c), s.Fetcher, Va lue{})
84 if err == nil { 84 if err == nil {
85 s.current = &result 85 s.current = &result
86 } 86 }
87 done = true 87 done = true
88 return 88 return
89 } 89 }
90 90
91 // We have a cached copy but it has expired. Maybe some other go routine is 91 // We have a cached copy but it has expired. Maybe some other go routine is
92 // fetching it already? Returns the cached stale copy if so. 92 // fetching it already? Returns the cached stale copy if so.
93 if s.currentFetcherCtx != nil { 93 if s.currentFetcherCtx != nil {
94 result = *s.current 94 result = *s.current
95 done = true 95 done = true
96 return 96 return
97 } 97 }
98 98
99 // No one is fetching the value now, we should do it. Prepare a new context 99 // No one is fetching the value now, we should do it. Prepare a new context
100 // that will be used to do the fetch once lock is released. 100 // that will be used to do the fetch once lock is released.
101 » » timeout := 5 * time.Second 101 » » s.currentFetcherCtx = s.makeFetcherCtx(c)
102 » » if s.Timeout != 0 {
103 » » » timeout = s.Timeout
104 » » }
105 » » s.currentFetcherCtx, _ = context.WithTimeout(c, timeout)
106 102
107 // Copy lock-protected guts into local variables before releasin g the lock. 103 // Copy lock-protected guts into local variables before releasin g the lock.
108 state.C = s.currentFetcherCtx 104 state.C = s.currentFetcherCtx
109 state.Fetcher = s.Fetcher 105 state.Fetcher = s.Fetcher
110 state.PrevValue = *s.current 106 state.PrevValue = *s.current
111 return 107 return
112 }() 108 }()
113 if done { 109 if done {
114 return 110 return
115 } 111 }
116 112
117 // Finish the fetch and update the cached value. 113 // Finish the fetch and update the cached value.
118 return func() (result Value, err error) { 114 return func() (result Value, err error) {
119 defer func() { 115 defer func() {
120 s.lock.Lock() 116 s.lock.Lock()
121 defer s.lock.Unlock() 117 defer s.lock.Unlock()
122 s.currentFetcherCtx = nil 118 s.currentFetcherCtx = nil
123 // result.Value is not nil iff fetch succeeded and didn' t panic. 119 // result.Value is not nil iff fetch succeeded and didn' t panic.
124 if result.Value != nil { 120 if result.Value != nil {
125 s.current = &result 121 s.current = &result
126 } 122 }
127 }() 123 }()
128 return doFetch(state.C, state.Fetcher, state.PrevValue) 124 return doFetch(state.C, state.Fetcher, state.PrevValue)
129 }() 125 }()
130 } 126 }
131 127
128 // makeFetcherCtx prepares a context to use for fetch operation.
129 //
130 // Must be called under the lock.
131 func (s *Slot) makeFetcherCtx(c context.Context) context.Context {
132 timeout := 15 * time.Second
133 if s.Timeout != 0 {
134 timeout = s.Timeout
135 }
136 fetcherCtx, _ := clock.WithTimeout(c, timeout)
137 return fetcherCtx
138 }
139
132 // doFetch calls fetcher callback and validates return value. 140 // doFetch calls fetcher callback and validates return value.
133 func doFetch(ctx context.Context, cb Fetcher, prev Value) (result Value, err err or) { 141 func doFetch(ctx context.Context, cb Fetcher, prev Value) (result Value, err err or) {
134 result, err = cb(ctx, prev) 142 result, err = cb(ctx, prev)
135 switch { 143 switch {
136 case err == nil && result.Value == nil: 144 case err == nil && result.Value == nil:
137 panic("lazyslot.Slot Fetcher returned nil value") 145 panic("lazyslot.Slot Fetcher returned nil value")
138 case err != nil: 146 case err != nil:
139 result = Value{} 147 result = Value{}
140 } 148 }
141 return 149 return
142 } 150 }
OLDNEW
« no previous file with comments | « no previous file | server/settings/settings.go » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698