mirror of
https://github.com/torlando-tech/pyxis.git
synced 2026-08-16 15:50:11 +00:00
172 lines
6.4 KiB
C++
172 lines
6.4 KiB
C++
#include "LocationPersistence.h"
|
|
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
|
|
namespace Telemetry {
|
|
|
|
TransactionalLocationPersistence::CandidateResult
|
|
TransactionalLocationPersistence::readCandidate(
|
|
LocationPersistenceSlot slot,
|
|
std::size_t& size) {
|
|
size = 0;
|
|
bool exists = false;
|
|
if (!storage_.stat(slot, exists)) return CandidateResult::IO_ERROR;
|
|
if (!exists) return CandidateResult::ABSENT;
|
|
if (!storage_.read(slot, buffer_, sizeof(buffer_), size)) {
|
|
return CandidateResult::IO_ERROR;
|
|
}
|
|
if (validateLocationStateRecord(buffer_, size) !=
|
|
LocationStateRecordResult::OK) {
|
|
return CandidateResult::INVALID;
|
|
}
|
|
return CandidateResult::VALID;
|
|
}
|
|
|
|
bool TransactionalLocationPersistence::removeIfPresent(
|
|
LocationPersistenceSlot slot) {
|
|
bool exists = false;
|
|
if (!storage_.stat(slot, exists)) return false;
|
|
return !exists || storage_.remove(slot);
|
|
}
|
|
|
|
LocationPersistenceResult TransactionalLocationPersistence::save(
|
|
const LocationStateSnapshot& state) {
|
|
if (!storage_.available()) return LocationPersistenceResult::UNAVAILABLE;
|
|
|
|
std::size_t encoded_size = 0;
|
|
if (encodeLocationStateRecord(
|
|
state, buffer_, sizeof(buffer_), encoded_size) !=
|
|
LocationStateRecordResult::OK) {
|
|
return LocationPersistenceResult::ENCODE_ERROR;
|
|
}
|
|
if (!removeIfPresent(LocationPersistenceSlot::TEMP)) {
|
|
return LocationPersistenceResult::IO_ERROR;
|
|
}
|
|
if (!storage_.write(LocationPersistenceSlot::TEMP, buffer_, encoded_size)) {
|
|
return LocationPersistenceResult::IO_ERROR;
|
|
}
|
|
|
|
std::size_t candidate_size = 0;
|
|
const CandidateResult temp =
|
|
readCandidate(LocationPersistenceSlot::TEMP, candidate_size);
|
|
if (temp == CandidateResult::IO_ERROR) {
|
|
return LocationPersistenceResult::IO_ERROR;
|
|
}
|
|
if (temp != CandidateResult::VALID) {
|
|
return LocationPersistenceResult::INVALID_STATE;
|
|
}
|
|
|
|
const CandidateResult live =
|
|
readCandidate(LocationPersistenceSlot::LIVE, candidate_size);
|
|
if (live == CandidateResult::IO_ERROR) {
|
|
return LocationPersistenceResult::IO_ERROR;
|
|
}
|
|
if (live == CandidateResult::VALID) {
|
|
if (!removeIfPresent(LocationPersistenceSlot::BACKUP)) {
|
|
return LocationPersistenceResult::IO_ERROR;
|
|
}
|
|
if (!storage_.rename(LocationPersistenceSlot::LIVE,
|
|
LocationPersistenceSlot::BACKUP)) {
|
|
return LocationPersistenceResult::IO_ERROR;
|
|
}
|
|
} else if (live == CandidateResult::INVALID) {
|
|
if (!storage_.remove(LocationPersistenceSlot::LIVE)) {
|
|
return LocationPersistenceResult::IO_ERROR;
|
|
}
|
|
}
|
|
|
|
if (!storage_.rename(LocationPersistenceSlot::TEMP,
|
|
LocationPersistenceSlot::LIVE)) {
|
|
return LocationPersistenceResult::IO_ERROR;
|
|
}
|
|
const CandidateResult promoted =
|
|
readCandidate(LocationPersistenceSlot::LIVE, candidate_size);
|
|
if (promoted == CandidateResult::IO_ERROR) {
|
|
// TEMP was fully read-back validated before the atomic promotion.
|
|
// Once rename succeeds, the new generation is committed even if an
|
|
// immediate second observation is unavailable.
|
|
return LocationPersistenceResult::SAVED;
|
|
}
|
|
if (promoted != CandidateResult::VALID) {
|
|
return LocationPersistenceResult::INVALID_STATE;
|
|
}
|
|
return LocationPersistenceResult::SAVED;
|
|
}
|
|
|
|
void TransactionalLocationPersistence::repairFromTemp() {
|
|
bool live_exists = false;
|
|
if (!storage_.stat(LocationPersistenceSlot::LIVE, live_exists)) return;
|
|
if (live_exists && !storage_.remove(LocationPersistenceSlot::LIVE)) return;
|
|
storage_.rename(LocationPersistenceSlot::TEMP,
|
|
LocationPersistenceSlot::LIVE);
|
|
}
|
|
|
|
void TransactionalLocationPersistence::repairFromBackup(std::size_t size) {
|
|
if (!removeIfPresent(LocationPersistenceSlot::TEMP)) return;
|
|
if (!storage_.write(LocationPersistenceSlot::TEMP, buffer_, size)) return;
|
|
|
|
std::size_t verified_size = 0;
|
|
if (readCandidate(LocationPersistenceSlot::TEMP, verified_size) !=
|
|
CandidateResult::VALID) {
|
|
return;
|
|
}
|
|
bool live_exists = false;
|
|
if (!storage_.stat(LocationPersistenceSlot::LIVE, live_exists)) return;
|
|
if (live_exists && !storage_.remove(LocationPersistenceSlot::LIVE)) return;
|
|
storage_.rename(LocationPersistenceSlot::TEMP,
|
|
LocationPersistenceSlot::LIVE);
|
|
}
|
|
|
|
LocationPersistenceResult TransactionalLocationPersistence::load(
|
|
LocationStateSnapshot& output) {
|
|
if (!storage_.available()) return LocationPersistenceResult::UNAVAILABLE;
|
|
|
|
bool saw_invalid = false;
|
|
std::size_t size = 0;
|
|
CandidateResult result = readCandidate(LocationPersistenceSlot::LIVE, size);
|
|
if (result == CandidateResult::VALID) {
|
|
return decodeLocationStateRecord(buffer_, size, output) ==
|
|
LocationStateRecordResult::OK
|
|
? LocationPersistenceResult::LOADED_LIVE
|
|
: LocationPersistenceResult::INVALID_STATE;
|
|
}
|
|
if (result == CandidateResult::IO_ERROR) {
|
|
return LocationPersistenceResult::IO_ERROR;
|
|
}
|
|
saw_invalid = result == CandidateResult::INVALID;
|
|
|
|
result = readCandidate(LocationPersistenceSlot::TEMP, size);
|
|
if (result == CandidateResult::VALID) {
|
|
if (decodeLocationStateRecord(buffer_, size, output) !=
|
|
LocationStateRecordResult::OK) {
|
|
return LocationPersistenceResult::INVALID_STATE;
|
|
}
|
|
repairFromTemp();
|
|
return LocationPersistenceResult::RECOVERED_TEMP;
|
|
}
|
|
if (result == CandidateResult::IO_ERROR) {
|
|
return LocationPersistenceResult::IO_ERROR;
|
|
}
|
|
saw_invalid = saw_invalid || result == CandidateResult::INVALID;
|
|
|
|
result = readCandidate(LocationPersistenceSlot::BACKUP, size);
|
|
if (result == CandidateResult::VALID) {
|
|
if (decodeLocationStateRecord(buffer_, size, output) !=
|
|
LocationStateRecordResult::OK) {
|
|
return LocationPersistenceResult::INVALID_STATE;
|
|
}
|
|
repairFromBackup(size);
|
|
return LocationPersistenceResult::RECOVERED_BACKUP;
|
|
}
|
|
if (result == CandidateResult::IO_ERROR) {
|
|
return LocationPersistenceResult::IO_ERROR;
|
|
}
|
|
saw_invalid = saw_invalid || result == CandidateResult::INVALID;
|
|
|
|
if (saw_invalid) return LocationPersistenceResult::INVALID_STATE;
|
|
return LocationPersistenceResult::NOT_FOUND;
|
|
}
|
|
|
|
} // namespace Telemetry
|