// balm_publicationscheduler.h -*-C++-*- // ---------------------------------------------------------------------------- // NOTICE // // This component is not up to date with current BDE coding standards, and // should not be used as an example for new development. // ---------------------------------------------------------------------------- #ifndef INCLUDED_BALM_PUBLICATIONSCHEDULER #define INCLUDED_BALM_PUBLICATIONSCHEDULER #include <bsls_ident.h> BSLS_IDENT("$Id: $") //@PURPOSE: Provide a scheduler for publishing metrics. // //@CLASSES: // balm::PublicationScheduler: a scheduler for publishing metrics // //@SEE_ALSO: balm_metricsmanager // //@DESCRIPTION: This component defines a class, 'balm::PublicationScheduler', // that provides a scheduling mechanism for the publication of metrics. At // construction, a 'balm::PublicationScheduler' is provided the addresses of a // 'balm::MetricsManager' and a 'bdlmt::TimerEventScheduler'. The publication // scheduler provides a 'scheduleCategory' method that schedules an individual // metric category to be published repeatedly at a given interval, and a // 'setDefaultSchedule' method that schedules the publication of any category // not given an individual schedule. The 'balm::PublicationScheduler' creates // timer events using the 'bdlmt::TimerEventScheduler'. At the end of a // scheduled time interval, the publication scheduler invokes the metrics // manager's 'publish' operation with the set of categories to publish. Note // that the publication scheduler will combine categories that occur at the // same frequency into a single invocation of the metrics manager's 'publish' // operation. The publication scheduler also provides a method to cancel the // publication of a particular category, or of all categories. // ///Alternative Systems for Telemetry ///--------------------------------- // Bloomberg software may alternatively use the GUTS telemetry API, which is // integrated into Bloomberg infrastructure. // ///Thread Safety ///------------- // 'balm::PublicationScheduler' is fully *thread-safe*, meaning that all // non-creator operations on a given instance can be safely invoked // simultaneously from multiple threads. // ///Usage ///----- // The following example demonstrates how to use 'balm::PublicationScheduler'. // Before instantiating the publication scheduler, we create a // 'bdlmt::TimerEventScheduler' as well as a 'balm::MetricsManager'. We obtain // collectors for three different metric categories, "A", "B", and "C", that // we will use to generate metric values for publication. //.. // bslma::Allocator *allocator = bslma::Default::allocator(0); // bdlmt::TimerEventScheduler timer(allocator); // balm::MetricsManager manager(allocator); // // balm::Collector *A = manager.collectorRepository().getDefaultCollector( // "A", "a"); // balm::Collector *B = manager.collectorRepository().getDefaultCollector( // "B", "b"); // balm::Collector *C = manager.collectorRepository().getDefaultCollector( // "C", "c"); //.. // We now create an instance of 'SimpleStreamPublisher', which implements the // 'balm::Publisher' protocol. Note that 'SimpleStreamPublisher' is an // example implementation of the 'balm::Publisher' protocol defined in the // 'balm_publisher' component. In practice, clients typically use a standard // publisher class (e.g., 'balm::StreamPublisher'). //.. // bsl::shared_ptr<balm::Publisher> publisher( // new (*allocator) SimpleStreamPublisher(bsl::cout), // allocator); //.. // We now register the 'publisher' we have created with the metrics 'manager' // to publish our categories. Then, we 'start' the timer-event scheduler we // will supply to the 'balm::PublicationScheduler'. //.. // manager.addGeneralPublisher(publisher); // timer.start(); //.. // Now we construct a 'balm::PublicationScheduler' and pass it the respective // addresses of both the metrics manager and the timer-event scheduler. We // schedule the publication of category "A" and "B" every .05 seconds, then we // set the scheduled default publication to every .10 seconds. Note that those // time intervals were chosen to ensure fast and consistent output for this // example. In normal usage the interval between publications should be large // enough to ensure that metric publication does not negatively affect the // performance of the application (a 30 second interval is typical). //.. // balm::PublicationScheduler scheduler(&manager, &timer, allocator); // scheduler.scheduleCategory("A", bsls::TimeInterval(.05)); // scheduler.scheduleCategory("B", bsls::TimeInterval(.05)); // scheduler.setDefaultSchedule(bsls::TimeInterval(.10)); //.. // We can use the accessor operations to verify the schedule that we have // specified. //.. // bsls::TimeInterval intervalA, intervalB, intervalC, defaultInterval; // assert( scheduler.findCategorySchedule(&intervalA, "A")); // assert( scheduler.findCategorySchedule(&intervalB, "B")); // assert(!scheduler.findCategorySchedule(&intervalC, "C")); // assert( scheduler.getDefaultSchedule(&defaultInterval)); // // assert(bsls::TimeInterval(.05) == intervalA); // assert(bsls::TimeInterval(.05) == intervalB); // assert(bsls::TimeInterval(.10) == defaultInterval); //.. // Finally we add a couple of metrics and wait just over .1 seconds. //.. // A->update(1.0); // B->update(2.0); // C->update(3.0); // bslmt::ThreadUtil::sleep(bsls::TimeInterval(.11)); //.. // The output of the publication should look similar to: //.. // 19NOV2008_18:34:26.766+0000 2 Records 0.0517s Elapsed Time // A.a [count = 1, total = 1, min = 1, max = 1] // B.b [count = 1, total = 2, min = 2, max = 2] // 19NOV2008_18:34:26.816+0000 2 Records 0.050183s Elapsed Time // A.a [count = 0, total = 0, min = inf, max = -inf] // B.b [count = 0, total = 0, min = inf, max = -inf] // 19NOV2008_18:34:26.817+0000 1 Records 0.102473s Elapsed Time // C.c [count = 1, total = 3, min = 3, max = 3] //.. // Note that category 'C' is published as part of the scheduled default // publication. Also note that categories 'A' and 'B' are emitted as a single // publication: the scheduler combines categories published at the same // frequency into a single publication event to minimize the number of times // 'balm::MetricsManager::publish' is invoked. #include <balscm_version.h> #include <balm_metricsmanager.h> #include <bslmt_mutex.h> #include <bdlmt_timereventscheduler.h> #include <bsls_timeinterval.h> #include <bslma_allocator.h> #include <bslmf_nestedtraitdeclaration.h> #include <bsl_iosfwd.h> #include <bsl_map.h> #include <bsl_memory.h> #include <bsl_set.h> #include <bsl_utility.h> #include <bsl_vector.h> #include <bsls_libraryfeatures.h> #ifdef BSLS_LIBRARYFEATURES_HAS_CPP17_PMR #include <memory_resource> // 'std::pmr::polymorphic_allocator' #endif // BSLS_LIBRARYFEATURES_HAS_CPP17_PMR #include <vector> // 'std::vector', 'std::pmr::vector' namespace BloombergLP { namespace balm { class Category; class PublicationScheduler_ClockData; // defined in implementation class PublicationScheduler_Proctor; // defined in implementation // ========================== // class PublicationScheduler // ========================== class PublicationScheduler { // This class defines a mechanism for scheduling the periodic publication // of metrics. Each publication scheduler object is supplied the address // of a 'MetricsManager' and a 'bdlmt::TimerEventScheduler' at // construction. The metrics manager is used to publish metrics, while // the timer-event scheduler provides the underlying scheduling mechanism. // Metrics are scheduled for publication using the 'scheduleCategory' and // 'setDefaultSchedule' methods. The 'scheduleCategory' method schedules // an individual category to be publisher periodically at the provided // interval, whereas 'setDefaultSchedule' schedules the periodic // publication of any category not given an individual schedule. The // publication scheduler will create a recurring timer for each unique // time interval supplied, and will group together categories that share a // common time interval into a single call to 'MetricsManager::publish'. // Note that it is left unspecified whether publication events that occur // on a common multiple of *different* intervals will be grouped into a // single invocation of 'MetricsManager::publish'. // PRIVATE TYPES typedef PublicationScheduler_ClockData ClockData; // A private implementation type holding the data for a scheduled // publication frequency (e.g., the set of categories published at that // frequency). Each "clock" created in the underlying // 'bdlmt::TimerEventScheduler' is associated with a 'ClockData' // object. typedef bsl::map<const Category *, bsls::TimeInterval> Categories; // A map from a category to the publication interval for that // category. typedef bsl::map<bsls::TimeInterval, bsl::shared_ptr<ClockData> > Clocks; // A map from a time interval (i.e., publication period) to the clock // information for that time interval. // DATA bdlmt::TimerEventScheduler *d_scheduler_p; // event scheduler (held) MetricsManager *d_manager_p; // metrics manager (held) Categories d_categories; // map of category => schedule Clocks d_clocks; // map of interval => clock // info bsls::TimeInterval d_defaultInterval; // default publication interval mutable bslmt::Mutex d_mutex; // synchronize access to data // ('d_categories', 'd_clocks', // and 'd_defaultInterval') bslma::Allocator *d_allocator_p; // allocator (held, not owned) private: // NOT IMPLEMENTED PublicationScheduler(const PublicationScheduler& ); PublicationScheduler& operator=(const PublicationScheduler& ); // FRIENDS friend class PublicationScheduler_Proctor; // PRIVATE MANIPULATORS void publish(bsl::shared_ptr<ClockData> clockData); // Publish, to the held 'MetricsManager' object, the categories // indicated by the specified 'clockData'. Note that this operation // serves as the event callback provided to the underlying // 'bdlmt::TimerEventScheduler': this method is bound with a // 'ClockData' object in the 'bsl::function' objects provided to // 'd_scheduler_p'. void cancelCategory(Categories::iterator categoryIterator); // Cancel the periodic publication of the category indicated by the // specified 'categoryIterator'. Any scheduled publication of the // indicated category is either canceled or completed before this // method returns. The behavior is undefined unless // 'categoryIterator' is a valid iterator over 'd_categories' and // 'd_mutex' is *locked*. int cancelDefaultSchedule(); // If the default publication schedule has been set (using // 'setDefaultSchedule'), cancel that periodic default publication, // and return 0. This method has no effect and will return a non-zero // value if a default publication schedule has not been set. Any // scheduled publication is either canceled or completed before this // method returns. The behavior is undefined unless 'd_mutex' is // *locked*. public: // TRAITS BSLMF_NESTED_TRAIT_DECLARATION(PublicationScheduler, bslma::UsesBslmaAllocator); // CREATORS PublicationScheduler(MetricsManager *metricsManager, bdlmt::TimerEventScheduler *eventScheduler, bslma::Allocator *basicAllocator = 0); // Create a publication scheduler that will use the specified // 'metricsManager' to publish metrics, and the specified // 'eventScheduler' to supply timer events. Optionally specify a // 'basicAllocator' used to supply memory. If 'basicAllocator' is 0, // the currently installed default allocator is used. ~PublicationScheduler(); // Destroy this publication scheduler and cancel any pending // publications. Note that, if any metrics are currently being // published, this operation will block until all of their // publications have completed. // MANIPULATORS void scheduleCategory(const char *category, const bsls::TimeInterval& interval); // Schedule the specified null-terminated string 'category' to be // published periodically at the specified 'interval' using the // 'MetricManager' supplied at construction. If 'category' has // *already* been scheduled, change the scheduled period to 'interval'; // any previously scheduled publication of 'category' is either // canceled or completed (atomically) prior to rescheduling. If a // category is rescheduled with the same 'interval' as it is currently // scheduled, this operation has no effect. The behavior is undefined // unless 'bsls::TimeInterval(0, 0) < interval'. void scheduleCategory(const Category *category, const bsls::TimeInterval& interval); // Schedule the specified 'category' to be published periodically at // the specified 'interval' using the 'MetricManager' supplied at // construction. If 'category' has *already* been scheduled, change // the scheduled period to 'interval'; any previously scheduled // publication of 'category' is either canceled or completed // (atomically) prior to rescheduling. If a category is rescheduled // with the same 'interval' as it is currently scheduled, this // operation has no effect. The behavior is undefined unless // 'bsls::TimeInterval(0, 0) < interval' and 'category' is a valid // address supplied by the 'balm::MetricRegistry' owned by the // 'MetricsManager' object supplied at construction. void setDefaultSchedule(const bsls::TimeInterval& interval); // Set, to the specified 'interval', the default interval for metrics // to be periodically published using the 'MetricsManager' supplied at // construction. This method schedules every metric category not given // a individual schedule (using 'scheduleCategory'), to be published // periodically until that category is either given an individual // schedule, or the default schedule is canceled (using either // 'clearDefaultSchedule' or 'cancelAllPublications'). If a default // publication has *already* been scheduled, change its schedule to // 'interval'; any previously scheduled publication is either canceled // or completed (atomically) before rescheduling. If the default // publication is rescheduled with the same 'interval' as it is // currently scheduled, this operation has no effect. The behavior is // undefined unless 'bsls::TimeInterval(0, 0) < interval'. Note that, // to exclude a category from any publication, clients can disable the // category using the 'MetricsManager' object supplied at construction. int cancelCategorySchedule(const char *category); // Cancel the periodic publication of the specified null-terminated // string 'category'. Return 0 on success, and a non-zero value if the // 'category' is not scheduled for publication. Any scheduled // publication of 'category' is either canceled or completed before // this method returns. Note that if a default publication schedule // has been set (using 'setDefaultSchedule'), then 'category' will // continue to be published as part of that scheduled default // publication; to exclude a category from any publication, clients // can disable the category using the 'MetricsManager' object supplied // at construction. int cancelCategorySchedule(const Category *category); // Cancel the periodic publication of the specified 'category'. Return // 0 on success, and a non-zero value if the 'category' is not // scheduled for publication. Any scheduled publication of 'category' // is either canceled or completed before this method returns. The // behavior is undefined unless 'category' is a valid address supplied // by the 'balm::MetricRegistry' owned by 'metricsManager'. Note that // if a default publication schedule has been set (using // 'setDefaultSchedule'), then 'category' will continue to be published // as part of that scheduled default publication; to exclude a category // from any publication, clients can disable the category using the // 'MetricsManager' object supplied at construction. int clearDefaultSchedule(); // If the default publication schedule has been set (using // 'setDefaultSchedule'), cancel that periodic default publication, and // return 0. This method has no effect and will return a non-zero // value if a default publication schedule has not been set. Any // scheduled publication is either canceled or completed before this // method returns. void cancelAll(); // Cancel all periodic publication of metrics. This operation // (atomically) clears the default publication schedule and cancels the // publication schedule of any category individually scheduled using // the 'scheduleCategory' method. Any scheduled publication is either // canceled or completed before this method returns. MetricsManager *manager(); // Return the address of the modifiable metrics manager for which this // publication scheduler publishes metrics. // ACCESSORS bool findCategorySchedule(bsls::TimeInterval *result, const char *category) const; // Load into the specified 'result' the individual schedule interval // (set using the 'scheduleCategory' method) that corresponds to the // specified null-terminated string 'category', if found, and return // 'true', or (if not found) return 'false' with no effect. This // method will return 'false' and will not modify 'result' if // 'category' is published as part of the default scheduled // publication. bool findCategorySchedule(bsls::TimeInterval *result, const Category *category) const; // Load into the specified 'result' the individual schedule interval // (set using the 'scheduleCategory' method) that corresponds to the // specified 'category', if found, and return 'true', or (if not // found) return 'false' with no effect. This method will return // 'false' and will not modify 'result' if 'category' is published as // part of the default scheduled publication. The behavior is // undefined unless 'category' is a valid address supplied by the // 'balm::MetricRegistry' owned by the 'MetricsManager' object // supplied at construction. bool getDefaultSchedule(bsls::TimeInterval *result) const; // Load into the specified 'result' the default scheduled interval, // (set using the 'setDefaultSchedule' method), for periodically // publishing metrics, if found, and return 'true', or (if not found) // return 'false' with no effect. int getCategorySchedule( bsl::vector<bsl::pair<const Category *, bsls::TimeInterval> > *result) const; int getCategorySchedule( std::vector<std::pair<const Category *, bsls::TimeInterval> > *result) const; #ifdef BSLS_LIBRARYFEATURES_HAS_CPP17_PMR int getCategorySchedule( std::pmr::vector<std::pair<const Category *, bsls::TimeInterval>> *result) const; #endif // Load into the specified 'result' a representation of the current // schedule for publishing categories being followed by this scheduler // and return the number of scheduled categories. The schedule is // represented using a series of (category address, time interval) // pairs; each pair in the series indicates the periodic time interval // that the associated category will be published. Note that the // 'result' of this operation contains only those categories scheduled // using the 'scheduleCategory' operation, and does *not* include // categories published as part of the default publication. const MetricsManager *manager() const; // Return the address of the non-modifiable metrics manager for which // this publication scheduler will publish metrics. bsl::ostream& print(bsl::ostream& stream, int level = 0, int spacesPerLevel = 4) const; // Print a formatted string describing the current state of this // 'PublicationScheduler' object to the specified 'stream' at the // (absolute value of) the optionally specified indentation 'level' // and return a reference to 'stream'. If 'level' is specified, // optionally specify 'spacesPerLevel', the number of spaces per // indentation level for this and all of its nested objects. If // 'level' is negative, suppress indentation of the first line. If // 'spacesPerLevel' is negative, suppress all indentation AND format // the entire output on one line. If 'stream' is not valid on entry, // this operation has no effect. Note that this is provided primarily // for debugging purposes. }; // ============================================================================ // INLINE DEFINITIONS // ============================================================================ // -------------------------- // class PublicationScheduler // -------------------------- // MANIPULATORS inline MetricsManager *PublicationScheduler::manager() { return d_manager_p; } inline void PublicationScheduler::scheduleCategory( const char *category, const bsls::TimeInterval& interval) { scheduleCategory(d_manager_p->metricRegistry().getCategory(category), interval); } inline int PublicationScheduler::cancelCategorySchedule(const char *category) { return cancelCategorySchedule( d_manager_p->metricRegistry().getCategory(category)); } // ACCESSORS inline const MetricsManager *PublicationScheduler::manager() const { return d_manager_p; } inline bool PublicationScheduler::findCategorySchedule( bsls::TimeInterval *result, const char *category) const { return findCategorySchedule( result, d_manager_p->metricRegistry().getCategory(category)); } } // close package namespace } // close enterprise namespace #endif // ---------------------------------------------------------------------------- // Copyright 2015 Bloomberg Finance L.P. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // ----------------------------- END-OF-FILE ----------------------------------