{"action":"created","comment":{"url":"https://api.github.com/repos/telefonicaid/fiware-orion/pulls/comments/46119649","id":46119649,"diff_hunk":"@@ -0,0 +1,1105 @@\n+/*\n+*\n+* Copyright 2015 Telefonica Investigacion y Desarrollo, S.A.U\n+*\n+* This file is part of Orion Context Broker.\n+*\n+* Orion Context Broker is free software: you can redistribute it and/or\n+* modify it under the terms of the GNU Affero General Public License as\n+* published by the Free Software Foundation, either version 3 of the\n+* License, or (at your option) any later version.\n+*\n+* Orion Context Broker is distributed in the hope that it will be useful,\n+* but WITHOUT ANY WARRANTY; without even the implied warranty of\n+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero\n+* General Public License for more details.\n+*\n+* You should have received a copy of the GNU Affero General Public License\n+* along with Orion Context Broker. If not, see http://www.gnu.org/licenses/.\n+*\n+* For those usages not covered by this license please contact with\n+* iot_support at tid dot es\n+*\n+* Author: Ken Zangelin\n+*/\n+#include <string>\n+#include <regex.h>\n+\n+#include \"logMsg/logMsg.h\"\n+#include \"logMsg/traceLevels.h\"\n+#include \"common/sem.h\"\n+#include \"common/string.h\"\n+#include \"mongoBackend/MongoGlobal.h\"\n+#include \"mongoBackend/mongoSubCache.h\"\n+#include \"ngsi10/SubscribeContextRequest.h\"\n+#include \"cache/subCache.h\"\n+\n+using std::map;\n+\n+\n+\n+//\n+// The subscription cache maintains in memory all subscriptions of type ONCHANGE.\n+// The reason for this cache is to avoid a very slow mongo operation ... (Fermin)\n+//\n+// The 'mongo part' of the cache is implemented in mongoBackend/mongoSubCache.cpp/h and used in:\n+//   - MongoCommonUpdate.cpp               (in function addTriggeredSubscriptions_withCache)\n+//   - mongoSubscribeContext.cpp           (in function mongoSubscribeContext)\n+//   - mongoUnsubscribeContext.cpp         (in function mongoUnsubscribeContext)\n+//   - mongoUpdateContextSubscription.cpp  (in function mongoUpdateContextSubscription)\n+//   - contextBroker.cpp                   (to initialize and sybchronize)\n+//\n+// To manipulate the subscription cache, a semaphore is necessary, as various threads can be\n+// reading/inserting/removing subs at the same time.\n+// This semaphore is NOT optional, like the mongo request semaphore.\n+//\n+// Two functions have been added to common/sem.cpp|h for this: cacheSemTake/Give.\n+//\n+\n+\n+/* ****************************************************************************\n+*\n+* EntityInfo::EntityInfo - \n+*/\n+EntityInfo::EntityInfo(const std::string& _entityId, const std::string& _entityType, const std::string& _isPattern)\n+{\n+  entityId     = _entityId;\n+  entityType   = _entityType;\n+  isPattern    = (_isPattern == \"true\") || (_isPattern == \"TRUE\") || (_isPattern == \"True\");\n+\n+  if (isPattern)\n+  {\n+    regcomp(&entityIdPattern, _entityId.c_str(), 0);\n+    entityIdPatternToBeFreed = true;\n+  }\n+  else\n+  {\n+    entityIdPatternToBeFreed = false;\n+  }\n+}\n+\n+\n+\n+/* ****************************************************************************\n+*\n+* EntityInfo::match - \n+*/\n+bool EntityInfo::match\n+(\n+  const std::string&  id,\n+  const std::string&  type\n+)\n+{\n+  //\n+  // If type non-empty - perfect match is mandatory\n+  // If type is empty - always matches\n+  //\n+  if ((type != \"\") && (entityType != type) && (entityType != \"\"))\n+  {\n+    return false;\n+  }\n+\n+  if (isPattern)\n+  {\n+    // REGEX-comparison this->entityIdPattern VS id\n+    return regexec(&entityIdPattern, id.c_str(), 0, NULL, 0) == 0;\n+  }\n+\n+  return (id == entityId);\n+}\n+\n+\n+\n+/* ****************************************************************************\n+*\n+* EntityInfo::release - \n+*/\n+void EntityInfo::release(void)\n+{\n+  if (entityIdPatternToBeFreed)\n+  {\n+    regfree(&entityIdPattern);\n+    entityIdPatternToBeFreed = false;\n+  }\n+}\n+\n+\n+\n+/* ****************************************************************************\n+*\n+* EntityInfo::present - \n+*/\n+void EntityInfo::present(const std::string& prefix)\n+{\n+  LM_T(LmtPresent, (\"%sid:        %s\", prefix.c_str(), entityId.c_str()));\n+  LM_T(LmtPresent, (\"%sisPattern: %s\", prefix.c_str(), FT(isPattern)));\n+  LM_T(LmtPresent, (\"%stype:      %s\", prefix.c_str(), entityType.c_str()));\n+}\n+\n+\n+\n+/* ****************************************************************************\n+*\n+* SubCache - \n+*/\n+typedef struct SubCache\n+{\n+  CachedSubscription* head;\n+  CachedSubscription* tail;\n+\n+  // Statistics counters\n+  int                 noOfRefreshes;\n+  int                 noOfInserts;\n+  int                 noOfRemoves;\n+  int                 noOfUpdates;\n+} SubCache;\n+\n+\n+\n+/* ****************************************************************************\n+*\n+* subCache - \n+*/\n+static SubCache  subCache       = { NULL, NULL, 0 };\n+bool             subCacheActive = false;\n+\n+\n+\n+/* ****************************************************************************\n+*\n+* subCacheInit - \n+*/\n+void subCacheInit(void)\n+{\n+  LM_T(LmtSubCache, (\"Initializing subscription cache\"));\n+\n+  subCache.head   = NULL;\n+  subCache.tail   = NULL;\n+\n+  subCacheStatisticsReset(\"subCacheInit\");\n+\n+  subCacheActive = true;\n+}\n+\n+\n+\n+/* ****************************************************************************\n+*\n+* subCacheItems - \n+*/\n+int subCacheItems(void)\n+{\n+  CachedSubscription* cSubP = subCache.head;\n+  int                 items = 0;\n+\n+  while (cSubP != NULL)\n+  {\n+    ++items;\n+    cSubP = cSubP->next;\n+  }\n+\n+  return items;\n+}\n+\n+\n+\n+/* ****************************************************************************\n+*\n+* attributeMatch - \n+*/\n+static bool attributeMatch(CachedSubscription* cSubP, const std::vector<std::string>& attrV)\n+{\n+  for (unsigned int ncvIx = 0; ncvIx < cSubP->notifyConditionVector.size(); ++ncvIx)\n+  {\n+    NotifyCondition* ncP = cSubP->notifyConditionVector[ncvIx];\n+\n+    for (unsigned int cvIx = 0; cvIx < ncP->condValueList.size(); ++cvIx)\n+    {\n+      for (unsigned int aIx = 0; aIx < attrV.size(); ++aIx)\n+      {\n+        if (ncP->condValueList[cvIx] == attrV[aIx])\n+        {\n+          return true;\n+        }\n+      }\n+    }\n+  }\n+\n+  return false;\n+}\n+\n+\n+\n+/* ****************************************************************************\n+*\n+* servicePathMatch - \n+*/\n+static bool servicePathMatch(CachedSubscription* cSubP, char* servicePath)\n+{\n+  const char*  spath;\n+\n+  // Special case. \"/#\" matches EVERYTHING\n+  if ((strlen(servicePath) == 2) && (servicePath[0] == '/') && (servicePath[1] == '#') && (servicePath[2] == 0))\n+  {\n+    return true;\n+  }\n+\n+  //\n+  // Empty service path?\n+  //\n+  if ((strlen(servicePath) == 0) && (strlen(cSubP->servicePath) == 0))\n+  {\n+    return true;\n+  }\n+\n+  //\n+  // Default service path for a subscription is \"/\".\n+  // So, if empty, set it to \"/\"\n+  //\n+  if (servicePath[0] == 0)\n+  {\n+    servicePath = (char*) \"/\";\n+  }\n+\n+  spath = cSubP->servicePath;\n+\n+  //\n+  // No wildcard - exact match\n+  //\n+  if (spath[strlen(spath) - 1] != '#')\n+  {\n+    return (strcmp(servicePath, cSubP->servicePath) == 0);\n+  }\n+\n+\n+  //\n+  // Wildcard - remove '#' and compare to the length of _servicePath.\n+  //            If equal upto the length of _servicePath, then it is a match\n+  //\n+  // Actually, there is more than one way to match here:\n+  // If we have a servicePath of the subscription as \n+  // \"/a/b/#\", then the following service-paths must match:\n+  //   1. /a/b\n+  //   2. /a/b/ AND /a/b/.+  (any path below /a/b/)\n+  //\n+  // What should NOT match is \"/a/b2\".\n+  //\n+  unsigned int len = strlen(spath) - 2;\n+\n+  // 1. /a/b - (removing 2 chars from the cache-subscription removes \"/#\"\n+\n+  if ((spath[len] == '/') && (strlen(servicePath) == len) && (strncmp(spath, servicePath, len) == 0))\n+  {\n+    return true;\n+  }\n+\n+  // 2. /a/b/.+\n+  len = strlen(spath) - 1;\n+  if (strncmp(spath, servicePath, len) == 0)\n+  {\n+    return true;\n+  }\n+  \n+  return false;\n+}\n+\n+\n+\n+/* ****************************************************************************\n+*\n+* subMatch - \n+*/\n+static bool subMatch\n+(\n+  CachedSubscription*              cSubP,\n+  const char*                      tenant,\n+  const char*                      servicePath,\n+  const char*                      entityId,\n+  const char*                      entityType,\n+  const std::vector<std::string>&  attrV\n+)\n+{\n+  if ((cSubP->tenant == NULL) || (tenant == NULL) || (cSubP->tenant[0] == 0) || (tenant[0] == 0))\n+  {\n+    if ((cSubP->tenant != NULL) && (cSubP->tenant[0] != 0))\n+    {\n+      LM_T(LmtSubCacheMatch, (\"No match due to tenant I\"));\n+      return false;\n+    }\n+\n+    if ((tenant != NULL) && (tenant[0] != 0))\n+    {\n+      LM_T(LmtSubCacheMatch, (\"No match due to tenant II\"));\n+      return false;\n+    }\n+  }\n+  else if (strcmp(cSubP->tenant, tenant) != 0)\n+  {\n+    LM_T(LmtSubCacheMatch, (\"No match due to tenant III\"));\n+    return false;\n+  }\n+\n+  if (servicePathMatch(cSubP, (char*) servicePath) == false)\n+  {\n+    LM_T(LmtSubCacheMatch, (\"No match due to servicePath\"));\n+    return false;\n+  }\n+\n+\n+  //\n+  // If ONCHANGE and one of the attribute names in the scope vector\n+  // of the subscription has the same name as the incoming attribute. there is a match.\n+  //\n+  if (!attributeMatch(cSubP, attrV))\n+  {\n+    LM_T(LmtSubCacheMatch, (\"No match due to attributes\"));\n+    return false;\n+  }\n+\n+  for (unsigned int ix = 0; ix < cSubP->entityIdInfos.size(); ++ix)\n+  {\n+    EntityInfo* eiP = cSubP->entityIdInfos[ix];\n+\n+    if (eiP->match(entityId, entityType))\n+    {\n+      return true;\n+    }\n+  }\n+\n+  LM_T(LmtSubCacheMatch, (\"No match due to EntityInfo\"));\n+  return false;\n+}\n+\n+\n+\n+/* ****************************************************************************\n+*\n+* subCacheMatch - \n+*/\n+void subCacheMatch\n+(\n+  const char*                        tenant,\n+  const char*                        servicePath,\n+  const char*                        entityId,\n+  const char*                        entityType,\n+  const char*                        attr,\n+  std::vector<CachedSubscription*>*  subVecP\n+)\n+{\n+  CachedSubscription* cSubP = subCache.head;\n+\n+  while (cSubP != NULL)\n+  {\n+    std::vector<std::string> attrV;\n+\n+    attrV.push_back(attr);\n+\n+    if (subMatch(cSubP, tenant, servicePath, entityId, entityType, attrV))\n+    {\n+      subVecP->push_back(cSubP);\n+      LM_T(LmtSubCache, (\"added subscription '%s': lastNotificationTime: %lu\", cSubP->subscriptionId, cSubP->lastNotificationTime));\n+    }\n+\n+    cSubP = cSubP->next;\n+  }\n+}\n+\n+\n+\n+/* ****************************************************************************\n+*\n+* subCacheMatch - \n+*/\n+void subCacheMatch\n+(\n+  const char*                        tenant,\n+  const char*                        servicePath,\n+  const char*                        entityId,\n+  const char*                        entityType,\n+  const std::vector<std::string>&    attrV,\n+  std::vector<CachedSubscription*>*  subVecP\n+)\n+{\n+  CachedSubscription* cSubP = subCache.head;\n+\n+  while (cSubP != NULL)\n+  {\n+    if (subMatch(cSubP, tenant, servicePath, entityId, entityType, attrV))\n+    {\n+      subVecP->push_back(cSubP);\n+      LM_T(LmtSubCache, (\"added subscription '%s': lastNotificationTime: %lu\", cSubP->subscriptionId, cSubP->lastNotificationTime));\n+    }\n+\n+    cSubP = cSubP->next;\n+  }\n+}\n+\n+\n+\n+/* ****************************************************************************\n+*\n+* subCacheItemDestroy - \n+*/\n+void subCacheItemDestroy(CachedSubscription* cSubP)\n+{\n+  if (cSubP->tenant != NULL)\n+  {\n+    free(cSubP->tenant);\n+    cSubP->tenant = NULL;\n+  }\n+\n+  if (cSubP->servicePath != NULL)\n+  {\n+    free(cSubP->servicePath);\n+    cSubP->servicePath = NULL;\n+  }\n+\n+  if (cSubP->subscriptionId != NULL)\n+  {\n+    free(cSubP->subscriptionId);\n+    cSubP->subscriptionId = NULL;\n+  }\n+\n+  if (cSubP->reference != NULL)\n+  {\n+    free(cSubP->reference);\n+    cSubP->reference = NULL;\n+  }\n+\n+  for (unsigned int ix = 0; ix < cSubP->entityIdInfos.size(); ++ix)\n+  {\n+    cSubP->entityIdInfos[ix]->release();\n+    delete cSubP->entityIdInfos[ix];\n+  }\n+\n+  cSubP->entityIdInfos.clear();\n+\n+  while (cSubP->attributes.size() > 0)\n+  {\n+    cSubP->attributes.erase(cSubP->attributes.begin());\n+  }\n+  cSubP->attributes.clear();\n+\n+  cSubP->notifyConditionVector.release();\n+  cSubP->notifyConditionVector.vec.clear();\n+\n+  cSubP->next = NULL;\n+}\n+\n+\n+\n+/* ****************************************************************************\n+*\n+* subCacheDestroy - \n+*/\n+void subCacheDestroy(void)\n+{\n+  LM_T(LmtSubCache, (\"destroying subscription cache\"));\n+\n+  CachedSubscription* cSubP  = subCache.head;\n+\n+  if (subCache.head == NULL)\n+  {\n+    return;\n+  }\n+\n+  while (cSubP->next)\n+  {\n+    CachedSubscription* prev = cSubP;\n+\n+    cSubP = cSubP->next;\n+    subCacheItemDestroy(prev);\n+    LM_T(LmtSubCache,  (\"removing CachedSubscription at %p\", prev));\n+    delete prev;\n+  }\n+\n+  subCacheItemDestroy(cSubP);\n+  LM_T(LmtSubCache,  (\"removing CachedSubscription at %p\", cSubP));\n+  delete cSubP;\n+\n+  subCache.head  = NULL;\n+  subCache.tail  = NULL;\n+}\n+\n+\n+\n+/* ****************************************************************************\n+*\n+* tenantMatch - \n+*/\n+static bool tenantMatch(const char* tenant1, const char* tenant2)\n+{\n+  //\n+  // Removing complications with NULL, giving NULL tenants the value of an empty string;\n+  //\n+  tenant1 = (tenant1 == NULL)? \"\" : tenant1;\n+  tenant2 = (tenant2 == NULL)? \"\" : tenant2;\n+\n+  if (strlen(tenant1) != strlen(tenant2))\n+  {\n+    return false;\n+  }\n+\n+  if (strcmp(tenant1, tenant2) == 0)\n+  {\n+    return true;\n+  }\n+\n+  return false;\n+}\n+\n+\n+\n+/* ****************************************************************************\n+*\n+* subCacheItemLookup - \n+*\n+* FIXME P7: lookups would be A LOT faster if the subCache used a hash-table instead of\n+*           just a single linked link for the subscriptions.\n+*           The hash could be adding each char in 'tenant' and 'subscriptionId' to a 8-bit\n+*           integer and then we'd have a vector of 256 linked lists.\n+*           Another way of implementing this would be to use a std::vector or std::map.\n+*/\n+CachedSubscription* subCacheItemLookup(const char* tenant, const char* subscriptionId)\n+{\n+  CachedSubscription* cSubP = subCache.head;\n+\n+  while (cSubP != NULL)\n+  {\n+    if ((tenantMatch(tenant, cSubP->tenant)) && (strcmp(subscriptionId, cSubP->subscriptionId) == 0))\n+    {\n+      return cSubP;\n+    }\n+\n+    cSubP = cSubP->next;\n+  }\n+\n+  return NULL;\n+}\n+\n+\n+\n+/* ****************************************************************************\n+*\n+* subCacheUpdateStatisticsIncrement - \n+*/\n+void subCacheUpdateStatisticsIncrement(void)\n+{\n+  ++subCache.noOfUpdates;\n+}\n+\n+\n+\n+/* ****************************************************************************\n+*\n+* subCacheItemInsert - \n+*/\n+void subCacheItemInsert(CachedSubscription* cSubP)\n+{\n+  //\n+  // First of all, insertion is done at the end of the list, so, \n+  // cSubP->next must ALWAYS be zero at insertion time\n+  //\n+  cSubP->next = NULL;\n+\n+  LM_T(LmtSubCache, (\"inserting sub '%s', lastNotificationTime: %lu\", cSubP->subscriptionId, cSubP->lastNotificationTime));\n+\n+  ++subCache.noOfInserts;\n+\n+  // First insertion?\n+  if ((subCache.head == NULL) && (subCache.tail == NULL))\n+  {\n+    subCache.head   = cSubP;\n+    subCache.tail   = cSubP;\n+\n+    return;\n+  }\n+\n+  subCache.tail->next  = cSubP;\n+  subCache.tail        = cSubP;\n+}\n+\n+\n+\n+/* ****************************************************************************\n+*\n+* subCacheItemInsert - create a new sub, fill it in, and add it to cache\n+*/\n+void subCacheItemInsert\n+(\n+  const char*               tenant,\n+  const char*               servicePath,\n+  SubscribeContextRequest*  scrP,\n+  const char*               subscriptionId,\n+  int64_t                   expirationTime,\n+  int64_t                   throttling,\n+  Format                    notifyFormat,\n+  bool                      notificationDone,\n+  int64_t                   lastNotificationTime\n+)\n+{\n+  //\n+  // Add the subscription to the subscription cache.\n+  // But only if any of the entities in entityIdVector is pattern based -\n+  // AND it is a subscription of type ONCHANGE\n+  //\n+\n+  //\n+  // 00. Check that the subscription is pattern based an ONCHANGE\n+  //\n+  bool onchange = false;\n+\n+  for (unsigned int ix = 0; ix < scrP->notifyConditionVector.size(); ++ix)\n+  {\n+    if (strcasecmp(scrP->notifyConditionVector[ix]->type.c_str(), ON_CHANGE_CONDITION) == 0)\n+    {\n+      onchange = true;\n+      break;\n+    }\n+  }\n+\n+  if (!onchange)\n+  {\n+    return;\n+  }\n+\n+    \n+  //\n+  // Now allocate the subscription\n+  //\n+  CachedSubscription* cSubP = new CachedSubscription();\n+  LM_T(LmtSubCache,  (\"allocated CachedSubscription at %p\", cSubP));\n+\n+\n+  //\n+  // 1. First the non-complex values\n+  //\n+  cSubP->tenant                = (tenant[0] == 0)? NULL : strdup(tenant);\n+  cSubP->servicePath           = strdup(servicePath);\n+  cSubP->subscriptionId        = strdup(subscriptionId);\n+  cSubP->reference             = strdup(scrP->reference.get().c_str());\n+  cSubP->expirationTime        = expirationTime;\n+  cSubP->throttling            = throttling;\n+  cSubP->lastNotificationTime  = lastNotificationTime;\n+  cSubP->notifyFormat          = notifyFormat;\n+  cSubP->next                  = NULL;\n+  cSubP->count                 = (notificationDone == true)? 1 : 0;","path":"src/lib/cache/subCache.cpp","position":699,"original_position":686,"commit_id":"473bd5c9ec77e7f58f17ee4c68768b8e7f5c37a0","original_commit_id":"e4a7b75d6858a43ccd93cf17c6a7d630e9bd37ac","user":{"login":"fgalan","id":1534240,"avatar_url":"https://avatars.githubusercontent.com/u/1534240?v=3","gravatar_id":"","url":"https://api.github.com/users/fgalan","html_url":"https://github.com/fgalan","followers_url":"https://api.github.com/users/fgalan/followers","following_url":"https://api.github.com/users/fgalan/following{/other_user}","gists_url":"https://api.github.com/users/fgalan/gists{/gist_id}","starred_url":"https://api.github.com/users/fgalan/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/fgalan/subscriptions","organizations_url":"https://api.github.com/users/fgalan/orgs","repos_url":"https://api.github.com/users/fgalan/repos","events_url":"https://api.github.com/users/fgalan/events{/privacy}","received_events_url":"https://api.github.com/users/fgalan/received_events","type":"User","site_admin":false},"body":"I understand that notificationDone is true when the situation is \"notification sent upon subscription creation\". In that case, looks good.\r\n\r\nNTC","created_at":"2015-11-30T09:02:38Z","updated_at":"2015-11-30T09:02:38Z","html_url":"https://github.com/telefonicaid/fiware-orion/pull/1557#discussion_r46119649","pull_request_url":"https://api.github.com/repos/telefonicaid/fiware-orion/pulls/1557","_links":{"self":{"href":"https://api.github.com/repos/telefonicaid/fiware-orion/pulls/comments/46119649"},"html":{"href":"https://github.com/telefonicaid/fiware-orion/pull/1557#discussion_r46119649"},"pull_request":{"href":"https://api.github.com/repos/telefonicaid/fiware-orion/pulls/1557"}}},"pull_request":{"url":"https://api.github.com/repos/telefonicaid/fiware-orion/pulls/1557","id":51991371,"html_url":"https://github.com/telefonicaid/fiware-orion/pull/1557","diff_url":"https://github.com/telefonicaid/fiware-orion/pull/1557.diff","patch_url":"https://github.com/telefonicaid/fiware-orion/pull/1557.patch","issue_url":"https://api.github.com/repos/telefonicaid/fiware-orion/issues/1557","number":1557,"state":"open","locked":false,"title":"Hardening/1405 mongo sub cache to own library","user":{"login":"kzangeli","id":5338380,"avatar_url":"https://avatars.githubusercontent.com/u/5338380?v=3","gravatar_id":"","url":"https://api.github.com/users/kzangeli","html_url":"https://github.com/kzangeli","followers_url":"https://api.github.com/users/kzangeli/followers","following_url":"https://api.github.com/users/kzangeli/following{/other_user}","gists_url":"https://api.github.com/users/kzangeli/gists{/gist_id}","starred_url":"https://api.github.com/users/kzangeli/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/kzangeli/subscriptions","organizations_url":"https://api.github.com/users/kzangeli/orgs","repos_url":"https://api.github.com/users/kzangeli/repos","events_url":"https://api.github.com/users/kzangeli/events{/privacy}","received_events_url":"https://api.github.com/users/kzangeli/received_events","type":"User","site_admin":false},"body":"Fixes issue #1405 and #1555.\r\nSubscription Cache to own library\r\n\r\nAlso 2 bugs fixed:\r\n- fixed a 'bug' about putting subscriptions for tenants when tenants not is use (-multiservice flag is not ON).\r\n- setting 'count' to ZERO after refreshing cache from DB\r\n","created_at":"2015-11-27T12:44:40Z","updated_at":"2015-11-30T09:02:38Z","closed_at":null,"merged_at":null,"merge_commit_sha":"f2cedbaddca3aa74e9b28e9da378eef87960af52","assignee":{"login":"fgalan","id":1534240,"avatar_url":"https://avatars.githubusercontent.com/u/1534240?v=3","gravatar_id":"","url":"https://api.github.com/users/fgalan","html_url":"https://github.com/fgalan","followers_url":"https://api.github.com/users/fgalan/followers","following_url":"https://api.github.com/users/fgalan/following{/other_user}","gists_url":"https://api.github.com/users/fgalan/gists{/gist_id}","starred_url":"https://api.github.com/users/fgalan/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/fgalan/subscriptions","organizations_url":"https://api.github.com/users/fgalan/orgs","repos_url":"https://api.github.com/users/fgalan/repos","events_url":"https://api.github.com/users/fgalan/events{/privacy}","received_events_url":"https://api.github.com/users/fgalan/received_events","type":"User","site_admin":false},"milestone":{"url":"https://api.github.com/repos/telefonicaid/fiware-orion/milestones/19","html_url":"https://github.com/telefonicaid/fiware-orion/milestones/0.26.0","labels_url":"https://api.github.com/repos/telefonicaid/fiware-orion/milestones/19/labels","id":1295074,"number":19,"title":"0.26.0","description":"","creator":{"login":"fgalan","id":1534240,"avatar_url":"https://avatars.githubusercontent.com/u/1534240?v=3","gravatar_id":"","url":"https://api.github.com/users/fgalan","html_url":"https://github.com/fgalan","followers_url":"https://api.github.com/users/fgalan/followers","following_url":"https://api.github.com/users/fgalan/following{/other_user}","gists_url":"https://api.github.com/users/fgalan/gists{/gist_id}","starred_url":"https://api.github.com/users/fgalan/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/fgalan/subscriptions","organizations_url":"https://api.github.com/users/fgalan/orgs","repos_url":"https://api.github.com/users/fgalan/repos","events_url":"https://api.github.com/users/fgalan/events{/privacy}","received_events_url":"https://api.github.com/users/fgalan/received_events","type":"User","site_admin":false},"open_issues":6,"closed_issues":28,"state":"open","created_at":"2015-09-08T10:31:21Z","updated_at":"2015-11-30T08:53:23Z","due_on":"2015-11-29T23:00:00Z","closed_at":null},"commits_url":"https://api.github.com/repos/telefonicaid/fiware-orion/pulls/1557/commits","review_comments_url":"https://api.github.com/repos/telefonicaid/fiware-orion/pulls/1557/comments","review_comment_url":"https://api.github.com/repos/telefonicaid/fiware-orion/pulls/comments{/number}","comments_url":"https://api.github.com/repos/telefonicaid/fiware-orion/issues/1557/comments","statuses_url":"https://api.github.com/repos/telefonicaid/fiware-orion/statuses/473bd5c9ec77e7f58f17ee4c68768b8e7f5c37a0","head":{"label":"telefonicaid:hardening/1405_mongo_sub_cache_to_own_library","ref":"hardening/1405_mongo_sub_cache_to_own_library","sha":"473bd5c9ec77e7f58f17ee4c68768b8e7f5c37a0","user":{"login":"telefonicaid","id":968363,"avatar_url":"https://avatars.githubusercontent.com/u/968363?v=3","gravatar_id":"","url":"https://api.github.com/users/telefonicaid","html_url":"https://github.com/telefonicaid","followers_url":"https://api.github.com/users/telefonicaid/followers","following_url":"https://api.github.com/users/telefonicaid/following{/other_user}","gists_url":"https://api.github.com/users/telefonicaid/gists{/gist_id}","starred_url":"https://api.github.com/users/telefonicaid/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/telefonicaid/subscriptions","organizations_url":"https://api.github.com/users/telefonicaid/orgs","repos_url":"https://api.github.com/users/telefonicaid/repos","events_url":"https://api.github.com/users/telefonicaid/events{/privacy}","received_events_url":"https://api.github.com/users/telefonicaid/received_events","type":"Organization","site_admin":false},"repo":{"id":12808420,"name":"fiware-orion","full_name":"telefonicaid/fiware-orion","owner":{"login":"telefonicaid","id":968363,"avatar_url":"https://avatars.githubusercontent.com/u/968363?v=3","gravatar_id":"","url":"https://api.github.com/users/telefonicaid","html_url":"https://github.com/telefonicaid","followers_url":"https://api.github.com/users/telefonicaid/followers","following_url":"https://api.github.com/users/telefonicaid/following{/other_user}","gists_url":"https://api.github.com/users/telefonicaid/gists{/gist_id}","starred_url":"https://api.github.com/users/telefonicaid/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/telefonicaid/subscriptions","organizations_url":"https://api.github.com/users/telefonicaid/orgs","repos_url":"https://api.github.com/users/telefonicaid/repos","events_url":"https://api.github.com/users/telefonicaid/events{/privacy}","received_events_url":"https://api.github.com/users/telefonicaid/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/telefonicaid/fiware-orion","description":"FIWARE Orion Context Broker","fork":false,"url":"https://api.github.com/repos/telefonicaid/fiware-orion","forks_url":"https://api.github.com/repos/telefonicaid/fiware-orion/forks","keys_url":"https://api.github.com/repos/telefonicaid/fiware-orion/keys{/key_id}","collaborators_url":"https://api.github.com/repos/telefonicaid/fiware-orion/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/telefonicaid/fiware-orion/teams","hooks_url":"https://api.github.com/repos/telefonicaid/fiware-orion/hooks","issue_events_url":"https://api.github.com/repos/telefonicaid/fiware-orion/issues/events{/number}","events_url":"https://api.github.com/repos/telefonicaid/fiware-orion/events","assignees_url":"https://api.github.com/repos/telefonicaid/fiware-orion/assignees{/user}","branches_url":"https://api.github.com/repos/telefonicaid/fiware-orion/branches{/branch}","tags_url":"https://api.github.com/repos/telefonicaid/fiware-orion/tags","blobs_url":"https://api.github.com/repos/telefonicaid/fiware-orion/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/telefonicaid/fiware-orion/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/telefonicaid/fiware-orion/git/refs{/sha}","trees_url":"https://api.github.com/repos/telefonicaid/fiware-orion/git/trees{/sha}","statuses_url":"https://api.github.com/repos/telefonicaid/fiware-orion/statuses/{sha}","languages_url":"https://api.github.com/repos/telefonicaid/fiware-orion/languages","stargazers_url":"https://api.github.com/repos/telefonicaid/fiware-orion/stargazers","contributors_url":"https://api.github.com/repos/telefonicaid/fiware-orion/contributors","subscribers_url":"https://api.github.com/repos/telefonicaid/fiware-orion/subscribers","subscription_url":"https://api.github.com/repos/telefonicaid/fiware-orion/subscription","commits_url":"https://api.github.com/repos/telefonicaid/fiware-orion/commits{/sha}","git_commits_url":"https://api.github.com/repos/telefonicaid/fiware-orion/git/commits{/sha}","comments_url":"https://api.github.com/repos/telefonicaid/fiware-orion/comments{/number}","issue_comment_url":"https://api.github.com/repos/telefonicaid/fiware-orion/issues/comments{/number}","contents_url":"https://api.github.com/repos/telefonicaid/fiware-orion/contents/{+path}","compare_url":"https://api.github.com/repos/telefonicaid/fiware-orion/compare/{base}...{head}","merges_url":"https://api.github.com/repos/telefonicaid/fiware-orion/merges","archive_url":"https://api.github.com/repos/telefonicaid/fiware-orion/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/telefonicaid/fiware-orion/downloads","issues_url":"https://api.github.com/repos/telefonicaid/fiware-orion/issues{/number}","pulls_url":"https://api.github.com/repos/telefonicaid/fiware-orion/pulls{/number}","milestones_url":"https://api.github.com/repos/telefonicaid/fiware-orion/milestones{/number}","notifications_url":"https://api.github.com/repos/telefonicaid/fiware-orion/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/telefonicaid/fiware-orion/labels{/name}","releases_url":"https://api.github.com/repos/telefonicaid/fiware-orion/releases{/id}","created_at":"2013-09-13T11:55:25Z","updated_at":"2015-11-25T10:36:09Z","pushed_at":"2015-11-30T09:01:55Z","git_url":"git://github.com/telefonicaid/fiware-orion.git","ssh_url":"git@github.com:telefonicaid/fiware-orion.git","clone_url":"https://github.com/telefonicaid/fiware-orion.git","svn_url":"https://github.com/telefonicaid/fiware-orion","homepage":"http://catalogue.fiware.org/enablers/publishsubscribe-context-broker-orion-context-broker","size":18349,"stargazers_count":36,"watchers_count":36,"language":"C++","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":48,"mirror_url":null,"open_issues_count":251,"forks":48,"open_issues":251,"watchers":36,"default_branch":"develop"}},"base":{"label":"telefonicaid:develop","ref":"develop","sha":"c7a5736c8c9940560cece0eb3906308fb0994d44","user":{"login":"telefonicaid","id":968363,"avatar_url":"https://avatars.githubusercontent.com/u/968363?v=3","gravatar_id":"","url":"https://api.github.com/users/telefonicaid","html_url":"https://github.com/telefonicaid","followers_url":"https://api.github.com/users/telefonicaid/followers","following_url":"https://api.github.com/users/telefonicaid/following{/other_user}","gists_url":"https://api.github.com/users/telefonicaid/gists{/gist_id}","starred_url":"https://api.github.com/users/telefonicaid/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/telefonicaid/subscriptions","organizations_url":"https://api.github.com/users/telefonicaid/orgs","repos_url":"https://api.github.com/users/telefonicaid/repos","events_url":"https://api.github.com/users/telefonicaid/events{/privacy}","received_events_url":"https://api.github.com/users/telefonicaid/received_events","type":"Organization","site_admin":false},"repo":{"id":12808420,"name":"fiware-orion","full_name":"telefonicaid/fiware-orion","owner":{"login":"telefonicaid","id":968363,"avatar_url":"https://avatars.githubusercontent.com/u/968363?v=3","gravatar_id":"","url":"https://api.github.com/users/telefonicaid","html_url":"https://github.com/telefonicaid","followers_url":"https://api.github.com/users/telefonicaid/followers","following_url":"https://api.github.com/users/telefonicaid/following{/other_user}","gists_url":"https://api.github.com/users/telefonicaid/gists{/gist_id}","starred_url":"https://api.github.com/users/telefonicaid/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/telefonicaid/subscriptions","organizations_url":"https://api.github.com/users/telefonicaid/orgs","repos_url":"https://api.github.com/users/telefonicaid/repos","events_url":"https://api.github.com/users/telefonicaid/events{/privacy}","received_events_url":"https://api.github.com/users/telefonicaid/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/telefonicaid/fiware-orion","description":"FIWARE Orion Context Broker","fork":false,"url":"https://api.github.com/repos/telefonicaid/fiware-orion","forks_url":"https://api.github.com/repos/telefonicaid/fiware-orion/forks","keys_url":"https://api.github.com/repos/telefonicaid/fiware-orion/keys{/key_id}","collaborators_url":"https://api.github.com/repos/telefonicaid/fiware-orion/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/telefonicaid/fiware-orion/teams","hooks_url":"https://api.github.com/repos/telefonicaid/fiware-orion/hooks","issue_events_url":"https://api.github.com/repos/telefonicaid/fiware-orion/issues/events{/number}","events_url":"https://api.github.com/repos/telefonicaid/fiware-orion/events","assignees_url":"https://api.github.com/repos/telefonicaid/fiware-orion/assignees{/user}","branches_url":"https://api.github.com/repos/telefonicaid/fiware-orion/branches{/branch}","tags_url":"https://api.github.com/repos/telefonicaid/fiware-orion/tags","blobs_url":"https://api.github.com/repos/telefonicaid/fiware-orion/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/telefonicaid/fiware-orion/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/telefonicaid/fiware-orion/git/refs{/sha}","trees_url":"https://api.github.com/repos/telefonicaid/fiware-orion/git/trees{/sha}","statuses_url":"https://api.github.com/repos/telefonicaid/fiware-orion/statuses/{sha}","languages_url":"https://api.github.com/repos/telefonicaid/fiware-orion/languages","stargazers_url":"https://api.github.com/repos/telefonicaid/fiware-orion/stargazers","contributors_url":"https://api.github.com/repos/telefonicaid/fiware-orion/contributors","subscribers_url":"https://api.github.com/repos/telefonicaid/fiware-orion/subscribers","subscription_url":"https://api.github.com/repos/telefonicaid/fiware-orion/subscription","commits_url":"https://api.github.com/repos/telefonicaid/fiware-orion/commits{/sha}","git_commits_url":"https://api.github.com/repos/telefonicaid/fiware-orion/git/commits{/sha}","comments_url":"https://api.github.com/repos/telefonicaid/fiware-orion/comments{/number}","issue_comment_url":"https://api.github.com/repos/telefonicaid/fiware-orion/issues/comments{/number}","contents_url":"https://api.github.com/repos/telefonicaid/fiware-orion/contents/{+path}","compare_url":"https://api.github.com/repos/telefonicaid/fiware-orion/compare/{base}...{head}","merges_url":"https://api.github.com/repos/telefonicaid/fiware-orion/merges","archive_url":"https://api.github.com/repos/telefonicaid/fiware-orion/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/telefonicaid/fiware-orion/downloads","issues_url":"https://api.github.com/repos/telefonicaid/fiware-orion/issues{/number}","pulls_url":"https://api.github.com/repos/telefonicaid/fiware-orion/pulls{/number}","milestones_url":"https://api.github.com/repos/telefonicaid/fiware-orion/milestones{/number}","notifications_url":"https://api.github.com/repos/telefonicaid/fiware-orion/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/telefonicaid/fiware-orion/labels{/name}","releases_url":"https://api.github.com/repos/telefonicaid/fiware-orion/releases{/id}","created_at":"2013-09-13T11:55:25Z","updated_at":"2015-11-25T10:36:09Z","pushed_at":"2015-11-30T09:01:55Z","git_url":"git://github.com/telefonicaid/fiware-orion.git","ssh_url":"git@github.com:telefonicaid/fiware-orion.git","clone_url":"https://github.com/telefonicaid/fiware-orion.git","svn_url":"https://github.com/telefonicaid/fiware-orion","homepage":"http://catalogue.fiware.org/enablers/publishsubscribe-context-broker-orion-context-broker","size":18349,"stargazers_count":36,"watchers_count":36,"language":"C++","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":48,"mirror_url":null,"open_issues_count":251,"forks":48,"open_issues":251,"watchers":36,"default_branch":"develop"}},"_links":{"self":{"href":"https://api.github.com/repos/telefonicaid/fiware-orion/pulls/1557"},"html":{"href":"https://github.com/telefonicaid/fiware-orion/pull/1557"},"issue":{"href":"https://api.github.com/repos/telefonicaid/fiware-orion/issues/1557"},"comments":{"href":"https://api.github.com/repos/telefonicaid/fiware-orion/issues/1557/comments"},"review_comments":{"href":"https://api.github.com/repos/telefonicaid/fiware-orion/pulls/1557/comments"},"review_comment":{"href":"https://api.github.com/repos/telefonicaid/fiware-orion/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/telefonicaid/fiware-orion/pulls/1557/commits"},"statuses":{"href":"https://api.github.com/repos/telefonicaid/fiware-orion/statuses/473bd5c9ec77e7f58f17ee4c68768b8e7f5c37a0"}}}}