opendal_core/raw/oio/write/
block_write.rs1use std::sync::Arc;
19
20use futures::Future;
21use futures::FutureExt;
22use futures::TryFutureExt;
23use futures::select;
24use uuid::Uuid;
25
26use crate::raw::*;
27use crate::*;
28
29pub trait BlockWrite: Send + Sync + Unpin + 'static {
61 fn write_once(
68 &self,
69 size: u64,
70 body: Buffer,
71 ) -> impl Future<Output = Result<Metadata>> + MaybeSend;
72
73 fn write_block(
80 &self,
81 block_id: Uuid,
82 size: u64,
83 body: Buffer,
84 ) -> impl Future<Output = Result<()>> + MaybeSend;
85
86 fn complete_block(
89 &self,
90 block_ids: Vec<Uuid>,
91 ) -> impl Future<Output = Result<Metadata>> + MaybeSend;
92
93 fn abort_block(&self, block_ids: Vec<Uuid>) -> impl Future<Output = Result<()>> + MaybeSend;
95}
96
97struct WriteInput<W: BlockWrite> {
98 w: Arc<W>,
99 executor: Executor,
100 block_id: Uuid,
101 bytes: Buffer,
102}
103
104pub struct BlockWriter<W: BlockWrite> {
107 w: Arc<W>,
108 executor: Executor,
109
110 started: bool,
111 block_ids: Vec<Uuid>,
112 cache: Option<Buffer>,
113 tasks: ConcurrentTasks<WriteInput<W>, Uuid>,
114}
115
116impl<W: BlockWrite> BlockWriter<W> {
117 pub fn new(executor: impl Into<Executor>, inner: W, concurrent: usize) -> Self {
119 let executor = executor.into();
120
121 Self {
122 w: Arc::new(inner),
123 executor: executor.clone(),
124 started: false,
125 block_ids: Vec::new(),
126 cache: None,
127
128 tasks: ConcurrentTasks::new(executor, concurrent, 8192, |input| {
129 Box::pin(async move {
130 let fut = input
131 .w
132 .write_block(
133 input.block_id,
134 input.bytes.len() as u64,
135 input.bytes.clone(),
136 )
137 .map_ok(|_| input.block_id);
138 match input.executor.timeout() {
139 None => {
140 let result = fut.await;
141 (input, result)
142 }
143 Some(timeout) => {
144 let result = select! {
145 result = fut.fuse() => {
146 result
147 }
148 _ = timeout.fuse() => {
149 Err(Error::new(
150 ErrorKind::Unexpected, "write block timeout")
151 .with_context("block_id", input.block_id.to_string())
152 .set_temporary())
153 }
154 };
155 (input, result)
156 }
157 }
158 })
159 }),
160 }
161 }
162
163 fn fill_cache(&mut self, bs: Buffer) -> usize {
164 let size = bs.len();
165 assert!(self.cache.is_none());
166 self.cache = Some(bs);
167 size
168 }
169}
170
171impl<W> oio::Write for BlockWriter<W>
172where
173 W: BlockWrite,
174{
175 async fn write(&mut self, bs: Buffer) -> Result<()> {
176 if !self.started && self.cache.is_none() {
177 self.fill_cache(bs);
178 return Ok(());
179 }
180
181 self.started = true;
183
184 let bytes = self.cache.clone().expect("pending write must exist");
185 self.tasks
186 .execute(WriteInput {
187 w: self.w.clone(),
188 executor: self.executor.clone(),
189 block_id: Uuid::new_v4(),
190 bytes,
191 })
192 .await?;
193 self.cache = None;
194 self.fill_cache(bs);
195 Ok(())
196 }
197
198 async fn close(&mut self) -> Result<Metadata> {
199 if !self.started {
200 let (size, body) = match self.cache.clone() {
201 Some(cache) => (cache.len(), cache),
202 None => (0, Buffer::new()),
203 };
204
205 let meta = self.w.write_once(size as u64, body).await?;
206 self.cache = None;
207 return Ok(meta);
208 }
209
210 if let Some(cache) = self.cache.clone() {
211 self.tasks
212 .execute(WriteInput {
213 w: self.w.clone(),
214 executor: self.executor.clone(),
215 block_id: Uuid::new_v4(),
216 bytes: cache,
217 })
218 .await?;
219 self.cache = None;
220 }
221
222 loop {
223 let Some(result) = self.tasks.next().await.transpose()? else {
224 break;
225 };
226 self.block_ids.push(result);
227 }
228
229 let block_ids = self.block_ids.clone();
230 self.w.complete_block(block_ids).await
231 }
232
233 async fn abort(&mut self) -> Result<()> {
234 if !self.started {
235 return Ok(());
236 }
237
238 self.tasks.clear();
239 self.cache = None;
240 self.w.abort_block(self.block_ids.clone()).await?;
241 Ok(())
242 }
243}
244
245#[cfg(test)]
246mod tests {
247 use std::collections::HashMap;
248 use std::sync::Mutex;
249
250 use pretty_assertions::assert_eq;
251 use rand::{Rng, RngExt, rng};
252 use tokio::time::sleep;
253
254 use super::*;
255 use crate::raw::oio::Write;
256
257 struct TestWrite {
258 length: u64,
259 bytes: HashMap<Uuid, Buffer>,
260 content: Option<Buffer>,
261 }
262
263 impl TestWrite {
264 pub fn new() -> Arc<Mutex<Self>> {
265 let v = Self {
266 length: 0,
267 bytes: HashMap::new(),
268 content: None,
269 };
270
271 Arc::new(Mutex::new(v))
272 }
273 }
274
275 impl BlockWrite for Arc<Mutex<TestWrite>> {
276 async fn write_once(&self, size: u64, body: Buffer) -> Result<Metadata> {
277 sleep(Duration::from_nanos(50)).await;
278
279 if rng().random_bool(1.0 / 10.0) {
280 return Err(
281 Error::new(ErrorKind::Unexpected, "I'm a crazy monkey!").set_temporary()
282 );
283 }
284
285 let mut this = self.lock().unwrap();
286 this.length = size;
287 this.content = Some(body);
288 Ok(MetadataBuilder::unknown().build())
289 }
290
291 async fn write_block(&self, block_id: Uuid, size: u64, body: Buffer) -> Result<()> {
292 sleep(Duration::from_millis(50)).await;
294
295 if rng().random_bool(1.0 / 10.0) {
297 return Err(
298 Error::new(ErrorKind::Unexpected, "I'm a crazy monkey!").set_temporary()
299 );
300 }
301
302 let mut this = self.lock().unwrap();
303 this.length += size;
304 this.bytes.insert(block_id, body);
305
306 Ok(())
307 }
308
309 async fn complete_block(&self, block_ids: Vec<Uuid>) -> Result<Metadata> {
310 let mut this = self.lock().unwrap();
311 let mut bs = Vec::new();
312 for id in block_ids {
313 bs.push(this.bytes[&id].clone());
314 }
315 this.content = Some(bs.into_iter().flatten().collect());
316
317 Ok(MetadataBuilder::unknown().build())
318 }
319
320 async fn abort_block(&self, _: Vec<Uuid>) -> Result<()> {
321 Ok(())
322 }
323 }
324
325 #[tokio::test]
326 async fn test_block_writer_with_concurrent_errors() {
327 let mut rng = rng();
328
329 let mut w = BlockWriter::new(Executor::default(), TestWrite::new(), 8);
330 let mut total_size = 0u64;
331 let mut expected_content = Vec::new();
332
333 for _ in 0..1000 {
334 let size = rng.random_range(1..1024);
335 total_size += size as u64;
336
337 let mut bs = vec![0; size];
338 rng.fill_bytes(&mut bs);
339
340 expected_content.extend_from_slice(&bs);
341
342 loop {
343 match w.write(bs.clone().into()).await {
344 Ok(_) => break,
345 Err(_) => continue,
346 }
347 }
348 }
349
350 loop {
351 match w.close().await {
352 Ok(_) => break,
353 Err(_) => continue,
354 }
355 }
356
357 let inner = w.w.lock().unwrap();
358
359 assert_eq!(total_size, inner.length, "length must be the same");
360 assert!(inner.content.is_some());
361 assert_eq!(
362 expected_content,
363 inner.content.clone().unwrap().to_bytes(),
364 "content must be the same"
365 );
366 }
367
368 #[tokio::test]
369 async fn test_block_writer_with_retry_when_write_once_error() {
370 let mut rng = rng();
371
372 for _ in 1..100 {
373 let mut w = BlockWriter::new(Executor::default(), TestWrite::new(), 8);
374
375 let size = rng.random_range(1..1024);
376 let mut bs = vec![0; size];
377 rng.fill_bytes(&mut bs);
378
379 loop {
380 match w.write(bs.clone().into()).await {
381 Ok(_) => break,
382 Err(_) => continue,
383 }
384 }
385
386 loop {
387 match w.close().await {
388 Ok(_) => break,
389 Err(_) => continue,
390 }
391 }
392
393 let inner = w.w.lock().unwrap();
394 assert_eq!(size as u64, inner.length, "length must be the same");
395 assert!(inner.content.is_some());
396 assert_eq!(
397 bs,
398 inner.content.clone().unwrap().to_bytes(),
399 "content must be the same"
400 );
401 }
402 }
403}