22using System . Security . Cryptography ;
33using System . Text ;
44using System . Text . Json ;
5+ using System . Threading ;
56using System . Threading . Tasks ;
67using AWS . Lambda . Powertools . Common ;
78using AWS . Lambda . Powertools . Idempotency . Exceptions ;
@@ -34,9 +35,15 @@ public abstract class BasePersistenceStore : IPersistenceStore
3435 private IdempotencyOptions _idempotencyOptions = null ! ;
3536
3637 /// <summary>
37- /// Function name
38+ /// Base function name (Lambda function name from environment or "testFunction")
3839 /// </summary>
39- private string _functionName ;
40+ private string _baseFunctionName = null ! ;
41+
42+ /// <summary>
43+ /// Full function name including method name (used for key generation)
44+ /// This is stored per-call via AsyncLocal to support multiple idempotent methods
45+ /// </summary>
46+ private readonly AsyncLocal < string > _fullFunctionName = new ( ) ;
4047
4148 /// <summary>
4249 /// Boolean to indicate whether or not payload validation is enabled
@@ -58,27 +65,26 @@ public abstract class BasePersistenceStore : IPersistenceStore
5865 public void Configure ( IdempotencyOptions idempotencyOptions , string functionName , string keyPrefix )
5966 {
6067 // Fast path - already configured
61- if ( _isConfigured ) return ;
68+ if ( _isConfigured )
69+ {
70+ // Even if already configured, we need to set the full function name for this call
71+ // This supports multiple idempotent methods in the same Lambda
72+ SetFullFunctionName ( functionName , keyPrefix ) ;
73+ return ;
74+ }
6275
6376 lock ( _configureLock )
6477 {
6578 // Double-check pattern
66- if ( _isConfigured ) return ;
67-
68- if ( ! string . IsNullOrEmpty ( keyPrefix ) )
69- {
70- _functionName = keyPrefix ;
71- }
72- else
79+ if ( _isConfigured )
7380 {
74- var funcEnv = Environment . GetEnvironmentVariable ( Constants . LambdaFunctionNameEnv ) ;
75-
76- _functionName = funcEnv ?? "testFunction" ;
77- if ( ! string . IsNullOrWhiteSpace ( functionName ) )
78- {
79- _functionName += "." + functionName ;
80- }
81+ SetFullFunctionName ( functionName , keyPrefix ) ;
82+ return ;
8183 }
84+
85+ // Set the base function name (Lambda function name from environment)
86+ var funcEnv = Environment . GetEnvironmentVariable ( Constants . LambdaFunctionNameEnv ) ;
87+ _baseFunctionName = funcEnv ?? "testFunction" ;
8288
8389 _idempotencyOptions = idempotencyOptions ;
8490
@@ -94,6 +100,33 @@ public void Configure(IdempotencyOptions idempotencyOptions, string functionName
94100 }
95101
96102 _isConfigured = true ;
103+
104+ // Set the full function name for this call
105+ SetFullFunctionName ( functionName , keyPrefix ) ;
106+ }
107+ }
108+
109+ /// <summary>
110+ /// Sets the full function name for the current call context.
111+ /// This method is called for each idempotent method invocation to ensure
112+ /// the correct method name is used in the idempotency key.
113+ /// </summary>
114+ /// <param name="functionName">The name of the decorated method</param>
115+ /// <param name="keyPrefix">Optional custom key prefix</param>
116+ private void SetFullFunctionName ( string functionName , string keyPrefix )
117+ {
118+ if ( ! string . IsNullOrEmpty ( keyPrefix ) )
119+ {
120+ _fullFunctionName . Value = keyPrefix ;
121+ }
122+ else
123+ {
124+ var fullName = _baseFunctionName ;
125+ if ( ! string . IsNullOrWhiteSpace ( functionName ) )
126+ {
127+ fullName += "." + functionName ;
128+ }
129+ _fullFunctionName . Value = fullName ;
97130 }
98131 }
99132
@@ -105,27 +138,24 @@ internal void Configure(IdempotencyOptions options, string functionName, string
105138 LRUCache < string , DataRecord > cache )
106139 {
107140 // Fast path - already configured
108- if ( _isConfigured ) return ;
141+ if ( _isConfigured )
142+ {
143+ SetFullFunctionName ( functionName , keyPrefix ) ;
144+ return ;
145+ }
109146
110147 lock ( _configureLock )
111148 {
112149 // Double-check pattern
113- if ( _isConfigured ) return ;
114-
115- if ( ! string . IsNullOrEmpty ( keyPrefix ) )
116- {
117- _functionName = keyPrefix ;
118- }
119- else
150+ if ( _isConfigured )
120151 {
121- var funcEnv = Environment . GetEnvironmentVariable ( Constants . LambdaFunctionNameEnv ) ;
122-
123- _functionName = funcEnv ?? "testFunction" ;
124- if ( ! string . IsNullOrWhiteSpace ( functionName ) )
125- {
126- _functionName += "." + functionName ;
127- }
152+ SetFullFunctionName ( functionName , keyPrefix ) ;
153+ return ;
128154 }
155+
156+ // Set the base function name (Lambda function name from environment)
157+ var funcEnv = Environment . GetEnvironmentVariable ( Constants . LambdaFunctionNameEnv ) ;
158+ _baseFunctionName = funcEnv ?? "testFunction" ;
129159
130160 _idempotencyOptions = options ;
131161
@@ -137,6 +167,8 @@ internal void Configure(IdempotencyOptions options, string functionName, string
137167 _cache = cache ;
138168
139169 _isConfigured = true ;
170+
171+ SetFullFunctionName ( functionName , keyPrefix ) ;
140172 }
141173 }
142174
@@ -356,7 +388,7 @@ private string GetHashedIdempotencyKey(JsonDocument data)
356388 }
357389
358390 var hash = GenerateHash ( node ) ;
359- return _functionName + "#" + hash ;
391+ return _fullFunctionName . Value + "#" + hash ;
360392 }
361393
362394 /// <summary>
0 commit comments