You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@opendal.apache.org by "PsiACE (via GitHub)" <gi...@apache.org> on 2023/07/16 05:41:04 UTC
[GitHub] [incubator-opendal] PsiACE opened a new pull request, #2648: feat(services/persy): add a basic persy service impl
PsiACE opened a new pull request, #2648:
URL: https://github.com/apache/incubator-opendal/pull/2648
This PR has initially implemented support for the persy backend, and added corresponding CI and documentation.
```
# persy
OPENDAL_PERSY_TEST=false
## That path to the persy data file. The directory in the path must already exist.
OPENDAL_PERSY_DATAFILE=/tmp/opendal/test.persy
OPENDAL_PERSY_SEGMENT=data
OPENDAL_PERSY_INDEX=index
```
Fixes: #2522
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@opendal.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [incubator-opendal] PsiACE commented on a diff in pull request #2648: feat(services/persy): add a basic persy service impl
Posted by "PsiACE (via GitHub)" <gi...@apache.org>.
PsiACE commented on code in PR #2648:
URL: https://github.com/apache/incubator-opendal/pull/2648#discussion_r1264703294
##########
core/src/services/persy/backend.rs:
##########
@@ -0,0 +1,245 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+use std::collections::HashMap;
+use std::fmt::Debug;
+use std::fmt::Formatter;
+use std::str;
+
+use async_trait::async_trait;
+use persy;
+
+use crate::raw::adapters::kv;
+use crate::Builder;
+use crate::Error;
+use crate::ErrorKind;
+use crate::Scheme;
+use crate::*;
+
+/// persy service support.
+#[doc = include_str!("docs.md")]
+#[derive(Default)]
+pub struct PersyBuilder {
+ /// That path to the persy data file. The directory in the path must already exist.
+ datafile: Option<String>,
+ /// That name of the persy segment.
+ segment: Option<String>,
+ /// That name of the persy index.
+ index: Option<String>,
+}
+
+impl PersyBuilder {
+ /// Set the path to the persy data directory. Will create if not exists.
+ pub fn datafile(&mut self, path: &str) -> &mut Self {
+ self.datafile = Some(path.into());
+ self
+ }
+
+ /// Set the name of the persy segment. Will create if not exists.
+ pub fn segment(&mut self, path: &str) -> &mut Self {
+ self.segment = Some(path.into());
+ self
+ }
+
+ /// Set the name of the persy index. Will create if not exists.
+ pub fn index(&mut self, path: &str) -> &mut Self {
+ self.index = Some(path.into());
+ self
+ }
+}
+
+impl Builder for PersyBuilder {
+ const SCHEME: Scheme = Scheme::Persy;
+ type Accessor = PersyBackend;
+
+ fn from_map(map: HashMap<String, String>) -> Self {
+ let mut builder = PersyBuilder::default();
+
+ map.get("datafile").map(|v| builder.datafile(v));
+ map.get("segment").map(|v| builder.segment(v));
+ map.get("index").map(|v| builder.index(v));
+
+ builder
+ }
+
+ fn build(&mut self) -> Result<Self::Accessor> {
+ let datafile_path = self.datafile.take().ok_or_else(|| {
+ Error::new(ErrorKind::ConfigInvalid, "datafile is required but not set")
+ .with_context("service", Scheme::Persy)
+ })?;
+
+ let segment_name = self.segment.take().ok_or_else(|| {
+ Error::new(ErrorKind::ConfigInvalid, "segment is required but not set")
+ .with_context("service", Scheme::Persy)
+ })?;
+
+ let segment = segment_name.clone();
+
+ let index_name = self.index.take().ok_or_else(|| {
+ Error::new(ErrorKind::ConfigInvalid, "index is required but not set")
+ .with_context("service", Scheme::Persy)
+ })?;
+
+ let index = index_name.clone();
+
+ let persy = persy::OpenOptions::new()
+ .create(true)
+ .prepare_with(move |p| init(p, &segment_name, &index_name))
+ .open(&datafile_path)
+ .map_err(|e| {
+ Error::new(ErrorKind::ConfigInvalid, "open db")
+ .with_context("service", Scheme::Persy)
+ .with_context("datafile", datafile_path.clone())
+ .set_source(e)
+ })?;
+
+ // This function will only be called on database creation
+ fn init(
+ persy: &persy::Persy,
+ segment_name: &str,
+ index_name: &str,
+ ) -> std::result::Result<(), Box<dyn std::error::Error>> {
+ let mut tx = persy.begin()?;
+
+ if !tx.exists_segment(segment_name)? {
+ tx.create_segment(segment_name)?;
+ }
+ if !tx.exists_index(index_name)? {
+ tx.create_index::<String, persy::PersyId>(index_name, persy::ValueMode::Replace)?;
+ }
+
+ let prepared = tx.prepare()?;
+ prepared.commit()?;
+
+ println!("Segment and Index successfully created");
+ Ok(())
+ }
+
+ Ok(PersyBackend::new(Adapter {
+ datafile: datafile_path,
+ segment,
+ index,
+ persy,
+ }))
+ }
+}
+
+/// Backend for persy services.
+pub type PersyBackend = kv::Backend<Adapter>;
+
+#[derive(Clone)]
+pub struct Adapter {
+ datafile: String,
+ segment: String,
+ index: String,
+ persy: persy::Persy,
+}
+
+impl Debug for Adapter {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ let mut ds = f.debug_struct("Adapter");
+ ds.field("path", &self.datafile);
+ ds.field("segment", &self.segment);
+ ds.field("index", &self.index);
+ ds.finish()
+ }
+}
+
+#[async_trait]
+impl kv::Adapter for Adapter {
+ fn metadata(&self) -> kv::Metadata {
+ kv::Metadata::new(
+ Scheme::Persy,
+ &self.datafile,
+ Capability {
+ read: true,
+ write: true,
+ delete: true,
+ blocking: true,
+ ..Default::default()
+ },
+ )
+ }
+
+ async fn get(&self, path: &str) -> Result<Option<Vec<u8>>> {
+ self.blocking_get(path)
+ }
+
+ fn blocking_get(&self, path: &str) -> Result<Option<Vec<u8>>> {
+ let mut read_id = self
+ .persy
+ .get::<String, persy::PersyId>(&self.index, &path.to_string())
+ .map_err(parse_error)?;
+ if let Some(id) = read_id.next() {
+ let value = self.persy.read(&self.segment, &id).map_err(parse_error)?;
+ return Ok(value);
+ }
+
+ Ok(None)
+ }
+
+ async fn set(&self, path: &str, value: &[u8]) -> Result<()> {
+ self.blocking_set(path, value)
+ }
+
+ fn blocking_set(&self, path: &str, value: &[u8]) -> Result<()> {
+ let mut tx = self.persy.begin().map_err(parse_error)?;
Review Comment:
https://persy.rs/posts/getting-started.html
```rust
let mut tx = persy.begin()?;
tx.create_segment("data")?;
tx.create_index::<String, PersyId>("index", ValueMode::Replace)?;
let prepared = tx.prepare()?;
prepared.commit()?;
let mut tx = persy.begin()?;
let rec = "record content".as_bytes();
let id = tx.insert("data", rec)?;
tx.put::<String, PersyId>("index", "key".to_string(), id)?;
let prepared = tx.prepare()?;
prepared.commit()?;
let read_id = persy.get::<String, PersyId>("index", &"key".to_string())?;
if let Some(id) = read_id.next() {
let value = persy.read("data", &id)?;
assert_eq!(Some(rec.to_vec()), value);
}
```
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@opendal.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [incubator-opendal] suyanhanx commented on a diff in pull request #2648: feat(services/persy): add a basic persy service impl
Posted by "suyanhanx (via GitHub)" <gi...@apache.org>.
suyanhanx commented on code in PR #2648:
URL: https://github.com/apache/incubator-opendal/pull/2648#discussion_r1264641133
##########
.github/workflows/service_test_persy.yml:
##########
@@ -0,0 +1,56 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+name: Service Test Persy
+
+on:
+ push:
+ branches:
+ - main
+ pull_request:
+ branches:
+ - main
+ paths:
+ - "core/src/**"
+ - "core/tests/**"
+ - "!core/src/docs/**"
+ - "!core/src/services/**"
+ - "core/src/services/persy/**"
+ - ".github/workflows/service_test_persy.yml"
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}
+ cancel-in-progress: true
+
+jobs:
+ persy:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ - name: Setup Rust toolchain
+ uses: ./.github/actions/setup
+ - name: Test persy
+ shell: bash
+ working-directory: core
+ run: cargo test persy --features services-persy -j=1
Review Comment:
Is this `j=1` necessary?
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@opendal.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [incubator-opendal] PsiACE commented on a diff in pull request #2648: feat(services/persy): add a basic persy service impl
Posted by "PsiACE (via GitHub)" <gi...@apache.org>.
PsiACE commented on code in PR #2648:
URL: https://github.com/apache/incubator-opendal/pull/2648#discussion_r1264644163
##########
.github/workflows/service_test_persy.yml:
##########
@@ -0,0 +1,56 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+name: Service Test Persy
+
+on:
+ push:
+ branches:
+ - main
+ pull_request:
+ branches:
+ - main
+ paths:
+ - "core/src/**"
+ - "core/tests/**"
+ - "!core/src/docs/**"
+ - "!core/src/services/**"
+ - "core/src/services/persy/**"
+ - ".github/workflows/service_test_persy.yml"
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}
+ cancel-in-progress: true
+
+jobs:
+ persy:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ - name: Setup Rust toolchain
+ uses: ./.github/actions/setup
+ - name: Test persy
+ shell: bash
+ working-directory: core
+ run: cargo test persy --features services-persy -j=1
Review Comment:
No, it's not necessary. I removed it
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@opendal.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [incubator-opendal] PsiACE commented on a diff in pull request #2648: feat(services/persy): add a basic persy service impl
Posted by "PsiACE (via GitHub)" <gi...@apache.org>.
PsiACE commented on code in PR #2648:
URL: https://github.com/apache/incubator-opendal/pull/2648#discussion_r1264703738
##########
core/src/services/persy/backend.rs:
##########
@@ -0,0 +1,245 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+use std::collections::HashMap;
+use std::fmt::Debug;
+use std::fmt::Formatter;
+use std::str;
+
+use async_trait::async_trait;
+use persy;
+
+use crate::raw::adapters::kv;
+use crate::Builder;
+use crate::Error;
+use crate::ErrorKind;
+use crate::Scheme;
+use crate::*;
+
+/// persy service support.
+#[doc = include_str!("docs.md")]
+#[derive(Default)]
+pub struct PersyBuilder {
+ /// That path to the persy data file. The directory in the path must already exist.
+ datafile: Option<String>,
+ /// That name of the persy segment.
+ segment: Option<String>,
+ /// That name of the persy index.
+ index: Option<String>,
+}
+
+impl PersyBuilder {
+ /// Set the path to the persy data directory. Will create if not exists.
+ pub fn datafile(&mut self, path: &str) -> &mut Self {
+ self.datafile = Some(path.into());
+ self
+ }
+
+ /// Set the name of the persy segment. Will create if not exists.
+ pub fn segment(&mut self, path: &str) -> &mut Self {
+ self.segment = Some(path.into());
+ self
+ }
+
+ /// Set the name of the persy index. Will create if not exists.
+ pub fn index(&mut self, path: &str) -> &mut Self {
+ self.index = Some(path.into());
+ self
+ }
+}
+
+impl Builder for PersyBuilder {
+ const SCHEME: Scheme = Scheme::Persy;
+ type Accessor = PersyBackend;
+
+ fn from_map(map: HashMap<String, String>) -> Self {
+ let mut builder = PersyBuilder::default();
+
+ map.get("datafile").map(|v| builder.datafile(v));
+ map.get("segment").map(|v| builder.segment(v));
+ map.get("index").map(|v| builder.index(v));
+
+ builder
+ }
+
+ fn build(&mut self) -> Result<Self::Accessor> {
+ let datafile_path = self.datafile.take().ok_or_else(|| {
+ Error::new(ErrorKind::ConfigInvalid, "datafile is required but not set")
+ .with_context("service", Scheme::Persy)
+ })?;
+
+ let segment_name = self.segment.take().ok_or_else(|| {
+ Error::new(ErrorKind::ConfigInvalid, "segment is required but not set")
+ .with_context("service", Scheme::Persy)
+ })?;
+
+ let segment = segment_name.clone();
+
+ let index_name = self.index.take().ok_or_else(|| {
+ Error::new(ErrorKind::ConfigInvalid, "index is required but not set")
+ .with_context("service", Scheme::Persy)
+ })?;
+
+ let index = index_name.clone();
+
+ let persy = persy::OpenOptions::new()
+ .create(true)
+ .prepare_with(move |p| init(p, &segment_name, &index_name))
+ .open(&datafile_path)
+ .map_err(|e| {
+ Error::new(ErrorKind::ConfigInvalid, "open db")
+ .with_context("service", Scheme::Persy)
+ .with_context("datafile", datafile_path.clone())
+ .set_source(e)
+ })?;
+
+ // This function will only be called on database creation
+ fn init(
+ persy: &persy::Persy,
+ segment_name: &str,
+ index_name: &str,
+ ) -> std::result::Result<(), Box<dyn std::error::Error>> {
+ let mut tx = persy.begin()?;
+
+ if !tx.exists_segment(segment_name)? {
+ tx.create_segment(segment_name)?;
+ }
+ if !tx.exists_index(index_name)? {
+ tx.create_index::<String, persy::PersyId>(index_name, persy::ValueMode::Replace)?;
+ }
+
+ let prepared = tx.prepare()?;
+ prepared.commit()?;
+
+ println!("Segment and Index successfully created");
Review Comment:
done! I deleted it.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@opendal.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [incubator-opendal] Xuanwo merged pull request #2648: feat(services/persy): add a basic persy service impl
Posted by "Xuanwo (via GitHub)" <gi...@apache.org>.
Xuanwo merged PR #2648:
URL: https://github.com/apache/incubator-opendal/pull/2648
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@opendal.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [incubator-opendal] PsiACE commented on pull request #2648: feat(services/persy): add a basic persy service impl
Posted by "PsiACE (via GitHub)" <gi...@apache.org>.
PsiACE commented on PR #2648:
URL: https://github.com/apache/incubator-opendal/pull/2648#issuecomment-1636989172
also cc @Anakin100100 , sorry for the delay, but perhaps you would be willing to help review.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@opendal.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [incubator-opendal] PsiACE commented on a diff in pull request #2648: feat(services/persy): add a basic persy service impl
Posted by "PsiACE (via GitHub)" <gi...@apache.org>.
PsiACE commented on code in PR #2648:
URL: https://github.com/apache/incubator-opendal/pull/2648#discussion_r1264703668
##########
core/src/services/persy/backend.rs:
##########
@@ -0,0 +1,245 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+use std::collections::HashMap;
+use std::fmt::Debug;
+use std::fmt::Formatter;
+use std::str;
+
+use async_trait::async_trait;
+use persy;
+
+use crate::raw::adapters::kv;
+use crate::Builder;
+use crate::Error;
+use crate::ErrorKind;
+use crate::Scheme;
+use crate::*;
+
+/// persy service support.
+#[doc = include_str!("docs.md")]
+#[derive(Default)]
+pub struct PersyBuilder {
+ /// That path to the persy data file. The directory in the path must already exist.
+ datafile: Option<String>,
+ /// That name of the persy segment.
+ segment: Option<String>,
+ /// That name of the persy index.
+ index: Option<String>,
+}
+
+impl PersyBuilder {
+ /// Set the path to the persy data directory. Will create if not exists.
+ pub fn datafile(&mut self, path: &str) -> &mut Self {
+ self.datafile = Some(path.into());
+ self
+ }
+
+ /// Set the name of the persy segment. Will create if not exists.
+ pub fn segment(&mut self, path: &str) -> &mut Self {
+ self.segment = Some(path.into());
+ self
+ }
+
+ /// Set the name of the persy index. Will create if not exists.
+ pub fn index(&mut self, path: &str) -> &mut Self {
+ self.index = Some(path.into());
+ self
+ }
+}
+
+impl Builder for PersyBuilder {
+ const SCHEME: Scheme = Scheme::Persy;
+ type Accessor = PersyBackend;
+
+ fn from_map(map: HashMap<String, String>) -> Self {
+ let mut builder = PersyBuilder::default();
+
+ map.get("datafile").map(|v| builder.datafile(v));
+ map.get("segment").map(|v| builder.segment(v));
+ map.get("index").map(|v| builder.index(v));
+
+ builder
+ }
+
+ fn build(&mut self) -> Result<Self::Accessor> {
+ let datafile_path = self.datafile.take().ok_or_else(|| {
+ Error::new(ErrorKind::ConfigInvalid, "datafile is required but not set")
+ .with_context("service", Scheme::Persy)
+ })?;
+
+ let segment_name = self.segment.take().ok_or_else(|| {
+ Error::new(ErrorKind::ConfigInvalid, "segment is required but not set")
+ .with_context("service", Scheme::Persy)
+ })?;
+
+ let segment = segment_name.clone();
+
+ let index_name = self.index.take().ok_or_else(|| {
+ Error::new(ErrorKind::ConfigInvalid, "index is required but not set")
+ .with_context("service", Scheme::Persy)
+ })?;
+
+ let index = index_name.clone();
+
+ let persy = persy::OpenOptions::new()
+ .create(true)
+ .prepare_with(move |p| init(p, &segment_name, &index_name))
+ .open(&datafile_path)
+ .map_err(|e| {
+ Error::new(ErrorKind::ConfigInvalid, "open db")
+ .with_context("service", Scheme::Persy)
+ .with_context("datafile", datafile_path.clone())
+ .set_source(e)
+ })?;
+
+ // This function will only be called on database creation
+ fn init(
+ persy: &persy::Persy,
+ segment_name: &str,
+ index_name: &str,
+ ) -> std::result::Result<(), Box<dyn std::error::Error>> {
+ let mut tx = persy.begin()?;
+
+ if !tx.exists_segment(segment_name)? {
+ tx.create_segment(segment_name)?;
+ }
+ if !tx.exists_index(index_name)? {
+ tx.create_index::<String, persy::PersyId>(index_name, persy::ValueMode::Replace)?;
+ }
+
+ let prepared = tx.prepare()?;
+ prepared.commit()?;
+
+ println!("Segment and Index successfully created");
+ Ok(())
+ }
+
+ Ok(PersyBackend::new(Adapter {
+ datafile: datafile_path,
+ segment,
+ index,
+ persy,
+ }))
+ }
+}
+
+/// Backend for persy services.
+pub type PersyBackend = kv::Backend<Adapter>;
+
+#[derive(Clone)]
+pub struct Adapter {
+ datafile: String,
+ segment: String,
+ index: String,
+ persy: persy::Persy,
+}
+
+impl Debug for Adapter {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ let mut ds = f.debug_struct("Adapter");
+ ds.field("path", &self.datafile);
+ ds.field("segment", &self.segment);
+ ds.field("index", &self.index);
+ ds.finish()
+ }
+}
+
+#[async_trait]
+impl kv::Adapter for Adapter {
+ fn metadata(&self) -> kv::Metadata {
+ kv::Metadata::new(
+ Scheme::Persy,
+ &self.datafile,
+ Capability {
+ read: true,
+ write: true,
+ delete: true,
+ blocking: true,
+ ..Default::default()
+ },
+ )
+ }
+
+ async fn get(&self, path: &str) -> Result<Option<Vec<u8>>> {
+ self.blocking_get(path)
+ }
+
+ fn blocking_get(&self, path: &str) -> Result<Option<Vec<u8>>> {
+ let mut read_id = self
+ .persy
+ .get::<String, persy::PersyId>(&self.index, &path.to_string())
+ .map_err(parse_error)?;
+ if let Some(id) = read_id.next() {
+ let value = self.persy.read(&self.segment, &id).map_err(parse_error)?;
+ return Ok(value);
+ }
+
+ Ok(None)
+ }
+
+ async fn set(&self, path: &str, value: &[u8]) -> Result<()> {
+ self.blocking_set(path, value)
+ }
+
+ fn blocking_set(&self, path: &str, value: &[u8]) -> Result<()> {
+ let mut tx = self.persy.begin().map_err(parse_error)?;
+ let id = tx.insert(&self.segment, value).map_err(parse_error)?;
+
+ tx.put::<String, persy::PersyId>(&self.index, path.to_string(), id)
+ .map_err(parse_error)?;
+ let prepared = tx.prepare().map_err(parse_error)?;
+ prepared.commit().map_err(parse_error)?;
+
+ Ok(())
+ }
+
+ async fn delete(&self, path: &str) -> Result<()> {
+ self.blocking_delete(path)
+ }
+
+ fn blocking_delete(&self, path: &str) -> Result<()> {
+ let mut delete_id = self
+ .persy
+ .get::<String, persy::PersyId>(&self.index, &path.to_string())
+ .map_err(parse_error)?;
+ if let Some(id) = delete_id.next() {
+ //Begin a transaction
Review Comment:
done!
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@opendal.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [incubator-opendal] Xuanwo commented on a diff in pull request #2648: feat(services/persy): add a basic persy service impl
Posted by "Xuanwo (via GitHub)" <gi...@apache.org>.
Xuanwo commented on code in PR #2648:
URL: https://github.com/apache/incubator-opendal/pull/2648#discussion_r1264702129
##########
core/src/services/persy/backend.rs:
##########
@@ -0,0 +1,245 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+use std::collections::HashMap;
+use std::fmt::Debug;
+use std::fmt::Formatter;
+use std::str;
+
+use async_trait::async_trait;
+use persy;
+
+use crate::raw::adapters::kv;
+use crate::Builder;
+use crate::Error;
+use crate::ErrorKind;
+use crate::Scheme;
+use crate::*;
+
+/// persy service support.
+#[doc = include_str!("docs.md")]
+#[derive(Default)]
+pub struct PersyBuilder {
+ /// That path to the persy data file. The directory in the path must already exist.
+ datafile: Option<String>,
+ /// That name of the persy segment.
+ segment: Option<String>,
+ /// That name of the persy index.
+ index: Option<String>,
+}
+
+impl PersyBuilder {
+ /// Set the path to the persy data directory. Will create if not exists.
+ pub fn datafile(&mut self, path: &str) -> &mut Self {
+ self.datafile = Some(path.into());
+ self
+ }
+
+ /// Set the name of the persy segment. Will create if not exists.
+ pub fn segment(&mut self, path: &str) -> &mut Self {
+ self.segment = Some(path.into());
+ self
+ }
+
+ /// Set the name of the persy index. Will create if not exists.
+ pub fn index(&mut self, path: &str) -> &mut Self {
+ self.index = Some(path.into());
+ self
+ }
+}
+
+impl Builder for PersyBuilder {
+ const SCHEME: Scheme = Scheme::Persy;
+ type Accessor = PersyBackend;
+
+ fn from_map(map: HashMap<String, String>) -> Self {
+ let mut builder = PersyBuilder::default();
+
+ map.get("datafile").map(|v| builder.datafile(v));
+ map.get("segment").map(|v| builder.segment(v));
+ map.get("index").map(|v| builder.index(v));
+
+ builder
+ }
+
+ fn build(&mut self) -> Result<Self::Accessor> {
+ let datafile_path = self.datafile.take().ok_or_else(|| {
+ Error::new(ErrorKind::ConfigInvalid, "datafile is required but not set")
+ .with_context("service", Scheme::Persy)
+ })?;
+
+ let segment_name = self.segment.take().ok_or_else(|| {
+ Error::new(ErrorKind::ConfigInvalid, "segment is required but not set")
+ .with_context("service", Scheme::Persy)
+ })?;
+
+ let segment = segment_name.clone();
+
+ let index_name = self.index.take().ok_or_else(|| {
+ Error::new(ErrorKind::ConfigInvalid, "index is required but not set")
+ .with_context("service", Scheme::Persy)
+ })?;
+
+ let index = index_name.clone();
+
+ let persy = persy::OpenOptions::new()
+ .create(true)
+ .prepare_with(move |p| init(p, &segment_name, &index_name))
+ .open(&datafile_path)
+ .map_err(|e| {
+ Error::new(ErrorKind::ConfigInvalid, "open db")
+ .with_context("service", Scheme::Persy)
+ .with_context("datafile", datafile_path.clone())
+ .set_source(e)
+ })?;
+
+ // This function will only be called on database creation
+ fn init(
+ persy: &persy::Persy,
+ segment_name: &str,
+ index_name: &str,
+ ) -> std::result::Result<(), Box<dyn std::error::Error>> {
+ let mut tx = persy.begin()?;
+
+ if !tx.exists_segment(segment_name)? {
+ tx.create_segment(segment_name)?;
+ }
+ if !tx.exists_index(index_name)? {
+ tx.create_index::<String, persy::PersyId>(index_name, persy::ValueMode::Replace)?;
+ }
+
+ let prepared = tx.prepare()?;
+ prepared.commit()?;
+
+ println!("Segment and Index successfully created");
+ Ok(())
+ }
+
+ Ok(PersyBackend::new(Adapter {
+ datafile: datafile_path,
+ segment,
+ index,
+ persy,
+ }))
+ }
+}
+
+/// Backend for persy services.
+pub type PersyBackend = kv::Backend<Adapter>;
+
+#[derive(Clone)]
+pub struct Adapter {
+ datafile: String,
+ segment: String,
+ index: String,
+ persy: persy::Persy,
+}
+
+impl Debug for Adapter {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ let mut ds = f.debug_struct("Adapter");
+ ds.field("path", &self.datafile);
+ ds.field("segment", &self.segment);
+ ds.field("index", &self.index);
+ ds.finish()
+ }
+}
+
+#[async_trait]
+impl kv::Adapter for Adapter {
+ fn metadata(&self) -> kv::Metadata {
+ kv::Metadata::new(
+ Scheme::Persy,
+ &self.datafile,
+ Capability {
+ read: true,
+ write: true,
+ delete: true,
+ blocking: true,
+ ..Default::default()
+ },
+ )
+ }
+
+ async fn get(&self, path: &str) -> Result<Option<Vec<u8>>> {
+ self.blocking_get(path)
+ }
+
+ fn blocking_get(&self, path: &str) -> Result<Option<Vec<u8>>> {
+ let mut read_id = self
+ .persy
+ .get::<String, persy::PersyId>(&self.index, &path.to_string())
+ .map_err(parse_error)?;
+ if let Some(id) = read_id.next() {
+ let value = self.persy.read(&self.segment, &id).map_err(parse_error)?;
+ return Ok(value);
+ }
+
+ Ok(None)
+ }
+
+ async fn set(&self, path: &str, value: &[u8]) -> Result<()> {
+ self.blocking_set(path, value)
+ }
+
+ fn blocking_set(&self, path: &str, value: &[u8]) -> Result<()> {
+ let mut tx = self.persy.begin().map_err(parse_error)?;
Review Comment:
We don't need `tx` to read data?
##########
core/src/services/persy/backend.rs:
##########
@@ -0,0 +1,245 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+use std::collections::HashMap;
+use std::fmt::Debug;
+use std::fmt::Formatter;
+use std::str;
+
+use async_trait::async_trait;
+use persy;
+
+use crate::raw::adapters::kv;
+use crate::Builder;
+use crate::Error;
+use crate::ErrorKind;
+use crate::Scheme;
+use crate::*;
+
+/// persy service support.
+#[doc = include_str!("docs.md")]
+#[derive(Default)]
+pub struct PersyBuilder {
+ /// That path to the persy data file. The directory in the path must already exist.
+ datafile: Option<String>,
+ /// That name of the persy segment.
+ segment: Option<String>,
+ /// That name of the persy index.
+ index: Option<String>,
+}
+
+impl PersyBuilder {
+ /// Set the path to the persy data directory. Will create if not exists.
+ pub fn datafile(&mut self, path: &str) -> &mut Self {
+ self.datafile = Some(path.into());
+ self
+ }
+
+ /// Set the name of the persy segment. Will create if not exists.
+ pub fn segment(&mut self, path: &str) -> &mut Self {
+ self.segment = Some(path.into());
+ self
+ }
+
+ /// Set the name of the persy index. Will create if not exists.
+ pub fn index(&mut self, path: &str) -> &mut Self {
+ self.index = Some(path.into());
+ self
+ }
+}
+
+impl Builder for PersyBuilder {
+ const SCHEME: Scheme = Scheme::Persy;
+ type Accessor = PersyBackend;
+
+ fn from_map(map: HashMap<String, String>) -> Self {
+ let mut builder = PersyBuilder::default();
+
+ map.get("datafile").map(|v| builder.datafile(v));
+ map.get("segment").map(|v| builder.segment(v));
+ map.get("index").map(|v| builder.index(v));
+
+ builder
+ }
+
+ fn build(&mut self) -> Result<Self::Accessor> {
+ let datafile_path = self.datafile.take().ok_or_else(|| {
+ Error::new(ErrorKind::ConfigInvalid, "datafile is required but not set")
+ .with_context("service", Scheme::Persy)
+ })?;
+
+ let segment_name = self.segment.take().ok_or_else(|| {
+ Error::new(ErrorKind::ConfigInvalid, "segment is required but not set")
+ .with_context("service", Scheme::Persy)
+ })?;
+
+ let segment = segment_name.clone();
+
+ let index_name = self.index.take().ok_or_else(|| {
+ Error::new(ErrorKind::ConfigInvalid, "index is required but not set")
+ .with_context("service", Scheme::Persy)
+ })?;
+
+ let index = index_name.clone();
+
+ let persy = persy::OpenOptions::new()
+ .create(true)
+ .prepare_with(move |p| init(p, &segment_name, &index_name))
+ .open(&datafile_path)
+ .map_err(|e| {
+ Error::new(ErrorKind::ConfigInvalid, "open db")
+ .with_context("service", Scheme::Persy)
+ .with_context("datafile", datafile_path.clone())
+ .set_source(e)
+ })?;
+
+ // This function will only be called on database creation
+ fn init(
+ persy: &persy::Persy,
+ segment_name: &str,
+ index_name: &str,
+ ) -> std::result::Result<(), Box<dyn std::error::Error>> {
+ let mut tx = persy.begin()?;
+
+ if !tx.exists_segment(segment_name)? {
+ tx.create_segment(segment_name)?;
+ }
+ if !tx.exists_index(index_name)? {
+ tx.create_index::<String, persy::PersyId>(index_name, persy::ValueMode::Replace)?;
+ }
+
+ let prepared = tx.prepare()?;
+ prepared.commit()?;
+
+ println!("Segment and Index successfully created");
Review Comment:
Maybe we should remove this?
##########
core/src/services/persy/backend.rs:
##########
@@ -0,0 +1,245 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+use std::collections::HashMap;
+use std::fmt::Debug;
+use std::fmt::Formatter;
+use std::str;
+
+use async_trait::async_trait;
+use persy;
+
+use crate::raw::adapters::kv;
+use crate::Builder;
+use crate::Error;
+use crate::ErrorKind;
+use crate::Scheme;
+use crate::*;
+
+/// persy service support.
+#[doc = include_str!("docs.md")]
+#[derive(Default)]
+pub struct PersyBuilder {
+ /// That path to the persy data file. The directory in the path must already exist.
+ datafile: Option<String>,
+ /// That name of the persy segment.
+ segment: Option<String>,
+ /// That name of the persy index.
+ index: Option<String>,
+}
+
+impl PersyBuilder {
+ /// Set the path to the persy data directory. Will create if not exists.
+ pub fn datafile(&mut self, path: &str) -> &mut Self {
+ self.datafile = Some(path.into());
+ self
+ }
+
+ /// Set the name of the persy segment. Will create if not exists.
+ pub fn segment(&mut self, path: &str) -> &mut Self {
+ self.segment = Some(path.into());
+ self
+ }
+
+ /// Set the name of the persy index. Will create if not exists.
+ pub fn index(&mut self, path: &str) -> &mut Self {
+ self.index = Some(path.into());
+ self
+ }
+}
+
+impl Builder for PersyBuilder {
+ const SCHEME: Scheme = Scheme::Persy;
+ type Accessor = PersyBackend;
+
+ fn from_map(map: HashMap<String, String>) -> Self {
+ let mut builder = PersyBuilder::default();
+
+ map.get("datafile").map(|v| builder.datafile(v));
+ map.get("segment").map(|v| builder.segment(v));
+ map.get("index").map(|v| builder.index(v));
+
+ builder
+ }
+
+ fn build(&mut self) -> Result<Self::Accessor> {
+ let datafile_path = self.datafile.take().ok_or_else(|| {
+ Error::new(ErrorKind::ConfigInvalid, "datafile is required but not set")
+ .with_context("service", Scheme::Persy)
+ })?;
+
+ let segment_name = self.segment.take().ok_or_else(|| {
+ Error::new(ErrorKind::ConfigInvalid, "segment is required but not set")
+ .with_context("service", Scheme::Persy)
+ })?;
+
+ let segment = segment_name.clone();
+
+ let index_name = self.index.take().ok_or_else(|| {
+ Error::new(ErrorKind::ConfigInvalid, "index is required but not set")
+ .with_context("service", Scheme::Persy)
+ })?;
+
+ let index = index_name.clone();
+
+ let persy = persy::OpenOptions::new()
+ .create(true)
+ .prepare_with(move |p| init(p, &segment_name, &index_name))
+ .open(&datafile_path)
+ .map_err(|e| {
+ Error::new(ErrorKind::ConfigInvalid, "open db")
+ .with_context("service", Scheme::Persy)
+ .with_context("datafile", datafile_path.clone())
+ .set_source(e)
+ })?;
+
+ // This function will only be called on database creation
+ fn init(
+ persy: &persy::Persy,
+ segment_name: &str,
+ index_name: &str,
+ ) -> std::result::Result<(), Box<dyn std::error::Error>> {
+ let mut tx = persy.begin()?;
+
+ if !tx.exists_segment(segment_name)? {
+ tx.create_segment(segment_name)?;
+ }
+ if !tx.exists_index(index_name)? {
+ tx.create_index::<String, persy::PersyId>(index_name, persy::ValueMode::Replace)?;
+ }
+
+ let prepared = tx.prepare()?;
+ prepared.commit()?;
+
+ println!("Segment and Index successfully created");
+ Ok(())
+ }
+
+ Ok(PersyBackend::new(Adapter {
+ datafile: datafile_path,
+ segment,
+ index,
+ persy,
+ }))
+ }
+}
+
+/// Backend for persy services.
+pub type PersyBackend = kv::Backend<Adapter>;
+
+#[derive(Clone)]
+pub struct Adapter {
+ datafile: String,
+ segment: String,
+ index: String,
+ persy: persy::Persy,
+}
+
+impl Debug for Adapter {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ let mut ds = f.debug_struct("Adapter");
+ ds.field("path", &self.datafile);
+ ds.field("segment", &self.segment);
+ ds.field("index", &self.index);
+ ds.finish()
+ }
+}
+
+#[async_trait]
+impl kv::Adapter for Adapter {
+ fn metadata(&self) -> kv::Metadata {
+ kv::Metadata::new(
+ Scheme::Persy,
+ &self.datafile,
+ Capability {
+ read: true,
+ write: true,
+ delete: true,
+ blocking: true,
+ ..Default::default()
+ },
+ )
+ }
+
+ async fn get(&self, path: &str) -> Result<Option<Vec<u8>>> {
+ self.blocking_get(path)
+ }
+
+ fn blocking_get(&self, path: &str) -> Result<Option<Vec<u8>>> {
+ let mut read_id = self
+ .persy
+ .get::<String, persy::PersyId>(&self.index, &path.to_string())
+ .map_err(parse_error)?;
+ if let Some(id) = read_id.next() {
+ let value = self.persy.read(&self.segment, &id).map_err(parse_error)?;
+ return Ok(value);
+ }
+
+ Ok(None)
+ }
+
+ async fn set(&self, path: &str, value: &[u8]) -> Result<()> {
+ self.blocking_set(path, value)
+ }
+
+ fn blocking_set(&self, path: &str, value: &[u8]) -> Result<()> {
+ let mut tx = self.persy.begin().map_err(parse_error)?;
+ let id = tx.insert(&self.segment, value).map_err(parse_error)?;
+
+ tx.put::<String, persy::PersyId>(&self.index, path.to_string(), id)
+ .map_err(parse_error)?;
+ let prepared = tx.prepare().map_err(parse_error)?;
+ prepared.commit().map_err(parse_error)?;
+
+ Ok(())
+ }
+
+ async fn delete(&self, path: &str) -> Result<()> {
+ self.blocking_delete(path)
+ }
+
+ fn blocking_delete(&self, path: &str) -> Result<()> {
+ let mut delete_id = self
+ .persy
+ .get::<String, persy::PersyId>(&self.index, &path.to_string())
+ .map_err(parse_error)?;
+ if let Some(id) = delete_id.next() {
+ //Begin a transaction
Review Comment:
Please follow the same comment style.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@opendal.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [incubator-opendal] PsiACE commented on a diff in pull request #2648: feat(services/persy): add a basic persy service impl
Posted by "PsiACE (via GitHub)" <gi...@apache.org>.
PsiACE commented on code in PR #2648:
URL: https://github.com/apache/incubator-opendal/pull/2648#discussion_r1264703383
##########
core/src/services/persy/backend.rs:
##########
@@ -0,0 +1,245 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+use std::collections::HashMap;
+use std::fmt::Debug;
+use std::fmt::Formatter;
+use std::str;
+
+use async_trait::async_trait;
+use persy;
+
+use crate::raw::adapters::kv;
+use crate::Builder;
+use crate::Error;
+use crate::ErrorKind;
+use crate::Scheme;
+use crate::*;
+
+/// persy service support.
+#[doc = include_str!("docs.md")]
+#[derive(Default)]
+pub struct PersyBuilder {
+ /// That path to the persy data file. The directory in the path must already exist.
+ datafile: Option<String>,
+ /// That name of the persy segment.
+ segment: Option<String>,
+ /// That name of the persy index.
+ index: Option<String>,
+}
+
+impl PersyBuilder {
+ /// Set the path to the persy data directory. Will create if not exists.
+ pub fn datafile(&mut self, path: &str) -> &mut Self {
+ self.datafile = Some(path.into());
+ self
+ }
+
+ /// Set the name of the persy segment. Will create if not exists.
+ pub fn segment(&mut self, path: &str) -> &mut Self {
+ self.segment = Some(path.into());
+ self
+ }
+
+ /// Set the name of the persy index. Will create if not exists.
+ pub fn index(&mut self, path: &str) -> &mut Self {
+ self.index = Some(path.into());
+ self
+ }
+}
+
+impl Builder for PersyBuilder {
+ const SCHEME: Scheme = Scheme::Persy;
+ type Accessor = PersyBackend;
+
+ fn from_map(map: HashMap<String, String>) -> Self {
+ let mut builder = PersyBuilder::default();
+
+ map.get("datafile").map(|v| builder.datafile(v));
+ map.get("segment").map(|v| builder.segment(v));
+ map.get("index").map(|v| builder.index(v));
+
+ builder
+ }
+
+ fn build(&mut self) -> Result<Self::Accessor> {
+ let datafile_path = self.datafile.take().ok_or_else(|| {
+ Error::new(ErrorKind::ConfigInvalid, "datafile is required but not set")
+ .with_context("service", Scheme::Persy)
+ })?;
+
+ let segment_name = self.segment.take().ok_or_else(|| {
+ Error::new(ErrorKind::ConfigInvalid, "segment is required but not set")
+ .with_context("service", Scheme::Persy)
+ })?;
+
+ let segment = segment_name.clone();
+
+ let index_name = self.index.take().ok_or_else(|| {
+ Error::new(ErrorKind::ConfigInvalid, "index is required but not set")
+ .with_context("service", Scheme::Persy)
+ })?;
+
+ let index = index_name.clone();
+
+ let persy = persy::OpenOptions::new()
+ .create(true)
+ .prepare_with(move |p| init(p, &segment_name, &index_name))
+ .open(&datafile_path)
+ .map_err(|e| {
+ Error::new(ErrorKind::ConfigInvalid, "open db")
+ .with_context("service", Scheme::Persy)
+ .with_context("datafile", datafile_path.clone())
+ .set_source(e)
+ })?;
+
+ // This function will only be called on database creation
+ fn init(
+ persy: &persy::Persy,
+ segment_name: &str,
+ index_name: &str,
+ ) -> std::result::Result<(), Box<dyn std::error::Error>> {
+ let mut tx = persy.begin()?;
+
+ if !tx.exists_segment(segment_name)? {
+ tx.create_segment(segment_name)?;
+ }
+ if !tx.exists_index(index_name)? {
+ tx.create_index::<String, persy::PersyId>(index_name, persy::ValueMode::Replace)?;
+ }
+
+ let prepared = tx.prepare()?;
+ prepared.commit()?;
+
+ println!("Segment and Index successfully created");
+ Ok(())
+ }
+
+ Ok(PersyBackend::new(Adapter {
+ datafile: datafile_path,
+ segment,
+ index,
+ persy,
+ }))
+ }
+}
+
+/// Backend for persy services.
+pub type PersyBackend = kv::Backend<Adapter>;
+
+#[derive(Clone)]
+pub struct Adapter {
+ datafile: String,
+ segment: String,
+ index: String,
+ persy: persy::Persy,
+}
+
+impl Debug for Adapter {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ let mut ds = f.debug_struct("Adapter");
+ ds.field("path", &self.datafile);
+ ds.field("segment", &self.segment);
+ ds.field("index", &self.index);
+ ds.finish()
+ }
+}
+
+#[async_trait]
+impl kv::Adapter for Adapter {
+ fn metadata(&self) -> kv::Metadata {
+ kv::Metadata::new(
+ Scheme::Persy,
+ &self.datafile,
+ Capability {
+ read: true,
+ write: true,
+ delete: true,
+ blocking: true,
+ ..Default::default()
+ },
+ )
+ }
+
+ async fn get(&self, path: &str) -> Result<Option<Vec<u8>>> {
+ self.blocking_get(path)
+ }
+
+ fn blocking_get(&self, path: &str) -> Result<Option<Vec<u8>>> {
+ let mut read_id = self
+ .persy
+ .get::<String, persy::PersyId>(&self.index, &path.to_string())
+ .map_err(parse_error)?;
+ if let Some(id) = read_id.next() {
+ let value = self.persy.read(&self.segment, &id).map_err(parse_error)?;
+ return Ok(value);
+ }
+
+ Ok(None)
+ }
+
+ async fn set(&self, path: &str, value: &[u8]) -> Result<()> {
+ self.blocking_set(path, value)
+ }
+
+ fn blocking_set(&self, path: &str, value: &[u8]) -> Result<()> {
+ let mut tx = self.persy.begin().map_err(parse_error)?;
Review Comment:
According to the example, we do not need to use "tx" when reading data.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@opendal.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org