Re: [Libguestfs] [libnbd PATCH v4 11/11] rust: Add some examples

2023-08-03 Thread Tage Johansson


On 8/2/2023 6:53 PM, Richard W.M. Jones wrote:

On Wed, Aug 02, 2023 at 12:40:56PM +, Tage Johansson wrote:

This patch adds a few examples in rust/examples/. The examples are
compiled and run as part of the test suite.
---
  rust/Makefile.am   |   3 +
  rust/examples/concurrent-read-write.rs | 135 +
  rust/examples/connect-command.rs   |  39 +++
  rust/examples/fetch-first-sector.rs|  38 +++
  rust/examples/get-size.rs  |  29 ++
  rust/run-tests.sh  |   7 ++
  6 files changed, 251 insertions(+)
  create mode 100644 rust/examples/concurrent-read-write.rs
  create mode 100644 rust/examples/connect-command.rs
  create mode 100644 rust/examples/fetch-first-sector.rs
  create mode 100644 rust/examples/get-size.rs

diff --git a/rust/Makefile.am b/rust/Makefile.am
index b954b22..d75163d 100644
--- a/rust/Makefile.am
+++ b/rust/Makefile.am
@@ -32,6 +32,9 @@ source_files = \
src/types.rs \
src/utils.rs \
src/async_handle.rs \
+   examples/connect-command.rs \
+   examples/get-size.rs \
+   examples/fetch-first-sector.rs \

This doesn't list all the source files, it is missing
examples/concurrent-read-write.rs.

If you split out examples/connect-command.rs, examples/get-size.rs and
examples/fetch-first-sector.rs into a separate patch (since those
don't depend on asynch), and moved that patch earlier in the sequence,
then it could go upstream earlier.



Yes, I have done that.


Best regards,

Tage



libnbd-sys/Cargo.toml \
libnbd-sys/build.rs \
$(NULL)
diff --git a/rust/examples/concurrent-read-write.rs 
b/rust/examples/concurrent-read-write.rs
new file mode 100644
index 000..a1c3e8a
--- /dev/null
+++ b/rust/examples/concurrent-read-write.rs
@@ -0,0 +1,135 @@
+//! Example usage with nbdkit:
+//!
+//! nbdkit -U - memory 100M \
+//!   --run 'cargo run --example concurrent-read-write -- $unixsocket'
+//!
+//! This will read and write randomly over the first megabyte of the
+//! plugin using multi-conn, multiple threads and multiple requests in
+//! flight on each thread.
+
+#![deny(warnings)]
+use rand::prelude::*;
+use std::env;
+use std::path::PathBuf;
+use std::sync::Arc;
+use tokio::task::JoinSet;
+
+/// Number of simultaneous connections to the NBD server.
+///
+/// Note that some servers only support a limited number of
+/// simultaneous connections, and/or have a configurable thread pool
+/// internally, and if you exceed those limits then something will break.
+const NR_MULTI_CONN: usize = 8;
+
+/// Number of commands that can be "in flight" at the same time on each
+/// connection.  (Therefore the total number of requests in flight may
+/// be up to NR_MULTI_CONN * MAX_IN_FLIGHT).
+const MAX_IN_FLIGHT: usize = 16;
+
+/// The size of large reads and writes, must be > 512.
+const BUFFER_SIZE: usize = 1024;
+
+/// Number of commands we issue (per [task][tokio::task]).
+const NR_CYCLES: usize = 32;
+
+/// Statistics gathered during the run.
+#[derive(Debug, Default)]
+struct Stats {
+/// The total number of requests made.
+requests: usize,
+}
+
+#[tokio::main]
+async fn main() -> anyhow::Result<()> {
+let args = env::args_os().collect::>();
+if args.len() != 2 {
+anyhow::bail!("Usage: {:?} socket", args[0]);
+}
+let socket = [1];
+
+// We begin by making a connection to the server to get the export size
+// and ensure that it supports multiple connections and is writable.
+let nbd = libnbd::Handle::new()?;
+nbd.connect_unix()?;
+let export_size = nbd.get_size()?;
+anyhow::ensure!(
+(BUFFER_SIZE as u64) < export_size,
+"export is {export_size}B, must be larger than {BUFFER_SIZE}B"
+);
+anyhow::ensure!(
+!nbd.is_read_only()?,
+"error: this NBD export is read-only"
+);
+anyhow::ensure!(
+nbd.can_multi_conn()?,
+"error: this NBD export does not support multi-conn"
+);
+drop(nbd); // Close the connection.
+
+// Start the worker tasks, one per connection.
+let mut tasks = JoinSet::new();
+for i in 0..NR_MULTI_CONN {
+tasks.spawn(run_thread(i, socket.clone().into(), export_size));
+}
+
+// Wait for the tasks to complete.
+let mut stats = Stats::default();
+while !tasks.is_empty() {
+let this_stats = tasks.join_next().await.unwrap().unwrap()?;
+stats.requests += this_stats.requests;
+}
+
+// Make sure the number of requests that were required matches what
+// we expect.
+assert_eq!(stats.requests, NR_MULTI_CONN * NR_CYCLES);
+
+Ok(())
+}
+
+async fn run_thread(
+task_idx: usize,
+socket: PathBuf,
+export_size: u64,
+) -> anyhow::Result {
+// Start a new connection to the server.
+// We shall spawn many commands concurrently on different tasks and those
+// futures must be `'static`, hence we wrap the handle in an [Arc].
+let 

Re: [Libguestfs] [libnbd PATCH v4 11/11] rust: Add some examples

2023-08-02 Thread Richard W.M. Jones
On Wed, Aug 02, 2023 at 12:40:56PM +, Tage Johansson wrote:
> This patch adds a few examples in rust/examples/. The examples are
> compiled and run as part of the test suite.
> ---
>  rust/Makefile.am   |   3 +
>  rust/examples/concurrent-read-write.rs | 135 +
>  rust/examples/connect-command.rs   |  39 +++
>  rust/examples/fetch-first-sector.rs|  38 +++
>  rust/examples/get-size.rs  |  29 ++
>  rust/run-tests.sh  |   7 ++
>  6 files changed, 251 insertions(+)
>  create mode 100644 rust/examples/concurrent-read-write.rs
>  create mode 100644 rust/examples/connect-command.rs
>  create mode 100644 rust/examples/fetch-first-sector.rs
>  create mode 100644 rust/examples/get-size.rs
> 
> diff --git a/rust/Makefile.am b/rust/Makefile.am
> index b954b22..d75163d 100644
> --- a/rust/Makefile.am
> +++ b/rust/Makefile.am
> @@ -32,6 +32,9 @@ source_files = \
>   src/types.rs \
>   src/utils.rs \
>   src/async_handle.rs \
> + examples/connect-command.rs \
> + examples/get-size.rs \
> + examples/fetch-first-sector.rs \

This doesn't list all the source files, it is missing
examples/concurrent-read-write.rs.

If you split out examples/connect-command.rs, examples/get-size.rs and
examples/fetch-first-sector.rs into a separate patch (since those
don't depend on asynch), and moved that patch earlier in the sequence,
then it could go upstream earlier.

>   libnbd-sys/Cargo.toml \
>   libnbd-sys/build.rs \
>   $(NULL)
> diff --git a/rust/examples/concurrent-read-write.rs 
> b/rust/examples/concurrent-read-write.rs
> new file mode 100644
> index 000..a1c3e8a
> --- /dev/null
> +++ b/rust/examples/concurrent-read-write.rs
> @@ -0,0 +1,135 @@
> +//! Example usage with nbdkit:
> +//!
> +//! nbdkit -U - memory 100M \
> +//!   --run 'cargo run --example concurrent-read-write -- $unixsocket'
> +//!
> +//! This will read and write randomly over the first megabyte of the
> +//! plugin using multi-conn, multiple threads and multiple requests in
> +//! flight on each thread.
> +
> +#![deny(warnings)]
> +use rand::prelude::*;
> +use std::env;
> +use std::path::PathBuf;
> +use std::sync::Arc;
> +use tokio::task::JoinSet;
> +
> +/// Number of simultaneous connections to the NBD server.
> +///
> +/// Note that some servers only support a limited number of
> +/// simultaneous connections, and/or have a configurable thread pool
> +/// internally, and if you exceed those limits then something will break.
> +const NR_MULTI_CONN: usize = 8;
> +
> +/// Number of commands that can be "in flight" at the same time on each
> +/// connection.  (Therefore the total number of requests in flight may
> +/// be up to NR_MULTI_CONN * MAX_IN_FLIGHT).
> +const MAX_IN_FLIGHT: usize = 16;
> +
> +/// The size of large reads and writes, must be > 512.
> +const BUFFER_SIZE: usize = 1024;
> +
> +/// Number of commands we issue (per [task][tokio::task]).
> +const NR_CYCLES: usize = 32;
> +
> +/// Statistics gathered during the run.
> +#[derive(Debug, Default)]
> +struct Stats {
> +/// The total number of requests made.
> +requests: usize,
> +}
> +
> +#[tokio::main]
> +async fn main() -> anyhow::Result<()> {
> +let args = env::args_os().collect::>();
> +if args.len() != 2 {
> +anyhow::bail!("Usage: {:?} socket", args[0]);
> +}
> +let socket = [1];
> +
> +// We begin by making a connection to the server to get the export size
> +// and ensure that it supports multiple connections and is writable.
> +let nbd = libnbd::Handle::new()?;
> +nbd.connect_unix()?;
> +let export_size = nbd.get_size()?;
> +anyhow::ensure!(
> +(BUFFER_SIZE as u64) < export_size,
> +"export is {export_size}B, must be larger than {BUFFER_SIZE}B"
> +);
> +anyhow::ensure!(
> +!nbd.is_read_only()?,
> +"error: this NBD export is read-only"
> +);
> +anyhow::ensure!(
> +nbd.can_multi_conn()?,
> +"error: this NBD export does not support multi-conn"
> +);
> +drop(nbd); // Close the connection.
> +
> +// Start the worker tasks, one per connection.
> +let mut tasks = JoinSet::new();
> +for i in 0..NR_MULTI_CONN {
> +tasks.spawn(run_thread(i, socket.clone().into(), export_size));
> +}
> +
> +// Wait for the tasks to complete.
> +let mut stats = Stats::default();
> +while !tasks.is_empty() {
> +let this_stats = tasks.join_next().await.unwrap().unwrap()?;
> +stats.requests += this_stats.requests;
> +}
> +
> +// Make sure the number of requests that were required matches what
> +// we expect.
> +assert_eq!(stats.requests, NR_MULTI_CONN * NR_CYCLES);
> +
> +Ok(())
> +}
> +
> +async fn run_thread(
> +task_idx: usize,
> +socket: PathBuf,
> +export_size: u64,
> +) -> anyhow::Result {
> +// Start a new connection to the server.
> +// We shall spawn many 

[Libguestfs] [libnbd PATCH v4 11/11] rust: Add some examples

2023-08-02 Thread Tage Johansson
This patch adds a few examples in rust/examples/. The examples are
compiled and run as part of the test suite.
---
 rust/Makefile.am   |   3 +
 rust/examples/concurrent-read-write.rs | 135 +
 rust/examples/connect-command.rs   |  39 +++
 rust/examples/fetch-first-sector.rs|  38 +++
 rust/examples/get-size.rs  |  29 ++
 rust/run-tests.sh  |   7 ++
 6 files changed, 251 insertions(+)
 create mode 100644 rust/examples/concurrent-read-write.rs
 create mode 100644 rust/examples/connect-command.rs
 create mode 100644 rust/examples/fetch-first-sector.rs
 create mode 100644 rust/examples/get-size.rs

diff --git a/rust/Makefile.am b/rust/Makefile.am
index b954b22..d75163d 100644
--- a/rust/Makefile.am
+++ b/rust/Makefile.am
@@ -32,6 +32,9 @@ source_files = \
src/types.rs \
src/utils.rs \
src/async_handle.rs \
+   examples/connect-command.rs \
+   examples/get-size.rs \
+   examples/fetch-first-sector.rs \
libnbd-sys/Cargo.toml \
libnbd-sys/build.rs \
$(NULL)
diff --git a/rust/examples/concurrent-read-write.rs 
b/rust/examples/concurrent-read-write.rs
new file mode 100644
index 000..a1c3e8a
--- /dev/null
+++ b/rust/examples/concurrent-read-write.rs
@@ -0,0 +1,135 @@
+//! Example usage with nbdkit:
+//!
+//! nbdkit -U - memory 100M \
+//!   --run 'cargo run --example concurrent-read-write -- $unixsocket'
+//!
+//! This will read and write randomly over the first megabyte of the
+//! plugin using multi-conn, multiple threads and multiple requests in
+//! flight on each thread.
+
+#![deny(warnings)]
+use rand::prelude::*;
+use std::env;
+use std::path::PathBuf;
+use std::sync::Arc;
+use tokio::task::JoinSet;
+
+/// Number of simultaneous connections to the NBD server.
+///
+/// Note that some servers only support a limited number of
+/// simultaneous connections, and/or have a configurable thread pool
+/// internally, and if you exceed those limits then something will break.
+const NR_MULTI_CONN: usize = 8;
+
+/// Number of commands that can be "in flight" at the same time on each
+/// connection.  (Therefore the total number of requests in flight may
+/// be up to NR_MULTI_CONN * MAX_IN_FLIGHT).
+const MAX_IN_FLIGHT: usize = 16;
+
+/// The size of large reads and writes, must be > 512.
+const BUFFER_SIZE: usize = 1024;
+
+/// Number of commands we issue (per [task][tokio::task]).
+const NR_CYCLES: usize = 32;
+
+/// Statistics gathered during the run.
+#[derive(Debug, Default)]
+struct Stats {
+/// The total number of requests made.
+requests: usize,
+}
+
+#[tokio::main]
+async fn main() -> anyhow::Result<()> {
+let args = env::args_os().collect::>();
+if args.len() != 2 {
+anyhow::bail!("Usage: {:?} socket", args[0]);
+}
+let socket = [1];
+
+// We begin by making a connection to the server to get the export size
+// and ensure that it supports multiple connections and is writable.
+let nbd = libnbd::Handle::new()?;
+nbd.connect_unix()?;
+let export_size = nbd.get_size()?;
+anyhow::ensure!(
+(BUFFER_SIZE as u64) < export_size,
+"export is {export_size}B, must be larger than {BUFFER_SIZE}B"
+);
+anyhow::ensure!(
+!nbd.is_read_only()?,
+"error: this NBD export is read-only"
+);
+anyhow::ensure!(
+nbd.can_multi_conn()?,
+"error: this NBD export does not support multi-conn"
+);
+drop(nbd); // Close the connection.
+
+// Start the worker tasks, one per connection.
+let mut tasks = JoinSet::new();
+for i in 0..NR_MULTI_CONN {
+tasks.spawn(run_thread(i, socket.clone().into(), export_size));
+}
+
+// Wait for the tasks to complete.
+let mut stats = Stats::default();
+while !tasks.is_empty() {
+let this_stats = tasks.join_next().await.unwrap().unwrap()?;
+stats.requests += this_stats.requests;
+}
+
+// Make sure the number of requests that were required matches what
+// we expect.
+assert_eq!(stats.requests, NR_MULTI_CONN * NR_CYCLES);
+
+Ok(())
+}
+
+async fn run_thread(
+task_idx: usize,
+socket: PathBuf,
+export_size: u64,
+) -> anyhow::Result {
+// Start a new connection to the server.
+// We shall spawn many commands concurrently on different tasks and those
+// futures must be `'static`, hence we wrap the handle in an [Arc].
+let nbd = Arc::new(libnbd::AsyncHandle::new()?);
+nbd.connect_unix(socket).await?;
+
+let mut rng = SmallRng::seed_from_u64(44 as u64);
+
+// Issue commands.
+let mut stats = Stats::default();
+let mut join_set = JoinSet::new();
+//tokio::time::sleep(std::time::Duration::from_secs(1)).await;
+while stats.requests < NR_CYCLES || !join_set.is_empty() {
+while stats.requests < NR_CYCLES && join_set.len() < MAX_IN_FLIGHT {
+// If we want to issue another request, do