1#![doc = include_str!("../README.md")]
19#![cfg_attr(docsrs, feature(doc_cfg))]
20#![cfg_attr(docsrs, doc(auto_cfg))]
21#![deny(missing_docs)]
22use std::sync::Arc;
23use std::sync::Mutex;
24
25use opendal_core::raw::*;
26use opendal_core::*;
27use rand::prelude::*;
28use rand::rngs::StdRng;
29
30#[derive(Clone, Debug)]
62pub struct ChaosLayer {
63 rng: Arc<Mutex<StdRng>>,
64 error_ratio: f64,
65}
66
67impl ChaosLayer {
68 pub fn new(error_ratio: f64) -> Self {
74 assert!(
75 (0.0..=1.0).contains(&error_ratio),
76 "error_ratio must between 0.0 and 1.0"
77 );
78 Self {
79 rng: Arc::new(Mutex::new(StdRng::from_rng(&mut rand::rng()))),
80 error_ratio,
81 }
82 }
83}
84
85impl Layer for ChaosLayer {
86 fn apply_service(&self, inner: Servicer) -> Servicer {
87 Arc::new(self.layer(inner))
88 }
89}
90
91impl ChaosLayer {
92 fn layer(&self, inner: Servicer) -> ChaosService {
93 ChaosService {
94 inner,
95 rng: self.rng.clone(),
96 error_ratio: self.error_ratio,
97 }
98 }
99}
100
101#[doc(hidden)]
102#[derive(Debug)]
103pub struct ChaosService {
104 inner: Servicer,
105 rng: Arc<Mutex<StdRng>>,
106 error_ratio: f64,
107}
108
109impl Service for ChaosService {
110 type Reader = ChaosReader<oio::Reader>;
111 type Writer = oio::Writer;
112 type Lister = oio::Lister;
113 type Deleter = oio::Deleter;
114 type Copier = oio::Copier;
115
116 fn info(&self) -> ServiceInfo {
117 self.inner.info()
118 }
119
120 fn capability(&self) -> Capability {
121 self.inner.capability()
122 }
123
124 async fn create_dir(
125 &self,
126 ctx: &OperationContext,
127 path: &str,
128 args: OpCreateDir,
129 ) -> Result<RpCreateDir> {
130 self.inner.create_dir(ctx, path, args).await
131 }
132
133 async fn stat(&self, ctx: &OperationContext, path: &str, args: OpStat) -> Result<RpStat> {
134 self.inner.stat(ctx, path, args).await
135 }
136
137 fn read(&self, ctx: &OperationContext, path: &str, args: OpRead) -> Result<Self::Reader> {
138 self.inner
139 .read(ctx, path, args)
140 .map(|r| ChaosReader::new(r, Arc::clone(&self.rng), self.error_ratio))
141 }
142
143 fn write(&self, ctx: &OperationContext, path: &str, args: OpWrite) -> Result<Self::Writer> {
144 self.inner.write(ctx, path, args)
145 }
146
147 fn copy(
148 &self,
149 ctx: &OperationContext,
150 from: &str,
151 to: &str,
152 args: OpCopy,
153 opts: OpCopier,
154 ) -> Result<Self::Copier> {
155 self.inner.copy(ctx, from, to, args, opts)
156 }
157
158 fn list(&self, ctx: &OperationContext, path: &str, args: OpList) -> Result<Self::Lister> {
159 self.inner.list(ctx, path, args)
160 }
161
162 fn delete(&self, ctx: &OperationContext) -> Result<Self::Deleter> {
163 self.inner.delete(ctx)
164 }
165
166 async fn rename(
167 &self,
168 ctx: &OperationContext,
169 from: &str,
170 to: &str,
171 args: OpRename,
172 ) -> Result<RpRename> {
173 self.inner.rename(ctx, from, to, args).await
174 }
175
176 async fn presign(
177 &self,
178 ctx: &OperationContext,
179 path: &str,
180 args: OpPresign,
181 ) -> Result<RpPresign> {
182 self.inner.presign(ctx, path, args).await
183 }
184}
185
186#[doc(hidden)]
187pub struct ChaosReader<R> {
188 inner: R,
189 rng: Arc<Mutex<StdRng>>,
190
191 error_ratio: f64,
192}
193
194impl<R> ChaosReader<R> {
195 fn new(inner: R, rng: Arc<Mutex<StdRng>>, error_ratio: f64) -> Self {
196 Self {
197 inner,
198 rng,
199 error_ratio,
200 }
201 }
202
203 fn i_feel_lucky(&self) -> bool {
206 let point: u32 = self.rng.lock().unwrap().random_range(0..100);
207 point >= (self.error_ratio * 100.0) as u32
208 }
209
210 fn unexpected_eof() -> Error {
211 Error::new(ErrorKind::Unexpected, "I am your chaos!")
212 .with_operation("chaos")
213 .set_temporary()
214 }
215}
216
217impl<R: oio::ReadStream> oio::ReadStream for ChaosReader<R> {
218 async fn read(&mut self) -> Result<Buffer> {
219 if self.i_feel_lucky() {
220 self.inner.read().await
221 } else {
222 Err(Self::unexpected_eof())
223 }
224 }
225}
226
227impl<R: oio::Read> oio::Read for ChaosReader<R> {
228 async fn open(&self, range: BytesRange) -> Result<(RpRead, Box<dyn oio::ReadStreamDyn>)> {
229 if self.i_feel_lucky() {
230 let (rp, stream) = self.inner.open(range).await?;
231 Ok((
232 rp,
233 Box::new(ChaosReader::new(stream, self.rng.clone(), self.error_ratio))
234 as Box<dyn oio::ReadStreamDyn>,
235 ))
236 } else {
237 Err(Self::unexpected_eof())
238 }
239 }
240
241 async fn read(&self, range: BytesRange) -> Result<(RpRead, Buffer)> {
242 if self.i_feel_lucky() {
243 self.inner.read(range).await
244 } else {
245 Err(Self::unexpected_eof())
246 }
247 }
248}