@@ -3,7 +3,8 @@ use std::sync::Arc;
33use crate :: history:: HistoryListener ;
44use crate :: retry_policy:: RetryPolicy ;
55use crate :: statement:: { prepared_statement:: PreparedStatement , query:: Query } ;
6- use crate :: transport:: execution_profile:: ExecutionProfileHandle ;
6+ use crate :: transport:: { execution_profile:: ExecutionProfileHandle , Node } ;
7+ use crate :: Session ;
78
89use super :: StatementConfig ;
910pub use super :: { Consistency , SerialConsistency } ;
@@ -144,6 +145,82 @@ impl Batch {
144145 pub fn get_execution_profile_handle ( & self ) -> Option < & ExecutionProfileHandle > {
145146 self . config . execution_profile_handle . as_ref ( )
146147 }
148+
149+ /// Associates the batch with a new execution profile that will have a load balancing policy
150+ /// that will enforce the use of the provided [`Node`] to the extent possible.
151+ ///
152+ /// This should typically be used in conjunction with [`Session::shard_for_statement`], where
153+ /// you would constitute a batch by assigning to the same batch all the statements that would be executed in
154+ /// the same shard.
155+ ///
156+ /// Since it is not guaranteed that subsequent calls to the load balancer would re-assign the statement
157+ /// to the same node, you should use this method to enforce the use of the original node that was envisioned by
158+ /// `shard_for_statement` for the batch:
159+ ///
160+ /// ```rust
161+ /// # use scylla::Session;
162+ /// # use std::error::Error;
163+ /// # async fn check_only_compiles(session: &Session) -> Result<(), Box<dyn Error>> {
164+ /// use scylla::{
165+ /// batch::Batch,
166+ /// frame::value::{SerializedValues, ValueList},
167+ /// };
168+ ///
169+ /// let prepared_statement = session
170+ /// .prepare("INSERT INTO ks.tab(a, b) VALUES(?, ?)")
171+ /// .await?;
172+ ///
173+ /// let serialized_values: SerializedValues = (1, 2).serialized()?.into_owned();
174+ /// let shard = session.shard_for_statement(&prepared_statement, &serialized_values)?;
175+ ///
176+ /// // Send that to a task that will handle statements targeted to the same shard
177+ ///
178+ /// // On that task:
179+ /// // Constitute a batch with all the statements that would be executed in the same shard
180+ ///
181+ /// let mut batch: Batch = Default::default();
182+ /// if let Some((node, _shard_idx)) = shard {
183+ /// batch.enforce_target_node(&node, &session);
184+ /// }
185+ /// let mut batch_values = Vec::new();
186+ ///
187+ /// // As the task handling statements targeted to this shard receives them,
188+ /// // it appends them to the batch
189+ /// batch.append_statement(prepared_statement);
190+ /// batch_values.push(serialized_values);
191+ ///
192+ /// // Run the batch
193+ /// session.batch(&batch, batch_values).await?;
194+ /// # Ok(())
195+ /// # }
196+ /// ```
197+ ///
198+ ///
199+ /// If the target node is not available anymore at the time of executing the statement, it will fallback to the
200+ /// original load balancing policy:
201+ /// - Either that currently set on the [`Batch`], if any
202+ /// - Or that of the [`Session`] if there isn't one on the `Batch`
203+ pub fn enforce_target_node < ' a > (
204+ & mut self ,
205+ node : & Arc < Node > ,
206+ base_execution_profile_from_session : & Session ,
207+ ) {
208+ let execution_profile_handle = self . get_execution_profile_handle ( ) . unwrap_or_else ( || {
209+ base_execution_profile_from_session. get_default_execution_profile_handle ( )
210+ } ) ;
211+ self . set_execution_profile_handle ( Some (
212+ execution_profile_handle
213+ . pointee_to_builder ( )
214+ . load_balancing_policy ( Arc :: new (
215+ crate :: load_balancing:: EnforceTargetNodePolicy :: new (
216+ node,
217+ execution_profile_handle. load_balancing_policy ( ) ,
218+ ) ,
219+ ) )
220+ . build ( )
221+ . into_handle ( ) ,
222+ ) )
223+ }
147224}
148225
149226impl Default for Batch {
0 commit comments