Skip to content

Commit

Permalink
rgw/sfs: change db schema as described in ADR.
Browse files Browse the repository at this point in the history
Changes the db schema following [this
ADR](https://github.com/aquarist-labs/s3gw/pull/497).

It also deletes repeated fields that we had in objects vs
versioned_objects tables.

Logic of `sfs` was intentionally not updated in this PR.
(For example new field `version_type` is added to the schema, but still
not used).
Updates in logic, specially in versioning logic, will come in a future
PR.

New Features:

**Sqliteorm type bindings for the following types.**
* `uuid_d`
* `ceph::real_time`
* `rgw::sal::sfs::ObjetState`
* `rgw::sal::sfs::VersionType`

In general, any enum type that has a `LAST_VALUE` item assigned to the
last valid value and that also starts with 0 is eligible to be binded to
sqliteorm, as the type binding for enums is generic.

For example: you can create an enum like:

```c++
enum class TestEnum  {
  WHATEVER = 0,
  SOMETHING_ELSE,
  AND_THIS,
  LAST_VALUE = AND_THIS
};
```
and use it right away in sqliteorm.

**Object has no explicit conversion code**
As all the types in the `object` are compatible with `sqliteorm` we
don't need extra conversion layer.
This is a preview of what we can do with the rest of database objects
once `BLOBS` are also type binded in the future.
That was not done in this PR to avoid too many changes.

Note: The new type `VersionType` was added as a new file `version_type.h`
because we are in a re-design process and I wasn't sure if it should be
located in any other existing file that might be eligible to be deleted
in the near future.

Fixes: https://github.com/aquarist-labs/s3gw/issues/480

Signed-off-by: Xavi Garcia <xavi.garcia@suse.com>
  • Loading branch information
0xavi0 committed May 16, 2023
1 parent 7fd738f commit e805143
Show file tree
Hide file tree
Showing 25 changed files with 1,483 additions and 975 deletions.
1 change: 0 additions & 1 deletion src/rgw/driver/sfs/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ set(sfs_srcs
sqlite/sqlite_lifecycle.cc
sqlite/users/users_conversions.cc
sqlite/buckets/bucket_conversions.cc
sqlite/objects/object_conversions.cc
sqlite/versioned_object/versioned_object_conversions.cc
sqlite/dbconn.cc
bucket.cc
Expand Down
8 changes: 4 additions & 4 deletions src/rgw/driver/sfs/bucket.cc
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ int SFSBucket::list(

auto last_version =
db_versioned_objects.get_last_versioned_object(objref->path.get_uuid());
if (last_version->object_state == rgw::sal::ObjectState::COMMITTED) {
if (last_version->object_state == rgw::sal::sfs::ObjectState::COMMITTED) {
// check for delimiter
if (check_add_common_prefix(dpp, objref->name, params, 0, results, y)) {
continue;
Expand Down Expand Up @@ -128,13 +128,13 @@ int SFSBucket::list_versions(
rgw_bucket_dir_entry dirent;
dirent.key = cls_rgw_obj_key(objref->name, object_version.version_id);
dirent.meta.accounted_size = object_version.size;
dirent.meta.mtime = object_version.creation_time;
dirent.meta.mtime = object_version.create_time;
dirent.meta.etag = object_version.etag;
dirent.flags = rgw_bucket_dir_entry::FLAG_VER;
if (last_version.has_value() && last_version->id == object_version.id) {
dirent.flags |= rgw_bucket_dir_entry::FLAG_CURRENT;
}
if (object_version.object_state == rgw::sal::ObjectState::DELETED) {
if (object_version.object_state == rgw::sal::sfs::ObjectState::DELETED) {
dirent.flags |= rgw_bucket_dir_entry::FLAG_DELETE_MARKER;
}
dirent.meta.owner_display_name = bucket->get_owner().display_name;
Expand Down Expand Up @@ -243,7 +243,7 @@ int SFSBucket::check_empty(const DoutPrefixProvider* dpp, optional_yield y) {
sfs::sqlite::SQLiteVersionedObjects db_versions(store->db_conn);
for (const auto& obj : objects) {
auto last_version = db_versions.get_last_versioned_object(obj);
if (last_version->object_state != rgw::sal::ObjectState::DELETED) {
if (last_version->object_state != rgw::sal::sfs::ObjectState::DELETED) {
ldpp_dout(dpp, -1) << __func__ << ": Bucket Not Empty.." << dendl;
return -ENOTEMPTY;
}
Expand Down
2 changes: 1 addition & 1 deletion src/rgw/driver/sfs/object.cc
Original file line number Diff line number Diff line change
Expand Up @@ -459,7 +459,7 @@ void SFSObject::_refresh_meta_from_object() {
auto db_version = db_versioned_objects.get_versioned_object(get_instance());
if (db_version.has_value()) {
auto uuid = objref->path.get_uuid();
auto deleted = db_version->object_state == ObjectState::DELETED;
auto deleted = db_version->object_state == sfs::ObjectState::DELETED;
objref.reset(sfs::Object::create_for_query(
get_name(), uuid, deleted, db_version->id
));
Expand Down
4 changes: 2 additions & 2 deletions src/rgw/driver/sfs/object_state.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
#ifndef RGW_SFS_OBJECT_STATE_H
#define RGW_SFS_OBJECT_STATE_H

namespace rgw::sal {
namespace rgw::sal::sfs {

enum class ObjectState {
OPEN = 0,
Expand All @@ -24,6 +24,6 @@ enum class ObjectState {
LAST_VALUE = DELETED
};

} // namespace rgw::sal
} // namespace rgw::sal::sfs

#endif // RGW_SFS_OBJECT_STATE_H
66 changes: 66 additions & 0 deletions src/rgw/driver/sfs/sqlite/bindings/enum.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t
// vim: ts=8 sw=2 smarttab ft=cpp
/*
* Ceph - scalable distributed file system
* SFS SAL implementation
*
* Copyright (C) 2023 SUSE LLC
*
* This is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software
* Foundation. See file COPYING.
*/
#pragma once

#include <type_traits>

#include "rgw/driver/sfs/sqlite/sqlite_orm.h"

/// sqliteorm binding for enum types.
/// This binding can be used for any enum that has the LAST_VALUE value set to
/// the last possible value and the initial value set to 0.
/// This is used for the conversion from uint to enum to ensure that the uint
/// value is not out of range.
namespace sqlite_orm {
template <class T>
struct type_printer<T, typename std::enable_if<std::is_enum_v<T>>::type>
: public integer_printer {};

template <class T>
struct statement_binder<T, typename std::enable_if<std::is_enum_v<T>>::type> {
int bind(sqlite3_stmt* stmt, int index, const T& value) const {
return statement_binder<uint>().bind(stmt, index, static_cast<uint>(value));
}
};

template <class T>
struct field_printer<T, typename std::enable_if<std::is_enum_v<T>>::type> {
std::string operator()(const T& value) const {
return std::to_string(static_cast<uint>(value));
}
};

template <class T>
struct row_extractor<T, typename std::enable_if<std::is_enum_v<T>>::type> {
T extract(uint row_value) const {
if (row_value > static_cast<uint>(T::LAST_VALUE)) {
throw(std::runtime_error(
"Invalid enum value found: (" + std::to_string(row_value) + ")"
));
}
return static_cast<T>(row_value);
}

T extract(sqlite3_stmt* stmt, int columnIndex) const {
auto int_value = sqlite3_column_int(stmt, columnIndex);
if (int_value < 0) {
throw(std::runtime_error(
"Invalid enum value found: (" + std::to_string(int_value) + ")"
));
}
return this->extract(static_cast<uint>(int_value));
}
};

} // namespace sqlite_orm
93 changes: 93 additions & 0 deletions src/rgw/driver/sfs/sqlite/bindings/real_time.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t
// vim: ts=8 sw=2 smarttab ft=cpp
/*
* Ceph - scalable distributed file system
* SFS SAL implementation
*
* Copyright (C) 2023 SUSE LLC
*
* This is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software
* Foundation. See file COPYING.
*/
#pragma once

#include "common/ceph_time.h"
#include "rgw/driver/sfs/sqlite/sqlite_orm.h"

/// ceph::real_time is represented as a uint64 (unsigned).
/// SQLite works with int64 (signed) values, which means we lose 1 bit of
/// resolution.
/// This means max possible time to be stored is 2262-04-11 23:47:16.854775807
/// timestamps are stored with the same resolution as
/// ceph::real_cock::time_point (nanoseconds)
namespace rgw::sal::sfs::sqlite {

static int64_t time_point_to_int64(const ceph::real_time& t) {
uint64_t nanos =
std::chrono::duration_cast<std::chrono::nanoseconds>(t.time_since_epoch())
.count();
// we check that the value is not greater than int64 max
if (nanos > std::numeric_limits<int64_t>::max()) {
std::stringstream oss;
oss << "Error converting ceph::real_time to int64. "
"Nanoseconds value: "
<< nanos << " is out of range";
throw std::runtime_error(oss.str());
}
// we can safely static_cast to int64_t now
return static_cast<int64_t>(nanos);
}

static ceph::real_time time_point_from_int64(int64_t value) {
std::optional<ceph::real_time> ret;
if (value < 0) {
// to ensure that we stick to the int64 positive range.
std::stringstream oss;
oss << "Error converting int64 nanoseconds value to "
"ceph::real_cock::time_point. Value: "
<< value << " is out of range";
throw std::runtime_error(oss.str());
}
uint64_t uint64_nanos = static_cast<uint64_t>(value);
return ceph::real_time(std::chrono::nanoseconds(uint64_nanos));
}

} // namespace rgw::sal::sfs::sqlite

namespace sqlite_orm {

template <>
struct type_printer<ceph::real_time> : public integer_printer {};

template <>
struct statement_binder<ceph::real_time> {
int bind(sqlite3_stmt* stmt, int index, const ceph::real_time& value) const {
return statement_binder<uint64_t>().bind(
stmt, index, rgw::sal::sfs::sqlite::time_point_to_int64(value)
);
}
};

template <>
struct field_printer<ceph::real_time> {
std::string operator()(const ceph::real_time& t) const {
auto int_value = rgw::sal::sfs::sqlite::time_point_to_int64(t);
return std::to_string(int_value);
}
};

template <>
struct row_extractor<ceph::real_time> {
ceph::real_time extract(int64_t row_value) const {
return rgw::sal::sfs::sqlite::time_point_from_int64(row_value);
}

ceph::real_time extract(sqlite3_stmt* stmt, int columnIndex) const {
auto int_value = sqlite3_column_int64(stmt, columnIndex);
return this->extract(int_value);
}
};

} // namespace sqlite_orm
68 changes: 68 additions & 0 deletions src/rgw/driver/sfs/sqlite/bindings/uuid_d.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t
// vim: ts=8 sw=2 smarttab ft=cpp
/*
* Ceph - scalable distributed file system
* SFS SAL implementation
*
* Copyright (C) 2023 SUSE LLC
*
* This is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software
* Foundation. See file COPYING.
*/
#pragma once

#include <type_traits>

#include "rgw/driver/sfs/sqlite/sqlite_orm.h"
#include "rgw_common.h"

namespace sqlite_orm {
template <>
struct type_printer<uuid_d> : public text_printer {};

template <>
struct statement_binder<uuid_d> {
int bind(sqlite3_stmt* stmt, int index, const uuid_d& value) const {
return statement_binder<std::string>().bind(stmt, index, value.to_string());
}
};

template <>
struct field_printer<uuid_d> {
std::string operator()(const uuid_d& value) const {
return value.to_string();
}
};

template <>
struct row_extractor<uuid_d> {
uuid_d extract(const char* row_value) const {
if (row_value) {
uuid_d ret_value;
if (!ret_value.parse(row_value)) {
throw std::runtime_error(
"incorrect uuid string (" + std::string(row_value) + ")"
);
}
return ret_value;
} else {
// ! row_value
throw std::runtime_error("incorrect uuid string (nullptr)");
}
}

uuid_d extract(sqlite3_stmt* stmt, int columnIndex) const {
auto str = sqlite3_column_text(stmt, columnIndex);
// sqlite3_colume_text returns const unsigned char*
return this->extract(reinterpret_cast<const char*>(str));
}
uuid_d extract(sqlite3_value* row_value) const {
// sqlite3_colume_text returns const unsigned char*
auto characters =
reinterpret_cast<const char*>(sqlite3_value_text(row_value));
return extract(characters);
}
};
} // namespace sqlite_orm
1 change: 1 addition & 0 deletions src/rgw/driver/sfs/sqlite/conversion_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
*/
#pragma once

#include "rgw_common.h"
namespace rgw::sal::sfs::sqlite {

template <typename BLOB_HOLDER, typename DEST>
Expand Down
33 changes: 18 additions & 15 deletions src/rgw/driver/sfs/sqlite/dbconn.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,9 @@ inline auto _make_storage(const std::string& path) {
),
sqlite_orm::make_index("bucket_ownerid_idx", &DBBucket::owner_id),
sqlite_orm::make_index("bucket_name_idx", &DBBucket::bucket_name),
sqlite_orm::make_index("objects_bucketid_idx", &DBObject::bucket_id),
sqlite_orm::make_index("objects_bucketid_idx", &DBObject::bucket_id),
sqlite_orm::make_index(
"objects_bucketid_idx", &DBOPObjectInfo::bucket_id
),
sqlite_orm::make_index(
"vobjs_versionid_idx", &DBVersionedObject::version_id
),
Expand Down Expand Up @@ -126,16 +127,11 @@ inline auto _make_storage(const std::string& path) {
sqlite_orm::make_table(
std::string(OBJECTS_TABLE),
sqlite_orm::make_column(
"object_id", &DBObject::object_id, sqlite_orm::primary_key()
"uuid", &DBOPObjectInfo::uuid, sqlite_orm::primary_key()
),
sqlite_orm::make_column("bucket_id", &DBObject::bucket_id),
sqlite_orm::make_column("name", &DBObject::name),
sqlite_orm::make_column("size", &DBObject::size),
sqlite_orm::make_column("etag", &DBObject::etag),
sqlite_orm::make_column("mtime", &DBObject::mtime),
sqlite_orm::make_column("set_mtime", &DBObject::set_mtime),
sqlite_orm::make_column("delete_at_time", &DBObject::delete_at_time),
sqlite_orm::foreign_key(&DBObject::bucket_id)
sqlite_orm::make_column("bucket_id", &DBOPObjectInfo::bucket_id),
sqlite_orm::make_column("name", &DBOPObjectInfo::name),
sqlite_orm::foreign_key(&DBOPObjectInfo::bucket_id)
.references(&DBBucket::bucket_id)
),
sqlite_orm::make_table(
Expand All @@ -146,21 +142,28 @@ inline auto _make_storage(const std::string& path) {
),
sqlite_orm::make_column("object_id", &DBVersionedObject::object_id),
sqlite_orm::make_column("checksum", &DBVersionedObject::checksum),
sqlite_orm::make_column("size", &DBVersionedObject::size),
sqlite_orm::make_column(
"deletion_time", &DBVersionedObject::deletion_time
"creation_time", &DBVersionedObject::create_time
),
sqlite_orm::make_column("size", &DBVersionedObject::size),
sqlite_orm::make_column(
"creation_time", &DBVersionedObject::creation_time
"deletion_time", &DBVersionedObject::delete_time
),
sqlite_orm::make_column(
"commit_time", &DBVersionedObject::commit_time
),
sqlite_orm::make_column("mtime", &DBVersionedObject::mtime),
sqlite_orm::make_column(
"object_state", &DBVersionedObject::object_state
),
sqlite_orm::make_column("version_id", &DBVersionedObject::version_id),
sqlite_orm::make_column("etag", &DBVersionedObject::etag),
sqlite_orm::make_column("attrs", &DBVersionedObject::attrs),
sqlite_orm::make_column(
"version_type", &DBVersionedObject::version_type
),
sqlite_orm::foreign_key(&DBVersionedObject::object_id)
.references(&DBObject::object_id)
.references(&DBOPObjectInfo::uuid)
),
sqlite_orm::make_table(
std::string(ACCESS_KEYS),
Expand Down

0 comments on commit e805143

Please sign in to comment.