Skip to main content

async_concurrency/
async_concurrency.rs

1//! 演示 torm 的异步并发查询能力。
2//!
3//! 运行方式:`cargo run --example async_concurrency`
4//!
5//! 本示例展示:
6//! 1. `Database`(基于 rusqlite)在多任务下并发执行查询与写入。
7//! 2. `AsyncStorageEngine`(纯 Rust 内存引擎的异步封装)在并发下安全读写。
8//! 3. 通过 `tokio::spawn` 并发执行多个异步任务。
9
10use std::collections::HashMap;
11use std::sync::Arc;
12use torm::{
13    AsyncStorageEngine, Database, Model, Query, TableSchema,
14    StorageColumnDefinition as ColumnDefinition, StorageColumnType as ColumnType, WhereClause,
15};
16
17/// 商品模型:由 `#[derive(Model)]` 自动生成建表 schema 与字段映射。
18#[derive(Debug, Clone, Model)]
19#[model(table_name = "products")]
20pub struct Product {
21    pub id: i64,
22    pub name: String,
23    pub price: i64,
24}
25
26#[tokio::main]
27async fn main() -> Result<(), Box<dyn std::error::Error>> {
28    println!("🚀 TORM - Async Concurrency Demo\n");
29
30    // 1. 使用 Database(SQLite 后端)做并发查询
31    println!("1) Database (SQLite) concurrent queries");
32    println!("========================================");
33    database_concurrency().await?;
34    println!();
35
36    // 2. 使用 AsyncStorageEngine(纯 Rust 内存引擎)做并发读写
37    println!("2) AsyncStorageEngine concurrent read/write");
38    println!("=============================================");
39    async_storage_concurrency().await?;
40    println!();
41
42    println!("🎉 All concurrent demos completed successfully!");
43    Ok(())
44}
45
46/// 使用 `Database`(SQLite 后端)演示并发查询。
47async fn database_concurrency() -> Result<(), Box<dyn std::error::Error>> {
48    let db = Arc::new(Database::sqlite(":memory:").await?);
49
50    // 依据 Product 模型自动建表。
51    db.auto_migrate::<Product>().await?;
52
53    // 预置数据:通过模型 create,零 SqlValue。
54    for i in 0..100 {
55        let mut p = Product {
56            id: 0,
57            name: format!("product_{}", i),
58            price: i,
59        };
60        db.create(&mut p).await?;
61    }
62
63    // 并发执行多个查询任务(按页读取,映射回模型)。
64    let mut handles = Vec::new();
65    for offset in (0..100).step_by(25) {
66        let db = Arc::clone(&db);
67        handles.push(tokio::spawn(async move {
68            let products: Vec<Product> = Query::new("products")
69                .order_by_asc("id")
70                .limit(25)
71                .offset(offset)
72                .query(&db)
73                .models::<Product>()
74                .await?;
75            Ok::<usize, torm::db::database::DbError>(products.len())
76        }));
77    }
78
79    let mut total = 0usize;
80    for h in handles {
81        total += h.await??;
82    }
83    println!("  4 concurrent queries fetched {} rows in total.", total);
84
85    // 使用 Query builder 并发查询(值类型自动转换,无需手写 SqlValue::I32)
86    let mut handles = Vec::new();
87    for min_price in [10i64, 20, 30, 40] {
88        let db = Arc::clone(&db);
89        handles.push(tokio::spawn(async move {
90            Query::new("products")
91                .where_gt("price", min_price)
92                .query(&db)
93                .count()
94                .await
95        }));
96    }
97    for (i, h) in handles.into_iter().enumerate() {
98        let count = h.await??;
99        let n = count
100            .rows
101            .first()
102            .and_then(|r| r.get("COUNT(*)").or_else(|| r.get("count")))
103            .and_then(|v| v.as_i64())
104            .unwrap_or(0);
105        println!("  price > {}  -> {} products", [10, 20, 30, 40][i], n);
106    }
107
108    Ok(())
109}
110
111/// 使用 `AsyncStorageEngine`(纯 Rust 内存引擎)演示并发读写。
112async fn async_storage_concurrency() -> Result<(), Box<dyn std::error::Error>> {
113    let engine = Arc::new(AsyncStorageEngine::new());
114
115    let schema = TableSchema {
116        name: "scores".to_string(),
117        columns: vec![
118            ColumnDefinition {
119                name: "id".to_string(),
120                column_type: ColumnType::Integer,
121                nullable: false,
122                default: None,
123                unique: true,
124            },
125            ColumnDefinition {
126                name: "player".to_string(),
127                column_type: ColumnType::Text,
128                nullable: false,
129                default: None,
130                unique: false,
131            },
132            ColumnDefinition {
133                name: "score".to_string(),
134                column_type: ColumnType::Integer,
135                nullable: true,
136                default: None,
137                unique: false,
138            },
139        ],
140        primary_key: Some("id".to_string()),
141    };
142    engine.create_table(schema).await?;
143
144    // 并发插入 50 名玩家(底层引擎接受 SqlValue,使用 .into() 由原生值自动转换)
145    let mut handles = Vec::new();
146    for i in 0..50u32 {
147        let engine = Arc::clone(&engine);
148        handles.push(tokio::spawn(async move {
149            engine
150                .insert(
151                    "scores",
152                    vec![
153                        (i as i32).into(),
154                        format!("player_{}", i).into(),
155                        ((i * 3 % 100) as i32).into(),
156                    ],
157                )
158                .await
159        }));
160    }
161    for h in handles {
162        h.await??;
163    }
164
165    // 并发读取 + 并发更新
166    let mut handles = Vec::new();
167    for i in 0..50u32 {
168        let engine = Arc::clone(&engine);
169        handles.push(tokio::spawn(async move {
170            // 每个任务并发读一次总量,并对自己的行做更新
171            let result = engine.select("scores", None, None, None, None).await?;
172            let mut updates = HashMap::new();
173            updates.insert("score".to_string(), (100 + i as i32).into());
174            let affected = engine
175                .update(
176                    "scores",
177                    updates,
178                    Some(WhereClause::Eq("id".to_string(), (i as i32).into())),
179                )
180                .await?;
181            Ok::<(usize, u64), torm::db::storage::StorageError>((result.rows.len(), affected))
182        }));
183    }
184
185    let mut total_reads = 0usize;
186    let mut total_affected = 0u64;
187    for h in handles {
188        let (read, affected) = h.await??;
189        total_reads += read;
190        total_affected += affected;
191    }
192    println!(
193        "  50 concurrent tasks: {} total reads, {} rows updated.",
194        total_reads, total_affected
195    );
196
197    // 验证最终数据
198    let result = engine.select("scores", None, None, None, None).await?;
199    let total_score: i32 = result
200        .rows
201        .iter()
202        .filter_map(|r| r.get("score").and_then(|v| v.as_i32()))
203        .sum();
204    println!(
205        "  Final: {} players, total score = {}",
206        result.rows.len(),
207        total_score
208    );
209
210    Ok(())
211}