Commit b0f055e7 authored by jan.koester's avatar jan.koester
Browse files

replica support

parent 9a78b4e5
Loading
Loading
Loading
Loading
+27 −0
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@
#include <cstring>
#include <vector>
#include <string>
#include <memory>

#ifdef  Windows
#include <wtypes.h>
@@ -140,4 +141,30 @@ namespace dbpp {
#endif
    };

    class ReplicatedDatabase {
    public:
        ReplicatedDatabase(const std::string &dbdriver, const std::string &primaryConn);
        ~ReplicatedDatabase();

        void addReplica(const std::string &dbdriver, const std::string &connstr);

        int exec(const SQL &sql, DBResult &res) const;
        const char *getDriverName() const;
        const SQL &autoincrement(SQL &sql) const;
        const SQL &getUUIDType(SQL &sql) const;
        bool isConnected() const;
        void reset();

        Database       &primary();
        const Database &primary() const;
        size_t          replicaCount() const;
        Database       &replica(size_t idx);
        const Database &replica(size_t idx) const;

    private:
        static bool isWriteStatement(const char *sql);
        std::unique_ptr<Database>              _primary;
        std::vector<std::unique_ptr<Database>>  _replicas;
    };

};
+101 −0
Original line number Diff line number Diff line
@@ -168,6 +168,107 @@ const char *dbpp::DBResult2::operator[](int value2){
    return nullptr;
}

// --- ReplicatedDatabase implementation ---

dbpp::ReplicatedDatabase::ReplicatedDatabase(const std::string &dbdriver, const std::string &primaryConn)
    : _primary(std::make_unique<Database>(dbdriver, primaryConn))
{
}

dbpp::ReplicatedDatabase::~ReplicatedDatabase() = default;

void dbpp::ReplicatedDatabase::addReplica(const std::string &dbdriver, const std::string &connstr) {
    _replicas.push_back(std::make_unique<Database>(dbdriver, connstr));
}

bool dbpp::ReplicatedDatabase::isWriteStatement(const char *sql) {
    if (!sql) return false;

    // Skip leading whitespace
    while (*sql == ' ' || *sql == '\t' || *sql == '\n' || *sql == '\r')
        ++sql;

    // Case-insensitive prefix check for write statements
    auto startsWith = [](const char *s, const char *prefix) -> bool {
        while (*prefix) {
            if (std::tolower((unsigned char)*s) != std::tolower((unsigned char)*prefix))
                return false;
            ++s; ++prefix;
        }
        return true;
    };

    return startsWith(sql, "insert") ||
           startsWith(sql, "update") ||
           startsWith(sql, "delete") ||
           startsWith(sql, "create") ||
           startsWith(sql, "alter")  ||
           startsWith(sql, "drop");
}

int dbpp::ReplicatedDatabase::exec(const SQL &sql, DBResult &res) const {
    if (isWriteStatement(sql.c_str())) {
        // Write: execute on primary first, then replicate to all replicas
        int rows = _primary->exec(sql, res);

        for (const auto &replica : _replicas) {
            try {
                DBResult rres;
                replica->exec(sql, rres);
            } catch (const std::exception &e) {
                std::fprintf(stderr, "ReplicatedDatabase: replica write failed: %s\n", e.what());
            }
        }

        return rows;
    } else {
        // Read: only from primary
        return _primary->exec(sql, res);
    }
}

const char *dbpp::ReplicatedDatabase::getDriverName() const {
    return _primary->getDriverName();
}

const dbpp::SQL &dbpp::ReplicatedDatabase::autoincrement(SQL &sql) const {
    return _primary->autoincrement(sql);
}

const dbpp::SQL &dbpp::ReplicatedDatabase::getUUIDType(SQL &sql) const {
    return _primary->getUUIDType(sql);
}

bool dbpp::ReplicatedDatabase::isConnected() const {
    return _primary->isConnected();
}

void dbpp::ReplicatedDatabase::reset() {
    _primary->reset();
    for (auto &replica : _replicas) {
        try {
            replica->reset();
        } catch (...) {}
    }
}

dbpp::Database &dbpp::ReplicatedDatabase::primary() {
    return *_primary;
}

const dbpp::Database &dbpp::ReplicatedDatabase::primary() const {
    return *_primary;
}

size_t dbpp::ReplicatedDatabase::replicaCount() const {
    return _replicas.size();
}

dbpp::Database &dbpp::ReplicatedDatabase::replica(size_t idx) {
    return *_replicas.at(idx);
}

const dbpp::Database &dbpp::ReplicatedDatabase::replica(size_t idx) const {
    return *_replicas.at(idx);
}