static bool _athenaLRUContentStore_PutContentObject(AthenaContentStoreImplementation *store, const CCNxContentObject *content) { AthenaLRUContentStore *impl = (AthenaLRUContentStore *) store; // If there's no capacity at all, just return now. if (impl->maxSizeInBytes <= 0) { return false; } // Check to see if the ContentObject is expired. If so, don't bother to cache it. if (ccnxContentObject_HasExpiryTime(content)) { if (ccnxContentObject_GetExpiryTime(content) <= parcClock_GetTime(impl->wallClock)) { // the expiration time is now or earlier, so don't bother adding it. return false; } } _AthenaLRUContentStoreEntry *newEntry = _athenaLRUContentStoreEntry_Create(content); bool result = _athenaLRUContentStore_PutLRUContentStoreEntry(store, newEntry); if (result == false) { // We didn't have enough room in the store to add the new item. } _athenaLRUContentStoreEntry_Release(&newEntry); return result; }
LONGBOW_TEST_CASE(Global, getMatchByName) { AthenaLRUContentStoreConfig config; config.capacityInMB = 10; AthenaContentStore *store = athenaContentStore_Create(&AthenaContentStore_LRUImplementation, &config); char *lci = "lci:/cakes/and/pies"; PARCClock *clock = parcClock_Wallclock(); CCNxName *truthName = ccnxName_CreateFromURI(lci); CCNxContentObject *truthObject = ccnxContentObject_CreateWithDataPayload(truthName, NULL); ccnxContentObject_SetExpiryTime(truthObject, parcClock_GetTime(clock) + 100); ccnxName_Release(&truthName); athenaContentStore_PutContentObject(store, truthObject); CCNxName *testName = ccnxName_CreateFromURI(lci); CCNxInterest *interest = ccnxInterest_CreateSimple(testName); ccnxName_Release(&testName); //athena_EncodeMessage(interest); CCNxContentObject *testObject = athenaContentStore_GetMatch(store, interest); ccnxInterest_Release(&interest); // TODO: match on other than name! assertTrue(ccnxContentObject_Equals(truthObject, testObject), "Expected to get the same ContentObject back"); assertTrue(truthObject == testObject, "Expected the same pointer back"); athenaContentStore_Release(&store); ccnxContentObject_Release(&truthObject); parcClock_Release(&clock); }
LONGBOW_TEST_CASE(Global, removeMatch) { AthenaLRUContentStoreConfig config; config.capacityInMB = 10; AthenaContentStore *store = athenaContentStore_Create(&AthenaContentStore_LRUImplementation, &config); PARCClock *clock = parcClock_Wallclock(); char *lci = "lci:/cakes/and/pies"; CCNxName *origName = ccnxName_CreateFromURI(lci); CCNxContentObject *contentObject = ccnxContentObject_CreateWithDataPayload(origName, NULL); ccnxContentObject_SetExpiryTime(contentObject, parcClock_GetTime(clock) + 100); ccnxName_Release(&origName); athenaContentStore_PutContentObject(store, contentObject); ccnxContentObject_Release(&contentObject); CCNxName *testName = ccnxName_CreateFromURI(lci); bool status = athenaContentStore_RemoveMatch(store, testName, NULL, NULL); // TODO: match on other than name! assertTrue(status, "Expected to remove the contentobject we had"); ccnxName_Release(&testName); parcClock_Release(&clock); athenaContentStore_Release(&store); }
LONGBOW_TEST_CASE(Local, putWithExpiryTime) { AthenaLRUContentStore *impl = _createLRUContentStore(); CCNxName *name1 = ccnxName_CreateFromURI("lci:/first/entry"); CCNxContentObject *contentObject1 = ccnxContentObject_CreateWithDataPayload(name1, NULL); CCNxName *name2 = ccnxName_CreateFromURI("lci:/second/entry"); CCNxContentObject *contentObject2 = ccnxContentObject_CreateWithDataPayload(name2, NULL); CCNxName *name3 = ccnxName_CreateFromURI("lci:/third/entry"); CCNxContentObject *contentObject3 = ccnxContentObject_CreateWithDataPayload(name3, NULL); uint64_t now = parcClock_GetTime(impl->wallClock); ccnxContentObject_SetExpiryTime(contentObject1, now + 200); // Expires AFTER object 2 ccnxContentObject_SetExpiryTime(contentObject2, now + 100); // contentObject3 has no expiry time, so it expires last. _AthenaLRUContentStoreEntry *entry1 = _athenaLRUContentStoreEntry_Create(contentObject1); _AthenaLRUContentStoreEntry *entry2 = _athenaLRUContentStoreEntry_Create(contentObject2); _AthenaLRUContentStoreEntry *entry3 = _athenaLRUContentStoreEntry_Create(contentObject3); bool status = _athenaLRUContentStore_PutContentObject(impl, contentObject1); assertTrue(status, "Exepected to insert content"); status = _athenaLRUContentStore_PutContentObject(impl, contentObject2); assertTrue(status, "Exepected to insert content"); _AthenaLRUContentStoreEntry *oldestEntry = _getEarliestExpiryTime(impl); assertTrue(oldestEntry->contentObject == entry2->contentObject, "Expected entry 2 to be the earliest expiring entry"); status = _athenaLRUContentStore_PutContentObject(impl, contentObject3); assertTrue(status, "Exepected to insert content"); // The entry with no expiration time should not affect list ordering. oldestEntry = _getEarliestExpiryTime(impl); assertTrue(oldestEntry->contentObject == entry2->contentObject, "Expected entry 2 to be the earliest expiring entry"); // Now remove the oldest one we added. The next oldest one should be contentObject1 _athenaLRUContentStore_RemoveMatch(impl, name2, NULL, NULL); // The entry with no expiration time should not affect list ordering. oldestEntry = _getEarliestExpiryTime(impl); assertTrue(oldestEntry->contentObject == entry1->contentObject, "Expected entry 1 to be the earliest expiring entry"); _athenaLRUContentStoreEntry_Release(&entry1); _athenaLRUContentStoreEntry_Release(&entry2); _athenaLRUContentStoreEntry_Release(&entry3); ccnxContentObject_Release(&contentObject1); ccnxContentObject_Release(&contentObject2); ccnxContentObject_Release(&contentObject3); ccnxName_Release(&name1); ccnxName_Release(&name2); ccnxName_Release(&name3); _athenaLRUContentStore_Release((AthenaContentStoreImplementation *) &impl); }
static CCNxContentObject * //_athenaLRUContentStore_GetMatch(AthenaContentStoreImplementation *store, const CCNxName *name, const PARCBuffer *keyIdRestriction, // const PARCBuffer *contentObjectHash) _athenaLRUContentStore_GetMatch(AthenaContentStoreImplementation *store, const CCNxInterest *interest) { CCNxContentObject *result = NULL; AthenaLRUContentStore *impl = (AthenaLRUContentStore *) store; _AthenaLRUContentStoreEntry *entry = NULL; CCNxName *name = ccnxInterest_GetName(interest); PARCBuffer *contentObjectHashRestriction = ccnxInterest_GetContentObjectHashRestriction(interest); PARCBuffer *keyIdRestriction = ccnxInterest_GetKeyIdRestriction(interest); if (contentObjectHashRestriction != NULL) { PARCObject *nameAndHashKey = _createHashableKey(name, NULL, contentObjectHashRestriction); entry = (_AthenaLRUContentStoreEntry *) parcHashMap_Get(impl->tableByNameAndObjectHash, nameAndHashKey); parcObject_Release((PARCObject **) &nameAndHashKey); } if ((entry == NULL) && (keyIdRestriction != NULL)) { PARCObject *nameAndKeyIdKey = _createHashableKey(name, keyIdRestriction, NULL); entry = (_AthenaLRUContentStoreEntry *) parcHashMap_Get(impl->tableByNameAndKeyId, nameAndKeyIdKey); parcObject_Release((PARCObject **) &nameAndKeyIdKey); } if (entry == NULL) { PARCObject *nameKey = _createHashableKey(name, NULL, NULL); entry = (_AthenaLRUContentStoreEntry *) parcHashMap_Get(impl->tableByName, nameKey); parcObject_Release((PARCObject **) &nameKey); } // Matching is done. Now check for validity, if necessary. if (entry != NULL) { // We found matching content. Now make sure it's not expired before returning it. If it is expired, // remove it from the store and don't return anything. if (entry->hasExpiryTime && (entry->expiryTime < parcClock_GetTime(impl->wallClock))) { _athenaLRUContentStore_PurgeContentStoreEntry(impl, entry); entry = NULL; } // XXX: TODO: Check that the KeyId, if any, was verified. } // At this point, the cached content is considered valid for responding with. Return it. if (entry != NULL) { result = entry->contentObject; // Update LRU so that the matched entry is at the top of the list. _moveContentStoreEntryToLRUHead(impl, entry); impl->stats.numMatchHits++; } else { impl->stats.numMatchMisses++; } return result; }
static bool _makeRoomInStore(AthenaLRUContentStore *impl, size_t sizeNeeded) { bool result = false; if (sizeNeeded > impl->maxSizeInBytes) { return false; // not possible. } uint64_t nowInMillis = parcClock_GetTime(impl->wallClock); // Evict expired items until we have enough room, or don't have any expired items. while (sizeNeeded > (impl->maxSizeInBytes - impl->currentSizeInBytes)) { _AthenaLRUContentStoreEntry *entry = _getEarliestExpiryTime(impl); if (entry == NULL) { break; } if (nowInMillis > entry->expiryTime) { _athenaLRUContentStore_PurgeContentStoreEntry(impl, entry); } else { break; } } // Evict items past their recommended cache time until we have enough room, or don't have any items. while (sizeNeeded > (impl->maxSizeInBytes - impl->currentSizeInBytes)) { _AthenaLRUContentStoreEntry *entry = _getEarliestRecommendedCacheTime(impl); if (entry == NULL) { break; } if (nowInMillis > entry->recommendedCacheTime) { _athenaLRUContentStore_PurgeContentStoreEntry(impl, entry); } else { break; } } while (sizeNeeded > (impl->maxSizeInBytes - impl->currentSizeInBytes)) { _AthenaLRUContentStoreEntry *entry = _getLeastUsedFromLRU(impl); if (entry == NULL) { break; } _athenaLRUContentStore_PurgeContentStoreEntry(impl, entry); } if (impl->maxSizeInBytes - impl->currentSizeInBytes > sizeNeeded) { return true; } return result; }
LONGBOW_TEST_CASE(Local, putWithExpiryTime_Expired) { AthenaLRUContentStore *impl = _createLRUContentStore(); CCNxName *name1 = ccnxName_CreateFromURI("lci:/first/entry"); CCNxContentObject *contentObject1 = ccnxContentObject_CreateWithDataPayload(name1, NULL); CCNxName *name2 = ccnxName_CreateFromURI("lci:/second/entry"); CCNxContentObject *contentObject2 = ccnxContentObject_CreateWithDataPayload(name2, NULL); CCNxName *name3 = ccnxName_CreateFromURI("lci:/third/entry"); CCNxContentObject *contentObject3 = ccnxContentObject_CreateWithDataPayload(name3, NULL); uint64_t now = parcClock_GetTime(impl->wallClock); // NOTE: These two are considered expired and should NOT be added to the store. ccnxContentObject_SetExpiryTime(contentObject1, now); _AthenaLRUContentStoreEntry *entry1 = _athenaLRUContentStoreEntry_Create(contentObject1); ccnxContentObject_SetExpiryTime(contentObject2, now - 100); _AthenaLRUContentStoreEntry *entry2 = _athenaLRUContentStoreEntry_Create(contentObject2); // NOTE: This one does not have an expiry time, so should be added. _AthenaLRUContentStoreEntry *entry3 = _athenaLRUContentStoreEntry_Create(contentObject3); bool status = _athenaLRUContentStore_PutContentObject(impl, contentObject1); assertFalse(status, "Exepected to fail on inserting expired content"); status = _athenaLRUContentStore_PutContentObject(impl, contentObject2); assertFalse(status, "Exepected to fail on inserting expired content"); status = _athenaLRUContentStore_PutContentObject(impl, contentObject3); assertTrue(status, "Exepected to insert a ContentObject with no expiry time."); _athenaLRUContentStoreEntry_Release(&entry1); _athenaLRUContentStoreEntry_Release(&entry2); _athenaLRUContentStoreEntry_Release(&entry3); ccnxContentObject_Release(&contentObject1); ccnxContentObject_Release(&contentObject2); ccnxContentObject_Release(&contentObject3); ccnxName_Release(&name1); ccnxName_Release(&name2); ccnxName_Release(&name3); _athenaLRUContentStore_Release((AthenaContentStoreImplementation *) &impl); }
static CCNxMetaMessage * _athenaLRUContentStore_ProcessMessage(AthenaContentStoreImplementation *store, const CCNxMetaMessage *message) { CCNxMetaMessage *result = NULL; AthenaLRUContentStore *impl = (AthenaLRUContentStore *) store; if (ccnxMetaMessage_IsInterest(message)) { CCNxInterest *interest = ccnxMetaMessage_GetInterest(message); CCNxName *queryName = ccnxInterest_GetName(interest); uint64_t chunkNumber = 0; bool hasChunkNumber = false; _getChunkNumberFromName(queryName, &chunkNumber, &hasChunkNumber); assertFalse(hasChunkNumber, "LRUContentStore queries don't yet support more than 1 chunk."); PARCBuffer *responsePayload = NULL; // Find the arguments to our query. size_t argSegmentIndex = 0; if (_getSegmentIndexOfQueryArgs(queryName, AthenaModule_ContentStore, &argSegmentIndex)) { CCNxNameSegment *queryTypeSegment = ccnxName_GetSegment(queryName, argSegmentIndex); char *queryTypeString = ccnxNameSegment_ToString(queryTypeSegment); // e.g. "stat" char *statString = "stat"; if (strncasecmp(queryTypeString, statString, strlen(statString)) == 0) { responsePayload = _processStatQuery(impl, queryName, argSegmentIndex + 1, chunkNumber); } parcMemory_Deallocate(&queryTypeString); } if (responsePayload != NULL) { CCNxContentObject *contentObjectResponse = ccnxContentObject_CreateWithNameAndPayload( ccnxInterest_GetName(interest), responsePayload); result = ccnxMetaMessage_CreateFromContentObject(contentObjectResponse); ccnxContentObject_SetExpiryTime(contentObjectResponse, parcClock_GetTime(impl->wallClock) + 100); // this response is good for 100 millis ccnxContentObject_Release(&contentObjectResponse); parcBuffer_Release(&responsePayload); } } return result; // could be NULL }
/** * Create a PARCBuffer payload containing a JSON string with information about this ContentStore's * size. */ static PARCBuffer * _createStatSizeResponsePayload(const AthenaLRUContentStore *impl, const CCNxName *name, uint64_t chunkNumber) { PARCJSON *json = parcJSON_Create(); parcJSON_AddString(json, "moduleName", AthenaContentStore_LRUImplementation.description); parcJSON_AddInteger(json, "time", parcClock_GetTime(impl->wallClock)); parcJSON_AddInteger(json, "numEntries", impl->numEntries); parcJSON_AddInteger(json, "sizeInBytes", impl->currentSizeInBytes); char *jsonString = parcJSON_ToString(json); parcJSON_Release(&json); PARCBuffer *result = parcBuffer_CreateFromArray(jsonString, strlen(jsonString)); parcMemory_Deallocate(&jsonString); return parcBuffer_Flip(result); }
LONGBOW_TEST_CASE(Local, putContentAndExpireByExpiryTime) { AthenaLRUContentStoreConfig config; config.capacityInMB = 1; // 2M AthenaLRUContentStore *impl = _athenaLRUContentStore_Create(&config); uint64_t now = parcClock_GetTime(impl->wallClock); PARCBuffer *payload = parcBuffer_Allocate(300 * 1000); // 300K payload. Should fit 3 into the store. CCNxName *name1 = ccnxName_CreateFromURI("lci:/object/1"); CCNxName *name2 = ccnxName_CreateFromURI("lci:/object/2"); CCNxName *name3 = ccnxName_CreateFromURI("lci:/object/3"); CCNxName *name4 = ccnxName_CreateFromURI("lci:/object/4"); CCNxContentObject *contentObject1 = ccnxContentObject_CreateWithDataPayload(name1, payload); _AthenaLRUContentStoreEntry *entry = _athenaLRUContentStoreEntry_Create(contentObject1); entry->hasExpiryTime = true; entry->expiryTime = now + 2000000; bool status = _athenaLRUContentStore_PutLRUContentStoreEntry(impl, entry); _athenaLRUContentStoreEntry_Release(&entry); assertTrue(status, "Expected to put the content in the store"); CCNxContentObject *contentObject2 = ccnxContentObject_CreateWithDataPayload(name2, payload); entry = _athenaLRUContentStoreEntry_Create(contentObject2); entry->expiryTime = now - 10000; // This one expires first. (it's already expired) entry->hasExpiryTime = true; status = _athenaLRUContentStore_PutLRUContentStoreEntry(impl, entry); _athenaLRUContentStoreEntry_Release(&entry); assertTrue(status, "Expected to put the content in the store"); CCNxContentObject *contentObject3 = ccnxContentObject_CreateWithDataPayload(name3, payload); entry = _athenaLRUContentStoreEntry_Create(contentObject3); entry->expiryTime = now + 3000000; entry->hasExpiryTime = true; status = _athenaLRUContentStore_PutLRUContentStoreEntry(impl, entry); _athenaLRUContentStoreEntry_Release(&entry); assertTrue(status, "Expected to put the content in the store"); // At this point, there are three items in the store. Try to put in a 4th, which should force the one // with the earliest expiration time to be expired. CCNxContentObject *contentObject4 = ccnxContentObject_CreateWithDataPayload(name4, payload); entry = _athenaLRUContentStoreEntry_Create(contentObject4); entry->expiryTime = now + 3000000; entry->hasExpiryTime = true; status = _athenaLRUContentStore_PutLRUContentStoreEntry(impl, entry); _athenaLRUContentStoreEntry_Release(&entry); assertTrue(status, "Expected to put the content in the store"); assertTrue(impl->currentSizeInBytes < impl->maxSizeInBytes, "Expected the current store size to be less than the capacity"); athenaLRUContentStore_Display(impl, 0); // Now check that the oldest was removed. CCNxInterest *interest = ccnxInterest_CreateSimple(name2); CCNxContentObject *match = _athenaLRUContentStore_GetMatch(impl, interest); assertNull(match, "Expected the content for name2 to have been removed from the store"); ccnxInterest_Release(&interest); // Now check that the others still exist. interest = ccnxInterest_CreateSimple(name1); match = _athenaLRUContentStore_GetMatch(impl, interest); assertNotNull(match, "Expected the content for name1 to be in the store"); ccnxInterest_Release(&interest); interest = ccnxInterest_CreateSimple(name3); match = _athenaLRUContentStore_GetMatch(impl, interest); assertNotNull(match, "Expected the content for name3 to be in the store"); ccnxInterest_Release(&interest); interest = ccnxInterest_CreateSimple(name4); match = _athenaLRUContentStore_GetMatch(impl, interest); assertNotNull(match, "Expected the content for name4 to be in the store"); ccnxInterest_Release(&interest); ccnxContentObject_Release(&contentObject1); ccnxContentObject_Release(&contentObject2); ccnxContentObject_Release(&contentObject3); ccnxContentObject_Release(&contentObject4); ccnxName_Release(&name1); ccnxName_Release(&name2); ccnxName_Release(&name3); ccnxName_Release(&name4); parcBuffer_Release(&payload); _athenaLRUContentStore_Release((AthenaContentStoreImplementation *) &impl); }