opendal/services/foundationdb/
backend.rs

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
18use std::fmt::Debug;
19use std::fmt::Formatter;
20use std::sync::Arc;
21
22use foundationdb::api::NetworkAutoStop;
23use foundationdb::Database;
24
25use crate::raw::adapters::kv;
26use crate::raw::*;
27use crate::services::FoundationdbConfig;
28use crate::Builder;
29use crate::Error;
30use crate::ErrorKind;
31use crate::Scheme;
32use crate::*;
33
34impl Configurator for FoundationdbConfig {
35    type Builder = FoundationdbBuilder;
36    fn into_builder(self) -> Self::Builder {
37        FoundationdbBuilder { config: self }
38    }
39}
40
41#[doc = include_str!("docs.md")]
42#[derive(Default)]
43pub struct FoundationdbBuilder {
44    config: FoundationdbConfig,
45}
46
47impl FoundationdbBuilder {
48    /// Set the root for Foundationdb.
49    pub fn root(mut self, path: &str) -> Self {
50        self.config.root = Some(path.into());
51        self
52    }
53
54    /// Set the config path for Foundationdb. If not set, will fallback to use default
55    pub fn config_path(mut self, path: &str) -> Self {
56        self.config.config_path = Some(path.into());
57        self
58    }
59}
60
61impl Builder for FoundationdbBuilder {
62    type Config = FoundationdbConfig;
63
64    fn build(self) -> Result<impl Access> {
65        let _network = Arc::new(unsafe { foundationdb::boot() });
66        let db;
67        if let Some(cfg_path) = &self.config.config_path {
68            db = Database::from_path(cfg_path).map_err(|e| {
69                Error::new(ErrorKind::ConfigInvalid, "open foundation db")
70                    .with_context("service", Scheme::Foundationdb)
71                    .set_source(e)
72            })?;
73        } else {
74            db = Database::default().map_err(|e| {
75                Error::new(ErrorKind::ConfigInvalid, "open foundation db")
76                    .with_context("service", Scheme::Foundationdb)
77                    .set_source(e)
78            })?
79        }
80
81        let db = Arc::new(db);
82
83        let root = normalize_root(
84            self.config
85                .root
86                .clone()
87                .unwrap_or_else(|| "/".to_string())
88                .as_str(),
89        );
90
91        Ok(FoundationdbBackend::new(Adapter { db, _network }).with_normalized_root(root))
92    }
93}
94
95/// Backend for Foundationdb services.
96pub type FoundationdbBackend = kv::Backend<Adapter>;
97
98#[derive(Clone)]
99pub struct Adapter {
100    db: Arc<Database>,
101    _network: Arc<NetworkAutoStop>,
102}
103
104impl Debug for Adapter {
105    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
106        let mut ds = f.debug_struct("Adapter");
107        ds.finish()
108    }
109}
110
111impl kv::Adapter for Adapter {
112    type Scanner = ();
113
114    fn info(&self) -> kv::Info {
115        kv::Info::new(
116            Scheme::Foundationdb,
117            "foundationdb",
118            Capability {
119                read: true,
120                write: true,
121                delete: true,
122                shared: true,
123                ..Default::default()
124            },
125        )
126    }
127
128    async fn get(&self, path: &str) -> Result<Option<Buffer>> {
129        let transaction = self.db.create_trx().expect("Unable to create transaction");
130
131        match transaction.get(path.as_bytes(), false).await {
132            Ok(slice) => match slice {
133                Some(data) => Ok(Some(Buffer::from(data.to_vec()))),
134                None => Err(Error::new(
135                    ErrorKind::NotFound,
136                    "foundationdb: key not found",
137                )),
138            },
139            Err(_) => Err(Error::new(
140                ErrorKind::NotFound,
141                "foundationdb: key not found",
142            )),
143        }
144    }
145
146    async fn set(&self, path: &str, value: Buffer) -> Result<()> {
147        let transaction = self.db.create_trx().expect("Unable to create transaction");
148
149        transaction.set(path.as_bytes(), &value.to_vec());
150
151        match transaction.commit().await {
152            Ok(_) => Ok(()),
153            Err(e) => Err(parse_transaction_commit_error(e)),
154        }
155    }
156
157    async fn delete(&self, path: &str) -> Result<()> {
158        let transaction = self.db.create_trx().expect("Unable to create transaction");
159        transaction.clear(path.as_bytes());
160
161        match transaction.commit().await {
162            Ok(_) => Ok(()),
163            Err(e) => Err(parse_transaction_commit_error(e)),
164        }
165    }
166}
167
168fn parse_transaction_commit_error(e: foundationdb::TransactionCommitError) -> Error {
169    Error::new(ErrorKind::Unexpected, e.to_string().as_str())
170        .with_context("service", Scheme::Foundationdb)
171}