-module(distribute@connection_pool). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/distribute/connection_pool.gleam"). -export([destroy/1, new/2, with_connection/2, send_batch/3, send_batch_parallel/3, stats/1, stress_test/3]). -export_type([pool/0, connection/0, pool_stats/0, pool_error/0, batch_result/1]). -if(?OTP_RELEASE >= 27). -define(MODULEDOC(Str), -moduledoc(Str)). -define(DOC(Str), -doc(Str)). -else. -define(MODULEDOC(Str), -compile([])). -define(DOC(Str), -compile([])). -endif. -type pool() :: any(). -type connection() :: any(). -type pool_stats() :: {pool_stats, binary(), integer(), integer(), integer()}. -type pool_error() :: pool_exhausted | {connection_failed, binary()} | {invalid_config, binary()} | invalid_pool. -type batch_result(IOZ) :: {batch_result, integer(), integer(), integer(), list(IOZ)}. -file("src/distribute/connection_pool.gleam", 133). ?DOC(" Destroy the pool and release all resources\n"). -spec destroy(pool()) -> {ok, nil} | {error, pool_error()}. destroy(Pool) -> Result = connection_pool_ffi:destroy_pool(Pool), case connection_pool_ffi:is_ok(Result) of true -> {ok, nil}; false -> {error, invalid_pool} end. -file("src/distribute/connection_pool.gleam", 229). -spec unwrap_pool(gleam@dynamic:dynamic_()) -> pool(). unwrap_pool(Result) -> erlang:element(2, Result). -file("src/distribute/connection_pool.gleam", 78). ?DOC(" Create a new connection pool backed by ETS for atomic operations\n"). -spec new(binary(), integer()) -> {ok, pool()} | {error, pool_error()}. new(Target_node, Max_connections) -> case Max_connections > 0 of false -> {error, {invalid_config, <<"max_connections must be > 0"/utf8>>}}; true -> Result = connection_pool_ffi:new_pool(Target_node, Max_connections), case connection_pool_ffi:is_ok(Result) of true -> {ok, unwrap_pool(Result)}; false -> {error, {connection_failed, connection_pool_ffi:get_error(Result)}} end end. -file("src/distribute/connection_pool.gleam", 233). -spec unwrap_connection(gleam@dynamic:dynamic_()) -> connection(). unwrap_connection(Result) -> erlang:element(2, Result). -file("src/distribute/connection_pool.gleam", 92). ?DOC(" Execute a function with a pooled connection (automatically released)\n"). -spec with_connection(pool(), fun((connection()) -> IPD)) -> {ok, IPD} | {error, pool_error()}. with_connection(Pool, F) -> Conn_result = connection_pool_ffi:get_connection(Pool), case connection_pool_ffi:is_ok(Conn_result) of true -> Connection = unwrap_connection(Conn_result), Result = F(Connection), _ = connection_pool_ffi:release_connection(Connection), {ok, Result}; false -> Error_msg = connection_pool_ffi:get_error(Conn_result), case Error_msg of <<"pool_exhausted"/utf8>> -> {error, pool_exhausted}; _ -> {error, {connection_failed, Error_msg}} end end. -file("src/distribute/connection_pool.gleam", 146). ?DOC(" Send multiple messages in a batch using a single connection\n"). -spec send_batch( pool(), list({binary(), IPK}), fun((binary(), IPK) -> {ok, nil} | {error, IPM}) ) -> {ok, list({ok, nil} | {error, IPM})} | {error, pool_error()}. send_batch(Pool, Messages, Send_fn) -> with_connection( Pool, fun(_) -> gleam@list:map( Messages, fun(Message) -> {Name, Msg} = Message, Send_fn(Name, Msg) end ) end ). -file("src/distribute/connection_pool.gleam", 161). ?DOC(" Send multiple messages in parallel (up to pool size) with error aggregation\n"). -spec send_batch_parallel( pool(), list({binary(), IPU}), fun((binary(), IPU) -> {ok, nil} | {error, IPW}) ) -> {ok, batch_result(IPW)} | {error, pool_error()}. send_batch_parallel(Pool, Messages, Send_fn) -> case send_batch(Pool, Messages, Send_fn) of {ok, Results} -> Successes = begin _pipe = gleam@list:filter(Results, fun(R) -> case R of {ok, _} -> true; {error, _} -> false end end), erlang:length(_pipe) end, Failures = gleam@list:fold(Results, [], fun(Acc, R@1) -> case R@1 of {error, E} -> [E | Acc]; {ok, _} -> Acc end end), {ok, {batch_result, erlang:length(Messages), Successes, erlang:length(Failures), Failures}}; {error, E@1} -> {error, E@1} end. -file("src/distribute/connection_pool.gleam", 237). -spec unwrap_stats_map(gleam@dynamic:dynamic_()) -> gleam@dynamic:dynamic_(). unwrap_stats_map(Result) -> erlang:element(2, Result). -file("src/distribute/connection_pool.gleam", 244). -spec get_map_string(gleam@dynamic:dynamic_(), binary()) -> binary(). get_map_string(Map, Key) -> maps:get(erlang:binary_to_atom(Key), Map). -file("src/distribute/connection_pool.gleam", 248). -spec get_map_int(gleam@dynamic:dynamic_(), binary()) -> integer(). get_map_int(Map, Key) -> maps:get(erlang:binary_to_atom(Key), Map). -file("src/distribute/connection_pool.gleam", 116). ?DOC(" Get pool statistics (atomic read from ETS)\n"). -spec stats(pool()) -> {ok, pool_stats()} | {error, pool_error()}. stats(Pool) -> Result = connection_pool_ffi:pool_stats(Pool), case connection_pool_ffi:is_ok(Result) of true -> Map = unwrap_stats_map(Result), {ok, {pool_stats, get_map_string(Map, <<"target_node"/utf8>>), get_map_int(Map, <<"max_connections"/utf8>>), get_map_int(Map, <<"active_connections"/utf8>>), get_map_int(Map, <<"available_connections"/utf8>>)}}; false -> {error, invalid_pool} end. -file("src/distribute/connection_pool.gleam", 200). ?DOC( " Run a concurrency stress-test against a pool. This spawns multiple worker processes\n" " each performing `ops` get/release cycles using the pool; returns final pool stats.\n" ). -spec stress_test(pool(), integer(), integer()) -> {ok, pool_stats()} | {error, pool_error()}. stress_test(Pool, Ops_per_worker, Workers) -> Res = connection_pool_ffi:stress_test_pool(Pool, Ops_per_worker, Workers), case connection_pool_ffi:is_ok(Res) of true -> Map = unwrap_stats_map(Res), {ok, {pool_stats, get_map_string(Map, <<"target_node"/utf8>>), get_map_int(Map, <<"max_connections"/utf8>>), get_map_int(Map, <<"active_connections"/utf8>>), get_map_int(Map, <<"available_connections"/utf8>>)}}; false -> {error, invalid_pool} end.