# Simple Non-Blocking Architecture (No Globals, No Locking) ## Core Principle **Single-threaded Rhai engine with fire-and-forget HTTP requests that dispatch response scripts** ## Architecture Flow ```mermaid graph TD A[Rhai: create_payment_intent] --> B[Function: create_payment_intent] B --> C[Spawn Thread] B --> D[Return Immediately] C --> E[HTTP Request to Stripe] E --> F{Response} F -->|Success| G[Dispatch: new_create_payment_intent_response.rhai] F -->|Error| H[Dispatch: new_create_payment_intent_error.rhai] G --> I[New Rhai Script Execution] H --> J[New Rhai Script Execution] ``` ## Key Design Principles 1. **No Global State** - All configuration passed as parameters 2. **No Locking** - No shared state between threads 3. **Fire-and-Forget** - Functions return immediately 4. **Self-Contained Threads** - Each thread has everything it needs 5. **Script Dispatch** - Responses trigger new Rhai script execution ## Implementation ### 1. Simple Function Signature ```rust #[rhai_fn(name = "create", return_raw)] pub fn create_payment_intent( intent: &mut RhaiPaymentIntent, worker_id: String, context_id: String, stripe_secret: String ) -> Result> { let form_data = prepare_payment_intent_data(intent); // Spawn completely independent thread thread::spawn(move || { let rt = Runtime::new().expect("Failed to create runtime"); rt.block_on(async { // Create HTTP client in thread let client = Client::new(); // Make HTTP request match make_stripe_request(&client, &stripe_secret, "payment_intents", &form_data).await { Ok(response) => { dispatch_response_script( &worker_id, &context_id, "new_create_payment_intent_response", &response ).await; } Err(error) => { dispatch_error_script( &worker_id, &context_id, "new_create_payment_intent_error", &error ).await; } } }); }); // Return immediately - no waiting! Ok("payment_intent_request_dispatched".to_string()) } ``` ### 2. Self-Contained HTTP Function ```rust async fn make_stripe_request( client: &Client, secret_key: &str, endpoint: &str, form_data: &HashMap ) -> Result { let url = format!("https://api.stripe.com/v1/{}", endpoint); let response = client .post(&url) .basic_auth(secret_key, None::<&str>) .form(form_data) .send() .await .map_err(|e| format!("HTTP request failed: {}", e))?; let response_text = response.text().await .map_err(|e| format!("Failed to read response: {}", e))?; // Return raw response - let script handle parsing Ok(response_text) } ``` ### 3. Simple Script Dispatch ```rust async fn dispatch_response_script( worker_id: &str, context_id: &str, script_name: &str, response_data: &str ) { let script_content = format!( r#" // Response data from API let response_json = `{}`; let parsed_data = parse_json(response_json); // Execute the response script eval_file("flows/{}.rhai"); "#, response_data.replace('`', r#"\`"#), script_name ); // Create dispatcher instance just for this dispatch if let Ok(dispatcher) = RhaiDispatcherBuilder::new() .caller_id("stripe") .worker_id(worker_id) .context_id(context_id) .redis_url("redis://127.0.0.1/") .build() { let _ = dispatcher .new_play_request() .script(&script_content) .submit() .await; } } async fn dispatch_error_script( worker_id: &str, context_id: &str, script_name: &str, error_data: &str ) { let script_content = format!( r#" // Error data from API let error_json = `{}`; let parsed_error = parse_json(error_json); // Execute the error script eval_file("flows/{}.rhai"); "#, error_data.replace('`', r#"\`"#), script_name ); // Create dispatcher instance just for this dispatch if let Ok(dispatcher) = RhaiDispatcherBuilder::new() .caller_id("stripe") .worker_id(worker_id) .context_id(context_id) .redis_url("redis://127.0.0.1/") .build() { let _ = dispatcher .new_play_request() .script(&script_content) .submit() .await; } } ``` ## Complete Function Implementations ### Payment Intent ```rust #[rhai_fn(name = "create_async", return_raw)] pub fn create_payment_intent_async( intent: &mut RhaiPaymentIntent, worker_id: String, context_id: String, stripe_secret: String ) -> Result> { let form_data = prepare_payment_intent_data(intent); thread::spawn(move || { let rt = Runtime::new().expect("Failed to create runtime"); rt.block_on(async { let client = Client::new(); match make_stripe_request(&client, &stripe_secret, "payment_intents", &form_data).await { Ok(response) => { dispatch_response_script(&worker_id, &context_id, "new_create_payment_intent_response", &response).await; } Err(error) => { dispatch_error_script(&worker_id, &context_id, "new_create_payment_intent_error", &error).await; } } }); }); Ok("payment_intent_request_dispatched".to_string()) } ``` ### Product ```rust #[rhai_fn(name = "create_async", return_raw)] pub fn create_product_async( product: &mut RhaiProduct, worker_id: String, context_id: String, stripe_secret: String ) -> Result> { let form_data = prepare_product_data(product); thread::spawn(move || { let rt = Runtime::new().expect("Failed to create runtime"); rt.block_on(async { let client = Client::new(); match make_stripe_request(&client, &stripe_secret, "products", &form_data).await { Ok(response) => { dispatch_response_script(&worker_id, &context_id, "new_create_product_response", &response).await; } Err(error) => { dispatch_error_script(&worker_id, &context_id, "new_create_product_error", &error).await; } } }); }); Ok("product_request_dispatched".to_string()) } ``` ### Price ```rust #[rhai_fn(name = "create_async", return_raw)] pub fn create_price_async( price: &mut RhaiPrice, worker_id: String, context_id: String, stripe_secret: String ) -> Result> { let form_data = prepare_price_data(price); thread::spawn(move || { let rt = Runtime::new().expect("Failed to create runtime"); rt.block_on(async { let client = Client::new(); match make_stripe_request(&client, &stripe_secret, "prices", &form_data).await { Ok(response) => { dispatch_response_script(&worker_id, &context_id, "new_create_price_response", &response).await; } Err(error) => { dispatch_error_script(&worker_id, &context_id, "new_create_price_error", &error).await; } } }); }); Ok("price_request_dispatched".to_string()) } ``` ### Subscription ```rust #[rhai_fn(name = "create_async", return_raw)] pub fn create_subscription_async( subscription: &mut RhaiSubscription, worker_id: String, context_id: String, stripe_secret: String ) -> Result> { let form_data = prepare_subscription_data(subscription); thread::spawn(move || { let rt = Runtime::new().expect("Failed to create runtime"); rt.block_on(async { let client = Client::new(); match make_stripe_request(&client, &stripe_secret, "subscriptions", &form_data).await { Ok(response) => { dispatch_response_script(&worker_id, &context_id, "new_create_subscription_response", &response).await; } Err(error) => { dispatch_error_script(&worker_id, &context_id, "new_create_subscription_error", &error).await; } } }); }); Ok("subscription_request_dispatched".to_string()) } ``` ## Usage Example ### main.rhai ```rhai // No initialization needed - no global state! let payment_intent = new_payment_intent() .amount(2000) .currency("usd") .customer("cus_customer123"); // Pass all required parameters - no globals! let result = payment_intent.create_async( "worker-1", // worker_id "context-123", // context_id "sk_test_..." // stripe_secret ); print(`Request dispatched: ${result}`); // Script ends immediately, HTTP happens in background // Response will trigger new_create_payment_intent_response.rhai ``` ### flows/new_create_payment_intent_response.rhai ```rhai let payment_intent_id = parsed_data.id; let status = parsed_data.status; print(`✅ Payment Intent Created: ${payment_intent_id}`); print(`Status: ${status}`); // Continue flow if needed if status == "requires_payment_method" { print("Ready for frontend payment collection"); } ``` ### flows/new_create_payment_intent_error.rhai ```rhai let error_type = parsed_error.error.type; let error_message = parsed_error.error.message; print(`❌ Payment Intent Failed: ${error_type}`); print(`Message: ${error_message}`); // Handle error appropriately if error_type == "card_error" { print("Card was declined"); } ``` ## Benefits of This Architecture 1. **Zero Global State** - Everything is passed as parameters 2. **Zero Locking** - No shared state to lock 3. **True Non-Blocking** - Functions return immediately 4. **Thread Independence** - Each thread is completely self-contained 5. **Simple Testing** - Easy to test individual functions 6. **Clear Data Flow** - Parameters make dependencies explicit 7. **No Memory Leaks** - No persistent global state 8. **Horizontal Scaling** - No shared state to synchronize ## Migration from Current Code 1. **Remove all global state** (ASYNC_REGISTRY, etc.) 2. **Remove all Mutex/locking code** 3. **Add parameters to function signatures** 4. **Create dispatcher instances in threads** 5. **Update Rhai scripts to pass parameters** This architecture is much simpler, has no global state, no locking, and provides true non-blocking behavior while maintaining the event-driven flow pattern you want.