/// <summary> /// Called to find(or create if one is not found) the appropriate OutboundSessionDriver for a given partition. /// This method will ensure that we have a one(targetPartition)-to-one(outBoundSessionDriver) relationship. /// /// TODO: this layer must catch all exceptions and decide what to do with them -- lower layer functions like ResolveAndNormalizeTargetPartition /// will throw in case of unrecoverable errors like FabricObjectClosedException /// </summary> /// <param name="targetPartition">Find or Create OutBoundSessionDriver for this Partition</param> /// <param name="streamManager">Stream manager that manages the reference to the driver</param> /// <param name="sessionConnectionManager">Use this session connection manager to create a new session if one if not found</param> /// <param name="timeout">Timeout</param> /// <returns>Returns OutboundSessionDriver if successfull, null otherwise</returns> internal static async Task <OutboundSessionDriver> FindOrCreateDriverAsync( PartitionKey targetPartition, StreamManager streamManager, SessionConnectionManager sessionConnectionManager, TimeSpan timeout) { OutboundSessionDriver streamOutboundSessionDriver = null; // Complete the call within this time. var remainingTimeout = timeout; try { FabricEvents.Events.FindOrCreateOutboundSessionDriver("Start@" + streamManager.TraceType, targetPartition.ToString()); var beforeResolve = DateTime.UtcNow; // The sync point pattern is used here because lock() pattern is not compatible with await pattern var normalizedPartitionKey = await streamManager.SessionConnectionManager.ResolveAndNormalizeTargetPartition(targetPartition, remainingTimeout); Diagnostics.Assert( null != normalizedPartitionKey, "normalizedPartitionKey is not expected to be null in OutboundSessionDriver.FindOrCreateDriverAsync()."); // Update remaining time for timeout var afterResolve = DateTime.UtcNow; remainingTimeout = TimeoutHandler.UpdateTimeout(remainingTimeout, beforeResolve, afterResolve); var syncPoint = new SyncPoint <PartitionKey>( streamManager, normalizedPartitionKey, streamManager.RuntimeResources.OutboundSessionDriverSyncpoints); try { // this is entering an await compatible critical section -- syncPoint.Dispose will leave it await syncPoint.EnterAsync(remainingTimeout); var afterEnter = DateTime.UtcNow; remainingTimeout = TimeoutHandler.UpdateTimeout(remainingTimeout, afterResolve, afterEnter); // Check if the driver already exists var exists = streamManager.RuntimeResources.OutboundSessionDrivers.TryGetValue(normalizedPartitionKey, out streamOutboundSessionDriver); // TODO: should we refcount how many streams depend on this session? // create a new driver if if it does not exist already if (!exists) { FabricEvents.Events.FindOrCreateOutboundSessionDriver("Creating@" + streamManager.TraceType, normalizedPartitionKey.ToString()); // we are actually the first stream attaching to this partition on start or recovery or all previous ones closed // Find or create a new reliable messaging session var session = await sessionConnectionManager.FindOrCreateOutboundSessionAsync(normalizedPartitionKey, remainingTimeout); Diagnostics.Assert(null != session, "Session is not expected to be null in OutboundSessionDriver.FindOrCreateDriverAsync()."); // Create a new outbound session driver streamOutboundSessionDriver = new OutboundSessionDriver(normalizedPartitionKey, streamManager, sessionConnectionManager, session); Diagnostics.Assert( null != streamOutboundSessionDriver, "Stream OutboundSessionDriver is not expected to be null in OutboundSessionDriver.FindOrCreateDriverAsync()."); // Add ref. to stream manager active runtime resources var addSuccess = streamManager.RuntimeResources.OutboundSessionDrivers.TryAdd(normalizedPartitionKey, streamOutboundSessionDriver); Diagnostics.Assert( addSuccess, "{0} Unexpected failure to add newSessionDriver in OutboundSessionDriver.FindOrCreateDriverAsync", streamManager.Tracer); } else { FabricEvents.Events.FindOrCreateOutboundSessionDriver("Found@" + streamManager.TraceType, normalizedPartitionKey.ToString()); } } finally { syncPoint.Dispose(); } FabricEvents.Events.FindOrCreateOutboundSessionDriver("Finish@" + streamManager.TraceType, normalizedPartitionKey.ToString()); } catch (TimeoutException) { FabricEvents.Events.FindOrCreateOutboundSessionDriver("Timeout@" + streamManager.TraceType, targetPartition.ToString()); throw; } catch (Exception e) { Tracing.WriteExceptionAsWarning("FindOrCreateDriverAsync.Failure", e, "{0}", streamManager.Tracer); throw; } return(streamOutboundSessionDriver); }