@@ -19,20 +19,23 @@ import (
1919 "google.golang.org/grpc/credentials/insecure"
2020 "log"
2121 "os"
22+ "strconv"
2223 "strings"
2324 "sync"
25+ "time"
2426)
2527
2628// ErrInvalidFormat indicates that the serialization format can only be JSON.
2729var ErrInvalidFormat = errors .New ("format can only be 'json'" )
2830
2931const (
30- defaultFormat = "json"
31- mapOrCacheExists = "the %s %s already exists with different type parameters"
32+ defaultFormat = "json"
33+ mapOrCacheExists = "the %s %s already exists with different type parameters"
34+ defaultSessionTimeout = "30000" // millis
3235)
3336
34- // Session provides APIs to create NamedCaches. The NewSession() method creates a
35- // new instance of a Session. This method also takes a variable number of arguments, called options,
37+ // Session provides APIs to create NamedCaches. The [ NewSession] method creates a
38+ // new instance of a [ Session] . This method also takes a variable number of arguments, called options,
3639// that can be passed to configure the Session.
3740type Session struct {
3841 sessionID uuid.UUID
@@ -60,6 +63,7 @@ type SessionOptions struct {
6063 ClientKeyPath string
6164 CaCertPath string
6265 PlainText bool
66+ Timeout time.Duration
6367}
6468
6569// NewSession creates a new Session with the specified sessionOptions.
@@ -94,8 +98,8 @@ type SessionOptions struct {
9498// export COHERENCE_TLS_CERTS_PATH=/path/to/cert/to/be/added/for/trust
9599// export COHERENCE_IGNORE_INVALID_CERTS=true // option to ignore self-signed certificates - for testing only. Not to be used in production
96100//
97- // Finally, the Close() method can be used to close the Session. Once a Session is closed, no APIs
98- // on the NamedMap instances should be invoked. If invoked they all will return an error.
101+ // Finally, the Close() method can be used to close the [ Session] . Once a [ Session] is closed, no APIs
102+ // on the [ NamedMap] instances should be invoked. If invoked they will return an error.
99103// [gRPC Naming]: https://github.com/grpc/grpc/blob/master/doc/naming.md
100104// [gRPC Proxy Server]: https://docs.oracle.com/en/middleware/standalone/coherence/14.1.1.2206/develop-remote-clients/using-coherence-grpc-server.html
101105func NewSession (ctx context.Context , options ... func (session * SessionOptions )) (* Session , error ) {
@@ -111,7 +115,8 @@ func NewSession(ctx context.Context, options ...func(session *SessionOptions)) (
111115 lifecycleListeners : []* SessionLifecycleListener {},
112116 sessOpts : & SessionOptions {
113117 PlainText : false ,
114- Format : defaultFormat },
118+ Format : defaultFormat ,
119+ Timeout : time .Duration (0 ) * time .Second },
115120 }
116121
117122 if getBoolValueFromEnvVarOrDefault (envSessionDebug , false ) {
@@ -135,9 +140,18 @@ func NewSession(ctx context.Context, options ...func(session *SessionOptions)) (
135140 session .sessOpts .Address = getStringValueFromEnvVarOrDefault (envHostName , "localhost:1408" )
136141 }
137142
143+ // if no timeout then use the env or default
144+ if session .sessOpts .Timeout == time .Duration (0 ) {
145+ timeoutString := getStringValueFromEnvVarOrDefault (envSessionTimeout , defaultSessionTimeout )
146+ timeout , err := strconv .ParseInt (timeoutString , 10 , 64 )
147+ if err != nil || timeout <= 0 {
148+ return nil , fmt .Errorf ("invalid value of %s for timeout" , timeoutString )
149+ }
150+ session .sessOpts .Timeout = time .Duration (timeout ) * time .Millisecond
151+ }
152+
138153 // ensure initial connection
139- err := session .ensureConnection ()
140- return session , err
154+ return session , session .ensureConnection ()
141155}
142156
143157// WithAddress returns a function to set the address for session.
@@ -169,6 +183,13 @@ func WithPlainText() func(sessionOptions *SessionOptions) {
169183 }
170184}
171185
186+ // WithSessionTimeout returns a function to set the session timeout.
187+ func WithSessionTimeout (timeout time.Duration ) func (sessionOptions * SessionOptions ) {
188+ return func (s * SessionOptions ) {
189+ s .Timeout = timeout
190+ }
191+ }
192+
172193// ID returns the identifier of a session.
173194func (s * Session ) ID () string {
174195 return s .sessionID .String ()
@@ -190,6 +211,11 @@ func (s *Session) String() string {
190211 len (s .caches ), len (s .maps ), s .sessOpts )
191212}
192213
214+ // GetSessionTimeout returns the session timeout in seconds.
215+ func (s * Session ) GetSessionTimeout () time.Duration {
216+ return s .sessOpts .Timeout
217+ }
218+
193219// ensureConnection ensures a session has a valid connection
194220func (s * Session ) ensureConnection () error {
195221 if s .firstConnectAttempted {
@@ -222,7 +248,12 @@ func (s *Session) ensureConnection() error {
222248 s .mutex .Lock ()
223249 locked = true
224250
225- conn , err := grpc .DialContext (s .sessionConnectCtx , s .sessOpts .Address , s .dialOptions ... )
251+ newCtx , cancel := s .ensureContext (s .sessionConnectCtx )
252+ if cancel != nil {
253+ defer cancel ()
254+ }
255+
256+ conn , err := grpc .DialContext (newCtx , s .sessOpts .Address , s .dialOptions ... )
226257
227258 if err != nil {
228259 log .Printf ("could not connect. Reason: %v" , err )
@@ -469,8 +500,8 @@ func validateFilePath(file string) error {
469500// String returns a string representation of SessionOptions.
470501func (s * SessionOptions ) String () string {
471502 var sb = strings.Builder {}
472- sb .WriteString (fmt .Sprintf ("SessionOptions{address=%v, tLSEnabled=%v, scope=%v, format=%v," ,
473- s .Address , s .TLSEnabled , s .Scope , s .Format ))
503+ sb .WriteString (fmt .Sprintf ("SessionOptions{address=%v, tLSEnabled=%v, scope=%v, format=%v, timeout=%v " ,
504+ s .Address , s .TLSEnabled , s .Scope , s .Format , s . Timeout ))
474505
475506 if s .TLSEnabled {
476507 sb .WriteString (fmt .Sprintf (" clientCertPath=%v, clientKeyPath=%v, caCertPath=%v," ,
@@ -490,3 +521,13 @@ func (s *Session) dispatch(eventType SessionLifecycleEventType,
490521 }
491522 }
492523}
524+
525+ // ensureContext will ensure that the context has deadline and if not will apply the timeout from the
526+ // [SessionOptions].
527+ func (s * Session ) ensureContext (ctx context.Context ) (context.Context , context.CancelFunc ) {
528+ if _ , ok := ctx .Deadline (); ! ok {
529+ // no deadline set, so wrap the context in a Timeout
530+ return context .WithTimeout (ctx , s .sessOpts .Timeout )
531+ }
532+ return ctx , nil
533+ }
0 commit comments