Skip to content

Commit 925d862

Browse files
Track deleted runtime objects to avoid erroneous recreation
1 parent 7d953f9 commit 925d862

3 files changed

Lines changed: 82 additions & 1 deletion

File tree

lib/remote/apilistener-configsync.cpp

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,13 @@ Value ApiListener::ConfigUpdateObjectAPIHandler(const MessageOrigin::Ptr& origin
9898
/* update the object */
9999
double objVersion = params->Get("version");
100100

101+
if (listener->GetRuntimeObjectDeletionTs(objType, objName) >= objVersion) {
102+
Log(LogInformation, "ApiListener")
103+
<< "Ignoring config update for deleted object '" << objName << "' of type '" << objType << "' from '"
104+
<< identity << "' (endpoint: '" << endpoint->GetName() << "', zone: '" << endpointZone->GetName() << "')";
105+
return Empty;
106+
}
107+
101108
Type::Ptr ptype = Type::GetByName(objType);
102109
auto *ctype = dynamic_cast<ConfigType *>(ptype.get());
103110

@@ -287,6 +294,11 @@ Value ApiListener::ConfigDeleteObjectAPIHandler(const MessageOrigin::Ptr& origin
287294
return Empty;
288295
}
289296

297+
/* We need to store the deletion timestamp even if no object exists, to protect against
298+
* endpoints that try to sync back objects that would have been deleted by this message.
299+
*/
300+
listener->UpdateRuntimeObjectDeletionTs(objType, objName, objVersion);
301+
290302
if (!object) {
291303
Log(LogNotice, "ApiListener")
292304
<< "Could not delete non-existent object '" << objName << "' with type '" << params->Get("type") << "'.";
@@ -450,6 +462,24 @@ void ApiListener::DeleteConfigObject(const ConfigObject::Ptr& object, const Mess
450462
if (object->GetPackage() != "_api")
451463
return;
452464

465+
auto typeName = object->GetReflectionType()->GetName();
466+
auto objName = object->GetName();
467+
468+
/* Update the deletion timestamp only if the deletion didn't originate from another
469+
* endpoint, because when this deletion was triggered via a JSON-RPC message, the
470+
* stored timestamp is the one that was set in `ApiListener::ConfigDeleteObjectAPIHandler`
471+
* and already reflects the time the original deletion was triggered.
472+
* Also set if no timestamp was stored yet, as a safeguard. There shouldn't be any
473+
* situation where this happens, but that relies on the correct execution order of
474+
* many other functions, like cascading deletes always propagating in the correct
475+
* order through JSON-RPC, so the children are always deleted first.
476+
*/
477+
double deletionTime = GetRuntimeObjectDeletionTs(typeName, objName);
478+
if (!origin || origin->IsLocal() || deletionTime == 0) {
479+
deletionTime = Utility::GetTime();
480+
UpdateRuntimeObjectDeletionTs(typeName, objName, deletionTime);
481+
}
482+
453483
/* only send objects to zones which have access to the object */
454484
if (client) {
455485
Zone::Ptr target_zone = client->GetEndpoint()->GetZone();
@@ -473,7 +503,7 @@ void ApiListener::DeleteConfigObject(const ConfigObject::Ptr& object, const Mess
473503

474504
params->Set("name", object->GetName());
475505
params->Set("type", object->GetReflectionType()->GetName());
476-
params->Set("version", object->GetVersion());
506+
params->Set("version", deletionTime);
477507

478508

479509
#ifdef I2_DEBUG
@@ -521,3 +551,47 @@ void ApiListener::SendRuntimeConfigObjects(const JsonRpcConnection::Ptr& aclient
521551
Log(LogInformation, "ApiListener")
522552
<< "Finished syncing runtime objects to endpoint '" << endpoint->GetName() << "'.";
523553
}
554+
555+
static String MakeDeletionTimestampKey(const String& typeName, const String& objName)
556+
{
557+
return typeName + ":" + objName;
558+
}
559+
560+
/**
561+
* Get the timestamp at which the object has been most recently deleted.
562+
*
563+
* @param typeName The name of the tyope of the object
564+
* @param objName The name of the object
565+
* @return the timestamp of the object, or 0 if there was no entry.
566+
*/
567+
double ApiListener::GetRuntimeObjectDeletionTs(const String& typeName, const String& objName)
568+
{
569+
auto dict = GetDeletedRuntimeObjects();
570+
auto ts = dict->Get(MakeDeletionTimestampKey(typeName, objName));
571+
if (ts.IsNumber()) {
572+
return ts.Get<double>();
573+
}
574+
return 0;
575+
}
576+
577+
/**
578+
* Updates the deletion timestamp for the object.
579+
*
580+
* The timestamp will not be changed in case a newer timestamp has already been stored
581+
* for the object.
582+
*
583+
* @param typeName The name of the tyope of the object
584+
* @param objName The name of the object
585+
* @param ts the timestamp
586+
*/
587+
bool ApiListener::UpdateRuntimeObjectDeletionTs(const String& typeName, const String& objName, double ts)
588+
{
589+
auto dict = GetDeletedRuntimeObjects();
590+
ObjectLock lock(dict);
591+
auto current = GetRuntimeObjectDeletionTs(typeName, objName);
592+
if (current > ts) {
593+
return false;
594+
}
595+
dict->Set(MakeDeletionTimestampKey(typeName, objName), ts);
596+
return true;
597+
}

lib/remote/apilistener.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,9 @@ class ApiListener final : public ObjectImpl<ApiListener>
204204
void CleanupCertificateRequestsTimerHandler();
205205
void CheckApiPackageIntegrity();
206206

207+
double GetRuntimeObjectDeletionTs(const String& typeName, const String& objName);
208+
bool UpdateRuntimeObjectDeletionTs(const String& typeName, const String& objName, double ts);
209+
207210
bool AddListener(const String& node, const String& service);
208211
void StopListener();
209212
void AddConnection(const Endpoint::Ptr& endpoint);

lib/remote/apilistener.ti

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,10 @@ class ApiListener : ConfigObject
6363
[no_user_modify] String identity;
6464

6565
[state, no_user_modify] Dictionary::Ptr last_failed_zones_stage_validation;
66+
67+
[state, no_user_modify] Dictionary::Ptr deleted_runtime_objects {
68+
default {{{ return new Dictionary(); }}}
69+
};
6670
};
6771

6872
}

0 commit comments

Comments
 (0)