opendal 0.1.8 → 0.1.9

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 (656) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +0 -1
  3. data/Cargo.lock +3448 -0
  4. data/Cargo.toml +1 -1
  5. data/DEPENDENCIES.rust.tsv +31 -31
  6. data/README.md +4 -1
  7. data/Rakefile +0 -7
  8. data/core/CHANGELOG.md +80 -0
  9. data/core/Cargo.lock +495 -599
  10. data/core/Cargo.toml +98 -95
  11. data/core/DEPENDENCIES.rust.tsv +319 -324
  12. data/core/NOTICE +5 -0
  13. data/core/README.md +11 -6
  14. data/core/benches/vs_fs/Cargo.toml +1 -1
  15. data/core/benches/vs_s3/Cargo.toml +1 -1
  16. data/core/core/Cargo.toml +2 -2
  17. data/core/core/LICENSE +201 -0
  18. data/core/core/NOTICE +5 -0
  19. data/core/core/README.md +93 -0
  20. data/core/core/src/blocking/delete.rs +2 -2
  21. data/core/core/src/blocking/list.rs +2 -1
  22. data/core/core/src/blocking/operator.rs +10 -5
  23. data/core/core/src/blocking/read/std_bytes_iterator.rs +2 -1
  24. data/core/core/src/blocking/read/std_reader.rs +2 -1
  25. data/core/core/src/blocking/write/std_writer.rs +1 -1
  26. data/core/core/src/docs/performance/http_optimization.md +9 -1
  27. data/core/core/src/docs/rfcs/7749_http_transporter.md +9 -5
  28. data/core/core/src/layers/mod.rs +6 -1
  29. data/core/core/src/lib.rs +2 -137
  30. data/core/core/src/raw/accessor.rs +23 -21
  31. data/core/core/src/raw/oio/buf/queue_buf.rs +4 -2
  32. data/core/core/src/raw/oio/write/block_write.rs +3 -2
  33. data/core/core/src/raw/operation.rs +5 -5
  34. data/core/core/src/raw/ops.rs +85 -63
  35. data/core/core/src/raw/std_io_util.rs +33 -1
  36. data/core/core/src/services/memory/backend.rs +0 -1
  37. data/core/core/src/services/memory/mod.rs +6 -2
  38. data/core/core/src/services/mod.rs +8 -2
  39. data/core/core/src/types/buffer.rs +4 -4
  40. data/core/core/src/types/builder.rs +15 -17
  41. data/core/core/src/types/copy.rs +3 -3
  42. data/core/core/src/types/entry.rs +3 -1
  43. data/core/core/src/types/error.rs +16 -11
  44. data/core/core/src/types/execute/executors/mod.rs +3 -3
  45. data/core/core/src/types/http_transport/mod.rs +6 -0
  46. data/core/core/src/types/list.rs +5 -6
  47. data/core/core/src/types/operator/info.rs +6 -4
  48. data/core/core/src/types/operator/mod.rs +1 -1
  49. data/core/core/src/types/operator/operator.rs +42 -8
  50. data/core/core/src/types/operator/operator_futures.rs +36 -38
  51. data/core/core/src/types/operator/registry.rs +73 -14
  52. data/core/core/src/types/options.rs +48 -42
  53. data/core/core/src/types/read/reader.rs +2 -1
  54. data/core/core/src/types/write/writer.rs +2 -1
  55. data/core/http-transports/reqwest/Cargo.toml +2 -1
  56. data/core/http-transports/reqwest/LICENSE +201 -0
  57. data/core/http-transports/reqwest/NOTICE +5 -0
  58. data/core/http-transports/reqwest/README.md +37 -9
  59. data/core/http-transports/reqwest/src/lib.rs +30 -51
  60. data/core/layers/async-backtrace/Cargo.toml +3 -2
  61. data/core/layers/async-backtrace/LICENSE +201 -0
  62. data/core/layers/async-backtrace/NOTICE +5 -0
  63. data/core/layers/async-backtrace/README.md +48 -0
  64. data/core/layers/async-backtrace/src/lib.rs +5 -5
  65. data/core/layers/await-tree/Cargo.toml +3 -2
  66. data/core/layers/await-tree/LICENSE +201 -0
  67. data/core/layers/await-tree/NOTICE +5 -0
  68. data/core/layers/await-tree/README.md +48 -0
  69. data/core/layers/await-tree/src/lib.rs +5 -6
  70. data/core/layers/capability-check/Cargo.toml +3 -2
  71. data/core/layers/capability-check/LICENSE +201 -0
  72. data/core/layers/capability-check/NOTICE +5 -0
  73. data/core/layers/capability-check/README.md +48 -0
  74. data/core/layers/capability-check/src/lib.rs +7 -8
  75. data/core/layers/chaos/Cargo.toml +3 -2
  76. data/core/layers/chaos/LICENSE +201 -0
  77. data/core/layers/chaos/NOTICE +5 -0
  78. data/core/layers/chaos/README.md +47 -0
  79. data/core/layers/chaos/src/lib.rs +6 -7
  80. data/core/layers/concurrent-limit/Cargo.toml +3 -2
  81. data/core/layers/concurrent-limit/LICENSE +201 -0
  82. data/core/layers/concurrent-limit/NOTICE +5 -0
  83. data/core/layers/concurrent-limit/README.md +48 -0
  84. data/core/layers/concurrent-limit/src/lib.rs +8 -14
  85. data/core/layers/dtrace/Cargo.toml +3 -2
  86. data/core/layers/dtrace/LICENSE +201 -0
  87. data/core/layers/dtrace/NOTICE +5 -0
  88. data/core/layers/dtrace/README.md +52 -0
  89. data/core/layers/dtrace/src/lib.rs +5 -5
  90. data/core/layers/fastmetrics/Cargo.toml +4 -3
  91. data/core/layers/fastmetrics/LICENSE +201 -0
  92. data/core/layers/fastmetrics/NOTICE +5 -0
  93. data/core/layers/fastmetrics/README.md +47 -0
  94. data/core/layers/fastmetrics/src/lib.rs +4 -4
  95. data/core/layers/fastrace/Cargo.toml +3 -2
  96. data/core/layers/fastrace/LICENSE +201 -0
  97. data/core/layers/fastrace/NOTICE +5 -0
  98. data/core/layers/fastrace/README.md +47 -0
  99. data/core/layers/fastrace/src/lib.rs +5 -5
  100. data/core/layers/foyer/Cargo.toml +3 -2
  101. data/core/layers/foyer/LICENSE +201 -0
  102. data/core/layers/foyer/NOTICE +5 -0
  103. data/core/layers/foyer/README.md +47 -0
  104. data/core/layers/foyer/src/deleter.rs +1 -0
  105. data/core/layers/foyer/src/lib.rs +27 -10
  106. data/core/layers/foyer/src/writer.rs +1 -0
  107. data/core/layers/hotpath/Cargo.toml +4 -3
  108. data/core/layers/hotpath/LICENSE +201 -0
  109. data/core/layers/hotpath/NOTICE +5 -0
  110. data/core/layers/hotpath/README.md +47 -0
  111. data/core/layers/hotpath/src/lib.rs +4 -4
  112. data/core/layers/immutable-index/Cargo.toml +3 -2
  113. data/core/layers/immutable-index/LICENSE +201 -0
  114. data/core/layers/immutable-index/NOTICE +5 -0
  115. data/core/layers/immutable-index/README.md +48 -0
  116. data/core/layers/immutable-index/src/lib.rs +3 -4
  117. data/core/layers/logging/Cargo.toml +3 -2
  118. data/core/layers/logging/LICENSE +201 -0
  119. data/core/layers/logging/NOTICE +5 -0
  120. data/core/layers/logging/README.md +47 -0
  121. data/core/layers/logging/src/lib.rs +11 -11
  122. data/core/layers/metrics/Cargo.toml +4 -3
  123. data/core/layers/metrics/LICENSE +201 -0
  124. data/core/layers/metrics/NOTICE +5 -0
  125. data/core/layers/metrics/README.md +47 -0
  126. data/core/layers/metrics/src/lib.rs +6 -6
  127. data/core/layers/mime-guess/Cargo.toml +3 -2
  128. data/core/layers/mime-guess/LICENSE +201 -0
  129. data/core/layers/mime-guess/NOTICE +5 -0
  130. data/core/layers/mime-guess/README.md +47 -0
  131. data/core/layers/mime-guess/src/lib.rs +13 -17
  132. data/core/layers/observe-metrics-common/Cargo.toml +2 -1
  133. data/core/layers/observe-metrics-common/LICENSE +201 -0
  134. data/core/layers/observe-metrics-common/NOTICE +5 -0
  135. data/core/layers/observe-metrics-common/README.md +27 -0
  136. data/core/layers/observe-metrics-common/src/lib.rs +15 -9
  137. data/core/layers/otelmetrics/Cargo.toml +4 -3
  138. data/core/layers/otelmetrics/LICENSE +201 -0
  139. data/core/layers/otelmetrics/NOTICE +5 -0
  140. data/core/layers/otelmetrics/README.md +47 -0
  141. data/core/layers/otelmetrics/src/lib.rs +4 -4
  142. data/core/layers/oteltrace/Cargo.toml +3 -2
  143. data/core/layers/oteltrace/LICENSE +201 -0
  144. data/core/layers/oteltrace/NOTICE +5 -0
  145. data/core/layers/oteltrace/README.md +48 -0
  146. data/core/layers/oteltrace/src/lib.rs +13 -4
  147. data/core/layers/prometheus/Cargo.toml +4 -3
  148. data/core/layers/prometheus/LICENSE +201 -0
  149. data/core/layers/prometheus/NOTICE +5 -0
  150. data/core/layers/prometheus/README.md +47 -0
  151. data/core/layers/prometheus/src/lib.rs +4 -4
  152. data/core/layers/prometheus-client/Cargo.toml +4 -3
  153. data/core/layers/prometheus-client/LICENSE +201 -0
  154. data/core/layers/prometheus-client/NOTICE +5 -0
  155. data/core/layers/prometheus-client/README.md +48 -0
  156. data/core/layers/prometheus-client/src/lib.rs +4 -4
  157. data/core/layers/retry/Cargo.toml +5 -4
  158. data/core/layers/retry/LICENSE +201 -0
  159. data/core/layers/retry/NOTICE +5 -0
  160. data/core/layers/retry/README.md +50 -0
  161. data/core/layers/retry/src/lib.rs +6 -7
  162. data/core/layers/route/Cargo.toml +4 -3
  163. data/core/layers/route/LICENSE +201 -0
  164. data/core/layers/route/NOTICE +5 -0
  165. data/core/layers/route/README.md +58 -0
  166. data/core/layers/route/src/lib.rs +39 -9
  167. data/core/layers/tail-cut/Cargo.toml +3 -2
  168. data/core/layers/tail-cut/LICENSE +201 -0
  169. data/core/layers/tail-cut/NOTICE +5 -0
  170. data/core/layers/tail-cut/README.md +47 -0
  171. data/core/layers/tail-cut/src/lib.rs +7 -9
  172. data/core/layers/throttle/Cargo.toml +3 -2
  173. data/core/layers/throttle/LICENSE +201 -0
  174. data/core/layers/throttle/NOTICE +5 -0
  175. data/core/layers/throttle/README.md +47 -0
  176. data/core/layers/throttle/src/lib.rs +5 -7
  177. data/core/layers/timeout/Cargo.toml +4 -3
  178. data/core/layers/timeout/LICENSE +201 -0
  179. data/core/layers/timeout/NOTICE +5 -0
  180. data/core/layers/timeout/README.md +50 -0
  181. data/core/layers/timeout/src/lib.rs +9 -10
  182. data/core/layers/tracing/Cargo.toml +3 -2
  183. data/core/layers/tracing/LICENSE +201 -0
  184. data/core/layers/tracing/NOTICE +5 -0
  185. data/core/layers/tracing/README.md +47 -0
  186. data/core/layers/tracing/src/lib.rs +4 -4
  187. data/core/services/aliyun-drive/Cargo.toml +2 -1
  188. data/core/services/aliyun-drive/LICENSE +201 -0
  189. data/core/services/aliyun-drive/NOTICE +5 -0
  190. data/core/services/aliyun-drive/README.md +56 -0
  191. data/core/services/aliyun-drive/src/backend.rs +2 -2
  192. data/core/services/aliyun-drive/src/docs.md +7 -9
  193. data/core/services/aliyun-drive/src/lib.rs +12 -2
  194. data/core/services/aliyun-drive/src/lister.rs +2 -2
  195. data/core/services/alluxio/Cargo.toml +2 -1
  196. data/core/services/alluxio/LICENSE +201 -0
  197. data/core/services/alluxio/NOTICE +5 -0
  198. data/core/services/alluxio/README.md +56 -0
  199. data/core/services/alluxio/src/backend.rs +3 -3
  200. data/core/services/alluxio/src/docs.md +7 -5
  201. data/core/services/alluxio/src/lib.rs +12 -2
  202. data/core/services/azblob/Cargo.toml +6 -5
  203. data/core/services/azblob/LICENSE +201 -0
  204. data/core/services/azblob/NOTICE +5 -0
  205. data/core/services/azblob/README.md +56 -0
  206. data/core/services/azblob/src/backend.rs +3 -3
  207. data/core/services/azblob/src/core.rs +3 -3
  208. data/core/services/azblob/src/docs.md +7 -9
  209. data/core/services/azblob/src/lib.rs +14 -2
  210. data/core/services/azdls/Cargo.toml +6 -5
  211. data/core/services/azdls/LICENSE +201 -0
  212. data/core/services/azdls/NOTICE +5 -0
  213. data/core/services/azdls/README.md +56 -0
  214. data/core/services/azdls/src/backend.rs +3 -3
  215. data/core/services/azdls/src/docs.md +7 -8
  216. data/core/services/azdls/src/lib.rs +12 -2
  217. data/core/services/azdls/src/lister.rs +1 -1
  218. data/core/services/azfile/Cargo.toml +6 -5
  219. data/core/services/azfile/LICENSE +201 -0
  220. data/core/services/azfile/NOTICE +5 -0
  221. data/core/services/azfile/README.md +56 -0
  222. data/core/services/azfile/src/backend.rs +2 -2
  223. data/core/services/azfile/src/docs.md +7 -8
  224. data/core/services/azfile/src/lib.rs +12 -2
  225. data/core/services/azure-common/Cargo.toml +2 -1
  226. data/core/services/azure-common/LICENSE +201 -0
  227. data/core/services/azure-common/NOTICE +5 -0
  228. data/core/services/azure-common/README.md +26 -0
  229. data/core/services/azure-common/src/lib.rs +2 -5
  230. data/core/services/b2/Cargo.toml +2 -1
  231. data/core/services/b2/LICENSE +201 -0
  232. data/core/services/b2/NOTICE +5 -0
  233. data/core/services/b2/README.md +56 -0
  234. data/core/services/b2/src/backend.rs +4 -4
  235. data/core/services/b2/src/docs.md +7 -8
  236. data/core/services/b2/src/lib.rs +9 -3
  237. data/core/services/cacache/Cargo.toml +2 -1
  238. data/core/services/cacache/LICENSE +201 -0
  239. data/core/services/cacache/NOTICE +5 -0
  240. data/core/services/cacache/README.md +56 -0
  241. data/core/services/cacache/src/backend.rs +0 -1
  242. data/core/services/cacache/src/docs.md +7 -4
  243. data/core/services/cacache/src/lib.rs +9 -4
  244. data/core/services/cloudflare-kv/Cargo.toml +2 -1
  245. data/core/services/cloudflare-kv/LICENSE +201 -0
  246. data/core/services/cloudflare-kv/NOTICE +5 -0
  247. data/core/services/cloudflare-kv/README.md +56 -0
  248. data/core/services/cloudflare-kv/src/backend.rs +0 -2
  249. data/core/services/cloudflare-kv/src/docs.md +7 -5
  250. data/core/services/cloudflare-kv/src/lib.rs +9 -3
  251. data/core/services/compfs/Cargo.toml +2 -1
  252. data/core/services/compfs/LICENSE +201 -0
  253. data/core/services/compfs/NOTICE +5 -0
  254. data/core/services/compfs/README.md +58 -0
  255. data/core/services/compfs/src/lib.rs +9 -3
  256. data/core/services/cos/Cargo.toml +5 -4
  257. data/core/services/cos/LICENSE +201 -0
  258. data/core/services/cos/NOTICE +5 -0
  259. data/core/services/cos/README.md +56 -0
  260. data/core/services/cos/src/backend.rs +4 -4
  261. data/core/services/cos/src/docs.md +7 -9
  262. data/core/services/cos/src/lib.rs +12 -2
  263. data/core/services/d1/Cargo.toml +2 -1
  264. data/core/services/d1/LICENSE +201 -0
  265. data/core/services/d1/NOTICE +5 -0
  266. data/core/services/d1/README.md +56 -0
  267. data/core/services/d1/src/docs.md +7 -8
  268. data/core/services/d1/src/lib.rs +9 -3
  269. data/core/services/dashmap/Cargo.toml +2 -1
  270. data/core/services/dashmap/LICENSE +201 -0
  271. data/core/services/dashmap/NOTICE +5 -0
  272. data/core/services/dashmap/README.md +56 -0
  273. data/core/services/dashmap/src/backend.rs +1 -2
  274. data/core/services/dashmap/src/docs.md +7 -4
  275. data/core/services/dashmap/src/lib.rs +9 -4
  276. data/core/services/dbfs/Cargo.toml +2 -1
  277. data/core/services/dbfs/LICENSE +201 -0
  278. data/core/services/dbfs/NOTICE +5 -0
  279. data/core/services/dbfs/README.md +56 -0
  280. data/core/services/dbfs/src/backend.rs +2 -2
  281. data/core/services/dbfs/src/core.rs +1 -1
  282. data/core/services/dbfs/src/docs.md +7 -6
  283. data/core/services/dbfs/src/lib.rs +9 -4
  284. data/core/services/dbfs/src/lister.rs +1 -1
  285. data/core/services/dropbox/Cargo.toml +2 -1
  286. data/core/services/dropbox/LICENSE +201 -0
  287. data/core/services/dropbox/NOTICE +5 -0
  288. data/core/services/dropbox/README.md +56 -0
  289. data/core/services/dropbox/src/docs.md +7 -4
  290. data/core/services/dropbox/src/lib.rs +9 -4
  291. data/core/services/etcd/Cargo.toml +2 -1
  292. data/core/services/etcd/LICENSE +201 -0
  293. data/core/services/etcd/NOTICE +5 -0
  294. data/core/services/etcd/README.md +56 -0
  295. data/core/services/etcd/src/docs.md +7 -10
  296. data/core/services/etcd/src/lib.rs +9 -4
  297. data/core/services/foundationdb/Cargo.toml +2 -1
  298. data/core/services/foundationdb/LICENSE +201 -0
  299. data/core/services/foundationdb/NOTICE +5 -0
  300. data/core/services/foundationdb/README.md +56 -0
  301. data/core/services/foundationdb/src/docs.md +7 -5
  302. data/core/services/foundationdb/src/lib.rs +9 -4
  303. data/core/services/foyer/Cargo.toml +4 -3
  304. data/core/services/foyer/LICENSE +201 -0
  305. data/core/services/foyer/NOTICE +5 -0
  306. data/core/services/foyer/README.md +57 -0
  307. data/core/services/foyer/src/backend.rs +1 -1
  308. data/core/services/foyer/src/docs.md +12 -1
  309. data/core/services/foyer/src/lib.rs +9 -11
  310. data/core/services/fs/Cargo.toml +2 -1
  311. data/core/services/fs/LICENSE +201 -0
  312. data/core/services/fs/NOTICE +5 -0
  313. data/core/services/fs/README.md +56 -0
  314. data/core/services/fs/src/backend.rs +1 -1
  315. data/core/services/fs/src/docs.md +7 -5
  316. data/core/services/fs/src/lib.rs +9 -3
  317. data/core/services/ftp/Cargo.toml +2 -1
  318. data/core/services/ftp/LICENSE +201 -0
  319. data/core/services/ftp/NOTICE +5 -0
  320. data/core/services/ftp/README.md +56 -0
  321. data/core/services/ftp/src/backend.rs +1 -1
  322. data/core/services/ftp/src/docs.md +7 -7
  323. data/core/services/ftp/src/lib.rs +12 -2
  324. data/core/services/ftp/src/lister.rs +1 -1
  325. data/core/services/gcs/Cargo.toml +5 -4
  326. data/core/services/gcs/LICENSE +201 -0
  327. data/core/services/gcs/NOTICE +5 -0
  328. data/core/services/gcs/README.md +56 -0
  329. data/core/services/gcs/src/core.rs +55 -1
  330. data/core/services/gcs/src/docs.md +7 -13
  331. data/core/services/gcs/src/lib.rs +9 -3
  332. data/core/services/gcs/src/writer.rs +5 -12
  333. data/core/services/gdrive/Cargo.toml +2 -1
  334. data/core/services/gdrive/LICENSE +201 -0
  335. data/core/services/gdrive/NOTICE +5 -0
  336. data/core/services/gdrive/README.md +56 -0
  337. data/core/services/gdrive/src/docs.md +8 -5
  338. data/core/services/gdrive/src/lib.rs +12 -2
  339. data/core/services/gdrive/src/lister.rs +6 -6
  340. data/core/services/ghac/Cargo.toml +5 -4
  341. data/core/services/ghac/LICENSE +201 -0
  342. data/core/services/ghac/NOTICE +5 -0
  343. data/core/services/ghac/README.md +56 -0
  344. data/core/services/ghac/src/docs.md +7 -4
  345. data/core/services/ghac/src/lib.rs +12 -2
  346. data/core/services/github/Cargo.toml +2 -1
  347. data/core/services/github/LICENSE +201 -0
  348. data/core/services/github/NOTICE +5 -0
  349. data/core/services/github/README.md +56 -0
  350. data/core/services/github/src/backend.rs +4 -4
  351. data/core/services/github/src/docs.md +7 -8
  352. data/core/services/github/src/lib.rs +12 -2
  353. data/core/services/goosefs/Cargo.toml +4 -3
  354. data/core/services/goosefs/LICENSE +201 -0
  355. data/core/services/goosefs/NOTICE +5 -0
  356. data/core/services/goosefs/README.md +56 -0
  357. data/core/services/goosefs/src/backend.rs +31 -12
  358. data/core/services/goosefs/src/config.rs +1 -1
  359. data/core/services/goosefs/src/core.rs +72 -25
  360. data/core/services/goosefs/src/docs.md +10 -10
  361. data/core/services/goosefs/src/lib.rs +12 -2
  362. data/core/services/goosefs/src/writer.rs +24 -20
  363. data/core/services/goosefs/tests/safe_conditional_put.rs +199 -0
  364. data/core/services/gridfs/Cargo.toml +2 -1
  365. data/core/services/gridfs/LICENSE +201 -0
  366. data/core/services/gridfs/NOTICE +5 -0
  367. data/core/services/gridfs/README.md +56 -0
  368. data/core/services/gridfs/src/docs.md +7 -6
  369. data/core/services/gridfs/src/lib.rs +9 -4
  370. data/core/services/hdfs/Cargo.toml +2 -1
  371. data/core/services/hdfs/LICENSE +201 -0
  372. data/core/services/hdfs/NOTICE +5 -0
  373. data/core/services/hdfs/README.md +58 -0
  374. data/core/services/hdfs/src/backend.rs +1 -1
  375. data/core/services/hdfs/src/docs.md +9 -9
  376. data/core/services/hdfs/src/lib.rs +12 -2
  377. data/core/services/hdfs-native/Cargo.toml +3 -2
  378. data/core/services/hdfs-native/LICENSE +201 -0
  379. data/core/services/hdfs-native/NOTICE +5 -0
  380. data/core/services/hdfs-native/README.md +56 -0
  381. data/core/services/hdfs-native/src/backend.rs +3 -2
  382. data/core/services/hdfs-native/src/docs.md +7 -4
  383. data/core/services/hdfs-native/src/lib.rs +12 -2
  384. data/core/services/hdfs-native/src/writer.rs +1 -1
  385. data/core/services/hf/Cargo.toml +3 -2
  386. data/core/services/hf/LICENSE +201 -0
  387. data/core/services/hf/NOTICE +5 -0
  388. data/core/services/hf/README.md +56 -0
  389. data/core/services/hf/src/backend.rs +11 -9
  390. data/core/services/hf/src/config.rs +1 -1
  391. data/core/services/hf/src/core.rs +12 -12
  392. data/core/services/hf/src/docs.md +11 -12
  393. data/core/services/hf/src/lib.rs +13 -3
  394. data/core/services/hf/src/lister.rs +1 -1
  395. data/core/services/http/Cargo.toml +4 -3
  396. data/core/services/http/LICENSE +201 -0
  397. data/core/services/http/NOTICE +5 -0
  398. data/core/services/http/README.md +56 -0
  399. data/core/services/http/src/backend.rs +1 -1
  400. data/core/services/http/src/docs.md +7 -5
  401. data/core/services/http/src/lib.rs +10 -4
  402. data/core/services/ipfs/Cargo.toml +2 -1
  403. data/core/services/ipfs/LICENSE +201 -0
  404. data/core/services/ipfs/NOTICE +5 -0
  405. data/core/services/ipfs/README.md +56 -0
  406. data/core/services/ipfs/src/backend.rs +2 -2
  407. data/core/services/ipfs/src/docs.md +7 -5
  408. data/core/services/ipfs/src/lib.rs +9 -3
  409. data/core/services/ipmfs/Cargo.toml +2 -1
  410. data/core/services/ipmfs/LICENSE +201 -0
  411. data/core/services/ipmfs/NOTICE +5 -0
  412. data/core/services/ipmfs/README.md +56 -0
  413. data/core/services/ipmfs/src/docs.md +10 -1
  414. data/core/services/ipmfs/src/lib.rs +12 -2
  415. data/core/services/ipmfs/src/lister.rs +2 -2
  416. data/core/services/koofr/Cargo.toml +2 -1
  417. data/core/services/koofr/LICENSE +201 -0
  418. data/core/services/koofr/NOTICE +5 -0
  419. data/core/services/koofr/README.md +56 -0
  420. data/core/services/koofr/src/backend.rs +4 -4
  421. data/core/services/koofr/src/docs.md +7 -7
  422. data/core/services/koofr/src/lib.rs +12 -2
  423. data/core/services/lakefs/Cargo.toml +2 -1
  424. data/core/services/lakefs/LICENSE +201 -0
  425. data/core/services/lakefs/NOTICE +5 -0
  426. data/core/services/lakefs/README.md +56 -0
  427. data/core/services/lakefs/src/backend.rs +5 -5
  428. data/core/services/lakefs/src/docs.md +7 -9
  429. data/core/services/lakefs/src/lib.rs +9 -4
  430. data/core/services/lakefs/src/lister.rs +1 -1
  431. data/core/services/memcached/Cargo.toml +3 -2
  432. data/core/services/memcached/LICENSE +201 -0
  433. data/core/services/memcached/NOTICE +5 -0
  434. data/core/services/memcached/README.md +56 -0
  435. data/core/services/memcached/src/docs.md +7 -8
  436. data/core/services/memcached/src/lib.rs +9 -4
  437. data/core/services/mini_moka/Cargo.toml +2 -1
  438. data/core/services/mini_moka/LICENSE +201 -0
  439. data/core/services/mini_moka/NOTICE +5 -0
  440. data/core/services/mini_moka/README.md +56 -0
  441. data/core/services/mini_moka/src/backend.rs +1 -1
  442. data/core/services/mini_moka/src/docs.md +10 -1
  443. data/core/services/mini_moka/src/lib.rs +9 -4
  444. data/core/services/moka/Cargo.toml +2 -1
  445. data/core/services/moka/LICENSE +201 -0
  446. data/core/services/moka/NOTICE +5 -0
  447. data/core/services/moka/README.md +56 -0
  448. data/core/services/moka/src/backend.rs +1 -2
  449. data/core/services/moka/src/docs.md +7 -7
  450. data/core/services/moka/src/lib.rs +9 -3
  451. data/core/services/mongodb/Cargo.toml +2 -1
  452. data/core/services/mongodb/LICENSE +201 -0
  453. data/core/services/mongodb/NOTICE +5 -0
  454. data/core/services/mongodb/README.md +56 -0
  455. data/core/services/mongodb/src/docs.md +7 -7
  456. data/core/services/mongodb/src/lib.rs +12 -2
  457. data/core/services/monoiofs/Cargo.toml +2 -1
  458. data/core/services/monoiofs/LICENSE +201 -0
  459. data/core/services/monoiofs/NOTICE +5 -0
  460. data/core/services/monoiofs/README.md +58 -0
  461. data/core/services/monoiofs/src/docs.md +7 -4
  462. data/core/services/monoiofs/src/lib.rs +9 -4
  463. data/core/services/mysql/Cargo.toml +2 -1
  464. data/core/services/mysql/LICENSE +201 -0
  465. data/core/services/mysql/NOTICE +5 -0
  466. data/core/services/mysql/README.md +56 -0
  467. data/core/services/mysql/src/docs.md +7 -6
  468. data/core/services/mysql/src/lib.rs +12 -2
  469. data/core/services/obs/Cargo.toml +5 -4
  470. data/core/services/obs/LICENSE +201 -0
  471. data/core/services/obs/NOTICE +5 -0
  472. data/core/services/obs/README.md +56 -0
  473. data/core/services/obs/src/backend.rs +4 -4
  474. data/core/services/obs/src/docs.md +7 -9
  475. data/core/services/obs/src/lib.rs +12 -2
  476. data/core/services/onedrive/Cargo.toml +2 -1
  477. data/core/services/onedrive/LICENSE +201 -0
  478. data/core/services/onedrive/NOTICE +5 -0
  479. data/core/services/onedrive/README.md +56 -0
  480. data/core/services/onedrive/src/docs.md +10 -10
  481. data/core/services/onedrive/src/lib.rs +9 -3
  482. data/core/services/opfs/Cargo.toml +4 -1
  483. data/core/services/opfs/LICENSE +201 -0
  484. data/core/services/opfs/NOTICE +5 -0
  485. data/core/services/opfs/README.md +63 -0
  486. data/core/services/opfs/src/core.rs +2 -0
  487. data/core/services/opfs/src/docs.md +7 -1
  488. data/core/services/opfs/src/lib.rs +11 -2
  489. data/core/services/oss/Cargo.toml +5 -4
  490. data/core/services/oss/LICENSE +201 -0
  491. data/core/services/oss/NOTICE +5 -0
  492. data/core/services/oss/README.md +56 -0
  493. data/core/services/oss/src/backend.rs +4 -4
  494. data/core/services/oss/src/docs.md +10 -22
  495. data/core/services/oss/src/lib.rs +9 -3
  496. data/core/services/pcloud/Cargo.toml +2 -1
  497. data/core/services/pcloud/LICENSE +201 -0
  498. data/core/services/pcloud/NOTICE +5 -0
  499. data/core/services/pcloud/README.md +56 -0
  500. data/core/services/pcloud/src/backend.rs +3 -3
  501. data/core/services/pcloud/src/docs.md +7 -7
  502. data/core/services/pcloud/src/lib.rs +9 -3
  503. data/core/services/persy/Cargo.toml +2 -1
  504. data/core/services/persy/LICENSE +201 -0
  505. data/core/services/persy/NOTICE +5 -0
  506. data/core/services/persy/README.md +56 -0
  507. data/core/services/persy/src/backend.rs +0 -1
  508. data/core/services/persy/src/docs.md +7 -6
  509. data/core/services/persy/src/lib.rs +9 -3
  510. data/core/services/postgresql/Cargo.toml +2 -1
  511. data/core/services/postgresql/LICENSE +201 -0
  512. data/core/services/postgresql/NOTICE +5 -0
  513. data/core/services/postgresql/README.md +56 -0
  514. data/core/services/postgresql/src/docs.md +7 -6
  515. data/core/services/postgresql/src/lib.rs +12 -2
  516. data/core/services/redb/Cargo.toml +2 -1
  517. data/core/services/redb/LICENSE +201 -0
  518. data/core/services/redb/NOTICE +5 -0
  519. data/core/services/redb/README.md +56 -0
  520. data/core/services/redb/src/backend.rs +0 -1
  521. data/core/services/redb/src/docs.md +7 -5
  522. data/core/services/redb/src/lib.rs +9 -3
  523. data/core/services/redis/Cargo.toml +2 -1
  524. data/core/services/redis/LICENSE +201 -0
  525. data/core/services/redis/NOTICE +5 -0
  526. data/core/services/redis/README.md +56 -0
  527. data/core/services/redis/src/docs.md +7 -9
  528. data/core/services/redis/src/lib.rs +9 -4
  529. data/core/services/rocksdb/Cargo.toml +2 -1
  530. data/core/services/rocksdb/LICENSE +201 -0
  531. data/core/services/rocksdb/NOTICE +5 -0
  532. data/core/services/rocksdb/README.md +56 -0
  533. data/core/services/rocksdb/src/backend.rs +0 -1
  534. data/core/services/rocksdb/src/docs.md +7 -5
  535. data/core/services/rocksdb/src/lib.rs +12 -2
  536. data/core/services/s3/Cargo.toml +5 -4
  537. data/core/services/s3/LICENSE +201 -0
  538. data/core/services/s3/NOTICE +5 -0
  539. data/core/services/s3/README.md +56 -0
  540. data/core/services/s3/src/backend.rs +60 -7
  541. data/core/services/s3/src/compatible_services.md +10 -2
  542. data/core/services/s3/src/core.rs +16 -8
  543. data/core/services/s3/src/docs.md +7 -28
  544. data/core/services/s3/src/lib.rs +9 -3
  545. data/core/services/seafile/Cargo.toml +2 -1
  546. data/core/services/seafile/LICENSE +201 -0
  547. data/core/services/seafile/NOTICE +5 -0
  548. data/core/services/seafile/README.md +56 -0
  549. data/core/services/seafile/src/backend.rs +3 -3
  550. data/core/services/seafile/src/docs.md +7 -8
  551. data/core/services/seafile/src/lib.rs +12 -2
  552. data/core/services/sftp/Cargo.toml +2 -1
  553. data/core/services/sftp/LICENSE +201 -0
  554. data/core/services/sftp/NOTICE +5 -0
  555. data/core/services/sftp/README.md +56 -0
  556. data/core/services/sftp/src/backend.rs +2 -2
  557. data/core/services/sftp/src/docs.md +7 -9
  558. data/core/services/sftp/src/lib.rs +9 -4
  559. data/core/services/sled/Cargo.toml +2 -1
  560. data/core/services/sled/LICENSE +201 -0
  561. data/core/services/sled/NOTICE +5 -0
  562. data/core/services/sled/README.md +56 -0
  563. data/core/services/sled/src/backend.rs +0 -1
  564. data/core/services/sled/src/docs.md +7 -4
  565. data/core/services/sled/src/lib.rs +12 -2
  566. data/core/services/sqlite/Cargo.toml +2 -1
  567. data/core/services/sqlite/LICENSE +201 -0
  568. data/core/services/sqlite/NOTICE +5 -0
  569. data/core/services/sqlite/README.md +56 -0
  570. data/core/services/sqlite/src/backend.rs +0 -1
  571. data/core/services/sqlite/src/docs.md +7 -6
  572. data/core/services/sqlite/src/lib.rs +12 -2
  573. data/core/services/surrealdb/Cargo.toml +2 -1
  574. data/core/services/surrealdb/LICENSE +201 -0
  575. data/core/services/surrealdb/NOTICE +5 -0
  576. data/core/services/surrealdb/README.md +56 -0
  577. data/core/services/surrealdb/src/docs.md +8 -10
  578. data/core/services/surrealdb/src/lib.rs +9 -3
  579. data/core/services/swift/Cargo.toml +2 -1
  580. data/core/services/swift/LICENSE +201 -0
  581. data/core/services/swift/NOTICE +5 -0
  582. data/core/services/swift/README.md +56 -0
  583. data/core/services/swift/src/backend.rs +2 -2
  584. data/core/services/swift/src/core.rs +24 -24
  585. data/core/services/swift/src/deleter.rs +1 -1
  586. data/core/services/swift/src/docs.md +7 -6
  587. data/core/services/swift/src/lib.rs +9 -4
  588. data/core/services/swift/src/writer.rs +1 -1
  589. data/core/services/tikv/Cargo.toml +2 -1
  590. data/core/services/tikv/LICENSE +201 -0
  591. data/core/services/tikv/NOTICE +5 -0
  592. data/core/services/tikv/README.md +56 -0
  593. data/core/services/tikv/src/backend.rs +1 -1
  594. data/core/services/tikv/src/docs.md +7 -8
  595. data/core/services/tikv/src/lib.rs +12 -2
  596. data/core/services/tos/Cargo.toml +5 -4
  597. data/core/services/tos/LICENSE +201 -0
  598. data/core/services/tos/NOTICE +5 -0
  599. data/core/services/tos/README.md +56 -0
  600. data/core/services/tos/src/docs.md +7 -10
  601. data/core/services/tos/src/lib.rs +9 -3
  602. data/core/services/upyun/Cargo.toml +2 -1
  603. data/core/services/upyun/LICENSE +201 -0
  604. data/core/services/upyun/NOTICE +5 -0
  605. data/core/services/upyun/README.md +56 -0
  606. data/core/services/upyun/src/backend.rs +3 -3
  607. data/core/services/upyun/src/docs.md +7 -7
  608. data/core/services/upyun/src/lib.rs +9 -4
  609. data/core/services/vercel-artifacts/Cargo.toml +2 -1
  610. data/core/services/vercel-artifacts/LICENSE +201 -0
  611. data/core/services/vercel-artifacts/NOTICE +5 -0
  612. data/core/services/vercel-artifacts/README.md +56 -0
  613. data/core/services/vercel-artifacts/src/docs.md +7 -7
  614. data/core/services/vercel-artifacts/src/lib.rs +11 -3
  615. data/core/services/vercel-blob/Cargo.toml +2 -1
  616. data/core/services/vercel-blob/LICENSE +201 -0
  617. data/core/services/vercel-blob/NOTICE +5 -0
  618. data/core/services/vercel-blob/README.md +56 -0
  619. data/core/services/vercel-blob/src/backend.rs +2 -2
  620. data/core/services/vercel-blob/src/docs.md +7 -5
  621. data/core/services/vercel-blob/src/lib.rs +12 -2
  622. data/core/services/webdav/Cargo.toml +2 -1
  623. data/core/services/webdav/LICENSE +201 -0
  624. data/core/services/webdav/NOTICE +5 -0
  625. data/core/services/webdav/README.md +56 -0
  626. data/core/services/webdav/src/backend.rs +1 -1
  627. data/core/services/webdav/src/docs.md +7 -7
  628. data/core/services/webdav/src/lib.rs +12 -2
  629. data/core/services/webhdfs/Cargo.toml +2 -1
  630. data/core/services/webhdfs/LICENSE +201 -0
  631. data/core/services/webhdfs/NOTICE +5 -0
  632. data/core/services/webhdfs/README.md +56 -0
  633. data/core/services/webhdfs/src/docs.md +11 -9
  634. data/core/services/webhdfs/src/lib.rs +12 -2
  635. data/core/services/yandex-disk/Cargo.toml +2 -1
  636. data/core/services/yandex-disk/LICENSE +201 -0
  637. data/core/services/yandex-disk/NOTICE +5 -0
  638. data/core/services/yandex-disk/README.md +56 -0
  639. data/core/services/yandex-disk/src/backend.rs +2 -2
  640. data/core/services/yandex-disk/src/docs.md +7 -5
  641. data/core/services/yandex-disk/src/lib.rs +9 -3
  642. data/core/src/lib.rs +30 -4
  643. data/core/testkit/Cargo.toml +5 -4
  644. data/core/testkit/LICENSE +201 -0
  645. data/core/testkit/NOTICE +5 -0
  646. data/core/testkit/README.md +25 -0
  647. data/core/testkit/src/lib.rs +4 -2
  648. data/core/testkit/src/read.rs +12 -13
  649. data/core/testkit/src/write.rs +12 -13
  650. data/core/tests/behavior/async_list.rs +2 -2
  651. data/core/tests/behavior/async_presign.rs +4 -4
  652. data/core/tests/behavior/async_stat.rs +1 -1
  653. data/core/tests/behavior/async_write.rs +1 -1
  654. data/core/users.md +2 -0
  655. data/opendal.gemspec +5 -1
  656. metadata +283 -2
@@ -0,0 +1,56 @@
1
+ # Apache OpenDAL™ GooseFS Service
2
+
3
+ `opendal-service-goosefs` provides access to GooseFS through its native gRPC API for
4
+ applications built with Apache OpenDAL™.
5
+
6
+ ## Use through `opendal`
7
+
8
+ Applications should normally enable this service through the `opendal` facade with the
9
+ `services-goosefs` feature:
10
+
11
+ ```shell
12
+ cargo add opendal --features services-goosefs
13
+ ```
14
+
15
+ The service is available as `opendal::services::GooseFs`. Configure the
16
+ service builder, then pass it to `opendal::Operator::new`.
17
+
18
+ ## Use with `opendal-core`
19
+
20
+ Add the split crates directly:
21
+
22
+ ```shell
23
+ cargo add opendal-core opendal-service-goosefs
24
+ ```
25
+
26
+ Pass a configured service builder to `Operator::new`:
27
+
28
+ ```rust
29
+ use opendal_core::{Operator, OperatorRegistry, Result};
30
+ use opendal_service_goosefs::{register_goosefs_service, GooseFs};
31
+
32
+ fn build_operator(builder: GooseFs) -> Result<Operator> {
33
+ Operator::new(builder)
34
+ }
35
+
36
+ fn register_for_uri() {
37
+ register_goosefs_service(OperatorRegistry::get());
38
+ }
39
+ ```
40
+
41
+ `register_for_uri` is only needed for scheme-driven construction through
42
+ `Operator::from_uri` or `Operator::via_iter`.
43
+
44
+ Services that send HTTP requests also require an HTTP transport in
45
+ `OperationContext`. See the
46
+ [`opendal-core` composition example](https://crates.io/crates/opendal-core).
47
+
48
+ ## Documentation
49
+
50
+ - [Service configuration and examples](https://opendal.apache.org/services/goosefs)
51
+ - [Rust API documentation](https://docs.rs/opendal-service-goosefs)
52
+ - [Apache OpenDAL documentation](https://opendal.apache.org/docs/)
53
+
54
+ ## License
55
+
56
+ Licensed under the Apache License, Version 2.0.
@@ -67,9 +67,10 @@ impl GoosefsBuilder {
67
67
  /// When provided here it **overrides** any address discovered from
68
68
  /// `goosefs-site.properties` or `GOOSEFS_MASTER_ADDR`. When left unset
69
69
  /// the builder falls back to the SDK's auto-discovery chain
70
- /// (defaults → properties → env) — see `goosefs-sdk`
71
- /// `docs/CLIENT_CONFIGURATION.md` §1. `build()` fails with
72
- /// `ConfigInvalid` only if **no** source supplies a master address.
70
+ /// (defaults → properties → env) — see
71
+ /// [goosefs-sdk `docs/CLIENT_CONFIGURATION.md`](https://github.com/Tencent/tencent-goosefs-rust-sdk/blob/main/docs/CLIENT_CONFIGURATION.md)
72
+ /// §1. `build()` fails with `ConfigInvalid` only if **no** source
73
+ /// supplies a master address.
73
74
  pub fn master_addr(mut self, addr: &str) -> Self {
74
75
  if !addr.is_empty() {
75
76
  self.config.master_addr = Some(addr.to_string());
@@ -128,16 +129,17 @@ impl Builder for GoosefsBuilder {
128
129
 
129
130
  /// Build the backend and return a GoosefsBackend.
130
131
  fn build(self) -> Result<impl Service> {
131
- debug!("GoosefsBuilder::build started: {:?}", &self);
132
+ debug!("GoosefsBuilder::build started: {:?}", self);
132
133
 
133
134
  let root = normalize_root(&self.config.root.clone().unwrap_or_default());
134
- debug!("GoosefsBuilder use root {}", &root);
135
+ debug!("GoosefsBuilder use root {}", root);
135
136
 
136
137
  // ── Step 1: establish the base SDK config ─────────────────────────────
137
138
  //
138
139
  // We follow the same priority chain that `FileSystemContext::connect`
139
140
  // (and its `ConfigRefresher`) uses — see
140
- // `docs/CLIENT_CONFIGURATION.md` §1 "Configuration Loading Priority":
141
+ // https://github.com/Tencent/tencent-goosefs-rust-sdk/blob/main/docs/CLIENT_CONFIGURATION.md
142
+ // §1 "Configuration Loading Priority":
141
143
  //
142
144
  // defaults < goosefs-site.properties < GOOSEFS_* env vars
143
145
  //
@@ -210,7 +212,7 @@ impl Builder for GoosefsBuilder {
210
212
  }
211
213
  debug!(
212
214
  "GoosefsBuilder use master_addr {} (addrs={:?})",
213
- &goosefs_config.master_addr, &goosefs_config.master_addrs
215
+ goosefs_config.master_addr, goosefs_config.master_addrs
214
216
  );
215
217
 
216
218
  if let Some(block_size) = self.config.block_size {
@@ -274,14 +276,16 @@ impl Builder for GoosefsBuilder {
274
276
  read: true,
275
277
  write: true,
276
278
  write_can_multi: true,
277
- // GooseFS createFile fails with AlreadyExists if file exists,
278
- // which naturally provides if_not_exists semantics.
279
- // Lance Dataset relies on this for manifest commit safety.
279
+ // Authoritative Create: write-via-temp then
280
+ // GoosefsCore::rename(..., if_not_exists=true), backed by Master
281
+ // no-replace rename. Not CreateFile on the final path
282
+ // (writes go to .opendal.tmp.*).
280
283
  write_with_if_not_exists: true,
281
284
  create_dir: true,
282
285
  delete: true,
283
286
  list: true,
284
287
  rename: true,
288
+ rename_with_if_not_exists: true,
285
289
  shared: true,
286
290
  ..Default::default()
287
291
  },
@@ -385,9 +389,9 @@ impl Service for GoosefsBackend {
385
389
  _ctx: &OperationContext,
386
390
  from: &str,
387
391
  to: &str,
388
- _: OpRename,
392
+ args: OpRename,
389
393
  ) -> Result<RpRename> {
390
- self.core.rename(from, to).await?;
394
+ self.core.rename(from, to, args.if_not_exists()).await?;
391
395
  Ok(RpRename::default())
392
396
  }
393
397
 
@@ -444,4 +448,19 @@ mod tests {
444
448
  "unexpected error message: {err}"
445
449
  );
446
450
  }
451
+
452
+ #[test]
453
+ fn test_capability_rename_with_if_not_exists() {
454
+ let backend = GoosefsBuilder::default()
455
+ .root("/data")
456
+ .master_addr("127.0.0.1:9200")
457
+ .build()
458
+ .expect("build");
459
+ let cap = backend.capability();
460
+ assert!(cap.write_with_if_not_exists);
461
+ assert!(
462
+ cap.rename_with_if_not_exists,
463
+ "rename_with_if_not_exists must be declared for Create publish"
464
+ );
465
+ }
447
466
  }
@@ -45,7 +45,7 @@ pub struct GoosefsConfig {
45
45
  /// `PollingMasterInquireClient` to discover the Primary Master automatically.
46
46
  ///
47
47
  /// Resolution precedence at `build()` time (highest → lowest), following
48
- /// `goosefs-sdk` `docs/CLIENT_CONFIGURATION.md` §1:
48
+ /// [goosefs-sdk `docs/CLIENT_CONFIGURATION.md`](https://github.com/Tencent/tencent-goosefs-rust-sdk/blob/main/docs/CLIENT_CONFIGURATION.md) §1:
49
49
  /// 1. This field (when set on the builder / OpenDAL config map)
50
50
  /// 2. `GOOSEFS_MASTER_ADDR` environment variable
51
51
  /// 3. `goosefs.master.rpc.addresses` / `goosefs.master.hostname` in
@@ -231,7 +231,9 @@ impl GoosefsCore {
231
231
 
232
232
  pub async fn delete(&self, path: &str) -> Result<()> {
233
233
  let full = self.full_path(path);
234
- let master = self.master().await?;
234
+ let ctx = self.ctx().await?;
235
+ let master = ctx.acquire_master();
236
+ ctx.invalidate_file_info(&full);
235
237
  match master.delete(&full, false).await {
236
238
  Ok(()) => Ok(()),
237
239
  Err(e) => {
@@ -245,29 +247,37 @@ impl GoosefsCore {
245
247
  }
246
248
  }
247
249
 
248
- pub async fn rename(&self, from: &str, to: &str) -> Result<()> {
250
+ /// Rename `from` `to`.
251
+ ///
252
+ /// When `if_not_exists` is true this is the authoritative publish step for
253
+ /// `write_with_if_not_exists` (Lance `PutMode::Create`). Conflict detection
254
+ /// is GooseFS Master no-replace rename
255
+ /// (`DefaultFileSystemMaster.renameInternal` → `FileAlreadyExistsException`
256
+ /// → gRPC ALREADY_EXISTS), reached via `MasterClient::rename`. Not a prior
257
+ /// `get_status`. Destination must never be deleted when `if_not_exists` is
258
+ /// true.
259
+ ///
260
+ /// Mirror HDFS / Alluxio semantics so GooseFS passes OpenDAL's behavior
261
+ /// contract for rename:
262
+ ///
263
+ /// 1. Destination parent directory is created on demand.
264
+ /// 2. If the destination exists as a file and `if_not_exists` is false,
265
+ /// overwrite by deleting it first. Never delete when `if_not_exists`
266
+ /// is true (that would destroy Master no-replace and re-introduce
267
+ /// TOCTOU overwrite).
268
+ /// 3. If the destination exists as a directory, surface `IsADirectory`.
269
+ ///
270
+ /// Source NotFound is reported by the underlying `rename` RPC, so we
271
+ /// don't pre-stat the source.
272
+ pub async fn rename(&self, from: &str, to: &str, if_not_exists: bool) -> Result<()> {
249
273
  let src = self.full_path(from);
250
274
  let dst = self.full_path(to);
251
- let master = self.master().await?;
275
+ let ctx = self.ctx().await?;
276
+ let master = ctx.acquire_master();
277
+
278
+ ctx.invalidate_file_info(&src);
279
+ ctx.invalidate_file_info(&dst);
252
280
 
253
- // Mirror HDFS / Alluxio semantics so GooseFS passes OpenDAL's
254
- // behavior contract for rename (see `rename_test.go::testRenameNested`
255
- // and `testRenameOverwrite`):
256
- //
257
- // 1. Destination parent directory is created on demand
258
- // (`testRenameNested` writes to `<uuid>/<uuid>/<uuid>` without
259
- // mkdir'ing intermediate levels first; S3-style backends treat
260
- // this as implicit, fs-style backends must emulate it).
261
- // 2. If the destination exists as a file, overwrite by deleting
262
- // it first (`testRenameOverwrite`). GooseFS `rename` itself
263
- // would otherwise return AlreadyExists.
264
- // 3. If the destination exists as a directory, surface
265
- // IsADirectory — matches the error code the behavior tests
266
- // assert in `testRenameTargetDir`.
267
- //
268
- // Source NotFound is reported by the underlying `rename` RPC
269
- // (`testRenameNonExistingSource` expects `NotFound`), so we don't
270
- // pre-stat the source.
271
281
  match master.get_status(&dst).await {
272
282
  Ok(info) => {
273
283
  if info.folder.unwrap_or(false) {
@@ -279,9 +289,19 @@ impl GoosefsCore {
279
289
  .with_context("from", from)
280
290
  .with_context("to", to));
281
291
  }
282
- // Destination is an existing file — delete it so the
283
- // subsequent rename can overwrite atomically at the
284
- // master level.
292
+ if if_not_exists {
293
+ // Fast-path only. Authoritative race check is master.rename
294
+ // below (AlreadyExists → ConditionNotMatch).
295
+ return Err(Error::new(
296
+ ErrorKind::ConditionNotMatch,
297
+ "target path already exists while if_not_exists is set",
298
+ )
299
+ .with_context("service", super::GOOSEFS_SCHEME)
300
+ .with_context("from", from)
301
+ .with_context("to", to));
302
+ }
303
+ // Overwrite path ONLY. Master rename rejects existing dst;
304
+ // delete first so overwrite can proceed.
285
305
  master.delete(&dst, false).await.map_err(parse_error)?;
286
306
  }
287
307
  Err(goosefs_sdk::error::Error::NotFound { .. }) => {
@@ -299,7 +319,29 @@ impl GoosefsCore {
299
319
  Err(e) => return Err(parse_error(e)),
300
320
  }
301
321
 
302
- master.rename(&src, &dst).await.map_err(parse_error)
322
+ match master.rename(&src, &dst).await {
323
+ Ok(()) => {
324
+ // Same inode id moved (Master RenameEntry.setId(srcInode.getId()))
325
+ // — not a fresh inode. Drop any cached FileInfo so a reader
326
+ // opening `dst` immediately after sees current metadata.
327
+ ctx.invalidate_file_info(&src);
328
+ ctx.invalidate_file_info(&dst);
329
+ Ok(())
330
+ }
331
+ // Authoritative TOCTOU close: concurrent winner published between
332
+ // get_status and this RPC. Master FileAlreadyExistsException →
333
+ // gRPC ALREADY_EXISTS → SDK Error::AlreadyExists.
334
+ Err(goosefs_sdk::error::Error::AlreadyExists { .. }) if if_not_exists => {
335
+ Err(Error::new(
336
+ ErrorKind::ConditionNotMatch,
337
+ "target path already exists while if_not_exists is set",
338
+ )
339
+ .with_context("service", super::GOOSEFS_SCHEME)
340
+ .with_context("from", from)
341
+ .with_context("to", to))
342
+ }
343
+ Err(e) => Err(parse_error(e)),
344
+ }
303
345
  }
304
346
 
305
347
  // ── Data I/O Operations ──────────────────────────────────
@@ -477,6 +519,11 @@ impl GoosefsCore {
477
519
  {
478
520
  metadata.set_last_modified(ts);
479
521
  }
522
+ // GooseFS Master rename preserves the same inode id; use it as etag
523
+ // for cache keys / checkout short-circuit (not commit conflict detection).
524
+ if let Some(fid) = info.file_id {
525
+ metadata.set_etag(&fid.to_string());
526
+ }
480
527
  metadata
481
528
  }
482
529
 
@@ -1,6 +1,6 @@
1
1
  ## Capabilities
2
2
 
3
- This service can be used to:
3
+ Depending on its configuration and the backing system, this service can expose:
4
4
 
5
5
  - [x] create_dir
6
6
  - [x] stat
@@ -10,8 +10,12 @@ This service can be used to:
10
10
  - [x] list
11
11
  - [ ] copy
12
12
  - [x] rename
13
+ - [x] rename (if_not_exists)
13
14
  - [ ] presign
14
15
 
16
+ Inspect the effective capability set with [`opendal_core::Operator::info`] and
17
+ [`opendal_core::OperatorInfo::capability`] after building an operator.
18
+
15
19
  ## Notes
16
20
 
17
21
  GooseFS service uses native gRPC protocol (not REST API like Alluxio),
@@ -23,18 +27,14 @@ Features:
23
27
  - **Block-level I/O**: Data reads/writes go through block-level gRPC bidirectional streaming.
24
28
  - **Consistent hash routing**: Worker selection uses consistent hashing on block IDs.
25
29
  - **All WriteTypes**: Supports MUST_CACHE, CACHE_THROUGH, THROUGH, and ASYNC_THROUGH.
30
+ - **Conditional Create**: `write_with_if_not_exists` publishes via Master no-replace
31
+ rename (`rename_with_if_not_exists`); destination is never deleted on the Create path.
26
32
 
27
33
  ## Configuration
28
34
 
29
- - `root`: Set the work directory for backend
30
- - `master_addr`: GooseFS Master address (`host:port`), supports comma-separated for HA
31
- - `block_size`: Block size for new files (default: 64 MiB)
32
- - `chunk_size`: Chunk size for streaming RPCs (default: 1 MiB)
33
- - `write_type`: Default write type (`must_cache`, `cache_through`, `through`, `async_through`)
34
- - `auth_type`: Authentication type (`nosasl`, `simple`). Default: `simple`
35
- - `auth_username`: Authentication username for SIMPLE mode. Default: current OS user
36
-
37
- You can refer to [`GoosefsBuilder`]'s docs for more information
35
+ Use [`crate::GoosefsConfig`] for serializable configuration and this builder's
36
+ methods for direct construction. The field and method documentation defines
37
+ accepted values, defaults, and environment interaction.
38
38
 
39
39
  ## Example
40
40
 
@@ -15,10 +15,20 @@
15
15
  // specific language governing permissions and limitations
16
16
  // under the License.
17
17
 
18
- /// Default scheme for GooseFS service.
18
+ #![doc = include_str!("../README.md")]
19
+ #![cfg_attr(docsrs, feature(doc_cfg))]
20
+ #![cfg_attr(docsrs, doc(auto_cfg))]
21
+ #![deny(missing_docs)]
22
+
23
+ /// URI scheme used for service registration and scheme-driven construction.
19
24
  pub const GOOSEFS_SCHEME: &str = "goosefs";
20
25
 
21
- /// Register this service into the given registry.
26
+ /// Register this service's URI scheme or schemes with an operator registry.
27
+ ///
28
+ /// Registration enables scheme-driven construction through
29
+ /// [`opendal_core::Operator::from_uri`] and
30
+ /// [`opendal_core::Operator::via_iter`]. Direct construction through
31
+ /// [`opendal_core::Operator::new`] does not require registration.
22
32
  pub fn register_goosefs_service(registry: &opendal_core::OperatorRegistry) {
23
33
  registry.register::<GooseFs>(GOOSEFS_SCHEME);
24
34
  }
@@ -150,12 +150,10 @@ impl GoosefsWriter {
150
150
  /// path, and cache both.
151
151
  async fn ensure_writer(&mut self) -> Result<&mut ClientWriter> {
152
152
  if self.writer.is_none() {
153
- // Fast-path: reject if-not-exists writes up front so we
154
- // don't open a temp that will only be thrown away at
155
- // finalize time. The *authoritative* check happens again
156
- // at finalize (see `close()` / `finalize_rename()`), which
157
- // handles the race where a concurrent writer publishes
158
- // the target between here and finalize.
153
+ // Fast-path only. Authority is GoosefsCore::rename(..., if_not_exists=true)
154
+ // Master no-replace rename (DefaultFileSystemMaster
155
+ // FileAlreadyExistsException gRPC ALREADY_EXISTS ConditionNotMatch).
156
+ // Not this get_status.
159
157
  if self.op.if_not_exists() && self.core.get_status(&self.path).await.is_ok() {
160
158
  return Err(Error::new(
161
159
  ErrorKind::ConditionNotMatch,
@@ -186,8 +184,8 @@ impl GoosefsWriter {
186
184
  return Ok(());
187
185
  };
188
186
 
189
- // Second if-not-exists check: a concurrent writer may have
190
- // published the target while we were streaming.
187
+ // Optional fast-path (optimization, not authority). Safe to keep:
188
+ // a miss here is closed by rename(..., if_not_exists) below.
191
189
  if self.op.if_not_exists() && self.core.get_status(&self.path).await.is_ok() {
192
190
  // Undo our staged temp before surfacing the error.
193
191
  let _ = self.core.delete(&tmp).await;
@@ -197,16 +195,13 @@ impl GoosefsWriter {
197
195
  ));
198
196
  }
199
197
 
200
- // Overwrite semantics: GooseFS rename does not silently replace
201
- // an existing target, so we pre-delete the final path. NotFound
202
- // is the common (fresh-write) case, so we ignore errors here
203
- // and let any real failure (e.g. permission denied) surface
204
- // through the rename below.
205
- if !self.op.if_not_exists() {
206
- let _ = self.core.delete(&self.path).await;
207
- }
208
-
209
- match self.core.rename(&tmp, &self.path).await {
198
+ // Authority: pass OpWrite.if_not_exists into core.rename.
199
+ // Overwrite delete lives only inside core.rename when if_not_exists=false.
200
+ match self
201
+ .core
202
+ .rename(&tmp, &self.path, self.op.if_not_exists())
203
+ .await
204
+ {
210
205
  Ok(()) => Ok(()),
211
206
  Err(e) => {
212
207
  // Rename failed — sweep the temp so we don't leak a
@@ -248,8 +243,13 @@ impl oio::Write for GoosefsWriter {
248
243
  }
249
244
  return Err(e);
250
245
  }
246
+ // Capture file_id before rename; Master rename keeps the same inode id.
247
+ let mut meta = Metadata::default();
248
+ if let Some(fid) = writer.file_info().file_id {
249
+ meta.set_etag(&fid.to_string());
250
+ }
251
251
  self.finalize_rename().await?;
252
- return Ok(Metadata::default());
252
+ return Ok(meta);
253
253
  }
254
254
 
255
255
  // Zero-write path: the caller closed without ever calling
@@ -271,9 +271,13 @@ impl oio::Write for GoosefsWriter {
271
271
  let _ = self.core.delete(&tmp).await;
272
272
  return Err(e);
273
273
  }
274
+ let mut meta = Metadata::default();
275
+ if let Some(fid) = w.file_info().file_id {
276
+ meta.set_etag(&fid.to_string());
277
+ }
274
278
  self.tmp_path = Some(tmp);
275
279
  self.finalize_rename().await?;
276
- Ok(Metadata::default())
280
+ Ok(meta)
277
281
  }
278
282
 
279
283
  async fn abort(&mut self) -> Result<()> {
@@ -0,0 +1,199 @@
1
+ // Licensed to the Apache Software Foundation (ASF) under one
2
+ // or more contributor license agreements. See the NOTICE file
3
+ // distributed with this work for additional information
4
+ // regarding copyright ownership. The ASF licenses this file
5
+ // to you under the Apache License, Version 2.0 (the
6
+ // "License"); you may not use this file except in compliance
7
+ // with the License. You may obtain a copy of the License at
8
+ //
9
+ // http://www.apache.org/licenses/LICENSE-2.0
10
+ //
11
+ // Unless required by applicable law or agreed to in writing,
12
+ // software distributed under the License is distributed on an
13
+ // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ // KIND, either express or implied. See the License for the
15
+ // specific language governing permissions and limitations
16
+ // under the License.
17
+
18
+ //! Acceptance tests for GooseFS safe Create / conditional rename (doc §8.8).
19
+ //!
20
+ //! Requires a live GooseFS master. Skip unless `OPENDAL_GOOSEFS_MASTER_ADDR`
21
+ //! is set (same env the behavior CI fixture exports).
22
+
23
+ use std::sync::Arc;
24
+
25
+ use opendal::ErrorKind;
26
+ use opendal::Operator;
27
+ use opendal::services::GooseFs;
28
+
29
+ fn maybe_operator() -> Option<Operator> {
30
+ let master = std::env::var("OPENDAL_GOOSEFS_MASTER_ADDR").ok()?;
31
+ if master.trim().is_empty() {
32
+ return None;
33
+ }
34
+ let root = std::env::var("OPENDAL_GOOSEFS_ROOT").unwrap_or_else(|_| "/".to_string());
35
+ let write_type =
36
+ std::env::var("OPENDAL_GOOSEFS_WRITE_TYPE").unwrap_or_else(|_| "must_cache".to_string());
37
+
38
+ let builder = GooseFs::default()
39
+ .root(&root)
40
+ .master_addr(&master)
41
+ .write_type(&write_type);
42
+ Some(Operator::new(builder).expect("build GooseFS operator"))
43
+ }
44
+
45
+ fn unique(prefix: &str) -> String {
46
+ format!(
47
+ "{prefix}-{}-{}",
48
+ std::process::id(),
49
+ std::time::SystemTime::now()
50
+ .duration_since(std::time::UNIX_EPOCH)
51
+ .unwrap()
52
+ .as_nanos()
53
+ )
54
+ }
55
+
56
+ #[tokio::test]
57
+ async fn capability_declares_rename_with_if_not_exists() {
58
+ let Some(op) = maybe_operator() else {
59
+ eprintln!("skip: OPENDAL_GOOSEFS_MASTER_ADDR unset");
60
+ return;
61
+ };
62
+ let cap = op.info().capability();
63
+ assert!(cap.write_with_if_not_exists);
64
+ assert!(cap.rename_with_if_not_exists);
65
+ }
66
+
67
+ /// T0 / T4: conditional rename against an existing dst → ConditionNotMatch;
68
+ /// destination content is unchanged (Master no-replace).
69
+ #[tokio::test]
70
+ async fn rename_if_not_exists_rejects_existing_dst() {
71
+ let Some(op) = maybe_operator() else {
72
+ eprintln!("skip: OPENDAL_GOOSEFS_MASTER_ADDR unset");
73
+ return;
74
+ };
75
+
76
+ let src = unique("t0-src");
77
+ let dst = unique("t0-dst");
78
+ op.write(&src, "from-src").await.expect("write src");
79
+ op.write(&dst, "dst-original").await.expect("write dst");
80
+
81
+ let err = op
82
+ .rename_with(&src, &dst)
83
+ .if_not_exists(true)
84
+ .await
85
+ .expect_err("rename must fail when dst exists");
86
+ assert_eq!(err.kind(), ErrorKind::ConditionNotMatch);
87
+
88
+ let dst_after = op.read(&dst).await.expect("read dst").to_bytes();
89
+ assert_eq!(&dst_after[..], b"dst-original");
90
+
91
+ let _ = op.delete(&src).await;
92
+ let _ = op.delete(&dst).await;
93
+ }
94
+
95
+ /// T3: serial second Create (write_with if_not_exists) must fail.
96
+ #[tokio::test]
97
+ async fn write_if_not_exists_serial_conflict() {
98
+ let Some(op) = maybe_operator() else {
99
+ eprintln!("skip: OPENDAL_GOOSEFS_MASTER_ADDR unset");
100
+ return;
101
+ };
102
+
103
+ let path = unique("t3-create");
104
+ op.write_with(&path, "winner")
105
+ .if_not_exists(true)
106
+ .await
107
+ .expect("first Create succeeds");
108
+
109
+ let err = op
110
+ .write_with(&path, "loser")
111
+ .if_not_exists(true)
112
+ .await
113
+ .expect_err("second Create must fail");
114
+ assert_eq!(err.kind(), ErrorKind::ConditionNotMatch);
115
+
116
+ let body = op.read(&path).await.expect("read").to_bytes();
117
+ assert_eq!(&body[..], b"winner");
118
+
119
+ let meta = op.stat(&path).await.expect("stat");
120
+ assert!(
121
+ meta.etag().is_some(),
122
+ "P1 etag (file_id) should be present after write"
123
+ );
124
+
125
+ let _ = op.delete(&path).await;
126
+ }
127
+
128
+ /// T5: overwrite (if_not_exists=false) still replaces an existing file.
129
+ #[tokio::test]
130
+ async fn overwrite_rename_still_works() {
131
+ let Some(op) = maybe_operator() else {
132
+ eprintln!("skip: OPENDAL_GOOSEFS_MASTER_ADDR unset");
133
+ return;
134
+ };
135
+
136
+ let src = unique("t5-src");
137
+ let dst = unique("t5-dst");
138
+ op.write(&src, "new-content").await.expect("write src");
139
+ op.write(&dst, "old-content").await.expect("write dst");
140
+
141
+ op.rename(&src, &dst).await.expect("overwrite rename");
142
+
143
+ let body = op.read(&dst).await.expect("read dst").to_bytes();
144
+ assert_eq!(&body[..], b"new-content");
145
+
146
+ let _ = op.delete(&dst).await;
147
+ }
148
+
149
+ /// T1: concurrent Create on the same path — exactly one wins; content is winner's.
150
+ #[tokio::test]
151
+ async fn concurrent_write_if_not_exists_exactly_one_wins() {
152
+ let Some(op) = maybe_operator() else {
153
+ eprintln!("skip: OPENDAL_GOOSEFS_MASTER_ADDR unset");
154
+ return;
155
+ };
156
+ let op = Arc::new(op);
157
+ let path = unique("t1-concurrent");
158
+
159
+ let rounds = 8u32;
160
+ for round in 0..rounds {
161
+ let p = format!("{path}-r{round}");
162
+ let op_a = op.clone();
163
+ let op_b = op.clone();
164
+ let path_a = p.clone();
165
+ let path_b = p.clone();
166
+
167
+ let (ra, rb) = tokio::join!(
168
+ async move {
169
+ op_a.write_with(&path_a, "A")
170
+ .if_not_exists(true)
171
+ .await
172
+ .map(|_| "A")
173
+ },
174
+ async move {
175
+ op_b.write_with(&path_b, "B")
176
+ .if_not_exists(true)
177
+ .await
178
+ .map(|_| "B")
179
+ },
180
+ );
181
+
182
+ match (&ra, &rb) {
183
+ (Ok(w), Err(e)) => {
184
+ assert_eq!(e.kind(), ErrorKind::ConditionNotMatch);
185
+ let body = op.read(&p).await.expect("read").to_bytes();
186
+ assert_eq!(&body[..], w.as_bytes());
187
+ }
188
+ (Err(e), Ok(w)) => {
189
+ assert_eq!(e.kind(), ErrorKind::ConditionNotMatch);
190
+ let body = op.read(&p).await.expect("read").to_bytes();
191
+ assert_eq!(&body[..], w.as_bytes());
192
+ }
193
+ (Ok(_), Ok(_)) => panic!("both concurrent Creates succeeded for {p}"),
194
+ (Err(ea), Err(eb)) => panic!("both concurrent Creates failed for {p}: {ea:?} / {eb:?}"),
195
+ }
196
+
197
+ let _ = op.delete(&p).await;
198
+ }
199
+ }
@@ -18,6 +18,7 @@
18
18
  [package]
19
19
  description = "Apache OpenDAL GridFS service implementation"
20
20
  name = "opendal-service-gridfs"
21
+ readme = "README.md"
21
22
 
22
23
  authors = { workspace = true }
23
24
  edition = { workspace = true }
@@ -34,7 +35,7 @@ all-features = true
34
35
  futures = { workspace = true }
35
36
  mea = { workspace = true }
36
37
  mongodb = "3.3.0"
37
- opendal-core = { path = "../../core", version = "0.58.0", default-features = false }
38
+ opendal-core = { path = "../../core", version = "0.58.1", default-features = false }
38
39
  serde = { workspace = true, features = ["derive"] }
39
40
 
40
41
  [dev-dependencies]