#include "../shared/GlobalConfigFile.h" #include "../shared/SimpleLogFile.h" #include "../shared/MarketHours.h" #include "../shared/DatabaseForThread.h" #include "../shared/ReplyToClient.h" #include "SimpleDatabaseFields.h" #include "AlertsDailyDatabase.h" #include "AlertsDailyData.h" #include "AutoSecondaryData.h" static DatabaseWithRetry &getThreadRODatabase() { return *DatabaseForThread(DatabaseWithRetry::SLAVE); } AutoSecondaryData::~AutoSecondaryData() { assert(false); } AutoSecondaryData::AutoSecondaryData() : ForeverThreadUser(IContainerThread::batch()), _alwaysWatchToday(getConfigItem("auto_alerts_daily") == "1"), _nextRefreshTime(time(NULL)) { AlertsDailyDataManager::install(); CommandDispatcher::getInstance() ->listenForCommand("reload_secondary_data", this, mtRequestDataNow); start(); } void AutoSecondaryData::checkTimer() { const time_t now = time(NULL); if (_nextRefreshTime > now) // Not ready yet. return; TclList msg; msg<second > 1) { // Remember, 1 means that we had the data but no one was currently asking // for it. Those cases are removed here. newCount[it->first] = it->second; add(newData, it->first, getThreadRODatabase()); } _alertsDailyCount = newCount; AlertsDailyDataManager::set(newData); if (_alwaysWatchToday) { // o Grab today, if it's in the database. // o Grab any future days as soon as they are available. Assume the // overnight process won't dump too much in there. That would waste our // memory. // o Grab the last day before today. That's useful if we're still looking // at data from yesterday's close. I'm looking at alerts_daily and // the holidays table. The latter is required by the top list's // live-during-market-hours flag. Perhaps TopListMicroService.C should // specifically request this day. std::string sql = "SELECT distinct date FROM alerts_daily WHERE date >= CURDATE() " "UNION SELECT MAX(date) FROM alerts_daily WHERE date < CURDATE() " "UNION SELECT MAX(day) FROM holidays WHERE day < CURDATE() " "AND FIND_IN_SET('US', closed) = 0"; for (MysqlResultRef result = getThreadRODatabase().tryQueryUntilSuccess(sql); result->rowIsValid(); result->nextRow()) { std::string date = result->getStringField(0); addAlertsDailyImpl(date); _todayAuto.push_back(date); } } TclList counts; for (auto it = _alertsDailyCount.begin(); it != _alertsDailyCount.end(); it++) counts<first<second; msg<<"alerts_daily"<callbackId) { case mtRequestDataNow: { auto current = dynamic_cast< ExternalRequest const & >(*original); auto const newRefreshTime = time(NULL); TclList msg; msg<", current.getResponseMessageId()); break; } default: // Unknown request type. assert(false); }; } void AutoSecondaryData::beforeSleep(IBeforeSleepCallbacks &callbacks) { checkTimer(); callbacks.wakeAfterMs(_nextRefreshTime - time(NULL)); } void AutoSecondaryData::initializeInThread() { // Load all the background data ASAP. In particular, load it before the // first call to notifyWhenReady(). checkTimer(); } void AutoSecondaryData::addAlertsDailyImpl(std::string const &date) { int &count = _alertsDailyCount[date]; if (count) // Already loaded. Just increment the ref count. count++; else { // Need to load the data. Copy the current data into a temporary variable, // add the new data from the database, then save the combined data. count = 2; AlertsDailyData newData(AlertsDailyDataManager::getAll()); add(newData, date, getThreadRODatabase()); AlertsDailyDataManager::set(newData); } } void AutoSecondaryData::removeAlertsDailyImpl(std::string const &date) { _alertsDailyCount[date]--; } void AutoSecondaryData::addAlertsDaily(std::string const &date) { class W : public IContainerThread::Work { public: std::string date; virtual void inThread() { AutoSecondaryData::instance().addAlertsDailyImpl(date); } }; W *work = new W; work->date = date; getContainer()->addToQueue(work); } void AutoSecondaryData::removeAlertsDaily(std::string const &date) { class W : public IContainerThread::Work { public: std::string date; virtual void inThread() { // Lower the reference count immediately. The thread will periodically // check if we need to delete anything. AutoSecondaryData::instance().removeAlertsDailyImpl(date); } }; W *work = new W; work->date = date; getContainer()->addToQueue(work); } void AutoSecondaryData::notifyWhenReady(RequestListener *listener, int callbackId) { class W : public IContainerThread::Work { public: RequestListener *listener; int finalId; virtual void inThread() { Request *const request = new Request(NULL); request->callbackId = finalId; listener->newRequest(request); } }; W *work = new W; work->listener = listener; work->finalId = callbackId; getContainer()->addToQueue(work); } void AutoSecondaryData::notifyWhenReady(std::function< void() > callback) { doWorkInThread(callback); } AutoSecondaryData &AutoSecondaryData::instance() { static AutoSecondaryData *i = new AutoSecondaryData; return *i; }