Skip to content
Snippets Groups Projects

Better Error Handling and Logging

Merged Timm Fitschen requested to merge dev into main
1 file
+ 5
0
Compare changes
  • Side-by-side
  • Inline
+ 100
26
@@ -17,27 +17,65 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
#include "caosdb/entity.h"
#include "caosdb/exceptions.h" // for AuthenticationError
#include "caosdb/transaction.h"
#include <memory>
#include <utility>
#include <iostream> // for basic_ostream::ope...
#include <stdexcept> // for runtime_error
#include <grpcpp/impl/codegen/client_context.h> // for ClientContext
#include <grpcpp/impl/codegen/status.h> // for Status
#include "caosdb/entity/v1alpha1/main.grpc.pb.h" // for EntityTransactionS...
#include "caosdb/entity/v1alpha1/main.pb.h" // for RetrieveRequest
#include "caosdb/status_code.h" // for StatusCode, AUTHEN...
#include "grpcpp/grpcpp.h" // for CompletionQueue
#include "grpcpp/impl/codegen/async_unary_call.h" // for ClientAsyncRespons...
#include "grpcpp/impl/codegen/client_context.h" // for ClientContext
#include "grpcpp/impl/codegen/completion_queue.h" // for CompletionQueue
#include "grpcpp/impl/codegen/status.h" // for Status
#include "grpcpp/impl/codegen/status_code_enum.h" // for StatusCode, UNAUTH...
#include "caosdb/entity/v1alpha1/main.pb.h"
#include "caosdb/entity/v1alpha1/main.grpc.pb.h"
#include <cassert> // for assert
#include <map> // for map
#include <memory> // for allocator, unique_ptr
#include <stdexcept> // for out_of_range
#include <utility> // for move
namespace caosdb {
// TODO(tf) move to another source file.
auto get_status_description(int code) -> const std::string & {
static const std::string MISSING_DESCRIPTION = "MISSING DESCRIPTION";
static const std::map<int, std::string> descriptions = {
{StatusCode::INITIAL, "The transaction has not been executed yet and "
"clients can stil change it."},
{StatusCode::EXECUTING, "The transaction is currently being executed."},
{StatusCode::SUCCESS, "The action was successful"},
{StatusCode::CONNECTION_ERROR,
"The attempt to execute this transaction was not successful because the "
"connection to the server could not be established."},
{StatusCode::AUTHENTICATION_ERROR,
"The attempt to execute this transaction has not been executed at all "
"because the authentication did not succeed."},
{StatusCode::GENERIC_RPC_ERROR,
"The attempt to execute this transaction was not successful because an "
"error occured in the transport or RPC protocol layer."},
{StatusCode::GENERIC_ERROR,
"An error occured. Please review the logs for more information."},
{StatusCode::GENERIC_TRANSACTION_ERROR,
"The transaction terminated unsuccessfully with transaction errors."},
{StatusCode::CONFIGURATION_ERROR,
"An error occurred during the configuration of the ConfigurationManager."},
{StatusCode::UNKNOWN_CONNECTION_ERROR,
"The ConnectionManager does not know any connection of this name."}};
try {
return descriptions.at(code);
} catch (const std::out_of_range &exc) {
return MISSING_DESCRIPTION;
}
}
} // namespace caosdb
namespace caosdb::transaction {
using caosdb::entity::v1alpha1::EntityTransactionService;
using caosdb::entity::v1alpha1::RetrieveRequest;
using caosdb::entity::v1alpha1::RetrieveResponse;
using grpc::ClientAsyncResponseReader;
using ProtoEntity = caosdb::entity::v1alpha1::Entity;
using caosdb::exceptions::AuthenticationError;
using caosdb::exceptions::ConnectionError;
using grpc::CompletionQueue;
[[nodiscard]] auto UniqueResult::GetEntity() const -> const Entity & {
const Entity *result = this->entity.get();
@@ -57,30 +95,66 @@ auto Transaction::RetrieveById(const std::string &id) -> void {
this->request = request;
}
auto Transaction::Execute() -> void {
// TODO(tf) throw error if not int INIT state
this->state = TransactionState::EXECUTING;
auto Transaction::Execute() -> TransactionStatus {
ExecuteAsynchronously();
this->status.ThrowExceptionIfError();
return this->status;
}
auto Transaction::ExecuteAsynchronously() noexcept -> void {
this->status = TransactionStatus::EXECUTING();
grpc::Status grpc_status;
CompletionQueue cq;
RetrieveResponse response;
grpc::ClientContext context;
const grpc::Status status =
this->service_stub->Retrieve(&context, this->request, &response);
std::unique_ptr<ClientAsyncResponseReader<RetrieveResponse>> rpc(
this->service_stub->PrepareAsyncRetrieve(&context, this->request, &cq));
rpc->StartCall();
int tag = 1;
void *send_tag = static_cast<void *>(&tag);
rpc->Finish(&response, &grpc_status, send_tag);
void *recv_tag = nullptr;
bool ok = false;
// TODO(tf) make this actually asynchronous by moving this to WaitForIt()
cq.Next(&recv_tag, &ok);
assert(recv_tag == send_tag);
assert(ok);
// const grpc::Status grpc_status =
// this->service_stub->Retrieve(&context, this->request, &response);
if (!status.ok()) {
switch (status.error_code()) {
if (!grpc_status.ok()) {
switch (grpc_status.error_code()) {
case grpc::StatusCode::UNAUTHENTICATED:
throw AuthenticationError(status.error_message());
this->status = TransactionStatus::AUTHENTICATION_ERROR();
break;
case grpc::StatusCode::UNAVAILABLE:
throw ConnectionError(status.error_message());
this->status = TransactionStatus::CONNECTION_ERROR();
break;
default:
std::cout << status.error_code() << "\n";
throw std::runtime_error(status.error_message());
auto error_details = std::to_string(grpc_status.error_code()) + " - " +
grpc_status.error_message();
this->status = TransactionStatus::RPC_ERROR(error_details);
}
return;
} else {
this->status = TransactionStatus::SUCCESS();
}
auto *entity = response.release_entity();
auto result_set = std::make_unique<UniqueResult>(entity);
this->result_set = std::move(result_set);
if (!entity->errors().empty()) {
this->status =
TransactionStatus::TRANSACTION_ERROR("The request returned with errors.");
}
this->result_set = std::make_unique<UniqueResult>(entity);
}
auto Transaction::WaitForIt() const noexcept -> TransactionStatus {
return this->status;
}
} // namespace caosdb::transaction
Loading