11
22using System ;
33using System . Collections . Generic ;
4+ using System . IO . Hashing ;
5+ using System . Numerics ;
46using Microsoft . Extensions . Logging ;
57using Orleans . CodeGeneration ;
68using Orleans . Serialization ;
@@ -9,34 +11,55 @@ namespace Orleans.Runtime
911{
1012 internal class MessageFactory
1113 {
12- private readonly DeepCopier deepCopier ;
13- private readonly ILogger logger ;
14- private readonly MessagingTrace messagingTrace ;
14+ [ ThreadStatic ]
15+ private static ulong _nextId ;
16+
17+ // The nonce reduces the chance of an id collision for a given grain to effectively zero. Id collisions are only relevant in scenarios
18+ // where where the infinitesimally small chance of a collision is acceptable, such as call cancellation.
19+ private readonly ulong _seed ;
20+ private readonly DeepCopier _deepCopier ;
21+ private readonly ILogger _logger ;
22+ private readonly MessagingTrace _messagingTrace ;
1523
1624 public MessageFactory ( DeepCopier deepCopier , ILogger < MessageFactory > logger , MessagingTrace messagingTrace )
1725 {
18- this . deepCopier = deepCopier ;
19- this . logger = logger ;
20- this . messagingTrace = messagingTrace ;
26+ _deepCopier = deepCopier ;
27+ _logger = logger ;
28+ _messagingTrace = messagingTrace ;
29+
30+ // Generate a 64-bit nonce for the host, to be combined with per-message correlation ids to get a unique, per-host value.
31+ // This avoids id collisions across different hosts for a given grain.
32+ _seed = unchecked ( ( ulong ) Random . Shared . NextInt64 ( ) ) ;
2133 }
2234
2335 public Message CreateMessage ( object body , InvokeMethodOptions options )
2436 {
2537 var message = new Message
2638 {
2739 Direction = ( options & InvokeMethodOptions . OneWay ) != 0 ? Message . Directions . OneWay : Message . Directions . Request ,
28- Id = CorrelationId . GetNext ( ) ,
40+ Id = GetNextCorrelationId ( ) ,
2941 IsReadOnly = ( options & InvokeMethodOptions . ReadOnly ) != 0 ,
3042 IsUnordered = ( options & InvokeMethodOptions . Unordered ) != 0 ,
3143 IsAlwaysInterleave = ( options & InvokeMethodOptions . AlwaysInterleave ) != 0 ,
3244 BodyObject = body ,
33- RequestContextData = RequestContextExtensions . Export ( this . deepCopier ) ,
45+ RequestContextData = RequestContextExtensions . Export ( _deepCopier ) ,
3446 } ;
3547
36- messagingTrace . OnCreateMessage ( message ) ;
48+ _messagingTrace . OnCreateMessage ( message ) ;
3749 return message ;
3850 }
3951
52+ private CorrelationId GetNextCorrelationId ( )
53+ {
54+ // To avoid cross-thread coordination, combine a thread-local counter with the managed thread id. The values are XOR'd together with a
55+ // 64-bit nonce. Rotating the thread id reduces the chance of collision further by putting the significant bits at the high end, where
56+ // they are less likely to collide with the per-thread counter, which could become relevant if the counter exceeded 2^32.
57+ var managedThreadId = Environment . CurrentManagedThreadId ;
58+ var tid = ( ulong ) ( managedThreadId << 16 | managedThreadId >> 16 ) << 32 ;
59+ var id = _seed ^ tid ^ ++ _nextId ;
60+ return new CorrelationId ( unchecked ( ( long ) id ) ) ;
61+ }
62+
4063 public Message CreateResponseMessage ( Message request )
4164 {
4265 var response = new Message
@@ -52,25 +75,25 @@ public Message CreateResponseMessage(Message request)
5275 SendingGrain = request . TargetGrain ,
5376 CacheInvalidationHeader = request . CacheInvalidationHeader ,
5477 TimeToLive = request . TimeToLive ,
55- RequestContextData = RequestContextExtensions . Export ( this . deepCopier ) ,
78+ RequestContextData = RequestContextExtensions . Export ( _deepCopier ) ,
5679 } ;
5780
58- messagingTrace . OnCreateMessage ( response ) ;
81+ _messagingTrace . OnCreateMessage ( response ) ;
5982 return response ;
6083 }
6184
6285 public Message CreateRejectionResponse ( Message request , Message . RejectionTypes type , string info , Exception ex = null )
6386 {
64- var response = this . CreateResponseMessage ( request ) ;
87+ var response = CreateResponseMessage ( request ) ;
6588 response . Result = Message . ResponseTypes . Rejection ;
6689 response . BodyObject = new RejectionResponse
6790 {
6891 RejectionType = type ,
6992 RejectionInfo = info ,
7093 Exception = ex ,
7194 } ;
72- if ( this . logger . IsEnabled ( LogLevel . Debug ) )
73- this . logger . LogDebug (
95+ if ( _logger . IsEnabled ( LogLevel . Debug ) )
96+ _logger . LogDebug (
7497 ex ,
7598 "Creating {RejectionType} rejection with info '{Info}' at:" + Environment . NewLine + "{StackTrace}" ,
7699 type ,
@@ -81,11 +104,11 @@ public Message CreateRejectionResponse(Message request, Message.RejectionTypes t
81104
82105 internal Message CreateDiagnosticResponseMessage ( Message request , bool isExecuting , bool isWaiting , List < string > diagnostics )
83106 {
84- var response = this . CreateResponseMessage ( request ) ;
107+ var response = CreateResponseMessage ( request ) ;
85108 response . Result = Message . ResponseTypes . Status ;
86109 response . BodyObject = new StatusResponse ( isExecuting , isWaiting , diagnostics ) ;
87110
88- if ( this . logger . IsEnabled ( LogLevel . Debug ) ) this . logger . LogDebug ( "Creating {RequestMessage} status update with diagnostics {Diagnostics}" , request , diagnostics ) ;
111+ if ( _logger . IsEnabled ( LogLevel . Debug ) ) _logger . LogDebug ( "Creating {RequestMessage} status update with diagnostics {Diagnostics}" , request , diagnostics ) ;
89112
90113 return response ;
91114 }
0 commit comments