Fawkes API  Fawkes Development Version
computables_manager.cpp
1 /***************************************************************************
2  * computables_manager.cpp - Class managing registered computables and
3  * checking if any computables are invoced by a query
4  *
5  * Created: 6:37:45 PM 2016
6  * Copyright 2016 Frederik Zwilling
7  ****************************************************************************/
8 
9 /* This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU Library General Public License for more details.
18  *
19  * Read the full text in the LICENSE.GPL file in the doc directory.
20  */
21 
22 #include "computables_manager.h"
23 
24 #include <core/exception.h>
25 #ifdef USE_TIMETRACKER
26 # include <utils/time/tracker.h>
27 #endif
28 #include <plugins/robot-memory/robot_memory.h>
29 #include <utils/time/tracker_macros.h>
30 
31 #include <chrono>
32 #include <mongocxx/exception/operation_exception.hpp>
33 
34 /** @class ComputablesManager computables_manager.h
35  * This class manages registering computables and can check
36  * if any computables are invoced by a query.
37  * @author Frederik Zwilling
38  */
39 
40 using namespace fawkes;
41 using namespace mongocxx;
42 using namespace bsoncxx;
43 
44 /**
45  * Constructor for class managing computables with refereces to plugin objects
46  * @param config Configuration
47  * @param robot_memory Robot Memory
48  */
50 : config_(config),
51  robot_memory_(robot_memory),
52  matching_test_collection_("robmem.computables_matching")
53 {
54  try {
55  matching_test_collection_ =
56  config_->get_string("/plugins/robot-memory/database") + ".computables_matching";
57  } catch (Exception &e) {
58  }
59 
60  srand(time(NULL));
61 #ifdef USE_TIMETRACKER
62  tt_ = new TimeTracker();
63  tt_loopcount_ = 0;
64  ttc_cleanup_ = tt_->add_class("RobotMemory Cleanup Function Call");
65  ttc_cleanup_inner_loop_ = tt_->add_class("RobotMemory Cleanup Inner Loop");
66  ttc_cleanup_remove_query_ = tt_->add_class("RobotMemory Cleanup Database Remove Query");
67 #endif
68 }
69 
70 /**
71  * Copy-construct a ComputablesManager.
72  * Do *not* copy-construct the computables that are managed by the manager, but
73  * only call the constructor with the other's config and robot memory.
74  * If you want to use a computable with the new manager, you will need to
75  * register it again.
76  * @param other A reference to the other ComputablesManager to copy from.
77  */
79 : ComputablesManager(other.config_, other.robot_memory_)
80 {
81 }
82 
83 ComputablesManager::~ComputablesManager()
84 {
85 #ifdef USE_TIMETRACKER
86  delete tt_;
87 #endif
88 }
89 
90 /**
91  * Remove previously registered computable
92  * @param computable The computable to remove
93  */
94 void
96 {
97  for (std::list<Computable *>::iterator it = computables.begin(); it != computables.end(); ++it) {
98  if ((*it) == computable) {
99  Computable *comp = *it;
100  computables.erase(it);
101  delete comp;
102  return;
103  }
104  }
105 }
106 
107 /**
108  * Checks if computable knowledge is queried and calls the compute functions in this case
109  * @param query The query that might ask for computable knowledge
110  * @param collection The collection that is querried
111  * @return Were computed documents added?
112  */
113 bool
114 ComputablesManager::check_and_compute(const document::view &query, std::string collection)
115 {
116  //check if computation result of the query is already cached
117  for (std::map<std::tuple<std::string, std::string>, long long>::iterator it =
118  cached_querries_.begin();
119  it != cached_querries_.end();
120  ++it) {
121  if (collection == std::get<0>(it->first) && to_json(query) == std::get<1>(it->first)) {
122  return false;
123  }
124  }
125  if (collection.find(matching_test_collection_) != std::string::npos)
126  return false; //not necessary for matching test itself
127  bool added_computed_docs = false;
128  //check if the query is matched by the computable identifyer
129  //to do that we just insert the query as if it would be a document and query for it with the computable identifiers
130  std::string current_test_collection = matching_test_collection_ + std::to_string(rand());
131  try {
132  robot_memory_->insert(query, current_test_collection);
133  } catch (mongocxx::operation_exception &e) {
134  // This may happen if the query contains fields that cannot be inserted, e.g., a $regex
135  robot_memory_->drop_collection(current_test_collection);
136  return false;
137  }
138  for (std::list<Computable *>::iterator it = computables.begin(); it != computables.end(); ++it) {
139  auto cursor = robot_memory_->query((*it)->get_query(), current_test_collection);
140  if (collection == (*it)->get_collection() && cursor.begin() != cursor.end()) {
141  std::list<document::value> computed_docs_list = (*it)->compute(query);
142  if (!computed_docs_list.empty()) {
143  //move list into vector
144  std::vector<document::view> computed_docs_vector{
145  std::make_move_iterator(std::begin(computed_docs_list)),
146  std::make_move_iterator(std::end(computed_docs_list))};
147  //remember how long a query is cached:
148  long long cached_until =
149  computed_docs_vector[0]["_robmem_info"]["cached_until"].get_int64();
150  cached_querries_[std::make_tuple(collection, to_json(query))] = cached_until;
151  //TODO: fix minor problem: equivalent queries in different order jield unequal strings
152  robot_memory_->insert(computed_docs_vector, (*it)->get_collection());
153  added_computed_docs = true;
154  }
155  }
156  }
157  robot_memory_->drop_collection(current_test_collection);
158  return added_computed_docs;
159 }
160 
161 /**
162  * Clean up all collections containing documents computed on demand
163  */
164 void
166 {
167  TIMETRACK_START(ttc_cleanup_);
168  long long current_time_ms =
169  std::chrono::system_clock::now().time_since_epoch() / std::chrono::milliseconds(1);
170  for (std::map<std::tuple<std::string, std::string>, long long>::iterator it =
171  cached_querries_.begin();
172  it != cached_querries_.end();
173  ++it) {
174  TIMETRACK_START(ttc_cleanup_inner_loop_);
175  if (current_time_ms > it->second) {
176  using namespace bsoncxx::builder;
177  basic::document doc;
178  doc.append(basic::kvp("_robmem_info.computed", true));
179  doc.append(
180  basic::kvp("_robmem_info.cached_until", [current_time_ms](basic::sub_document subdoc) {
181  subdoc.append(basic::kvp("$lt", static_cast<std::int64_t>(current_time_ms)));
182  }));
183  TIMETRACK_START(ttc_cleanup_remove_query_);
184  robot_memory_->remove(doc, std::get<0>(it->first));
185  TIMETRACK_END(ttc_cleanup_remove_query_);
186  cached_querries_.erase(it->first);
187  }
188  TIMETRACK_END(ttc_cleanup_inner_loop_);
189  }
190  TIMETRACK_END(ttc_cleanup_);
191 #ifdef USE_TIMETRACKER
192  if (++tt_loopcount_ % 5 == 0) {
193  tt_->print_to_stdout();
194  }
195 #endif
196 }
ComputablesManager::ComputablesManager
ComputablesManager(fawkes::Configuration *config, RobotMemory *robot_memory)
Constructor for class managing computables with refereces to plugin objects.
Definition: computables_manager.cpp:49
RobotMemory::insert
int insert(bsoncxx::document::view, const std::string &collection="")
Inserts a document into the robot memory.
Definition: robot_memory.cpp:252
ComputablesManager
This class manages registering computables and can check if any computables are invoced by a query.
Definition: computables_manager.h:49
RobotMemory
Access to the robot memory based on mongodb.
Definition: robot_memory.h:47
fawkes::Configuration
Interface for configuration handling.
Definition: config.h:65
fawkes
Fawkes library namespace.
RobotMemory::drop_collection
int drop_collection(const std::string &collection)
Drop (= remove) a whole collection and all documents inside it.
Definition: robot_memory.cpp:531
ComputablesManager::check_and_compute
bool check_and_compute(const bsoncxx::document::view &query, std::string collection)
Checks if computable knowledge is queried and calls the compute functions in this case.
Definition: computables_manager.cpp:114
ComputablesManager::cleanup_computed_docs
void cleanup_computed_docs()
Clean up all collections containing documents computed on demand.
Definition: computables_manager.cpp:165
fawkes::TimeTracker
Time tracking utility.
Definition: tracker.h:37
RobotMemory::query
mongocxx::cursor query(bsoncxx::document::view query, const std::string &collection_name="", mongocxx::options::find query_options=mongocxx::options::find())
Query information from the robot memory.
Definition: robot_memory.cpp:178
Computable
Class holding information for a single computable this class also enhances computed documents by addi...
Definition: computable.h:32
fawkes::Configuration::get_string
virtual std::string get_string(const char *path)=0
Get value from configuration which is of type string.
ComputablesManager::remove_computable
void remove_computable(Computable *computable)
Remove previously registered computable.
Definition: computables_manager.cpp:95
fawkes::Exception
Base class for exceptions in Fawkes.
Definition: exception.h:36