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    const SCHEME: Scheme = Scheme::Foundationdb;
63    type Config = FoundationdbConfig;
64
65    fn build(self) -> Result<impl Access> {
66        let _network = Arc::new(unsafe { foundationdb::boot() });
67        let db;
68        if let Some(cfg_path) = &self.config.config_path {
69            db = Database::from_path(cfg_path).map_err(|e| {
70                Error::new(ErrorKind::ConfigInvalid, "open foundation db")
71                    .with_context("service", Scheme::Foundationdb)
72                    .set_source(e)
73            })?;
74        } else {
75            db = Database::default().map_err(|e| {
76                Error::new(ErrorKind::ConfigInvalid, "open foundation db")
77                    .with_context("service", Scheme::Foundationdb)
78                    .set_source(e)
79            })?
80        }
81
82        let db = Arc::new(db);
83
84        let root = normalize_root(
85            self.config
86                .root
87                .clone()
88                .unwrap_or_else(|| "/".to_string())
89                .as_str(),
90        );
91
92        Ok(FoundationdbBackend::new(Adapter { db, _network }).with_normalized_root(root))
93    }
94}
95
96/// Backend for Foundationdb services.
97pub type FoundationdbBackend = kv::Backend<Adapter>;
98
99#[derive(Clone)]
100pub struct Adapter {
101    db: Arc<Database>,
102    _network: Arc<NetworkAutoStop>,
103}
104
105impl Debug for Adapter {
106    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
107        let mut ds = f.debug_struct("Adapter");
108        ds.finish()
109    }
110}
111
112impl kv::Adapter for Adapter {
113    type Scanner = ();
114
115    fn info(&self) -> kv::Info {
116        kv::Info::new(
117            Scheme::Foundationdb,
118            "foundationdb",
119            Capability {
120                read: true,
121                write: true,
122                delete: true,
123                shared: true,
124                ..Default::default()
125            },
126        )
127    }
128
129    async fn get(&self, path: &str) -> Result<Option<Buffer>> {
130        let transaction = self.db.create_trx().expect("Unable to create transaction");
131
132        match transaction.get(path.as_bytes(), false).await {
133            Ok(slice) => match slice {
134                Some(data) => Ok(Some(Buffer::from(data.to_vec()))),
135                None => Err(Error::new(
136                    ErrorKind::NotFound,
137                    "foundationdb: key not found",
138                )),
139            },
140            Err(_) => Err(Error::new(
141                ErrorKind::NotFound,
142                "foundationdb: key not found",
143            )),
144        }
145    }
146
147    async fn set(&self, path: &str, value: Buffer) -> Result<()> {
148        let transaction = self.db.create_trx().expect("Unable to create transaction");
149
150        transaction.set(path.as_bytes(), &value.to_vec());
151
152        match transaction.commit().await {
153            Ok(_) => Ok(()),
154            Err(e) => Err(parse_transaction_commit_error(e)),
155        }
156    }
157
158    async fn delete(&self, path: &str) -> Result<()> {
159        let transaction = self.db.create_trx().expect("Unable to create transaction");
160        transaction.clear(path.as_bytes());
161
162        match transaction.commit().await {
163            Ok(_) => Ok(()),
164            Err(e) => Err(parse_transaction_commit_error(e)),
165        }
166    }
167}
168
169fn parse_transaction_commit_error(e: foundationdb::TransactionCommitError) -> Error {
170    Error::new(ErrorKind::Unexpected, e.to_string().as_str())
171        .with_context("service", Scheme::Foundationdb)
172}