@@ -4,12 +4,9 @@ use crate::resolver::in_process::storage::connector::{Connector, QueuePayloadTyp
44use crate :: { CacheService , CacheSettings } ;
55use anyhow:: Result ;
66use async_trait:: async_trait;
7- use flagd_evaluator:: evaluation:: {
8- evaluate_bool_flag, evaluate_float_flag, evaluate_flag, evaluate_int_flag,
9- evaluate_string_flag, EvaluationResult ,
10- } ;
7+ use flagd_evaluator:: evaluation:: EvaluationResult ;
118use flagd_evaluator:: model:: ParsingResult ;
12- use flagd_evaluator:: storage:: { update_flag_state , ValidationMode } ;
9+ use flagd_evaluator:: storage:: ValidationMode ;
1310use open_feature:: provider:: { FeatureProvider , ProviderMetadata , ResolutionDetails } ;
1411use open_feature:: { EvaluationContext , EvaluationError , Value } ;
1512use serde_json:: Value as JsonValue ;
@@ -20,24 +17,22 @@ use tracing::debug;
2017pub struct FileResolver {
2118 /// Connector for watching file changes
2219 connector : Arc < FileConnector > ,
20+ /// Instance-based flag evaluator with its own state and validation mode
21+ evaluator : Arc < tokio:: sync:: RwLock < flagd_evaluator:: FlagEvaluator > > ,
2322 metadata : ProviderMetadata ,
2423 cache : Option < Arc < CacheService < Value > > > ,
2524}
2625
2726impl FileResolver {
2827 pub async fn new ( source_path : String , cache_settings : Option < CacheSettings > ) -> Result < Self > {
29- // Set validation mode to permissive to match other providers
30- // NOTE: This sets a thread-local global state. If multiple resolver instances
31- // are created in the same thread with different validation requirements, this
32- // could cause issues. Currently both InProcessResolver and FileResolver use
33- // Permissive mode, so this is not a problem in practice. A future improvement
34- // would be to make validation mode configurable per CacheSettings and pass it
35- // to evaluator functions, or for flagd-evaluator to support per-instance config.
36- flagd_evaluator:: storage:: set_validation_mode ( ValidationMode :: Permissive ) ;
37-
3828 let connector = Arc :: new ( FileConnector :: new ( source_path) ) ;
3929 let cache = cache_settings. map ( |settings| Arc :: new ( CacheService :: new ( settings) ) ) ;
4030
31+ // Create evaluator instance with permissive validation mode
32+ let evaluator = Arc :: new ( tokio:: sync:: RwLock :: new (
33+ flagd_evaluator:: FlagEvaluator :: new ( ValidationMode :: Permissive )
34+ ) ) ;
35+
4136 // Initialize the connector to start watching the file
4237 connector. init ( ) . await ?;
4338
@@ -53,7 +48,8 @@ impl FileResolver {
5348 debug ! ( "Received initial flag configuration from file" ) ;
5449 match ParsingResult :: parse ( & payload. flag_data ) {
5550 Ok ( _) => {
56- if let Err ( e) = update_flag_state ( & payload. flag_data ) {
51+ let mut eval = evaluator. write ( ) . await ;
52+ if let Err ( e) = eval. update_state ( & payload. flag_data ) {
5753 return Err ( anyhow:: anyhow!( "Failed to update flag state: {}" , e) ) ;
5854 }
5955 }
@@ -78,6 +74,7 @@ impl FileResolver {
7874 // Spawn task to handle subsequent config updates
7975 let stream_clone = stream. clone ( ) ;
8076 let cache_clone = cache. clone ( ) ;
77+ let evaluator_clone = evaluator. clone ( ) ;
8178 tokio:: spawn ( async move {
8279 let mut receiver_opt = stream_clone. lock ( ) . await ;
8380 if let Some ( receiver) = receiver_opt. as_mut ( ) {
@@ -88,14 +85,16 @@ impl FileResolver {
8885 // Parse and update state in evaluator
8986 match ParsingResult :: parse ( & payload. flag_data ) {
9087 Ok ( _) => {
91- if let Err ( e) = update_flag_state ( & payload. flag_data ) {
88+ let mut eval = evaluator_clone. write ( ) . await ;
89+ if let Err ( e) = eval. update_state ( & payload. flag_data ) {
9290 tracing:: error!( "Failed to update flag state: {}" , e) ;
9391 } else {
9492 // Clear cache when flags update
9593 if let Some ( cache) = & cache_clone {
9694 cache. purge ( ) . await ;
9795 }
9896 }
97+ drop ( eval) ; // Explicitly drop lock before continuing
9998 }
10099 Err ( e) => {
101100 tracing:: error!( "Failed to parse flag configuration: {}" , e) ;
@@ -108,6 +107,7 @@ impl FileResolver {
108107
109108 Ok ( Self {
110109 connector,
110+ evaluator,
111111 metadata : ProviderMetadata :: new ( "flagd" ) ,
112112 cache,
113113 } )
@@ -131,7 +131,7 @@ impl FileResolver {
131131 & self ,
132132 flag_key : & str ,
133133 context : & EvaluationContext ,
134- evaluator_fn : impl Fn ( & serde_json :: Map < String , JsonValue > ) -> EvaluationResult ,
134+ evaluator_fn : impl Fn ( & flagd_evaluator :: FlagEvaluator , & JsonValue ) -> EvaluationResult ,
135135 value_extractor : impl Fn ( & JsonValue ) -> Option < T > ,
136136 cache_value_fn : impl Fn ( T ) -> Value ,
137137 ) -> Result < ResolutionDetails < T > , EvaluationError >
@@ -156,12 +156,11 @@ impl FileResolver {
156156
157157 // Build context for evaluator
158158 let ctx_json = common:: build_context_json ( context) ;
159- let ctx_map = ctx_json. as_object ( ) . unwrap_or_else ( || {
160- panic ! ( "build_context_json should always return an object" )
161- } ) ;
162159
163- // Call evaluator (no clone needed)
164- let result = evaluator_fn ( ctx_map) ;
160+ // Get read lock on evaluator and call evaluation function
161+ let eval = self . evaluator . read ( ) . await ;
162+ let result = evaluator_fn ( & * eval, & ctx_json) ;
163+ drop ( eval) ; // Release lock before awaiting
165164
166165 // Convert result to details
167166 let details = common:: result_to_details ( & result, value_extractor) ?;
@@ -191,10 +190,7 @@ impl FeatureProvider for FileResolver {
191190 self . resolve_value (
192191 flag_key,
193192 context,
194- |ctx| {
195- let ( flag, metadata) = common:: get_flag_and_metadata ( flag_key) ;
196- evaluate_bool_flag ( & flag, & JsonValue :: Object ( ctx. clone ( ) ) , & metadata)
197- } ,
193+ |eval, ctx| eval. evaluate_bool ( flag_key, ctx) ,
198194 |v| v. as_bool ( ) ,
199195 Value :: Bool ,
200196 )
@@ -209,10 +205,7 @@ impl FeatureProvider for FileResolver {
209205 self . resolve_value (
210206 flag_key,
211207 context,
212- |ctx| {
213- let ( flag, metadata) = common:: get_flag_and_metadata ( flag_key) ;
214- evaluate_string_flag ( & flag, & JsonValue :: Object ( ctx. clone ( ) ) , & metadata)
215- } ,
208+ |eval, ctx| eval. evaluate_string ( flag_key, ctx) ,
216209 |v| v. as_str ( ) . map ( String :: from) ,
217210 Value :: String ,
218211 )
@@ -227,10 +220,7 @@ impl FeatureProvider for FileResolver {
227220 self . resolve_value (
228221 flag_key,
229222 context,
230- |ctx| {
231- let ( flag, metadata) = common:: get_flag_and_metadata ( flag_key) ;
232- evaluate_int_flag ( & flag, & JsonValue :: Object ( ctx. clone ( ) ) , & metadata)
233- } ,
223+ |eval, ctx| eval. evaluate_int ( flag_key, ctx) ,
234224 |v| v. as_i64 ( ) ,
235225 Value :: Int ,
236226 )
@@ -245,10 +235,7 @@ impl FeatureProvider for FileResolver {
245235 self . resolve_value (
246236 flag_key,
247237 context,
248- |ctx| {
249- let ( flag, metadata) = common:: get_flag_and_metadata ( flag_key) ;
250- evaluate_float_flag ( & flag, & JsonValue :: Object ( ctx. clone ( ) ) , & metadata)
251- } ,
238+ |eval, ctx| eval. evaluate_float ( flag_key, ctx) ,
252239 |v| v. as_f64 ( ) ,
253240 Value :: Float ,
254241 )
@@ -263,10 +250,7 @@ impl FeatureProvider for FileResolver {
263250 self . resolve_value (
264251 flag_key,
265252 context,
266- |ctx| {
267- let ( flag, metadata) = common:: get_flag_and_metadata ( flag_key) ;
268- evaluate_flag ( & flag, & JsonValue :: Object ( ctx. clone ( ) ) , & metadata)
269- } ,
253+ |eval, ctx| eval. evaluate_flag ( flag_key, ctx) ,
270254 |v| {
271255 v. as_object ( ) . map ( |obj| {
272256 let fields = obj
0 commit comments