@@ -23,12 +23,15 @@ import (
23
23
"strings"
24
24
"sync"
25
25
"sync/atomic"
26
+ "time"
26
27
27
28
"github.com/digitalocean/go-openvswitch/ovsdb/internal/jsonrpc"
28
29
)
29
30
30
- // A Client is an OVSDB client.
31
+ // A Client is an OVSDB client. Clients can be customized by using OptionFuncs
32
+ // in the Dial and New functions.
31
33
type Client struct {
34
+ // The RPC connection, and its logger.
32
35
c * jsonrpc.Conn
33
36
ll * log.Logger
34
37
@@ -39,7 +42,12 @@ type Client struct {
39
42
cbMu sync.RWMutex
40
43
callbacks map [int ]callback
41
44
42
- wg * sync.WaitGroup
45
+ // Interval at which echo RPCs should occur in the background.
46
+ echoInterval time.Duration
47
+
48
+ // Track and clean up background goroutines.
49
+ cancel func ()
50
+ wg * sync.WaitGroup
43
51
}
44
52
45
53
// An OptionFunc is a function which can configure a Client.
@@ -53,6 +61,18 @@ func Debug(ll *log.Logger) OptionFunc {
53
61
}
54
62
}
55
63
64
+ // EchoInterval specifies an interval at which the Client will send
65
+ // echo RPCs to an OVSDB server to keep the connection alive. If this
66
+ // option is not used, the Client will not send any echo RPCs on its own.
67
+ //
68
+ // Specify a duration of 0 to disable sending background echo RPCs.
69
+ func EchoInterval (d time.Duration ) OptionFunc {
70
+ return func (c * Client ) error {
71
+ c .echoInterval = d
72
+ return nil
73
+ }
74
+ }
75
+
56
76
// Dial dials a connection to an OVSDB server and returns a Client.
57
77
func Dial (network , addr string , options ... OptionFunc ) (* Client , error ) {
58
78
conn , err := net .Dial (network , addr )
@@ -82,10 +102,22 @@ func New(conn net.Conn, options ...OptionFunc) (*Client, error) {
82
102
// Set up callbacks.
83
103
client .callbacks = make (map [int ]callback )
84
104
85
- // Start up any background routines.
105
+ // Start up any background routines, and enable canceling them via context.
106
+ ctx , cancel := context .WithCancel (context .Background ())
107
+ client .cancel = cancel
108
+
86
109
var wg sync.WaitGroup
87
110
wg .Add (1 )
88
111
112
+ // If configured, send echo RPCs in the background at a fixed interval.
113
+ if d := client .echoInterval ; d != 0 {
114
+ wg .Add (1 )
115
+ go func () {
116
+ defer wg .Done ()
117
+ client .echoLoop (ctx , d )
118
+ }()
119
+ }
120
+
89
121
// Handle all incoming RPC responses and notifications.
90
122
go func () {
91
123
defer wg .Done ()
@@ -102,9 +134,10 @@ func (c *Client) requestID() int {
102
134
return int (atomic .AddInt64 (c .rpcID , 1 ))
103
135
}
104
136
105
- // Close closes a Client's connection.
137
+ // Close closes a Client's connection and cleans up its resources .
106
138
func (c * Client ) Close () error {
107
139
err := c .c .Close ()
140
+ c .cancel ()
108
141
c .wg .Wait ()
109
142
return err
110
143
}
@@ -229,6 +262,25 @@ func (c *Client) listen() {
229
262
}
230
263
}
231
264
265
+ // echoLoop starts a loop that sends echo RPCs at the interval defined by d.
266
+ func (c * Client ) echoLoop (ctx context.Context , d time.Duration ) {
267
+ t := time .NewTicker (d )
268
+ defer t .Stop ()
269
+
270
+ for {
271
+ select {
272
+ case <- ctx .Done ():
273
+ return
274
+ case <- t .C :
275
+ }
276
+
277
+ // No feasible way to handle errors here. In the future, it may be
278
+ // possible to do something like re-establishing the connection.
279
+ // TOOD(mdlayher): improve error handling for echo loop.
280
+ _ = c .Echo (ctx )
281
+ }
282
+ }
283
+
232
284
// A callback can be used to send a message back to a caller, or
233
285
// allow the caller to cancel waiting for a message.
234
286
type callback struct {
0 commit comments