From 2e4276c4a7869d2b19e46ddf76adde6ee3ac34a7 Mon Sep 17 00:00:00 2001 From: Tapan Karwa Date: Mon, 11 May 2015 10:04:30 -0700 Subject: [PATCH] Scaling changes for introspect. 1. Currently, the IFMapTableShowReq and IFMapTableShowReq introspects buffer the entire table and then send in batches. This will not work in scaling scenarios where the number of nodes/links is huge. Change both the introspects to buffer/send 50 entries at a time and add code to iterate by providing a next_batch link on the client page. 2. Add the table size for the link-table as part of the introspect output. The link-table size output cannot be put in IFMapNodeTableListShowReq since the table names in that show are clickable. 3. Add new APIs in the db-partition code needed by the above changes. Closes-Bug: 1457216 Change-Id: Ia5694269917913da36c2efc341293ba4f6f9350d --- src/db/db.cc | 4 + src/db/db.h | 1 + src/db/db_table_partition.cc | 12 + src/db/db_table_partition.h | 4 + src/ifmap/SConscript | 1 + src/ifmap/ifmap_link_table.cc | 9 + src/ifmap/ifmap_link_table.h | 1 + src/ifmap/ifmap_server_show.cc | 585 ++++++++++++++----- src/ifmap/ifmap_server_show.sandesh | 7 +- src/ifmap/ifmap_server_show_internal.sandesh | 12 + src/ifmap/ifmap_table.cc | 14 +- src/ifmap/ifmap_table.h | 1 + 12 files changed, 490 insertions(+), 161 deletions(-) create mode 100644 src/ifmap/ifmap_server_show_internal.sandesh diff --git a/src/db/db.cc b/src/db/db.cc index 94be50ea7ec..b55bc34405a 100644 --- a/src/db/db.cc +++ b/src/db/db.cc @@ -64,6 +64,10 @@ DBTableBase *DB::FindTable(const string &name) { return NULL; } +DB::iterator DB::FindTableIter(const string &name) { + return tables_.find(name); +} + void DB::AddTable(DBTableBase *tbl_base) { pair result = tables_.insert(make_pair(tbl_base->name(), tbl_base)); diff --git a/src/db/db.h b/src/db/db.h index 74005281809..52c2addd45f 100644 --- a/src/db/db.h +++ b/src/db/db.h @@ -38,6 +38,7 @@ class DB { // Table creation. DBTableBase *CreateTable(const std::string &name); DBTableBase *FindTable(const std::string &name); + iterator FindTableIter(const std::string &name); void RemoveTable(DBTableBase *tbl_base); // Table walker diff --git a/src/db/db_table_partition.cc b/src/db/db_table_partition.cc index 4144aa7d61a..8530ad485ea 100644 --- a/src/db/db_table_partition.cc +++ b/src/db/db_table_partition.cc @@ -144,6 +144,18 @@ DBEntry *DBTablePartition::Find(const DBRequestKey *key) { return NULL; } +DBEntry *DBTablePartition::FindNext(const DBRequestKey *key) { + tbb::mutex::scoped_lock lock(mutex_); + DBTable *table = static_cast(parent()); + std::auto_ptr entry_ptr = table->AllocEntry(key); + + Tree::iterator loc = tree_.upper_bound(*(entry_ptr.get())); + if (loc != tree_.end()) { + return loc.operator->(); + } + return NULL; +} + // Returns the matching entry or next in lex order DBEntry *DBTablePartition::lower_bound(const DBEntryBase *key) { const DBEntry *entry = static_cast(key); diff --git a/src/db/db_table_partition.h b/src/db/db_table_partition.h index 77c8612baf9..dcc3b49e3cb 100644 --- a/src/db/db_table_partition.h +++ b/src/db/db_table_partition.h @@ -77,6 +77,7 @@ class DBTablePartition : public DBTablePartBase { void Process(DBClient *client, DBRequest *req); // Returns the matching route or next in lex order virtual DBEntry *lower_bound(const DBEntryBase *entry); + // Returns the next route (Doesn't search). Threaded walk virtual DBEntry *GetNext(const DBEntryBase *entry); @@ -102,6 +103,9 @@ class DBTablePartition : public DBTablePartBase { // Find DB Entry. Get key from from argument DBEntry *Find(const DBRequestKey *key); + // Find the next in lex order + DBEntry *FindNext(const DBRequestKey *key); + DBTable *table(); size_t size() const { return tree_.size(); } diff --git a/src/ifmap/SConscript b/src/ifmap/SConscript index a69c2faf1dd..e2c3777362b 100644 --- a/src/ifmap/SConscript +++ b/src/ifmap/SConscript @@ -17,6 +17,7 @@ except_env.CppEnableExceptions() except_env.Append(CPPPATH = env['TOP']) SandeshGenFiles = env.SandeshGenCpp('ifmap_server_show.sandesh') +SandeshGenFiles += env.SandeshGenOnlyCpp('ifmap_server_show_internal.sandesh') SandeshGenFiles += env.SandeshGenCpp('ifmap_log.sandesh') SandeshGenSrcs = env.ExtractCpp(SandeshGenFiles) diff --git a/src/ifmap/ifmap_link_table.cc b/src/ifmap/ifmap_link_table.cc index 57da2d29d09..0d63516dbf2 100644 --- a/src/ifmap/ifmap_link_table.cc +++ b/src/ifmap/ifmap_link_table.cc @@ -75,6 +75,15 @@ IFMapLink *IFMapLinkTable::FindLink(const string &name) { return static_cast(partition->Find(&key)); } +IFMapLink *IFMapLinkTable::FindNextLink(const string &name) { + + DBTablePartition *partition = + static_cast(GetTablePartition(0)); + RequestKey key; + key.name = name; + return static_cast(partition->FindNext(&key)); +} + void IFMapLinkTable::DeleteLink(DBGraphEdge *edge) { IFMapLink *link = static_cast(edge); link->set_last_change_at_to_now(); diff --git a/src/ifmap/ifmap_link_table.h b/src/ifmap/ifmap_link_table.h index a4c44e3eb64..efd4f875e29 100644 --- a/src/ifmap/ifmap_link_table.h +++ b/src/ifmap/ifmap_link_table.h @@ -50,6 +50,7 @@ class IFMapLinkTable : public DBTable { static DBTable *CreateTable(DB *db, const std::string &name, DBGraph *graph); IFMapLink *FindLink(const std::string &name); + IFMapLink *FindNextLink(const std::string &name); protected: void DeleteLink(DBGraphEdge *edge); diff --git a/src/ifmap/ifmap_server_show.cc b/src/ifmap/ifmap_server_show.cc index 7c560b62997..cd95cbfa139 100644 --- a/src/ifmap/ifmap_server_show.cc +++ b/src/ifmap/ifmap_server_show.cc @@ -25,6 +25,7 @@ #include "ifmap/ifmap_sandesh_context.h" #include "ifmap/ifmap_server.h" #include "ifmap/ifmap_server_show_types.h" +#include "ifmap/ifmap_server_show_internal_types.h" #include "ifmap/ifmap_log_types.h" #include "ifmap/ifmap_table.h" #include "ifmap/ifmap_update.h" @@ -105,71 +106,111 @@ class IFMapNodeCopier { } }; +const string kShowIterSeparator = "||"; + // almost everything in this class is static since we dont really want to // intantiate this class class ShowIFMapTable { public: - static const int kMaxElementsPerRound = 50; + static const uint32_t kMaxElementsPerRound = 50; struct ShowData : public RequestPipeline::InstData { vector send_buffer; + string next_table_name; + string last_node_name; }; static RequestPipeline::InstData *AllocBuffer(int stage) { return static_cast(new ShowData); } - struct TrackerData : public RequestPipeline::InstData { - // init as 1 indicates we need to init 'first' to begin() since there is - // no way to initialize an iterator here. - TrackerData() : init(1) { } - int init; - vector::const_iterator first; - }; - - static RequestPipeline::InstData *AllocTracker(int stage) { - return static_cast(new TrackerData); - } - + static bool BufferStageCommon(const IFMapTableShowReq *request, + RequestPipeline::InstData *data, + const string &next_table_name, + const string &last_node_name); static bool BufferStage(const Sandesh *sr, const RequestPipeline::PipeSpec ps, int stage, int instNum, RequestPipeline::InstData *data); + static bool BufferStageIterate(const Sandesh *sr, + const RequestPipeline::PipeSpec ps, int stage, + int instNum, RequestPipeline::InstData *data); + static void SendStageCommon(const IFMapTableShowReq *request, + const RequestPipeline::PipeSpec ps, + IFMapTableShowResp *response); static bool SendStage(const Sandesh *sr, const RequestPipeline::PipeSpec ps, int stage, int instNum, RequestPipeline::InstData *data); - static void TableToBuffer(const IFMapTableShowReq *request, IFMapTable *table, - ShowData *show_data, IFMapServer *server); - static bool BufferAllTables(const RequestPipeline::PipeSpec ps, - RequestPipeline::InstData *data); - static bool BufferSomeTables(const RequestPipeline::PipeSpec ps, - RequestPipeline::InstData *data); + static bool SendStageIterate(const Sandesh *sr, + const RequestPipeline::PipeSpec ps, int stage, + int instNum, RequestPipeline::InstData *data); + static bool TableToBuffer(const IFMapTableShowReq *request, + IFMapTable *table, IFMapServer *server, + const string &last_node_name, + ShowData *show_data); + static bool BufferAllTables(const IFMapTableShowReq *req, + RequestPipeline::InstData *data, + const string &next_table_name, + const string &last_node_name); + static bool BufferOneTable(const IFMapTableShowReq *request, + RequestPipeline::InstData *data, + const string &last_node_name); + static bool ConvertReqIterateToReq( + const IFMapTableShowReqIterate *req_iterate, + IFMapTableShowReq *req, string *next_table_name, + string *last_node_name); }; -void ShowIFMapTable::TableToBuffer(const IFMapTableShowReq *request, - IFMapTable *table, ShowData *show_data, IFMapServer *server) { +// Return true if the buffer is full. +bool ShowIFMapTable::TableToBuffer(const IFMapTableShowReq *request, + IFMapTable *table, IFMapServer *server, const string &last_node_name, + ShowData *show_data) { + + DBEntryBase *src = NULL; + if (last_node_name.length()) { + // If the last_node_name is set, it was the last node printed in the + // previous round. Search for the node 'after' last_node_name and start + // this round with it. If there is no next node, we are done with this + // table. + IFMapNode *last_node = table->FindNextNode(last_node_name); + if (last_node) { + src = last_node; + } else { + return false; + } + } + + bool buffer_full = false; string search_string = request->get_search_string(); - for (int i = 0; i < IFMapTable::kPartitionCount; i++) { - DBTablePartBase *partition = table->GetTablePartition(i); - DBEntryBase *src = partition->GetFirst(); - while (src) { - IFMapNode *src_node = static_cast(src); - if (!search_string.empty() && - (src_node->ToString().find(search_string) == string::npos)) { - src = partition->GetNext(src); - continue; - } - IFMapNodeShowInfo dest; - IFMapNodeCopier copyNode(&dest, src, server); - show_data->send_buffer.push_back(dest); - src = partition->GetNext(src); + DBTablePartBase *partition = table->GetTablePartition(0); + if (!src) { + src = partition->GetFirst(); + } + for (; src != NULL; src = partition->GetNext(src)) { + IFMapNode *src_node = static_cast(src); + if (!search_string.empty() && + (src_node->ToString().find(search_string) == string::npos)) { + continue; + } + IFMapNodeShowInfo dest; + IFMapNodeCopier copyNode(&dest, src, server); + show_data->send_buffer.push_back(dest); + // If we have picked up enough nodes for this round... + if (show_data->send_buffer.size() == kMaxElementsPerRound) { + // Save the values needed for the next round. When we come + // back, we will use the 'names' to lookup the elements since + // the 'names' are the keys in the respective tables. + show_data->next_table_name = table->name(); + show_data->last_node_name = src_node->name(); + buffer_full = true; + break; } } + + return buffer_full; } -bool ShowIFMapTable::BufferSomeTables(const RequestPipeline::PipeSpec ps, - RequestPipeline::InstData *data) { - const IFMapTableShowReq *request = - static_cast(ps.snhRequest_.get()); +bool ShowIFMapTable::BufferOneTable(const IFMapTableShowReq *request, + RequestPipeline::InstData *data, const string &last_node_name) { IFMapSandeshContext *sctx = static_cast(request->module_context("IFMap")); @@ -177,8 +218,9 @@ bool ShowIFMapTable::BufferSomeTables(const RequestPipeline::PipeSpec ps, request->get_table_name()); if (table) { ShowData *show_data = static_cast(data); - show_data->send_buffer.reserve(table->Size()); - TableToBuffer(request, table, show_data, sctx->ifmap_server()); + show_data->send_buffer.reserve(kMaxElementsPerRound); + TableToBuffer(request, table, sctx->ifmap_server(), last_node_name, + show_data); } else { IFMAP_WARN(IFMapTblNotFound, "Cant show/find table ", request->get_table_name()); @@ -187,97 +229,192 @@ bool ShowIFMapTable::BufferSomeTables(const RequestPipeline::PipeSpec ps, return true; } -bool ShowIFMapTable::BufferAllTables(const RequestPipeline::PipeSpec ps, - RequestPipeline::InstData *data) { - const IFMapTableShowReq *request = - static_cast(ps.snhRequest_.get()); +bool ShowIFMapTable::BufferAllTables(const IFMapTableShowReq *request, + RequestPipeline::InstData *data, const string &next_table_name, + const string &last_node_name) { IFMapSandeshContext *sctx = static_cast(request->module_context("IFMap")); + string last_name = last_node_name; DB *db = sctx->ifmap_server()->database(); + DB::iterator iter; + if (next_table_name.empty()) { + iter = db->lower_bound("__ifmap__."); + } else { + iter = db->FindTableIter(next_table_name); + } - // Get the sum total of entries in all the tables - int num_entries = 0; - for (DB::iterator iter = db->lower_bound("__ifmap__."); - iter != db->end(); ++iter) { + ShowData *show_data = static_cast(data); + show_data->send_buffer.reserve(kMaxElementsPerRound); + for (; iter != db->end(); ++iter) { if (iter->first.find("__ifmap__.") != 0) { break; } IFMapTable *table = static_cast(iter->second); - num_entries += table->Size(); - } - ShowData *show_data = static_cast(data); - show_data->send_buffer.reserve(num_entries); - for (DB::iterator iter = db->lower_bound("__ifmap__."); - iter != db->end(); ++iter) { - if (iter->first.find("__ifmap__.") != 0) { + bool buffer_full = TableToBuffer(request, table, sctx->ifmap_server(), + last_name, show_data); + if (buffer_full) { break; } - IFMapTable *table = static_cast(iter->second); - TableToBuffer(request, table, show_data, sctx->ifmap_server()); + // last_node_name is only relevant for the first iteration. + last_name.clear(); + } + + return true; +} + +// Format of node_info string: +// table_name||search_string||next_table_name||last_node_name +// table_name/search_string: original input. Could be empty. +// next_table_name: next table to lookup in +// last_node_name: name of last node that was printed in the previous round +bool ShowIFMapTable::ConvertReqIterateToReq( + const IFMapTableShowReqIterate *req_iterate, + IFMapTableShowReq *req, string *next_table_name, + string *last_node_name) { + // First, set the context from the original request since we might return + // due to parsing errors. + req->set_context(req_iterate->context()); + + string node_info = req_iterate->get_node_info(); + size_t sep_size = kShowIterSeparator.size(); + + // table_name + size_t pos1 = node_info.find(kShowIterSeparator); + if (pos1 == string::npos) { + return false; } + string table_name = node_info.substr(0, pos1); + // search_string + size_t pos2 = node_info.find(kShowIterSeparator, (pos1 + sep_size)); + if (pos2 == string::npos) { + return false; + } + string search_string = node_info.substr((pos1 + sep_size), + pos2 - (pos1 + sep_size)); + + // next_table_name + size_t pos3 = node_info.find(kShowIterSeparator, (pos2 + sep_size)); + if (pos3 == string::npos) { + return false; + } + *next_table_name = node_info.substr((pos2 + sep_size), + pos3 - (pos2 + sep_size)); + + // last_node_name + *last_node_name = node_info.substr(pos3 + sep_size); + + // Fill up the fields of IFMapTableShowReq appropriately. + req->set_table_name(table_name); + req->set_search_string(search_string); return true; } +bool ShowIFMapTable::BufferStageCommon(const IFMapTableShowReq *request, + RequestPipeline::InstData *data, const string &next_table_name, + const string &last_node_name) { + // If table name has not been passed, print all tables + if (request->get_table_name().length()) { + return BufferOneTable(request, data, last_node_name); + } else { + return BufferAllTables(request, data, next_table_name, last_node_name); + } +} + bool ShowIFMapTable::BufferStage(const Sandesh *sr, const RequestPipeline::PipeSpec ps, int stage, int instNum, RequestPipeline::InstData *data) { - const IFMapTableShowReq *request = static_cast(ps.snhRequest_.get()); - // If table name has not been passed, print all tables - if (request->get_table_name().length()) { - return BufferSomeTables(ps, data); - } else { - return BufferAllTables(ps, data); + string next_table_name; + string last_node_name; + return BufferStageCommon(request, data, next_table_name, last_node_name); +} + +bool ShowIFMapTable::BufferStageIterate(const Sandesh *sr, + const RequestPipeline::PipeSpec ps, + int stage, int instNum, + RequestPipeline::InstData *data) { + const IFMapTableShowReqIterate *request_iterate = + static_cast(ps.snhRequest_.get()); + + string next_table_name; + string last_node_name; + IFMapTableShowReq *request = new IFMapTableShowReq; + bool success = ConvertReqIterateToReq(request_iterate, request, + &next_table_name, &last_node_name); + if (success) { + BufferStageCommon(request, data, next_table_name, last_node_name); } + request->Release(); + return true; +} + +void ShowIFMapTable::SendStageCommon(const IFMapTableShowReq *request, + const RequestPipeline::PipeSpec ps, + IFMapTableShowResp *response) { + const RequestPipeline::StageData *prev_stage_data = ps.GetStageData(0); + const ShowIFMapTable::ShowData &show_data = + static_cast (prev_stage_data->at(0)); + + vector dest_buffer; + dest_buffer = show_data.send_buffer; + + // If we have filled the buffer, set next_batch with all the values we will + // need in the next round. + string next_batch; + if (dest_buffer.size() == kMaxElementsPerRound) { + next_batch = request->get_table_name() + kShowIterSeparator + + request->get_search_string() + kShowIterSeparator + + show_data.next_table_name + kShowIterSeparator + + show_data.last_node_name; + } + + response->set_ifmap_db(dest_buffer); + response->set_next_batch(next_batch); } -// Can be called multiple times i.e. approx total/kMaxElementsPerRound bool ShowIFMapTable::SendStage(const Sandesh *sr, const RequestPipeline::PipeSpec ps, int stage, int instNum, RequestPipeline::InstData *data) { - const RequestPipeline::StageData *prev_stage_data = ps.GetStageData(0); - const ShowIFMapTable::ShowData &show_data = - static_cast (prev_stage_data->at(0)); - // Data for this stage - TrackerData *tracker_data = static_cast(data); + const IFMapTableShowReq *request = + static_cast(ps.snhRequest_.get()); + IFMapTableShowResp *response = new IFMapTableShowResp; + SendStageCommon(request, ps, response); - vector dest_buffer; - vector::const_iterator first, last; - bool more = false; + response->set_context(request->context()); + response->set_more(false); + response->Response(); + return true; +} - if (tracker_data->init) { - first = show_data.send_buffer.begin(); - tracker_data->init = 0; - } else { - first = tracker_data->first; - } - int rem_num = show_data.send_buffer.end() - first; - int send_num = (rem_num < kMaxElementsPerRound) ? rem_num : - kMaxElementsPerRound; - last = first + send_num; - copy(first, last, back_inserter(dest_buffer)); - // Decide if we want to be called again. - if ((rem_num - send_num) > 0) { - more = true; - } else { - more = false; +bool ShowIFMapTable::SendStageIterate(const Sandesh *sr, + const RequestPipeline::PipeSpec ps, + int stage, int instNum, + RequestPipeline::InstData *data) { + const IFMapTableShowReqIterate *request_iterate = + static_cast(ps.snhRequest_.get()); + + string next_table_name; + string last_node_name; + + IFMapTableShowResp *response = new IFMapTableShowResp; + IFMapTableShowReq *request = new IFMapTableShowReq; + bool success = ConvertReqIterateToReq(request_iterate, request, + &next_table_name, &last_node_name); + if (success) { + SendStageCommon(request, ps, response); } - const IFMapTableShowReq *request = - static_cast(ps.snhRequest_.get()); - IFMapTableShowResp *response = new IFMapTableShowResp(); - response->set_ifmap_db(dest_buffer); + response->set_context(request->context()); - response->set_more(more); + response->set_more(false); response->Response(); - tracker_data->first = first + send_num; - // Return 'false' to be called again - return (!more); + request->Release(); + return true; } void IFMapTableShowReq::HandleRequest() const { @@ -294,7 +431,6 @@ void IFMapTableShowReq::HandleRequest() const { // control-node ifmap show command task s1.taskId_ = scheduler->GetTaskId("cn_ifmap::ShowCommand"); - s1.allocFn_ = ShowIFMapTable::AllocTracker; s1.cbFn_ = ShowIFMapTable::SendStage; s1.instances_.push_back(0); @@ -303,44 +439,73 @@ void IFMapTableShowReq::HandleRequest() const { RequestPipeline rp(ps); } +void IFMapTableShowReqIterate::HandleRequest() const { + + RequestPipeline::StageSpec s0, s1; + TaskScheduler *scheduler = TaskScheduler::GetInstance(); + + // 2 stages - first: gather/read, second: send + + s0.taskId_ = scheduler->GetTaskId("db::DBTable"); + s0.allocFn_ = ShowIFMapTable::AllocBuffer; + s0.cbFn_ = ShowIFMapTable::BufferStageIterate; + s0.instances_.push_back(0); + + // control-node ifmap show command task + s1.taskId_ = scheduler->GetTaskId("cn_ifmap::ShowCommand"); + s1.cbFn_ = ShowIFMapTable::SendStageIterate; + s1.instances_.push_back(0); + + RequestPipeline::PipeSpec ps(this); + ps.stages_= list_of(s0)(s1); + RequestPipeline rp(ps); +} + // Code to display link-table entries class ShowIFMapLinkTable { public: - static const int kMaxElementsPerRound = 50; + static const uint32_t kMaxElementsPerRound = 50; struct ShowData : public RequestPipeline::InstData { vector send_buffer; + uint32_t table_size; + string last_link_name; }; static RequestPipeline::InstData *AllocBuffer(int stage) { return static_cast(new ShowData); } - struct TrackerData : public RequestPipeline::InstData { - // init as 1 indicates we need to init 'first' to begin() since there is - // no way to initialize an iterator here. - TrackerData() : init(1) { } - int init; - vector::const_iterator first; - }; - - static RequestPipeline::InstData *AllocTracker(int stage) { - return static_cast(new TrackerData); - } - static bool SkipLink(DBEntryBase *src, const string &search_string); static void CopyNode(IFMapLinkShowInfo *dest, DBEntryBase *src, IFMapServer *server); + static bool BufferStageCommon(const IFMapLinkTableShowReq *request, + RequestPipeline::InstData *data, + const string &last_link_name); static bool BufferStage(const Sandesh *sr, const RequestPipeline::PipeSpec ps, int stage, int instNum, RequestPipeline::InstData *data); + static bool BufferStageIterate(const Sandesh *sr, + const RequestPipeline::PipeSpec ps, + int stage, int instNum, + RequestPipeline::InstData *data); + static void SendStageCommon(const IFMapLinkTableShowReq *request, + const RequestPipeline::PipeSpec ps, + IFMapLinkTableShowResp *response); static bool SendStage(const Sandesh *sr, const RequestPipeline::PipeSpec ps, int stage, int instNum, RequestPipeline::InstData *data); + static bool SendStageIterate(const Sandesh *sr, + const RequestPipeline::PipeSpec ps, int stage, + int instNum, RequestPipeline::InstData *data); + static bool ConvertReqIterateToReq( + const IFMapLinkTableShowReqIterate *req_iterate, + IFMapLinkTableShowReq *req, string *last_link_name); }; -bool ShowIFMapLinkTable::SkipLink(DBEntryBase *src, const string &search_string) { +bool ShowIFMapLinkTable::SkipLink(DBEntryBase *src, + const string &search_string) { IFMapLink *link = static_cast(src); IFMapNode *left = link->left(); IFMapNode *right = link->right(); @@ -399,85 +564,168 @@ void ShowIFMapLinkTable::CopyNode(IFMapLinkShowInfo *dest, DBEntryBase *src, } } -bool ShowIFMapLinkTable::BufferStage(const Sandesh *sr, - const RequestPipeline::PipeSpec ps, - int stage, int instNum, - RequestPipeline::InstData *data) { - const IFMapLinkTableShowReq *request = - static_cast(ps.snhRequest_.get()); - IFMapSandeshContext *sctx = +// Format of link_info string: +// search_string||last_link_name +// search_string: original input. Could be empty. +// last_link_name: name of last link that was printed in the previous round +bool ShowIFMapLinkTable::ConvertReqIterateToReq( + const IFMapLinkTableShowReqIterate *req_iterate, + IFMapLinkTableShowReq *req, string *last_link_name) { + // First, set the context from the original request since we might return + // due to parsing errors. + req->set_context(req_iterate->context()); + + string link_info = req_iterate->get_link_info(); + size_t sep_size = kShowIterSeparator.size(); + + // search_string + size_t pos1 = link_info.find(kShowIterSeparator); + if (pos1 == string::npos) { + return false; + } + string search_string = link_info.substr(0, pos1); + + // last_link_name + *last_link_name = link_info.substr(pos1 + sep_size); + + // Fill up the fields of IFMapLinkTableShowReq appropriately. + req->set_search_string(search_string); + return true; +} + +bool ShowIFMapLinkTable::BufferStageCommon(const IFMapLinkTableShowReq *request, + RequestPipeline::InstData *data, + const string &last_link_name) { + bool buffer_full = false; + IFMapSandeshContext *sctx = static_cast(request->module_context("IFMap")); IFMapLinkTable *table = static_cast( sctx->ifmap_server()->database()->FindTable("__ifmap_metadata__.0")); if (table) { ShowData *show_data = static_cast(data); - show_data->send_buffer.reserve(table->Size()); + show_data->send_buffer.reserve(kMaxElementsPerRound); + show_data->table_size = table->Size(); + DBEntryBase *src = NULL; DBTablePartBase *partition = table->GetTablePartition(0); - DBEntryBase *src = partition->GetFirst(); - while (src) { + if (last_link_name.length()) { + src = table->FindNextLink(last_link_name); + } else { + src = partition->GetFirst(); + } + for (; src != NULL; src = partition->GetNext(src)) { + IFMapLink *src_link = static_cast(src); if (SkipLink(src, request->get_search_string())) { - src = partition->GetNext(src); continue; } IFMapLinkShowInfo dest; CopyNode(&dest, src, sctx->ifmap_server()); show_data->send_buffer.push_back(dest); - src = partition->GetNext(src); + // If we have picked up enough links for this round... + if (show_data->send_buffer.size() == kMaxElementsPerRound) { + show_data->last_link_name = src_link->link_name(); + buffer_full = true; + break; + } } } else { IFMAP_WARN(IFMapTblNotFound, "Cant show/find ", "link table"); } + return buffer_full; +} + +bool ShowIFMapLinkTable::BufferStage(const Sandesh *sr, + const RequestPipeline::PipeSpec ps, + int stage, int instNum, + RequestPipeline::InstData *data) { + const IFMapLinkTableShowReq *request = + static_cast(ps.snhRequest_.get()); + string last_link_name; + BufferStageCommon(request, data, last_link_name); return true; } -// Can be called multiple times i.e. approx total/kMaxElementsPerRound -bool ShowIFMapLinkTable::SendStage(const Sandesh *sr, - const RequestPipeline::PipeSpec ps, - int stage, int instNum, - RequestPipeline::InstData *data) { +bool ShowIFMapLinkTable::BufferStageIterate(const Sandesh *sr, + const RequestPipeline::PipeSpec ps, + int stage, int instNum, + RequestPipeline::InstData *data) { + const IFMapLinkTableShowReqIterate *request_iterate = + static_cast(ps.snhRequest_.get()); + + IFMapLinkTableShowReq *request = new IFMapLinkTableShowReq; + string last_link_name; + bool success = ConvertReqIterateToReq(request_iterate, request, + &last_link_name); + if (success) { + BufferStageCommon(request, data, last_link_name); + } + request->Release(); + return true; +} + +void ShowIFMapLinkTable::SendStageCommon(const IFMapLinkTableShowReq *request, + const RequestPipeline::PipeSpec ps, + IFMapLinkTableShowResp *response) { const RequestPipeline::StageData *prev_stage_data = ps.GetStageData(0); const ShowIFMapLinkTable::ShowData &show_data = static_cast (prev_stage_data->at(0)); - // Data for this stage - TrackerData *tracker_data = static_cast(data); - vector dest_buffer; - vector::const_iterator first, last; - bool more = false; - - if (tracker_data->init) { - first = show_data.send_buffer.begin(); - tracker_data->init = 0; - } else { - first = tracker_data->first; - } - int rem_num = show_data.send_buffer.end() - first; - int send_num = (rem_num < kMaxElementsPerRound) ? rem_num : - kMaxElementsPerRound; - last = first + send_num; - copy(first, last, back_inserter(dest_buffer)); - // Decide if we want to be called again. - if ((rem_num - send_num) > 0) { - more = true; - } else { - more = false; + dest_buffer = show_data.send_buffer; + + // If we have filled the buffer, set next_batch with all the values we will + // need in the next round. + string next_batch; + if (dest_buffer.size() == kMaxElementsPerRound) { + next_batch = request->get_search_string() + kShowIterSeparator + + show_data.last_link_name; } + + response->set_table_size(show_data.table_size); + response->set_ifmap_db(dest_buffer); + response->set_next_batch(next_batch); +} + +bool ShowIFMapLinkTable::SendStage(const Sandesh *sr, + const RequestPipeline::PipeSpec ps, + int stage, int instNum, + RequestPipeline::InstData *data) { const IFMapLinkTableShowReq *request = static_cast(ps.snhRequest_.get()); - IFMapLinkTableShowResp *response = new IFMapLinkTableShowResp(); - response->set_ifmap_db(dest_buffer); + IFMapLinkTableShowResp *response = new IFMapLinkTableShowResp; + SendStageCommon(request, ps, response); + response->set_context(request->context()); - response->set_more(more); + response->set_more(false); response->Response(); - tracker_data->first = first + send_num; + return true; +} - // Return 'false' to be called again - return (!more); +bool ShowIFMapLinkTable::SendStageIterate(const Sandesh *sr, + const RequestPipeline::PipeSpec ps, + int stage, int instNum, + RequestPipeline::InstData *data) { + const IFMapLinkTableShowReqIterate *request_iterate = + static_cast(ps.snhRequest_.get()); + + IFMapLinkTableShowResp *response = new IFMapLinkTableShowResp; + IFMapLinkTableShowReq *request = new IFMapLinkTableShowReq; + string last_link_name; + bool success = ConvertReqIterateToReq(request_iterate, request, + &last_link_name); + if (success) { + SendStageCommon(request, ps, response); + } + + response->set_context(request->context()); + response->set_more(false); + response->Response(); + + request->Release(); + return true; } void IFMapLinkTableShowReq::HandleRequest() const { @@ -494,7 +742,6 @@ void IFMapLinkTableShowReq::HandleRequest() const { // control-node ifmap show command task s1.taskId_ = scheduler->GetTaskId("cn_ifmap::ShowCommand"); - s1.allocFn_ = ShowIFMapLinkTable::AllocTracker; s1.cbFn_ = ShowIFMapLinkTable::SendStage; s1.instances_.push_back(0); @@ -503,6 +750,28 @@ void IFMapLinkTableShowReq::HandleRequest() const { RequestPipeline rp(ps); } +void IFMapLinkTableShowReqIterate::HandleRequest() const { + + RequestPipeline::StageSpec s0, s1; + TaskScheduler *scheduler = TaskScheduler::GetInstance(); + + // 2 stages - first: gather/read, second: send + + s0.taskId_ = scheduler->GetTaskId("db::DBTable"); + s0.allocFn_ = ShowIFMapLinkTable::AllocBuffer; + s0.cbFn_ = ShowIFMapLinkTable::BufferStageIterate; + s0.instances_.push_back(0); + + // control-node ifmap show command task + s1.taskId_ = scheduler->GetTaskId("cn_ifmap::ShowCommand"); + s1.cbFn_ = ShowIFMapLinkTable::SendStageIterate; + s1.instances_.push_back(0); + + RequestPipeline::PipeSpec ps(this); + ps.stages_= list_of(s0)(s1); + RequestPipeline rp(ps); +} + static bool IFMapNodeShowReqHandleRequest(const Sandesh *sr, const RequestPipeline::PipeSpec ps, int stage, int instNum, diff --git a/src/ifmap/ifmap_server_show.sandesh b/src/ifmap/ifmap_server_show.sandesh index 6a2ef10f197..f54720c7ac6 100644 --- a/src/ifmap/ifmap_server_show.sandesh +++ b/src/ifmap/ifmap_server_show.sandesh @@ -28,6 +28,8 @@ request sandesh IFMapTableShowReq { response sandesh IFMapTableShowResp { 1: list ifmap_db; + 2: optional string next_batch (link="IFMapTableShowReqIterate", + link_title="next_batch"); } /** Definitions for showing link table - IFMapLink **/ @@ -54,7 +56,10 @@ request sandesh IFMapLinkTableShowReq { } response sandesh IFMapLinkTableShowResp { - 1: list ifmap_db; + 1: u32 table_size; + 2: list ifmap_db; + 3: optional string next_batch (link="IFMapLinkTableShowReqIterate", + link_title="next_batch"); } /** Definitions for showing the internal Update Queue **/ diff --git a/src/ifmap/ifmap_server_show_internal.sandesh b/src/ifmap/ifmap_server_show_internal.sandesh new file mode 100644 index 00000000000..93e521817dc --- /dev/null +++ b/src/ifmap/ifmap_server_show_internal.sandesh @@ -0,0 +1,12 @@ +/* + * Copyright (c) 2015 Juniper Networks, Inc. All rights reserved. + */ + +request sandesh IFMapTableShowReqIterate { + 1: string node_info; +} + +request sandesh IFMapLinkTableShowReqIterate { + 1: string link_info; +} + diff --git a/src/ifmap/ifmap_table.cc b/src/ifmap/ifmap_table.cc index c0fc2b059fa..7150cb4b525 100644 --- a/src/ifmap/ifmap_table.cc +++ b/src/ifmap/ifmap_table.cc @@ -11,6 +11,7 @@ #include #include "db/db.h" #include "db/db_table.h" +#include "db/db_table_partition.h" #include "ifmap/autogen.h" #include "ifmap/ifmap_node.h" #include "ifmap/ifmap_server_show_types.h" @@ -21,10 +22,19 @@ IFMapTable::IFMapTable(DB *db, const std::string &name) : DBTable(db, name) { } IFMapNode *IFMapTable::FindNode(const std::string &name) { + DBTablePartition *partition = + static_cast(GetTablePartition(0)); IFMapTable::RequestKey reqkey; reqkey.id_name = name; - auto_ptr key(AllocEntry(&reqkey)); - return static_cast(Find(key.get())); + return static_cast(partition->Find(&reqkey)); +} + +IFMapNode *IFMapTable::FindNextNode(const std::string &name) { + DBTablePartition *partition = + static_cast(GetTablePartition(0)); + IFMapTable::RequestKey reqkey; + reqkey.id_name = name; + return static_cast(partition->FindNext(&reqkey)); } IFMapTable *IFMapTable::FindTable(DB *db, const std::string &element_type) { diff --git a/src/ifmap/ifmap_table.h b/src/ifmap/ifmap_table.h index f2cc36a933b..3968465ff8f 100644 --- a/src/ifmap/ifmap_table.h +++ b/src/ifmap/ifmap_table.h @@ -32,6 +32,7 @@ class IFMapTable : public DBTable { virtual IFMapObject *AllocObject() = 0; IFMapNode *FindNode(const std::string &name); + IFMapNode *FindNextNode(const std::string &name); virtual void Clear() = 0;