From 9c0ad8e2d59fc13614a0e9e889ea684bec8496d5 Mon Sep 17 00:00:00 2001 From: xq9mend Date: Tue, 28 Apr 2026 15:43:21 +0000 Subject: [PATCH 1/6] Consume FDB LEARN/AGED/MOVE events from ProducerStateTable Add ConsumerStateTable consumer for FDB_EVENT_STATE table in FdbOrch. LEARN/AGED/MOVE events are now dispatched via doTask(Consumer&) with key-based dedup, so MAC flap storms collapse to one entry per MAC+BVID. The existing NotificationConsumer path (PUBLISH/SUBSCRIBE) now handles only FLUSH events plus a fallback safety-net for any non-FLUSH events that arrive during transition or in ZeroMQ mode. Also renames the local sai_fdb_type variable to fdb_type in the NotificationConsumer loop to avoid shadowing. Signed-off-by: xq9mend --- orchagent/fdborch.cpp | 85 +++++++++++++++++++++++++++++++++++++++---- orchagent/fdborch.h | 2 + 2 files changed, 80 insertions(+), 7 deletions(-) diff --git a/orchagent/fdborch.cpp b/orchagent/fdborch.cpp index 6b0c528574d..2795947ca45 100644 --- a/orchagent/fdborch.cpp +++ b/orchagent/fdborch.cpp @@ -12,6 +12,7 @@ #include "macmoveguard.h" #include "crmorch.h" #include "notifier.h" +#include "consumerstatetable.h" #include "sai_serialize.h" #include "mlagorch.h" #include "vxlanorch.h" @@ -97,11 +98,11 @@ FdbOrch::FdbOrch(DBConnector* applDbConnector, vector app m_macMoveGuard.reset(new MacMoveGuard(configDb, stateDbFdbConnector.first, CFG_MAC_MOVE_GUARD_TABLE_NAME, m_portsOrch, this)); -} -FdbOrch::~FdbOrch() -{ - m_portsOrch->detach(this); + /* Add ConsumerStateTable for FDB LEARN/AGED/MOVE events (key-based dedup) */ + auto fdbEventConsumer = new ConsumerStateTable(m_notificationsDb.get(), ASIC_FDB_EVENT_STATE_TABLE); + auto fdbEventExecutor = new Consumer(fdbEventConsumer, this, ASIC_FDB_EVENT_STATE_TABLE); + Orch::addExecutor(fdbEventExecutor); } bool FdbOrch::bake() @@ -1029,6 +1030,67 @@ void FdbOrch::doTask(Consumer& consumer) FdbOrigin origin = FDB_ORIGIN_PROVISIONED; + /* + * Handle FDB events from ASIC via ProducerStateTable (LEARN/AGED/MOVE). + * These arrive with key-based dedup — multiple events for same MAC+BVID + * are merged by ConsumerStateTable, solving the MAC flap memory storm. + */ + if (table_name == ASIC_FDB_EVENT_STATE_TABLE) + { + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + KeyOpFieldsValuesTuple t = it->second; + string key = kfvKey(t); + string op = kfvOp(t); + + /* Deserialize the FDB entry from the key */ + sai_fdb_entry_t fdb_entry; + sai_deserialize_fdb_entry(key, fdb_entry); + + if (op == DEL_COMMAND) + { + /* AGED event: DEL means the MAC was aged out */ + sai_object_id_t bridge_port_id = SAI_NULL_OBJECT_ID; + sai_fdb_entry_type_t sai_fdb_type = SAI_FDB_ENTRY_TYPE_DYNAMIC; + + this->update(SAI_FDB_EVENT_AGED, &fdb_entry, bridge_port_id, sai_fdb_type); + it = consumer.m_toSync.erase(it); + } + else + { + /* LEARN or MOVE event */ + sai_fdb_event_t event_type = SAI_FDB_EVENT_LEARNED; + sai_object_id_t bridge_port_id = SAI_NULL_OBJECT_ID; + sai_fdb_entry_type_t sai_fdb_type = SAI_FDB_ENTRY_TYPE_DYNAMIC; + + for (auto &fv : kfvFieldsValues(t)) + { + if (fvField(fv) == "event_type") + { + sai_deserialize_fdb_event(fvValue(fv), event_type); + } + else if (fvField(fv) == "bridge_port_id") + { + sai_deserialize_object_id(fvValue(fv), bridge_port_id); + } + else if (fvField(fv) == "sai_fdb_type") + { + int32_t val; + sai_deserialize_enum(fvValue(fv), + &sai_metadata_enum_sai_fdb_entry_type_t, val); + sai_fdb_type = (sai_fdb_entry_type_t)val; + } + } + + this->update(event_type, &fdb_entry, bridge_port_id, sai_fdb_type); + it = consumer.m_toSync.erase(it); + } + } + return; + } + + if(table_name == APP_VXLAN_FDB_TABLE_NAME) { origin = FDB_ORIGIN_VXLAN_ADVERTIZED; @@ -1400,6 +1462,15 @@ void FdbOrch::doTask(NotificationConsumer& consumer) } else if (&consumer == m_fdbNotificationConsumer && op == "fdb_event") { + /* + * After the ProducerStateTable migration, only FLUSH events arrive + * here via PUBLISH/SUBSCRIBE. LEARN/AGED/MOVE events are now handled + * by doTask(Consumer&) via ConsumerStateTable with key-based dedup. + * + * As a safety net, any non-FLUSH events that still arrive on this + * path (e.g., during transition or from ZeroMQ mode) are also + * processed. + */ uint32_t count; sai_fdb_event_notification_data_t *fdbevent = nullptr; sai_deserialize_fdb_event_ntf(data, count, &fdbevent); @@ -1407,7 +1478,7 @@ void FdbOrch::doTask(NotificationConsumer& consumer) for (uint32_t i = 0; i < count; ++i) { sai_object_id_t oid = SAI_NULL_OBJECT_ID; - sai_fdb_entry_type_t sai_fdb_type = SAI_FDB_ENTRY_TYPE_DYNAMIC; + sai_fdb_entry_type_t fdb_type = SAI_FDB_ENTRY_TYPE_DYNAMIC; for (uint32_t j = 0; j < fdbevent[i].attr_count; ++j) { @@ -1417,11 +1488,11 @@ void FdbOrch::doTask(NotificationConsumer& consumer) } else if (fdbevent[i].attr[j].id == SAI_FDB_ENTRY_ATTR_TYPE) { - sai_fdb_type = (sai_fdb_entry_type_t)fdbevent[i].attr[j].value.s32; + fdb_type = (sai_fdb_entry_type_t)fdbevent[i].attr[j].value.s32; } } - this->update(fdbevent[i].event_type, &fdbevent[i].fdb_entry, oid, sai_fdb_type); + this->update(fdbevent[i].event_type, &fdbevent[i].fdb_entry, oid, fdb_type); } sai_deserialize_free_fdb_event_ntf(count, fdbevent); diff --git a/orchagent/fdborch.h b/orchagent/fdborch.h index c79634c3de5..0d58ce94220 100644 --- a/orchagent/fdborch.h +++ b/orchagent/fdborch.h @@ -114,6 +114,8 @@ typedef unordered_map> saved_fdb_entries_by_port_t */ typedef unordered_map> fdb_entries_by_port_t; +#define ASIC_FDB_EVENT_STATE_TABLE "FDB_EVENT_STATE" + class FdbOrch: public Orch, public Subject, public Observer { /* Embedded MacMoveGuard registers its config-table Consumer and recovery From 06840af17df832c1f18204641980ea6d50921b2d Mon Sep 17 00:00:00 2001 From: xq9mend Date: Wed, 29 Apr 2026 20:27:34 +0000 Subject: [PATCH 2/6] Fix sai_deserialize_fdb_event call: use .c_str() for C API compatibility The installed saimetadata.h in CI has sai_deserialize_fdb_event with C-style signature (const char*, sai_fdb_event_t*). Use .c_str() and pass pointer to match. Signed-off-by: xq9mend --- orchagent/fdborch.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/orchagent/fdborch.cpp b/orchagent/fdborch.cpp index 2795947ca45..7885600be6f 100644 --- a/orchagent/fdborch.cpp +++ b/orchagent/fdborch.cpp @@ -105,6 +105,11 @@ FdbOrch::FdbOrch(DBConnector* applDbConnector, vector app Orch::addExecutor(fdbEventExecutor); } +FdbOrch::~FdbOrch() +{ + m_portsOrch->detach(this); +} + bool FdbOrch::bake() { Orch::bake(); @@ -1068,7 +1073,7 @@ void FdbOrch::doTask(Consumer& consumer) { if (fvField(fv) == "event_type") { - sai_deserialize_fdb_event(fvValue(fv), event_type); + sai_deserialize_fdb_event(fvValue(fv).c_str(), &event_type); } else if (fvField(fv) == "bridge_port_id") { From 9d8b3bd2b32d12be3c68052cc1c266e5c76e2596 Mon Sep 17 00:00:00 2001 From: xq9mend Date: Thu, 6 Aug 2026 10:10:55 +0000 Subject: [PATCH 3/6] ci: re-trigger CI (vstest infra flake) Signed-off-by: xq9mend From ba77720022a128a298aa7ef1811a15b04509c10d Mon Sep 17 00:00:00 2001 From: xq9mend Date: Fri, 7 Aug 2026 10:30:11 +0000 Subject: [PATCH 4/6] ci: re-trigger CI (infra flake) Signed-off-by: xq9mend From e7167f5f9742fe837b9da4b0f7f2b03f0b3e89f2 Mon Sep 17 00:00:00 2001 From: xq9mend Date: Fri, 7 Aug 2026 20:30:06 +0000 Subject: [PATCH 5/6] ci: re-trigger CI (infra flake) Signed-off-by: xq9mend From a6fb0ef1936f04da320b4a3788de652a2eb96214 Mon Sep 17 00:00:00 2001 From: xq9mend Date: Sun, 9 Aug 2026 14:49:49 +0000 Subject: [PATCH 6/6] ci: re-trigger CI (infra flake) Signed-off-by: xq9mend