opendal 0.1.6.pre.rc.1-arm64-darwin-23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (191) hide show
  1. checksums.yaml +7 -0
  2. data/.standard.yml +20 -0
  3. data/.tool-versions +1 -0
  4. data/.yardopts +1 -0
  5. data/DEPENDENCIES.md +9 -0
  6. data/DEPENDENCIES.rust.tsv +277 -0
  7. data/Gemfile +35 -0
  8. data/README.md +159 -0
  9. data/Rakefile +149 -0
  10. data/core/CHANGELOG.md +4929 -0
  11. data/core/CONTRIBUTING.md +61 -0
  12. data/core/DEPENDENCIES.md +3 -0
  13. data/core/DEPENDENCIES.rust.tsv +185 -0
  14. data/core/LICENSE +201 -0
  15. data/core/README.md +228 -0
  16. data/core/benches/README.md +18 -0
  17. data/core/benches/ops/README.md +26 -0
  18. data/core/benches/types/README.md +9 -0
  19. data/core/benches/vs_fs/README.md +35 -0
  20. data/core/benches/vs_s3/README.md +55 -0
  21. data/core/edge/README.md +3 -0
  22. data/core/edge/file_write_on_full_disk/README.md +14 -0
  23. data/core/edge/s3_aws_assume_role_with_web_identity/README.md +18 -0
  24. data/core/edge/s3_read_on_wasm/.gitignore +3 -0
  25. data/core/edge/s3_read_on_wasm/README.md +42 -0
  26. data/core/edge/s3_read_on_wasm/webdriver.json +15 -0
  27. data/core/examples/README.md +23 -0
  28. data/core/examples/basic/README.md +15 -0
  29. data/core/examples/concurrent-upload/README.md +15 -0
  30. data/core/examples/multipart-upload/README.md +15 -0
  31. data/core/fuzz/.gitignore +5 -0
  32. data/core/fuzz/README.md +68 -0
  33. data/core/src/docs/comparisons/vs_object_store.md +183 -0
  34. data/core/src/docs/performance/concurrent_write.md +101 -0
  35. data/core/src/docs/performance/http_optimization.md +124 -0
  36. data/core/src/docs/rfcs/0000_example.md +74 -0
  37. data/core/src/docs/rfcs/0000_foyer_integration.md +111 -0
  38. data/core/src/docs/rfcs/0041_object_native_api.md +185 -0
  39. data/core/src/docs/rfcs/0044_error_handle.md +198 -0
  40. data/core/src/docs/rfcs/0057_auto_region.md +160 -0
  41. data/core/src/docs/rfcs/0069_object_stream.md +145 -0
  42. data/core/src/docs/rfcs/0090_limited_reader.md +155 -0
  43. data/core/src/docs/rfcs/0112_path_normalization.md +79 -0
  44. data/core/src/docs/rfcs/0191_async_streaming_io.md +328 -0
  45. data/core/src/docs/rfcs/0203_remove_credential.md +96 -0
  46. data/core/src/docs/rfcs/0221_create_dir.md +89 -0
  47. data/core/src/docs/rfcs/0247_retryable_error.md +87 -0
  48. data/core/src/docs/rfcs/0293_object_id.md +67 -0
  49. data/core/src/docs/rfcs/0337_dir_entry.md +191 -0
  50. data/core/src/docs/rfcs/0409_accessor_capabilities.md +67 -0
  51. data/core/src/docs/rfcs/0413_presign.md +154 -0
  52. data/core/src/docs/rfcs/0423_command_line_interface.md +268 -0
  53. data/core/src/docs/rfcs/0429_init_from_iter.md +107 -0
  54. data/core/src/docs/rfcs/0438_multipart.md +163 -0
  55. data/core/src/docs/rfcs/0443_gateway.md +73 -0
  56. data/core/src/docs/rfcs/0501_new_builder.md +111 -0
  57. data/core/src/docs/rfcs/0554_write_refactor.md +96 -0
  58. data/core/src/docs/rfcs/0561_list_metadata_reuse.md +210 -0
  59. data/core/src/docs/rfcs/0599_blocking_api.md +157 -0
  60. data/core/src/docs/rfcs/0623_redis_service.md +300 -0
  61. data/core/src/docs/rfcs/0627_split_capabilities.md +89 -0
  62. data/core/src/docs/rfcs/0661_path_in_accessor.md +126 -0
  63. data/core/src/docs/rfcs/0793_generic_kv_services.md +209 -0
  64. data/core/src/docs/rfcs/0926_object_reader.md +93 -0
  65. data/core/src/docs/rfcs/0977_refactor_error.md +151 -0
  66. data/core/src/docs/rfcs/1085_object_handler.md +73 -0
  67. data/core/src/docs/rfcs/1391_object_metadataer.md +110 -0
  68. data/core/src/docs/rfcs/1398_query_based_metadata.md +125 -0
  69. data/core/src/docs/rfcs/1420_object_writer.md +147 -0
  70. data/core/src/docs/rfcs/1477_remove_object_concept.md +159 -0
  71. data/core/src/docs/rfcs/1735_operation_extension.md +117 -0
  72. data/core/src/docs/rfcs/2083_writer_sink_api.md +106 -0
  73. data/core/src/docs/rfcs/2133_append_api.md +88 -0
  74. data/core/src/docs/rfcs/2299_chain_based_operator_api.md +99 -0
  75. data/core/src/docs/rfcs/2602_object_versioning.md +138 -0
  76. data/core/src/docs/rfcs/2758_merge_append_into_write.md +79 -0
  77. data/core/src/docs/rfcs/2774_lister_api.md +66 -0
  78. data/core/src/docs/rfcs/2779_list_with_metakey.md +143 -0
  79. data/core/src/docs/rfcs/2852_native_capability.md +58 -0
  80. data/core/src/docs/rfcs/2884_merge_range_read_into_read.md +80 -0
  81. data/core/src/docs/rfcs/3017_remove_write_copy_from.md +94 -0
  82. data/core/src/docs/rfcs/3197_config.md +237 -0
  83. data/core/src/docs/rfcs/3232_align_list_api.md +69 -0
  84. data/core/src/docs/rfcs/3243_list_prefix.md +128 -0
  85. data/core/src/docs/rfcs/3356_lazy_reader.md +111 -0
  86. data/core/src/docs/rfcs/3526_list_recursive.md +59 -0
  87. data/core/src/docs/rfcs/3574_concurrent_stat_in_list.md +80 -0
  88. data/core/src/docs/rfcs/3734_buffered_reader.md +64 -0
  89. data/core/src/docs/rfcs/3898_concurrent_writer.md +66 -0
  90. data/core/src/docs/rfcs/3911_deleter_api.md +165 -0
  91. data/core/src/docs/rfcs/4382_range_based_read.md +213 -0
  92. data/core/src/docs/rfcs/4638_executor.md +215 -0
  93. data/core/src/docs/rfcs/5314_remove_metakey.md +120 -0
  94. data/core/src/docs/rfcs/5444_operator_from_uri.md +162 -0
  95. data/core/src/docs/rfcs/5479_context.md +140 -0
  96. data/core/src/docs/rfcs/5485_conditional_reader.md +112 -0
  97. data/core/src/docs/rfcs/5495_list_with_deleted.md +81 -0
  98. data/core/src/docs/rfcs/5556_write_returns_metadata.md +121 -0
  99. data/core/src/docs/rfcs/5871_read_returns_metadata.md +112 -0
  100. data/core/src/docs/rfcs/6189_remove_native_blocking.md +106 -0
  101. data/core/src/docs/rfcs/6209_glob_support.md +132 -0
  102. data/core/src/docs/rfcs/6213_options_api.md +142 -0
  103. data/core/src/docs/rfcs/README.md +62 -0
  104. data/core/src/docs/upgrade.md +1556 -0
  105. data/core/src/services/aliyun_drive/docs.md +61 -0
  106. data/core/src/services/alluxio/docs.md +45 -0
  107. data/core/src/services/azblob/docs.md +77 -0
  108. data/core/src/services/azdls/docs.md +73 -0
  109. data/core/src/services/azfile/docs.md +65 -0
  110. data/core/src/services/b2/docs.md +54 -0
  111. data/core/src/services/cacache/docs.md +38 -0
  112. data/core/src/services/cloudflare_kv/docs.md +21 -0
  113. data/core/src/services/cos/docs.md +55 -0
  114. data/core/src/services/d1/docs.md +48 -0
  115. data/core/src/services/dashmap/docs.md +38 -0
  116. data/core/src/services/dbfs/docs.md +57 -0
  117. data/core/src/services/dropbox/docs.md +64 -0
  118. data/core/src/services/etcd/docs.md +45 -0
  119. data/core/src/services/foundationdb/docs.md +42 -0
  120. data/core/src/services/fs/docs.md +49 -0
  121. data/core/src/services/ftp/docs.md +42 -0
  122. data/core/src/services/gcs/docs.md +76 -0
  123. data/core/src/services/gdrive/docs.md +65 -0
  124. data/core/src/services/ghac/docs.md +84 -0
  125. data/core/src/services/github/docs.md +52 -0
  126. data/core/src/services/gridfs/docs.md +46 -0
  127. data/core/src/services/hdfs/docs.md +140 -0
  128. data/core/src/services/hdfs_native/docs.md +35 -0
  129. data/core/src/services/http/docs.md +45 -0
  130. data/core/src/services/huggingface/docs.md +61 -0
  131. data/core/src/services/ipfs/docs.md +45 -0
  132. data/core/src/services/ipmfs/docs.md +14 -0
  133. data/core/src/services/koofr/docs.md +51 -0
  134. data/core/src/services/lakefs/docs.md +62 -0
  135. data/core/src/services/memcached/docs.md +47 -0
  136. data/core/src/services/memory/docs.md +36 -0
  137. data/core/src/services/mini_moka/docs.md +19 -0
  138. data/core/src/services/moka/docs.md +42 -0
  139. data/core/src/services/mongodb/docs.md +49 -0
  140. data/core/src/services/monoiofs/docs.md +46 -0
  141. data/core/src/services/mysql/docs.md +47 -0
  142. data/core/src/services/obs/docs.md +54 -0
  143. data/core/src/services/onedrive/docs.md +115 -0
  144. data/core/src/services/opfs/docs.md +18 -0
  145. data/core/src/services/oss/docs.md +74 -0
  146. data/core/src/services/pcloud/docs.md +51 -0
  147. data/core/src/services/persy/docs.md +43 -0
  148. data/core/src/services/postgresql/docs.md +47 -0
  149. data/core/src/services/redb/docs.md +41 -0
  150. data/core/src/services/redis/docs.md +43 -0
  151. data/core/src/services/rocksdb/docs.md +54 -0
  152. data/core/src/services/s3/compatible_services.md +126 -0
  153. data/core/src/services/s3/docs.md +244 -0
  154. data/core/src/services/seafile/docs.md +54 -0
  155. data/core/src/services/sftp/docs.md +49 -0
  156. data/core/src/services/sled/docs.md +39 -0
  157. data/core/src/services/sqlite/docs.md +46 -0
  158. data/core/src/services/surrealdb/docs.md +54 -0
  159. data/core/src/services/swift/compatible_services.md +53 -0
  160. data/core/src/services/swift/docs.md +52 -0
  161. data/core/src/services/tikv/docs.md +43 -0
  162. data/core/src/services/upyun/docs.md +51 -0
  163. data/core/src/services/vercel_artifacts/docs.md +40 -0
  164. data/core/src/services/vercel_blob/docs.md +45 -0
  165. data/core/src/services/webdav/docs.md +49 -0
  166. data/core/src/services/webhdfs/docs.md +90 -0
  167. data/core/src/services/yandex_disk/docs.md +45 -0
  168. data/core/tests/behavior/README.md +77 -0
  169. data/core/tests/data/normal_dir/.gitkeep +0 -0
  170. data/core/tests/data/normal_file.txt +1041 -0
  171. data/core/tests/data/special_dir !@#$%^&()_+-=;',/.gitkeep +0 -0
  172. data/core/tests/data/special_file !@#$%^&()_+-=;',.txt +1041 -0
  173. data/core/users.md +13 -0
  174. data/extconf.rb +24 -0
  175. data/lib/opendal.rb +25 -0
  176. data/lib/opendal_ruby/entry.rb +35 -0
  177. data/lib/opendal_ruby/io.rb +70 -0
  178. data/lib/opendal_ruby/metadata.rb +44 -0
  179. data/lib/opendal_ruby/opendal_ruby.bundle +0 -0
  180. data/lib/opendal_ruby/operator.rb +29 -0
  181. data/lib/opendal_ruby/operator_info.rb +26 -0
  182. data/opendal.gemspec +91 -0
  183. data/test/blocking_op_test.rb +112 -0
  184. data/test/capability_test.rb +42 -0
  185. data/test/io_test.rb +172 -0
  186. data/test/lister_test.rb +77 -0
  187. data/test/metadata_test.rb +78 -0
  188. data/test/middlewares_test.rb +46 -0
  189. data/test/operator_info_test.rb +35 -0
  190. data/test/test_helper.rb +36 -0
  191. metadata +240 -0
@@ -0,0 +1,300 @@
1
+ - Proposal Name: `redis_service`
2
+ - Start Date: 2022-08-31
3
+ - RFC PR: [apache/opendal#0623](https://github.com/apache/opendal/pull/0623)
4
+ - Tracking Issue: [apache/opendal#641](https://github.com/apache/opendal/issues/0641)
5
+
6
+ # Summary
7
+
8
+ Use [redis](https://redis.io) as a service of OpenDAL.
9
+
10
+ # Motivation
11
+
12
+ Redis is a fast, in-memory cache with persistent and distributed storage functionalities. It's widely used in production.
13
+
14
+ Users also demand more backend support. Redis is a good candidate.
15
+
16
+ # Guide-level explanation
17
+
18
+ Users only need to provide the network address, username and password to create a Redis Operator. Then everything will act as other operators do.
19
+
20
+ ```rust
21
+ use opendal::services::redis::Builder;
22
+ use opendal::Operator;
23
+
24
+ let builder = Builder::default();
25
+
26
+ // set the endpoint of redis server
27
+ builder.endpoint("tcps://domain.to.redis:2333");
28
+ // set the username of redis
29
+ builder.username("example");
30
+ // set the password
31
+ builder.password(&std::env::var("OPENDAL_REDIS_PASSWORD").expect("env OPENDAL_REDIS_PASSWORD not set"));
32
+ // root path
33
+ builder.root("/example/");
34
+
35
+ let op = Operator::new(
36
+ builder.build()? // services::redis::Backend
37
+ );
38
+
39
+ // congratulations, you can use `op` just like any other operators!
40
+ ```
41
+
42
+ # Reference-level explanation
43
+
44
+ To ease the development, [redis-rs](https://crates.io/crates/redis) will be used.
45
+
46
+ Redis offers a key-value view, so the path of files could be represented as the key of the key-value pair.
47
+
48
+ The content of file will be represented directly as `String`, and metadata will be encoded as [`bincode`](https://github.com/bincode-org/bincode.git) before storing as `String`.
49
+
50
+ ```text
51
+ +------------------------------------------+
52
+ |Object: /home/monika/ |
53
+ | | SET
54
+ |child: Key: v0:k:/home/monika/ -+---------> 1) /home/monika/poem0.txt
55
+ | |
56
+ |/* directory has no content */ |
57
+ | |
58
+ |metadata: Key: v0:m:/home/monika/ |
59
+ +------------------------------------------+
60
+
61
+ +------------------------------------------+
62
+ |Object: /home/monika/poem0.txt |
63
+ | |
64
+ | /* file has no children */ |
65
+ | |
66
+ |content: Key: v0:c:/home/monika/poem0.txt-+--+
67
+ | | |
68
+ |metadata: Key: v0:m:/home/monika/poem0.txt| |
69
+ | | | |
70
+ +--+---------------------------------------+ |
71
+ | v
72
+ +> STRING STRING
73
+ +----------------------+ +--------------------+
74
+ |\x00\x00\x00\x00\xe6\a| |1JU5T3MON1K413097321|
75
+ |\x00\x00\xf8\x00\a4)!V| |&JU5$T!M0N1K4$%#@#$%|
76
+ |\x81&\x00\x00\x00Q\x00| |3231J)U_ST#MONIKA@#$|
77
+ | ... | |1557(m0N1ka3just4M |
78
+ +----------------------+ | ... |
79
+ +--------------------+
80
+ ```
81
+
82
+ The [redis-rs](https://crates.io/crates/redis)'s high level APIs is preferred.
83
+
84
+ ```rust
85
+ const VERSION: usize = 0;
86
+
87
+ /// meta_key will produce the key to object's metadata
88
+ /// "/path/to/object/" -> "v{VERSION}:m:/path/to/object"
89
+ fn meta_key(path: &str) -> String {
90
+ format!("v{}:m:{}", VERSION, path)
91
+ }
92
+
93
+ /// content_key will produce the key to object's content
94
+ /// "/path/to/object/" -> "v{VERSION}:c:/path/to/object"
95
+ fn content_key(path: &str) -> String {
96
+ format!("v{}:c:{}", VERSION, path)
97
+ }
98
+
99
+ fn connect() -> Result<()> {
100
+ let client = redis::Client::open("redis://localhost:6379")?;
101
+ let con = client.get_async_connection()?;
102
+ }
103
+ ```
104
+
105
+ ## Forward Compatibility
106
+
107
+ All keys used will have a `v0` prefix, indicating it's using the very first version of `OpenDAL` `Redis` API.
108
+
109
+ When there are changes to the layout, like refactoring the layout of storage, the version number should be updated, too. Further versions should take the compatibility with former implementations into consideration.
110
+
111
+ ## Create File
112
+
113
+ If user is creating a file with root `/home/monika/`, and relative path `poem0.txt`.
114
+
115
+ ```rust
116
+ // mode: ObjectMode
117
+ // path: relative path string
118
+ let path = get_abs_path(path); // /home/monika/ <> /poem.txt -> /home/monika/poem.txt
119
+ let m_path = meta_key(path); // path of metadata
120
+ let c_path = content_key(path); // path of content
121
+ let last_modified = OffsetDatetime::now_utc().to_string();
122
+
123
+ let mut meta = ObjectMeta::default();
124
+ meta.set_mode(ObjectMode::FILE);
125
+ meta.set_last_modified(OffsetDatetime::now_utc());
126
+
127
+ let encoded = bincode::encode_to_vec(meta)?;
128
+
129
+ con.set(c_path, "".to_string())?;
130
+ con.set(m_path, encoded.as_slice())?;
131
+ ```
132
+
133
+ This will create two key-value pair. For object content, its key is `v0:c:/home/monika/poem0.txt`, the value is an empty `String`; For metadata, the key is `v0:m:/home/monika/poem0.txt`, the value is a `bincode` encoded `ObjectMetadata` structure binary string.
134
+
135
+ On creating a file or directory, the backend should also create its all parent directories if not present.
136
+
137
+ ```rust
138
+ // create a file under `PATH`
139
+ let mut path = std::path::PathBuf::new(PATH);
140
+ let mut con = client.new_async_connection().await?;
141
+
142
+ while let Some(parent) = path.parent() {
143
+ let (p, c): (String, String) = (parent.display(), path.display());
144
+ let to_children = format!("v0:ch:{}", p);
145
+ con.sadd(to_children, c).await?;
146
+ path = parent;
147
+ }
148
+ ```
149
+
150
+ ## Read File
151
+
152
+ Opendal empowers users to read with the `path` object, `offset` of the cursor and `size` to read. Redis provided a `GETRANGE` command which perfectly fit into it.
153
+
154
+ ```rust
155
+ // path: "poem0.txt"
156
+ // offset: Option<u64>, the offset of reading
157
+ // size: Option<u64>, the size of reading
158
+ let path = get_abs_path(path);
159
+ let c_path = content_key(path);
160
+ let (mut start, mut end) = (0, -1);
161
+ if let Some(offset) = offset {
162
+ start = offset;
163
+ }
164
+ if let Some(size) = size {
165
+ end = start + size;
166
+ }
167
+ let buf: Vec<u8> = con.getrange(c_path, start, end).await?;
168
+ Box::new(buf)
169
+ ```
170
+
171
+ ```redis
172
+ GET v0:c:/home/monika/poem0.txt
173
+ ```
174
+
175
+ ## Write File
176
+
177
+ Redis ensures the writing of a single entry to be atomic, no locking is required.
178
+
179
+ What needs to take care by opendal, besides the content of object, is its metadata. For example, though offering a `OBJECT IDLETIME` command, redis cannot record the last modified time of a key, so this should be done in opendal.
180
+
181
+ ```rust
182
+ use redis::AsyncCommands;
183
+ // args: &OpWrite
184
+ // r: BytesReader
185
+ let mut buf = vec![];
186
+ let content_length: u64 = futures::io::copy(r, &mut buf).await?;
187
+ let last_modified: String = OffsetDateTime::now().to_string();
188
+
189
+ // content md5 will not be offered
190
+
191
+ let mut meta = ObjectMetadata::default();
192
+ meta.set_content_length(content_length);
193
+ meta.set_last_modified(last_modified);
194
+
195
+ // `ObjectMetadata` has implemented the `Serialize` and `Deserialize` trait of `Serde`
196
+ // so bincode could serialize and deserialize it.
197
+ let bytes = bincode::encode_to_vec(&meta)?;
198
+
199
+ let path = get_abs_path(args.path());
200
+ let m_path = meta_key(path);
201
+ let c_path = content_key(path);
202
+
203
+ con.set(c_path, content).await?;
204
+ con.set(m_path, bytes).await?;
205
+ ```
206
+
207
+ ```redis
208
+ SET v0:c:/home/monika/poem.txt content_string
209
+ SET v0:m:/home/monika/poem.txt <bincode encoded metadata>
210
+ ```
211
+
212
+ ## Stat
213
+
214
+ To get the metadata of an object, using the `GET` command and deserialize from bincode.
215
+
216
+ ```rust
217
+ let path = get_abs_path(args.path());
218
+ let meta = meta_key(path);
219
+ let bin: Vec<u8> = con.get(meta).await?;
220
+ let meta: ObjectMeta = bincode::deserialize(bin.as_slice())?;
221
+ ```
222
+
223
+ ```redis
224
+ GET v0:m:/home/monika/poem.txt
225
+ ```
226
+
227
+ ## List
228
+
229
+ For listing directories, just `SSCAN` through the child list of the directory, nice and correct.
230
+
231
+ ```rust
232
+ // print all sub-directories of `PATH`
233
+
234
+ let s_key = format!("v0:k:{}", PATH);
235
+ let mut con = client.new_async_connection().await?;
236
+ let mut it = con.sscan::<&str, String>(s_key).await?;
237
+
238
+ while let Some(child) = it.next_item().await {
239
+ println!("get sub-dir: {}", child);
240
+ }
241
+ ```
242
+
243
+ ## Delete
244
+
245
+ All subdirectories of path will be listed and removed.
246
+
247
+ On deleting a file or directory, the backend should remove the entry from its parent's `SET`, and remove all children of entry.
248
+
249
+ This could be done by postorder deleting.
250
+
251
+ ```rust
252
+ async fn remove_entry(con: &mut redis::aio::AsyncConnection, entry: String) {
253
+ let skey = format!("v0:ch:{}", entry);
254
+ let it = con.sscan::<&str, String>(skey).await?;
255
+ while let Some(child) = it.next_item().await {
256
+ remove_entry(&mut con, child).await;
257
+ }
258
+ if let Some(parent) = std::PathBuf::new(entry).parent() {
259
+ let p: String = parent.display();
260
+ let parent_skey = format!("v0:ch:{}", p);
261
+ let _ = con.srem(parent_skey, entry).await;
262
+ }
263
+ // remove metadata and content
264
+ }
265
+ ```
266
+
267
+ ## Blocking APIs
268
+
269
+ `redis-rs` also offers a synchronous version of API, just port the functions above to its synchronous version.
270
+
271
+ # Drawbacks
272
+
273
+ 1. New dependencies is introduced: `redis-rs` and `bincode`;
274
+ 2. Some calculations have to be done in client side, this will affect the performance;
275
+ 3. Grouping atomic operations together doesn't promise transactional access, this may lead to data racing issues.
276
+ 4. Writing large binary strings requiring copying all data from pipe(or `BytesReader` in opendal) to RAM, and then send to redis.
277
+
278
+ # Rationale and alternatives
279
+
280
+ ## RedisJSON module
281
+
282
+ The [`RedisJSON`](https://redis.io/docs/stack/json/) module provides JSON support for Redis, and supports depth up to 128. Working on a JSON api could be easier than manually parsing or deserializing from `HASH`.
283
+
284
+ Since `bincode` also offers the ability of deserializing and serializing, `RedisJSON` won't be used.
285
+
286
+ # Prior art
287
+
288
+ None
289
+
290
+ # Unresolved questions
291
+
292
+ None
293
+
294
+ # Future possibilities
295
+
296
+ The implementation proposed here is far from perfect.
297
+
298
+ - The data organization could be optimized to make it acts more like a filesystem
299
+ - Making a customized redis module to calculate metadata on redis side
300
+ - Wait for stable of `bincode` 2.0, and bump to it.
@@ -0,0 +1,89 @@
1
+ - Proposal Name: `split-capabilities`
2
+ - Start Date: 2022-09-04
3
+ - RFC PR: [apache/opendal#627](https://github.com/apache/opendal/pull/627)
4
+ - Tracking Issue: [apache/opendal#628](https://github.com/apache/opendal/issues/628)
5
+
6
+ # Summary
7
+
8
+ Split basic operations into `read`, `write`, and `list` capabilities.
9
+
10
+ # Motivation
11
+
12
+ In [RFC-0409: Accessor Capabilities](./0409-accessor-capabilities.md), we introduce the ideas of `Accessor Capabilities`. Services could have different capabilities, and users can check them via:
13
+
14
+ ```rust
15
+ let meta = op.metadata();
16
+ let _: bool = meta.can_presign();
17
+ let _: bool = meta.can_multipart();
18
+ ```
19
+
20
+ If users call not supported capabilities, OpenDAL will return [`io::ErrorKind::Unsupported`](https://doc.rust-lang.org/stable/std/io/enum.ErrorKind.html#variant.Unsupported) instead.
21
+
22
+ Along with that RFC, we also introduce an idea about `Basic Operations`: the operations that all services must support, including:
23
+
24
+ - metadata
25
+ - create
26
+ - read
27
+ - write
28
+ - delete
29
+ - list
30
+
31
+ However, not all storage services support them. In our existing services, exception includes:
32
+
33
+ - HTTP services don't support `write`, `delete`, and `list`.
34
+ - IPFS HTTP gateway doesn't support `write` and `delete`.
35
+ - NOTE: ipfs has a writable HTTP gateway, but there is no available instance.
36
+ - fs could be read-only if mounted as `RO`.
37
+ - object storage like `s3` and `gcs` could not have enough permission.
38
+ - cache services may not support `list`.
39
+
40
+ So in this RFC, we want to remove the idea about `Basic Operations` and convert them into different capabilities:
41
+
42
+ - `read`: `read` and `stat`
43
+ - `write`: `write` and `delete`
44
+ - `list`: `list`
45
+
46
+ # Guide-level explanation
47
+
48
+ No public API changes.
49
+
50
+ # Reference-level explanation
51
+
52
+ This RFC will add three new capabilities:
53
+
54
+ - `read`: `read` and `stat`
55
+ - `write`: `write` and `delete`
56
+ - `list`: `list`
57
+
58
+ After this change, all services must declare the features they support.
59
+
60
+ Most of this RFCs work is to refactor the tests. This RFC will refactor the behavior tests into several parts based on capabilities.
61
+
62
+ # Drawbacks
63
+
64
+ None
65
+
66
+ # Rationale and alternatives
67
+
68
+ None
69
+
70
+ # Prior art
71
+
72
+ None
73
+
74
+ # Unresolved questions
75
+
76
+ None
77
+
78
+ # Future possibilities
79
+
80
+ ## Read-only Services
81
+
82
+ OpenDAL can implement read-only services after this change:
83
+
84
+ - HTTP Service
85
+ - IPFS HTTP Gateway
86
+
87
+ ## Add new capabilities with Layers
88
+
89
+ We can implement a layer that can add `list` capability for underlying storage services. For example, `IndexLayer` for HTTP services.
@@ -0,0 +1,126 @@
1
+ - Proposal Name: `path_in_accessor`
2
+ - Start Date: 2022-09-12
3
+ - RFC PR: [apache/opendal#661](https://github.com/apache/opendal/pull/661)
4
+ - Tracking Issue: [apache/opendal#662](https://github.com/apache/opendal/issues/662)
5
+
6
+ # Summary
7
+
8
+ Move the path from `OpXxx` to `Accessor` directly.
9
+
10
+ # Motivation
11
+
12
+ `Accessor` uses `OpXxx` to carry `path` input:
13
+
14
+ ```rust
15
+ impl Accessor {
16
+ async fn read(&self, args: &OpRead) -> Result<BytesReader> {
17
+ let _ = args;
18
+ unimplemented!()
19
+ }
20
+ }
21
+
22
+ #[derive(Debug, Clone, Default)]
23
+ pub struct OpRead {
24
+ path: String,
25
+ offset: Option<u64>,
26
+ size: Option<u64>,
27
+ }
28
+ ```
29
+
30
+ However, nearly all operation requires a `path`. And the path is represented in `String`, which means we have to clone it:
31
+
32
+ ```rust
33
+ impl OpRead {
34
+ pub fn new(path: &str, range: impl RangeBounds<u64>) -> Result<Self> {
35
+ let br = BytesRange::from(range);
36
+
37
+ Ok(Self {
38
+ path: path.to_string(),
39
+ offset: br.offset(),
40
+ size: br.size(),
41
+ })
42
+ }
43
+ }
44
+ ```
45
+
46
+ Besides, we can't expose low-level APIs like:
47
+
48
+ ```rust
49
+ impl Object {
50
+ pub async fn read_with(&self, op: OpRead) -> Result<Vec<u8>> {
51
+ ..
52
+ }
53
+ }
54
+ ```
55
+
56
+ Because users can't build the required `OpRead`.
57
+
58
+ # Guide-level explanation
59
+
60
+ With this RFC, users can use low-level APIs can control the `OpXxx` directly:
61
+
62
+ ```rust
63
+ impl Object {
64
+ pub async fn read_with(&self, op: OpRead) -> Result<Vec<u8>> {
65
+ ..
66
+ }
67
+
68
+ pub async fn write_with(&self, op: OpWrite, bs: impl Into<Vec<u8>>) -> Result<()> {
69
+ ..
70
+ }
71
+ }
72
+ ```
73
+
74
+ So we can add more args in requests like:
75
+
76
+ ```rust
77
+ o.write_with(OpWrite::new().with_content_md5("xxxxx"), bs).await;
78
+ ```
79
+
80
+ # Reference-level explanation
81
+
82
+ All `path` in `OpXxx` will be moved to `Accessor` directly:
83
+
84
+ ```rust
85
+ pub trait Accessor: Send + Sync + Debug {
86
+ async fn create(&self, path: &str, args: OpCreate) -> Result<()> {}
87
+
88
+ async fn read(&self, path: &str, args: OpRead) -> Result<BytesReader> {}
89
+
90
+ ...
91
+ }
92
+ ```
93
+
94
+ - All functions that accept `OpXxx` requires ownership instead of reference.
95
+ - All `OpXxx::new()` will introduce breaking changes:
96
+ ```diff
97
+ - pub fn new(path: &str, range: impl RangeBounds<u64>) -> Result<Self>
98
+ + pub fn new(range: impl RangeBounds<u64>) -> Self
99
+ ```
100
+
101
+ # Drawbacks
102
+
103
+ ## Breaking Changes
104
+
105
+ This RFC may break users' code in the following ways:
106
+
107
+ - Code that depends on `Accessor`:
108
+ - Self-implemented Services
109
+ - Self-implemented Layers
110
+ - Code that depends on `OpXxx`
111
+
112
+ # Rationale and alternatives
113
+
114
+ None.
115
+
116
+ # Prior art
117
+
118
+ None.
119
+
120
+ # Unresolved questions
121
+
122
+ None.
123
+
124
+ # Future possibilities
125
+
126
+ We can add more fields in `OpXxx`.