Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(528)

Side by Side Diff: state/apiserver/root.go

Issue 24040044: apiserver: analyzing ping timeouts
Patch Set: apiserver: analyzing ping timeouts Created 10 years, 4 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
View unified diff | Download patch
« no previous file with comments | « state/apiserver/export_test.go ('k') | state/apiserver/root_test.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 2013 Canonical Ltd. 1 // Copyright 2013 Canonical Ltd.
2 // Licensed under the AGPLv3, see LICENCE file for details. 2 // Licensed under the AGPLv3, see LICENCE file for details.
3 3
4 package apiserver 4 package apiserver
5 5
6 import ( 6 import (
7 "time"
8
9 "launchpad.net/tomb"
10
7 "launchpad.net/juju-core/state" 11 "launchpad.net/juju-core/state"
8 "launchpad.net/juju-core/state/apiserver/agent" 12 "launchpad.net/juju-core/state/apiserver/agent"
9 "launchpad.net/juju-core/state/apiserver/client" 13 "launchpad.net/juju-core/state/apiserver/client"
10 "launchpad.net/juju-core/state/apiserver/common" 14 "launchpad.net/juju-core/state/apiserver/common"
11 "launchpad.net/juju-core/state/apiserver/deployer" 15 "launchpad.net/juju-core/state/apiserver/deployer"
12 loggerapi "launchpad.net/juju-core/state/apiserver/logger" 16 loggerapi "launchpad.net/juju-core/state/apiserver/logger"
13 "launchpad.net/juju-core/state/apiserver/machine" 17 "launchpad.net/juju-core/state/apiserver/machine"
14 "launchpad.net/juju-core/state/apiserver/provisioner" 18 "launchpad.net/juju-core/state/apiserver/provisioner"
15 "launchpad.net/juju-core/state/apiserver/uniter" 19 "launchpad.net/juju-core/state/apiserver/uniter"
16 "launchpad.net/juju-core/state/apiserver/upgrader" 20 "launchpad.net/juju-core/state/apiserver/upgrader"
17 "launchpad.net/juju-core/state/multiwatcher" 21 "launchpad.net/juju-core/state/multiwatcher"
18 ) 22 )
19 23
20 type clientAPI struct{ *client.API } 24 type clientAPI struct{ *client.API }
21 25
22 type taggedAuthenticator interface { 26 type taggedAuthenticator interface {
23 state.Entity 27 state.Entity
24 state.Authenticator 28 state.Authenticator
25 } 29 }
26 30
27 // srvRoot represents a single client's connection to the state 31 // srvRoot represents a single client's connection to the state
28 // after it has logged in. 32 // after it has logged in.
29 type srvRoot struct { 33 type srvRoot struct {
30 clientAPI 34 clientAPI
31 » srv *Server 35 » initialRoot *initialRoot
32 » resources *common.Resources 36 » resources *common.Resources
37 » pingTimeout *pingTimeout
33 38
34 entity taggedAuthenticator 39 entity taggedAuthenticator
35 } 40 }
36 41
37 func newSrvRoot(srv *Server, entity taggedAuthenticator) *srvRoot { 42 func newSrvRoot(root *initialRoot, entity taggedAuthenticator) *srvRoot {
38 r := &srvRoot{ 43 r := &srvRoot{
39 » » srv: srv, 44 » » initialRoot: root,
rog 2013/11/18 18:19:48 Rather than having initialRoot inside srvRoot, I'd
mue 2013/11/19 13:50:46 Done.
40 » » resources: common.NewResources(), 45 » » resources: common.NewResources(),
41 » » entity: entity, 46 » » entity: entity,
42 } 47 }
43 » r.clientAPI.API = client.NewAPI(srv.state, r.resources, r) 48 » action := func() error {
49 » » return r.initialRoot.rpcConn.Close()
50 » }
51 » r.clientAPI.API = client.NewAPI(root.srv.state, r.resources, r)
52 » r.pingTimeout = newPingTimeout(r.entity.Tag(), action, 3*time.Minute)
rog 2013/11/18 18:19:48 There's nothing that cleans this up when the conne
mue 2013/11/19 13:50:46 Yep, cleanup has been prepared but led to the dead
44 return r 53 return r
45 } 54 }
46 55
47 // Kill implements rpc.Killer. It cleans up any resources that need 56 // Kill implements rpc.Killer. It cleans up any resources that need
48 // cleaning up to ensure that all outstanding requests return. 57 // cleaning up to ensure that all outstanding requests return.
49 func (r *srvRoot) Kill() { 58 func (r *srvRoot) Kill() {
50 r.resources.StopAll() 59 r.resources.StopAll()
51 } 60 }
52 61
53 // requireAgent checks whether the current client is an agent and hence 62 // requireAgent checks whether the current client is an agent and hence
(...skipping 17 matching lines...) Expand all
71 } 80 }
72 81
73 // Machiner returns an object that provides access to the Machiner API 82 // Machiner returns an object that provides access to the Machiner API
74 // facade. The id argument is reserved for future use and currently 83 // facade. The id argument is reserved for future use and currently
75 // needs to be empty. 84 // needs to be empty.
76 func (r *srvRoot) Machiner(id string) (*machine.MachinerAPI, error) { 85 func (r *srvRoot) Machiner(id string) (*machine.MachinerAPI, error) {
77 if id != "" { 86 if id != "" {
78 // Safeguard id for possible future use. 87 // Safeguard id for possible future use.
79 return nil, common.ErrBadId 88 return nil, common.ErrBadId
80 } 89 }
81 » return machine.NewMachinerAPI(r.srv.state, r.resources, r) 90 » return machine.NewMachinerAPI(r.initialRoot.srv.state, r.resources, r)
82 } 91 }
83 92
84 // Provisioner returns an object that provides access to the 93 // Provisioner returns an object that provides access to the
85 // Provisioner API facade. The id argument is reserved for future use 94 // Provisioner API facade. The id argument is reserved for future use
86 // and currently needs to be empty. 95 // and currently needs to be empty.
87 func (r *srvRoot) Provisioner(id string) (*provisioner.ProvisionerAPI, error) { 96 func (r *srvRoot) Provisioner(id string) (*provisioner.ProvisionerAPI, error) {
88 if id != "" { 97 if id != "" {
89 // Safeguard id for possible future use. 98 // Safeguard id for possible future use.
90 return nil, common.ErrBadId 99 return nil, common.ErrBadId
91 } 100 }
92 » return provisioner.NewProvisionerAPI(r.srv.state, r.resources, r) 101 » return provisioner.NewProvisionerAPI(r.initialRoot.srv.state, r.resource s, r)
93 } 102 }
94 103
95 // MachineAgent returns an object that provides access to the machine 104 // MachineAgent returns an object that provides access to the machine
96 // agent API. The id argument is reserved for future use and must currently 105 // agent API. The id argument is reserved for future use and must currently
97 // be empty. 106 // be empty.
98 // DEPRECATED(v1.14) 107 // DEPRECATED(v1.14)
99 func (r *srvRoot) MachineAgent(id string) (*machine.AgentAPI, error) { 108 func (r *srvRoot) MachineAgent(id string) (*machine.AgentAPI, error) {
100 if id != "" { 109 if id != "" {
101 return nil, common.ErrBadId 110 return nil, common.ErrBadId
102 } 111 }
103 » return machine.NewAgentAPI(r.srv.state, r) 112 » return machine.NewAgentAPI(r.initialRoot.srv.state, r)
104 } 113 }
105 114
106 // Uniter returns an object that provides access to the Uniter API 115 // Uniter returns an object that provides access to the Uniter API
107 // facade. The id argument is reserved for future use and currently 116 // facade. The id argument is reserved for future use and currently
108 // needs to be empty. 117 // needs to be empty.
109 func (r *srvRoot) Uniter(id string) (*uniter.UniterAPI, error) { 118 func (r *srvRoot) Uniter(id string) (*uniter.UniterAPI, error) {
110 if id != "" { 119 if id != "" {
111 // Safeguard id for possible future use. 120 // Safeguard id for possible future use.
112 return nil, common.ErrBadId 121 return nil, common.ErrBadId
113 } 122 }
114 » return uniter.NewUniterAPI(r.srv.state, r.resources, r) 123 » return uniter.NewUniterAPI(r.initialRoot.srv.state, r.resources, r)
115 } 124 }
116 125
117 // Agent returns an object that provides access to the 126 // Agent returns an object that provides access to the
118 // agent API. The id argument is reserved for future use and must currently 127 // agent API. The id argument is reserved for future use and must currently
119 // be empty. 128 // be empty.
120 func (r *srvRoot) Agent(id string) (*agent.API, error) { 129 func (r *srvRoot) Agent(id string) (*agent.API, error) {
121 if id != "" { 130 if id != "" {
122 return nil, common.ErrBadId 131 return nil, common.ErrBadId
123 } 132 }
124 » return agent.NewAPI(r.srv.state, r) 133 » return agent.NewAPI(r.initialRoot.srv.state, r)
125 } 134 }
126 135
127 // Deployer returns an object that provides access to the Deployer API facade. 136 // Deployer returns an object that provides access to the Deployer API facade.
128 // The id argument is reserved for future use and must be empty. 137 // The id argument is reserved for future use and must be empty.
129 func (r *srvRoot) Deployer(id string) (*deployer.DeployerAPI, error) { 138 func (r *srvRoot) Deployer(id string) (*deployer.DeployerAPI, error) {
130 if id != "" { 139 if id != "" {
131 // TODO(dimitern): There is no direct test for this 140 // TODO(dimitern): There is no direct test for this
132 return nil, common.ErrBadId 141 return nil, common.ErrBadId
133 } 142 }
134 » return deployer.NewDeployerAPI(r.srv.state, r.resources, r) 143 » return deployer.NewDeployerAPI(r.initialRoot.srv.state, r.resources, r)
135 } 144 }
136 145
137 // Logger returns an object that provides access to the Logger API facade. 146 // Logger returns an object that provides access to the Logger API facade.
138 // The id argument is reserved for future use and must be empty. 147 // The id argument is reserved for future use and must be empty.
139 func (r *srvRoot) Logger(id string) (*loggerapi.LoggerAPI, error) { 148 func (r *srvRoot) Logger(id string) (*loggerapi.LoggerAPI, error) {
140 if id != "" { 149 if id != "" {
141 // TODO: There is no direct test for this 150 // TODO: There is no direct test for this
142 return nil, common.ErrBadId 151 return nil, common.ErrBadId
143 } 152 }
144 » return loggerapi.NewLoggerAPI(r.srv.state, r.resources, r) 153 » return loggerapi.NewLoggerAPI(r.initialRoot.srv.state, r.resources, r)
145 } 154 }
146 155
147 // Upgrader returns an object that provides access to the Upgrader API facade. 156 // Upgrader returns an object that provides access to the Upgrader API facade.
148 // The id argument is reserved for future use and must be empty. 157 // The id argument is reserved for future use and must be empty.
149 func (r *srvRoot) Upgrader(id string) (*upgrader.UpgraderAPI, error) { 158 func (r *srvRoot) Upgrader(id string) (*upgrader.UpgraderAPI, error) {
150 if id != "" { 159 if id != "" {
151 // TODO: There is no direct test for this 160 // TODO: There is no direct test for this
152 return nil, common.ErrBadId 161 return nil, common.ErrBadId
153 } 162 }
154 » return upgrader.NewUpgraderAPI(r.srv.state, r.resources, r) 163 » return upgrader.NewUpgraderAPI(r.initialRoot.srv.state, r.resources, r)
155 } 164 }
156 165
157 // NotifyWatcher returns an object that provides 166 // NotifyWatcher returns an object that provides
158 // API access to methods on a state.NotifyWatcher. 167 // API access to methods on a state.NotifyWatcher.
159 // Each client has its own current set of watchers, stored 168 // Each client has its own current set of watchers, stored
160 // in r.resources. 169 // in r.resources.
161 func (r *srvRoot) NotifyWatcher(id string) (*srvNotifyWatcher, error) { 170 func (r *srvRoot) NotifyWatcher(id string) (*srvNotifyWatcher, error) {
162 if err := r.requireAgent(); err != nil { 171 if err := r.requireAgent(); err != nil {
163 return nil, err 172 return nil, err
164 } 173 }
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after
221 if !ok { 230 if !ok {
222 return nil, common.ErrUnknownWatcher 231 return nil, common.ErrUnknownWatcher
223 } 232 }
224 return &srvClientAllWatcher{ 233 return &srvClientAllWatcher{
225 watcher: watcher, 234 watcher: watcher,
226 id: id, 235 id: id,
227 resources: r.resources, 236 resources: r.resources,
228 }, nil 237 }, nil
229 } 238 }
230 239
231 // Pinger returns object with a single "Ping" method that does nothing. 240 // Pinger returns a server pinger tracing the client pings and
rog 2013/11/18 18:19:48 // Pinger returns an object that can be pinged //
mue 2013/11/19 13:50:46 Done.
232 func (r *srvRoot) Pinger(id string) (srvPinger, error) { 241 // terminating the root after 3 minutes with no pings.
233 » return srvPinger{}, nil 242 func (r *srvRoot) Pinger(id string) (pinger, error) {
243 » return r.pingTimeout, nil
234 } 244 }
235 245
236 type srvPinger struct{}
237
238 // Ping is a no-op used by client heartbeat monitor.
239 func (r srvPinger) Ping() {}
240
241 // AuthMachineAgent returns whether the current client is a machine agent. 246 // AuthMachineAgent returns whether the current client is a machine agent.
242 func (r *srvRoot) AuthMachineAgent() bool { 247 func (r *srvRoot) AuthMachineAgent() bool {
243 _, ok := r.entity.(*state.Machine) 248 _, ok := r.entity.(*state.Machine)
244 return ok 249 return ok
245 } 250 }
246 251
247 // AuthUnitAgent returns whether the current client is a unit agent. 252 // AuthUnitAgent returns whether the current client is a unit agent.
248 func (r *srvRoot) AuthUnitAgent() bool { 253 func (r *srvRoot) AuthUnitAgent() bool {
249 _, ok := r.entity.(*state.Unit) 254 _, ok := r.entity.(*state.Unit)
250 return ok 255 return ok
(...skipping 19 matching lines...) Expand all
270 275
271 // GetAuthTag returns the tag of the authenticated entity. 276 // GetAuthTag returns the tag of the authenticated entity.
272 func (r *srvRoot) GetAuthTag() string { 277 func (r *srvRoot) GetAuthTag() string {
273 return r.entity.Tag() 278 return r.entity.Tag()
274 } 279 }
275 280
276 // GetAuthEntity returns the authenticated entity. 281 // GetAuthEntity returns the authenticated entity.
277 func (r *srvRoot) GetAuthEntity() state.Entity { 282 func (r *srvRoot) GetAuthEntity() state.Entity {
278 return r.entity 283 return r.entity
279 } 284 }
285
286 // pinger describes a type that can be pinged.
287 type pinger interface {
288 Ping()
289 }
290
291 // pingTimeout listens for pings and will call the
292 // passed action in case of a timeout. This way broken
293 // or inactive connections can be closed.
294 type pingTimeout struct {
295 tomb tomb.Tomb
296 tag string
rog 2013/11/18 18:19:48 Do we need the tag here?
mue 2013/11/19 13:50:46 Removed, I only needed it during testing.
297 action func() error
298 timeout time.Duration
299 reset chan struct{}
300 }
301
302 // newPingTimeout creates a ping timeout instance
rog 2013/11/18 18:19:48 // newPingTimeout returns a new pingTimeout instan
mue 2013/11/19 13:50:46 Done.
303 // for the passed action and timeout.
304 func newPingTimeout(tag string, action func() error, timeout time.Duration) *pin gTimeout {
305 pt := &pingTimeout{
306 tag: tag,
307 action: action,
308 timeout: timeout,
309 reset: make(chan struct{}),
310 }
311 go func() {
rog 2013/11/18 18:19:48 I *think* that if you do: defer action() here
mue 2013/11/19 13:50:46 Done.
312 defer pt.tomb.Done()
313 pt.tomb.Kill(pt.loop())
314 }()
315 return pt
316 }
317
318 // Ping is used by the client heartbeat monitor and resets
319 // the killer.
320 func (pt *pingTimeout) Ping() {
321 select {
322 case <-pt.tomb.Dying():
323 case pt.reset <- struct{}{}:
324 }
325 }
326
327 // stop terminates the resource timeout.
328 func (pt *pingTimeout) stop() error {
rog 2013/11/18 18:19:48 I don't think this is ever called.
mue 2013/11/19 13:50:46 No it's done, it already had been intended for the
329 pt.tomb.Kill(nil)
330 return pt.tomb.Wait()
331 }
332
333 // loop waits for a reset signal, otherwise it performs
334 // the initially passed action.
335 func (pt *pingTimeout) loop() error {
336 timer := time.NewTimer(pt.timeout)
337 defer timer.Stop()
338 for {
339 select {
340 case <-pt.tomb.Dying():
341 return nil
342 case <-timer.C:
343 return pt.action()
344 case <-pt.reset:
345 timer.Reset(pt.timeout)
346 }
347 }
348 }
OLDNEW
« no previous file with comments | « state/apiserver/export_test.go ('k') | state/apiserver/root_test.go » ('j') | no next file with comments »

Powered by Google App Engine
RSS Feeds Recent Issues | This issue
This is Rietveld f62528b