Skip to content

Commit

Permalink
access_mode first version
Browse files Browse the repository at this point in the history
  • Loading branch information
hououou committed Sep 29, 2023
1 parent 53d91db commit ccbcab0
Show file tree
Hide file tree
Showing 10 changed files with 311 additions and 9 deletions.
26 changes: 25 additions & 1 deletion src/common/file_utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ int64_t FileInfo::getFileSize() {
#endif
}

std::unique_ptr<FileInfo> FileUtils::openFile(const std::string& path, int flags) {
std::unique_ptr<FileInfo> FileUtils::openFile(
const std::string& path, int flags, FileLockType lock_type) {
#if defined(_WIN32)
auto dwDesiredAccess = 0ul;
auto dwCreationDisposition = (flags & O_CREAT) ? OPEN_ALWAYS : OPEN_EXISTING;
Expand All @@ -62,12 +63,35 @@ std::unique_ptr<FileInfo> FileUtils::openFile(const std::string& path, int flags
throw Exception(StringUtils::string_format("Cannot open file. path: {} - Error {}: {}",
path, GetLastError(), std::system_category().message(GetLastError())));
}
if (lock_type != FileLockType::NO_LOCK) {
DWORD dwFlags = lock_type == FileLockType::READ_LOCK ?
LOCKFILE_FAIL_IMMEDIATELY :
LOCKFILE_FAIL_IMMEDIATELY | LOCKFILE_EXCLUSIVE_LOCK;
OVERLAPPED overlapped = {0};
overlapped.Offset = 0;
BOOL rc = LockFileEx(handle, dwFlags, 0, 0, 0, &overlapped);
if (!rc) {
throw Exception("Could not set lock on file : " + path);
}
}
return std::make_unique<FileInfo>(path, handle);
#else
int fd = open(path.c_str(), flags, 0644);
if (fd == -1) {
throw Exception("Cannot open file: " + path);
}
if (lock_type != FileLockType::NO_LOCK) {
struct flock fl;
memset(&fl, 0, sizeof fl);
fl.l_type = lock_type == FileLockType::READ_LOCK ? F_RDLCK : F_WRLCK;
fl.l_whence = SEEK_SET;
fl.l_start = 0;
fl.l_len = 0;
int rc = fcntl(fd, F_SETLK, &fl);
if (rc == -1) {
throw Exception("Could not set lock on file : " + path);
}
}
return std::make_unique<FileInfo>(path, fd);
#endif
}
Expand Down
1 change: 1 addition & 0 deletions src/include/common/constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ struct StorageConstants {
static constexpr char CATALOG_FILE_NAME_FOR_WAL[] = "catalog.kz.wal";
static constexpr char DATA_FILE_NAME[] = "data.kz";
static constexpr char METADATA_FILE_NAME[] = "metadata.kz";
static constexpr char LOCK_FILE_NAME[] = ".lock";

// The number of pages that we add at one time when we need to grow a file.
static constexpr uint64_t PAGE_GROUP_SIZE_LOG2 = 10;
Expand Down
5 changes: 4 additions & 1 deletion src/include/common/file_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
namespace kuzu {
namespace common {

enum class FileLockType : uint8_t { NO_LOCK = 0, READ_LOCK = 1, WRITE_LOCK = 2 };

struct FileInfo {
#ifdef _WIN32
FileInfo(std::string path, const void* handle) : path{std::move(path)}, handle{handle} {}
Expand All @@ -36,7 +38,8 @@ struct FileInfo {

class FileUtils {
public:
static std::unique_ptr<FileInfo> openFile(const std::string& path, int flags);
static std::unique_ptr<FileInfo> openFile(
const std::string& path, int flags, FileLockType lock_type = FileLockType::NO_LOCK);

static void readFromFile(
FileInfo* fileInfo, void* buffer, uint64_t numBytes, uint64_t position);
Expand Down
25 changes: 21 additions & 4 deletions src/include/main/database.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,41 @@
#include "common/api.h"
#include "common/constants.h"
#include "kuzu_fwd.h"

namespace kuzu {
namespace main {

enum class AccessMode : uint8_t { READ_ONLY = 0, READ_WRITE = 1 };

/**
* @brief Stores buffer pool size and max number of threads configurations.
*/
KUZU_API struct SystemConfig {
/**
* @brief Creates a SystemConfig object with default buffer pool size and max num of threads.
* @brief Creates a SystemConfig object with default buffer pool size, default accessmode
* (READ_WRIRE), and and max num of threads.
*/
explicit SystemConfig();
/**
* @brief Creates a SystemConfig object.
* @brief Creates a SystemConfig object with default accessmode (READ_WRIRE) and max num of
* threads.
* @param bufferPoolSize Max size of the buffer pool in bytes.
*/
explicit SystemConfig(uint64_t bufferPoolSize);
/**
* @brief Creates a SystemConfig object with default buffer pool size and max num of threads.
* @param accessMode Access mode to the database (READ_ONLY or READ_WRITE).
*/
explicit SystemConfig(AccessMode accessMode);
/**
* @brief Creates a SystemConfig object with max num of threads.
* @param bufferPoolSize Max size of the buffer pool in bytes.
* @param accessMode Access mode to the database (READ_ONLY or READ_WRITE).
*/
explicit SystemConfig(uint64_t bufferPoolSize, AccessMode accessMode);

uint64_t bufferPoolSize;
uint64_t maxNumThreads;
AccessMode accessMode;
};

/**
Expand Down Expand Up @@ -63,7 +78,8 @@ class Database {
static void setLoggingLevel(std::string loggingLevel);

private:
void initDBDirAndCoreFilesIfNecessary() const;
void checkAccessMode();
void initDBDirAndCoreFilesIfNecessary();
static void initLoggers();
static void dropLoggers();

Expand All @@ -88,6 +104,7 @@ class Database {
std::unique_ptr<transaction::TransactionManager> transactionManager;
std::unique_ptr<storage::WAL> wal;
std::shared_ptr<spdlog::logger> logger;
std::unique_ptr<common::FileInfo> lockFile;
};

} // namespace main
Expand Down
1 change: 1 addition & 0 deletions src/include/main/kuzu_fwd.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ class Catalog;
namespace common {
enum class StatementType : uint8_t;
class Value;
class FileInfo;
} // namespace common

namespace storage {
Expand Down
4 changes: 4 additions & 0 deletions src/include/storage/storage_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,10 @@ class StorageUtils {
common::StorageConstants::CATALOG_FILE_NAME_FOR_WAL);
}

static inline std::string getLockFilePath(const std::string& directory) {
return common::FileUtils::joinPath(directory, common::StorageConstants::LOCK_FILE_NAME);
}

// Note: This is a relatively slow function because of division and mod and making std::pair.
// It is not meant to be used in performance critical code path.
static inline std::pair<uint64_t, uint64_t> getQuotientRemainder(uint64_t i, uint64_t divisor) {
Expand Down
11 changes: 11 additions & 0 deletions src/main/connection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,24 @@ std::unique_ptr<PreparedStatement> Connection::prepare(const std::string& query)
std::unique_ptr<QueryResult> Connection::query(const std::string& query) {
lock_t lck{mtx};
auto preparedStatement = prepareNoLock(query);
bool statementReadOnly = preparedStatement->isReadOnly();
bool isReadOnlyMode = this->database->systemConfig.accessMode == AccessMode::READ_ONLY;
if (isReadOnlyMode && !preparedStatement->isReadOnly()) {
throw ConnectionException(
"Cannot execute write operations in a read-only access mode database!");
}
return executeAndAutoCommitIfNecessaryNoLock(preparedStatement.get());
}

std::unique_ptr<QueryResult> Connection::query(
const std::string& query, const std::string& encodedJoin) {
lock_t lck{mtx};
auto preparedStatement = prepareNoLock(query, true /* enumerate all plans */, encodedJoin);
bool isReadOnlyMode = this->database->systemConfig.accessMode == AccessMode::READ_ONLY;
if (isReadOnlyMode && !preparedStatement->isReadOnly()) {
throw ConnectionException(
"Cannot execute write operations in a read-only access mode database!");

Check warning on line 86 in src/main/connection.cpp

View check run for this annotation

Codecov / codecov/patch

src/main/connection.cpp#L83-L86

Added lines #L83 - L86 were not covered by tests
}
return executeAndAutoCommitIfNecessaryNoLock(preparedStatement.get());
}

Expand Down
40 changes: 37 additions & 3 deletions src/main/database.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
#include <unistd.h>
#endif

#include "common/exception/exception.h"
#include "common/file_utils.h"
#include "common/logging_level_utils.h"
#include "processor/processor.h"
#include "spdlog/spdlog.h"
Expand All @@ -21,9 +23,24 @@ using namespace kuzu::transaction;
namespace kuzu {
namespace main {

SystemConfig::SystemConfig() : SystemConfig(-1u) {}
static void getLockFileFlagsAndType(
AccessMode accessMode, bool createNew, int& flags, FileLockType& lock) {
flags = accessMode == AccessMode::READ_ONLY ? O_RDONLY : O_RDWR;
if (createNew) {
assert(flags == O_RDWR);
flags |= O_CREAT;
}
lock = accessMode == AccessMode::READ_ONLY ? FileLockType::READ_LOCK : FileLockType::WRITE_LOCK;
}

SystemConfig::SystemConfig() : SystemConfig(-1u, AccessMode::READ_WRITE) {}

SystemConfig::SystemConfig(uint64_t bufferPoolSize_)
: SystemConfig(bufferPoolSize_, AccessMode::READ_WRITE) {}

SystemConfig::SystemConfig(uint64_t bufferPoolSize_) {
SystemConfig::SystemConfig(AccessMode accessMode_) : SystemConfig(-1u, accessMode_) {}

Check warning on line 41 in src/main/database.cpp

View check run for this annotation

Codecov / codecov/patch

src/main/database.cpp#L41

Added line #L41 was not covered by tests

SystemConfig::SystemConfig(uint64_t bufferPoolSize_, AccessMode accessMode_) {
if (bufferPoolSize_ == -1u) {
#if defined(_WIN32)
MEMORYSTATUSEX status;
Expand All @@ -38,6 +55,7 @@ SystemConfig::SystemConfig(uint64_t bufferPoolSize_) {
(double_t)std::min(systemMemSize, (std::uint64_t)UINTPTR_MAX));
}
bufferPoolSize = bufferPoolSize_;
accessMode = accessMode_;
maxNumThreads = std::thread::hardware_concurrency();
}

Expand Down Expand Up @@ -68,10 +86,26 @@ void Database::setLoggingLevel(std::string loggingLevel) {
spdlog::set_level(LoggingLevelUtils::convertStrToLevelEnum(std::move(loggingLevel)));
}

void Database::initDBDirAndCoreFilesIfNecessary() const {
void Database::checkAccessMode() {
int flags;
FileLockType lock;
auto lockFilePath = StorageUtils::getLockFilePath(databasePath);
if (!FileUtils::fileOrPathExists(lockFilePath)) {
getLockFileFlagsAndType(systemConfig.accessMode, true, flags, lock);
} else {
getLockFileFlagsAndType(systemConfig.accessMode, false, flags, lock);
}
lockFile = FileUtils::openFile(lockFilePath, flags, lock);
}

void Database::initDBDirAndCoreFilesIfNecessary() {
if (!FileUtils::fileOrPathExists(databasePath)) {
if (systemConfig.accessMode == AccessMode::READ_ONLY) {
throw Exception("Cannot create an empty database under READ ONLY mode.");
}
FileUtils::createDir(databasePath);
}
checkAccessMode();
if (!FileUtils::fileOrPathExists(StorageUtils::getNodesStatisticsAndDeletedIDsFilePath(
databasePath, DBFileType::ORIGINAL))) {
NodesStoreStatsAndDeletedIDs::saveInitialNodesStatisticsAndDeletedIDsToFile(databasePath);
Expand Down
1 change: 1 addition & 0 deletions test/runner/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ add_kuzu_test(e2e_copy_csv_transaction_test e2e_copy_transaction_test.cpp)
add_kuzu_test(e2e_ddl_test e2e_ddl_test.cpp)
add_kuzu_test(e2e_test e2e_test.cpp)
add_kuzu_test(cleanup_test cleanup_test.cpp)
add_kuzu_test(test_locking test_locking.cpp)
Loading

0 comments on commit ccbcab0

Please sign in to comment.