470 lines
13 KiB
C++
470 lines
13 KiB
C++
/*
|
|
|
|
1.1.1 Class Implementation
|
|
|
|
----
|
|
This file is part of SECONDO.
|
|
|
|
Copyright (C) 2017,
|
|
Faculty of Mathematics and Computer Science,
|
|
Database Systems for New Applications.
|
|
|
|
SECONDO is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
SECONDO is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with SECONDO; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
----
|
|
|
|
*/
|
|
#include "Algebras/DBService2/Replica.hpp"
|
|
#include "Algebras/DBService2/SecondoReplicaAdapter.hpp"
|
|
#include "Algebras/DBService2/TriggerReplicaDeletionRunnable.hpp"
|
|
|
|
#include <string>
|
|
#include <sstream>
|
|
#include <map>
|
|
|
|
#include <loguru.hpp>
|
|
|
|
using namespace std;
|
|
|
|
namespace DBService {
|
|
|
|
string Replica::statusWaiting = "waiting";
|
|
string Replica::statusReplicated = "replicated";
|
|
string Replica::statusFailed = "failed";
|
|
|
|
string Replica::replicaTypeRelation = "relation";
|
|
string Replica::replicaTypeDerivative = "derivative";
|
|
|
|
Replica::Replica() : Record::Record() {
|
|
setStatusWaiting();
|
|
setDatabase("");
|
|
setType(Replica::replicaTypeRelation);
|
|
setDerivativeId(-1);
|
|
}
|
|
|
|
void Replica::setStatusWaiting() {
|
|
if (status != statusWaiting) {
|
|
status = statusWaiting;
|
|
setDirty();
|
|
}
|
|
}
|
|
|
|
void Replica::setStatusReplicated() {
|
|
if (status != statusReplicated) {
|
|
status = statusReplicated;
|
|
setDirty();
|
|
}
|
|
}
|
|
|
|
void Replica::setStatusFailed() {
|
|
if (status != statusFailed) {
|
|
status = statusFailed;
|
|
setDirty();
|
|
}
|
|
}
|
|
|
|
void Replica::setStatus(string newStatus) {
|
|
|
|
if (status != newStatus) {
|
|
status = newStatus;
|
|
setDirty();
|
|
}
|
|
}
|
|
|
|
string Replica::getStatus() const {
|
|
return status;
|
|
}
|
|
|
|
string Replica::getType() const {
|
|
return type;
|
|
}
|
|
|
|
void Replica::setType(string newType) {
|
|
type = newType;
|
|
}
|
|
|
|
shared_ptr<DBService::Node> Replica::getTargetNode() const {
|
|
return targetNode;
|
|
}
|
|
|
|
int Replica::getTargetNodeId() const {
|
|
return targetNodeId;
|
|
}
|
|
|
|
void Replica::setTargetNodeId(int newTargetNodeId) {
|
|
targetNodeId = newTargetNodeId;
|
|
}
|
|
|
|
|
|
void Replica::setTargetNode(shared_ptr<DBService::Node> newTargetNode) {
|
|
if (newTargetNode == nullptr)
|
|
throw "Can't execute setTargetNode(nullptr)!";
|
|
|
|
//TODO Remove code duplicity across Record subclasses;
|
|
if (newTargetNode != targetNode) {
|
|
|
|
targetNode = newTargetNode;
|
|
targetNodeId = newTargetNode->getId();
|
|
|
|
if (getIsNew() == true)
|
|
setDirty();
|
|
}
|
|
}
|
|
|
|
int Replica::getRelationId() const {
|
|
return relationId;
|
|
}
|
|
|
|
void Replica::setRelationId(int newRelationId) {
|
|
relationId = newRelationId;
|
|
}
|
|
|
|
int Replica::getDerivativeId() const {
|
|
return derivativeId;
|
|
}
|
|
|
|
void Replica::setDerivativeId(int newDerivativeId) {
|
|
derivativeId = newDerivativeId;
|
|
}
|
|
|
|
shared_ptr<DBService::Relation> Replica::getRelation() const {
|
|
return relation;
|
|
}
|
|
|
|
void Replica::setRelation(shared_ptr<DBService::Relation> newRelation,
|
|
bool force) {
|
|
|
|
if (newRelation == nullptr)
|
|
throw "Can't execute setRelation(nullptr)!";
|
|
|
|
if (newRelation != relation) {
|
|
if ((getIsNew() == true) || force == true) {
|
|
relation = newRelation;
|
|
relationId = relation->getId();
|
|
|
|
if (force == false)
|
|
setDirty();
|
|
} else {
|
|
throw "Can't change the relation of a non-new replica!";
|
|
}
|
|
}
|
|
}
|
|
|
|
void Replica::setDerivative(shared_ptr<Derivative> newDerivative,
|
|
bool force) {
|
|
|
|
if(newDerivative == nullptr)
|
|
throw "Can't execute setDervivative(nullptr)!";
|
|
|
|
if(newDerivative != derivative) {
|
|
derivative = newDerivative;
|
|
|
|
if(force == false)
|
|
setDirty();
|
|
}
|
|
}
|
|
|
|
shared_ptr<Derivative> Replica::getDerivative() const {
|
|
return derivative;
|
|
}
|
|
|
|
string Replica::str(int indentationLevel) const {
|
|
string ind = std::string(2 * indentationLevel, ' ');
|
|
string ind2 = ind + " ";
|
|
|
|
/*
|
|
With SecondoPLTTYCS
|
|
These won't work:
|
|
|
|
std::string(((4 * indentationLevel) - 2), ' ');
|
|
std::string(--indentationLevel, '\t');
|
|
|
|
With indentationLevel = 2
|
|
|
|
*/
|
|
|
|
stringstream ret;
|
|
|
|
ret << ind << "{" << endl;
|
|
ret << ind2 << "Type: " << getType() << endl;
|
|
ret << ind2 << "Status: " << getStatus() << endl;
|
|
ret << ind2 << "isDirty: " << getIsDirty() << endl;
|
|
|
|
ret << ind2 << "TargetNode: " << endl;
|
|
|
|
if (targetNode != nullptr)
|
|
ret << targetNode->str( indentationLevel + 1 );
|
|
else
|
|
ret << ind2 << targetNodeId;
|
|
|
|
ret << endl;
|
|
|
|
ret << ind2 << "Relation: " << endl;
|
|
|
|
if (relation != nullptr)
|
|
ret << relation->str( indentationLevel + 1);
|
|
else
|
|
ret << ind2 << relationId;
|
|
|
|
ret << endl;
|
|
|
|
ret << ind << "}";
|
|
|
|
return ret.str();
|
|
}
|
|
|
|
string Replica::getRelationName() {
|
|
return "dbs_replicas";
|
|
}
|
|
|
|
string Replica::createRelationStatement() {
|
|
return "let " + getRelationName() + " = [const rel(tuple([RelationId: int, \
|
|
TargetNodeId: int, Status: string, Type: string, DerivativeId: int])) \
|
|
value ()]";
|
|
|
|
}
|
|
|
|
string Replica::createStatement() const {
|
|
const string quote = "\"";
|
|
stringstream createStatement;
|
|
|
|
if (getType().empty())
|
|
throw "Can't save Replica without ReplicaType!";
|
|
|
|
if (getTargetNode() == nullptr) {
|
|
stringstream msg;
|
|
msg << "Can't execute Replica::createStatement() for " << str();
|
|
msg << " without target node!";
|
|
throw msg.str();
|
|
}
|
|
|
|
if (getRelation() == nullptr)
|
|
throw "Can't execute Replica::createStatement() without relation";
|
|
|
|
createStatement << "query " << Replica::getRelationName();
|
|
createStatement << " inserttuple" << "[";
|
|
|
|
// RelationId: int, TargetNodeId: int, Status: string
|
|
createStatement << relationId << ", ";
|
|
createStatement << targetNodeId << ", ";
|
|
createStatement << quote << getStatus() << quote << ", ";
|
|
createStatement << quote << getType() << quote << ", ";
|
|
createStatement << derivativeId;
|
|
|
|
createStatement << "] consume";
|
|
return createStatement.str();
|
|
}
|
|
|
|
string Replica::updateStatement() const {
|
|
|
|
stringstream updatedirect;
|
|
updatedirect << "updatedirect[";
|
|
updatedirect << "RelationId: " << relationId << ", ";
|
|
updatedirect << "TargetNodeId: " << targetNodeId << ", ";
|
|
updatedirect << "Status: \"" << getStatus() << "\"" << ", ";
|
|
updatedirect << "Type: \"" << getType() << "\"" << ", ";
|
|
updatedirect << "DerivativeId: " << derivativeId << "]";
|
|
|
|
Query updateQuery = Replica::query(getDatabase()).feed().filter(
|
|
".Type = ?", getType()).filter(".RelationId = ?", relationId).filter(
|
|
".TargetNodeId = ?", targetNodeId).relation(
|
|
Replica::getRelationName()).appendString(updatedirect.str()).consume();
|
|
|
|
return updateQuery.str();
|
|
}
|
|
|
|
//TODO Remove code duplication across Record subclasses.
|
|
Query Replica::query(string database) {
|
|
stringstream query;
|
|
|
|
query << "query" << " " << getRelationName();
|
|
|
|
return Query(database, query.str());
|
|
}
|
|
|
|
/*
|
|
Two Replicas are equal if they share the same target Node and
|
|
represent the same Relation.
|
|
*/
|
|
bool Replica::operator==(const Replica &other) const {
|
|
|
|
// Dereference objects to avoid comparing their shared_ptrs.
|
|
return (*getTargetNode() == *other.getTargetNode()
|
|
&& *getRelation() == *other.getRelation()
|
|
&& getType() == other.getType()
|
|
);
|
|
}
|
|
|
|
bool Replica::operator!=(const Replica &other) const {
|
|
// Dereference objects to avoid comparing their shared_ptrs.
|
|
return (*getTargetNode() != *other.getTargetNode()
|
|
|| *getRelation() != *other.getRelation()
|
|
|| getType() != other.getType());
|
|
}
|
|
|
|
vector<shared_ptr<Replica> > Replica::findByRelationId(
|
|
string database, int relationId) {
|
|
|
|
LOG_SCOPE_FUNCTION(INFO);
|
|
|
|
//TODO is there a way to use a template function with lamdas
|
|
// to make the cache lookup generic?
|
|
|
|
// Cache
|
|
vector<shared_ptr<Replica> > resultsFromCache;
|
|
// std::map<int, std::shared_ptr<Replica> >::iterator it = cache.begin();
|
|
|
|
//TODO Remove
|
|
// while(it != cache.end()) {
|
|
// auto currentReplica = it->second;
|
|
|
|
// if(currentReplica->getRelationId() == relationId) {
|
|
// LOG_F(INFO, "Cache relationId %d, relationId: %d",
|
|
// currentReplica->getRelationId(), relationId);
|
|
|
|
// resultsFromCache.push_back(currentReplica);
|
|
// }
|
|
|
|
// it++;
|
|
// }
|
|
|
|
auto matchPredicate = [=](
|
|
std::pair<int, std::shared_ptr<Replica>> const& currentReplicaPair) {
|
|
return currentReplicaPair.second->getRelationId() == relationId;
|
|
};
|
|
|
|
resultsFromCache = findManyInCache(matchPredicate);
|
|
|
|
|
|
|
|
if(useCache() && cacheValidity == true) {
|
|
LOG_F(INFO, "Cache hit. Found %d cached Replicas.",
|
|
resultsFromCache.size());
|
|
|
|
return resultsFromCache;
|
|
}
|
|
|
|
LOG_F(INFO, "%s", "Cache miss. Loading from DB...");
|
|
|
|
string type(Replica::replicaTypeRelation);
|
|
Query query = DBService::Replica::query(database).feed().filter(
|
|
".RelationId = ?", relationId).filter(
|
|
".Type = ?", type).addid().consume();
|
|
|
|
return query.retrieveVector<Replica, SecondoReplicaAdapter>();
|
|
}
|
|
|
|
vector<shared_ptr<Replica> > Replica::findByRelationIdAndType(string database,
|
|
int relationId, string replicaType) {
|
|
|
|
LOG_SCOPE_FUNCTION(INFO);
|
|
|
|
Query query = DBService::Replica::query(database).feed().filter(
|
|
".RelationId = ?", relationId).filter(
|
|
".Type = ?", replicaType).addid().consume();
|
|
|
|
return query.retrieveVector<Replica, SecondoReplicaAdapter>();
|
|
}
|
|
|
|
vector<shared_ptr<Replica> > Replica::findByDerivativeId(string database,
|
|
int derivativeId) {
|
|
|
|
LOG_SCOPE_FUNCTION(INFO);
|
|
|
|
//TODO is there a way to use a template function with lamdas
|
|
// to make the cache lookup generic?
|
|
|
|
// Cache
|
|
vector<shared_ptr<Replica> > resultsFromCache;
|
|
|
|
//TODO Remove
|
|
// std::map<int, std::shared_ptr<Replica> >::iterator it = cache.begin();
|
|
// while(it != cache.end()) {
|
|
// auto currentReplica = it->second;
|
|
|
|
// if(currentReplica->getDerivativeId() == derivativeId) {
|
|
// LOG_F(INFO, "Cache derivativeId %d, derivativeId: %d",
|
|
// currentReplica->getDerivativeId(), derivativeId);
|
|
|
|
// resultsFromCache.push_back(currentReplica);
|
|
// }
|
|
|
|
// it++;
|
|
// }
|
|
|
|
auto matchPredicate = [=] (
|
|
std::pair<int, std::shared_ptr<Replica>> const& currentReplicaPair) {
|
|
return currentReplicaPair.second->getDerivativeId() == derivativeId;
|
|
};
|
|
|
|
resultsFromCache = findManyInCache(matchPredicate);
|
|
|
|
|
|
if(useCache() && cacheValidity == true) {
|
|
LOG_F(INFO, "Cache hit. Found %d cached Replica(s).",
|
|
resultsFromCache.size());
|
|
|
|
return resultsFromCache;
|
|
}
|
|
|
|
LOG_F(INFO, "%s", "Cache miss. Loading from DB...");
|
|
|
|
string replicaType = Replica::replicaTypeDerivative;
|
|
|
|
Query query = DBService::Replica::query(database).feed().filter(
|
|
".DerivativeId = ?", derivativeId).filter(
|
|
".Type = ?", replicaType).addid().consume();
|
|
return query.retrieveVector<Replica, SecondoReplicaAdapter>();
|
|
}
|
|
|
|
void Replica::beforeDestroy() {
|
|
string derivativeName;
|
|
string relationName;
|
|
string relationDatabase;
|
|
|
|
if (getRelation() == nullptr)
|
|
throw "Can't destroy Replica without a pointer to a Relation";
|
|
|
|
if (getType() == replicaTypeDerivative) {
|
|
if (getDerivative() == nullptr) {
|
|
throw "Can't destroy Derivative Replica without a ptr to a Derivative";
|
|
} else {
|
|
/* If no derivative is given, derivateName will be "" which will trigger
|
|
the deletion of the entire replica. This unfortunate implicit behavor
|
|
is the reason why it must be verified that the replicaType is truly
|
|
"relation" to avoid accidentally deleting a relation if derivativeName
|
|
accidentally is set to "".
|
|
*/
|
|
derivativeName = getDerivative()->getName();
|
|
}
|
|
}
|
|
|
|
relationName = getRelation()->getName();
|
|
relationDatabase = getRelation()->getRelationDatabase();
|
|
|
|
TriggerReplicaDeletionRunnable replicaEraser(
|
|
targetNode->getHost().getHostname(),
|
|
targetNode->getComPort(),
|
|
relationDatabase, relationName, derivativeName);
|
|
|
|
replicaEraser.run();
|
|
}
|
|
|
|
ostream &operator<<(ostream &os, Replica const &replica) {
|
|
|
|
os << replica.str();
|
|
|
|
return os;
|
|
}
|
|
} |