CMSDK 2.0.1
Cross-platform C++ base library and SDK for the Psyclone AIOS platform
Loading...
Searching...
No Matches
MessageIndex.cpp
Go to the documentation of this file.
1
6#include "MessageIndex.h"
7#include "UnitTestFramework.h"
8
9namespace cmlabs {
10
12 lastMaintenance = GetTimeNow();
13 buffertime = 10000;
14 maintenanceInterval = 10000;
15 defaultTTL = PSYMINUTE;
16 typeMap = NULL;
17 timeMap = NULL;
18}
19
21 timeMaps.clear();
22 stringMaps.clear();
23 integerMaps.clear();
24 floatMaps.clear();
25
26 if (typeMap)
27 delete(typeMap);
28 typeMap = NULL;
29
30 if (timeMap)
31 delete(timeMap);
32 timeMap = NULL;
33
34 std::multimap<uint64, DataMessage*>::iterator i = eolMap.begin(), e = eolMap.end();
35 while (i != e) {
36 delete(i->second);
37 i++;
38 }
39 eolMap.clear();
40}
41
42bool MessageIndex::setDefaultTTL(uint64 defaultTTL) {
43 this->defaultTTL = defaultTTL;
44 return true;
45}
46
47bool MessageIndex::addIndexKey(const char* key, uint8 type) {
48 switch(type) {
49 case INDEX_TIME:
50 if ((stricmp(key, "time") == 0) && !timeMap) {
51 timeMap = new std::multimap<uint64, DataMessage*>;
52 return true;
53 }
54 else {
55 timeMaps[key];
56 return true;
57 }
58 case INDEX_STRING:
59 stringMaps[key];
60 return true;
61 case INDEX_INTEGER:
62 integerMaps[key];
63 return true;
64 case INDEX_FLOAT:
65 floatMaps[key];
66 return true;
67 default:
68 return false;
69 }
70}
71
72bool MessageIndex::removeIndexKey(const char* key, uint8 type) {
73 switch(type) {
74 case INDEX_TIME:
75 if ((stricmp(key, "time") == 0) && timeMap) {
76 delete(timeMap);
77 timeMap = NULL;
78 return true;
79 }
80 else
81 return deleteIndexKey<uint64>(key, timeMaps);
82 case INDEX_STRING:
83 return deleteIndexKey<std::string>(key, stringMaps);
84 case INDEX_INTEGER:
85 return deleteIndexKey<int64>(key, integerMaps);
86 case INDEX_FLOAT:
87 return deleteIndexKey<float64>(key, floatMaps);
88 default:
89 return false;
90 }
91}
92
93
94bool MessageIndex::setFileStorage(const char* dir, uint32 buffertime, const char* indexfilename) {
95 this->dir = dir;
96 this->indexfilename = indexfilename;
97 this->buffertime = buffertime;
98 return true;
99}
100
102 return (uint32)eolMap.size();
103}
104
106 if (GetTimeAgeMS(lastMaintenance) > maintenanceInterval)
108
109 // Add to each key in each type map
110 addMessageToTime(msg);
111 addMessageToString(msg);
112 addMessageToInteger(msg);
113 addMessageToFloat(msg);
114
115 if (timeMap)
116 timeMap->insert(Time_Pair(msg->getCreatedTime(), msg));
117 if (typeMap)
118 typeMap->insert(Type_Pair(msg->getType(), msg));
119
120 // Add to EOL map
121 // if msg->ttl is not set we need to use the default timeout to determine when to delete it
122 if (!msg->getTTL())
123 eolMap.insert(Time_Pair(msg->getCreatedTime() + defaultTTL, msg));
124 else
125 eolMap.insert(Time_Pair(msg->getEOL(), msg));
126 return true;
127}
128
129char* MessageIndex::queryMessages(RetrieveSpec* spec, uint32 &size, uint32 &count) {
130 if (GetTimeAgeMS(lastMaintenance) > maintenanceInterval)
132
133 size = count = 0;
134 if (!spec)
135 return NULL;
136
137 std::map<std::string, std::multimap<uint64, DataMessage*> >::iterator it;
138 std::map<std::string, std::multimap<std::string, DataMessage*> >::iterator is;
139 std::map<std::string, std::multimap<int64, DataMessage*> >::iterator ii;
140 std::map<std::string, std::multimap<float64, DataMessage*> >::iterator ifl;
141
142// printf("Querying for key '%s'...\n", spec->key);
143
144 switch(spec->keytype) {
145 case INDEX_TIME:
146 if ((stricmp(spec->key, "time") == 0) && timeMap) {
147 if (spec->startTime && spec->endTime)
148 return queryMessages<uint64>(spec->startTime, spec->endTime, spec->from, spec->to, spec->type, spec->maxcount, spec->maxage, size, count, *timeMap);
149 else if (spec->startTime)
150 return queryMessages<uint64>(spec->startTime, spec->from, spec->to, spec->type, spec->maxcount, spec->maxage, size, count, *timeMap);
151 else
152 return queryMessages<uint64>(spec->from, spec->to, spec->type, spec->maxcount, spec->maxage, size, count, *timeMap);
153 }
154 else {
155 it = timeMaps.find(spec->key);
156 if (it != timeMaps.end()) {
157 if (spec->startTime && spec->endTime)
158 return queryMessages<uint64>(spec->startTime, spec->endTime, spec->from, spec->to, spec->type, spec->maxcount, spec->maxage, size, count, it->second);
159 else if (spec->startTime)
160 return queryMessages<uint64>(spec->startTime, spec->from, spec->to, spec->type, spec->maxcount, spec->maxage, size, count, it->second);
161 else
162 return queryMessages<uint64>(spec->from, spec->to, spec->type, spec->maxcount, spec->maxage, size, count, it->second);
163 }
164 else
165 return NULL;
166 }
167 case INDEX_STRING:
168 is = stringMaps.find(spec->key);
169 if (is != stringMaps.end()) {
170 std::string start = spec->startString;
171 std::string end = spec->endString;
172 if (start.size() && end.size())
173 return queryMessages<std::string>(start, end, spec->from, spec->to, spec->type, spec->maxcount, spec->maxage, size, count, is->second);
174 else if (spec->startTime)
175 return queryMessages<std::string>(start, spec->from, spec->to, spec->type, spec->maxcount, spec->maxage, size, count, is->second);
176 else
177 return queryMessages<std::string>(spec->from, spec->to, spec->type, spec->maxcount, spec->maxage, size, count, is->second);
178 }
179 else
180 return NULL;
181 case INDEX_INTEGER:
182 ii = integerMaps.find(spec->key);
183 if (ii != integerMaps.end()) {
184 // printf("Querying for key '%s' (of %u)...\n", spec->key, ii->second.size());
185 if ((spec->startInt != INT64_NOVALUE) && (spec->endInt != INT64_NOVALUE))
186 return queryMessages<int64>(spec->startInt, spec->endInt, spec->from, spec->to, spec->type, spec->maxcount, spec->maxage, size, count, ii->second);
187 else if (spec->startInt != INT64_NOVALUE)
188 return queryMessages<int64>(spec->startInt, spec->from, spec->to, spec->type, spec->maxcount, spec->maxage, size, count, ii->second);
189 else
190 return queryMessages<int64>(spec->from, spec->to, spec->type, spec->maxcount, spec->maxage, size, count, ii->second);
191 }
192 else
193 return NULL;
194 case INDEX_FLOAT:
195 ifl = floatMaps.find(spec->key);
196 if (ifl != floatMaps.end()) {
197 if ((spec->startFloat != FLOAT64_NOVALUE) && (spec->endFloat != FLOAT64_NOVALUE))
198 return queryMessages<float64>(spec->startFloat, spec->endFloat, spec->from, spec->to, spec->type, spec->maxcount, spec->maxage, size, count, ifl->second);
199 else if (spec->startFloat != FLOAT64_NOVALUE)
200 return queryMessages<float64>(spec->startFloat, spec->from, spec->to, spec->type, spec->maxcount, spec->maxage, size, count, ifl->second);
201 else
202 return queryMessages<float64>(spec->from, spec->to, spec->type, spec->maxcount, spec->maxage, size, count, ifl->second);
203 }
204 else
205 return NULL;
206 default:
207 return NULL;
208 }
209}
210
211
212
213
214
215
216
217
218
220 uint64 now = lastMaintenance = GetTimeNow();
221
222 std::map<std::string, std::multimap<uint64, DataMessage*> >::iterator it, et = timeMaps.end();
223 std::map<std::string, std::multimap<std::string, DataMessage*> >::iterator is, es = stringMaps.end();
224 std::map<std::string, std::multimap<int64, DataMessage*> >::iterator ii, ei = integerMaps.end();
225 std::map<std::string, std::multimap<float64, DataMessage*> >::iterator ifl, efl = floatMaps.end();
226
227 std::multimap<uint64, DataMessage*>::iterator i = eolMap.begin(), e = eolMap.end();
228 while (i != e) {
229 if (i->first < now) {
230 // remove message from all other maps
231 if (timeMap) {
232 uint64 ctval = i->second->getCreatedTime();
233 if (ctval)
234 removeMessage<uint64>(i->second, *timeMap, ctval);
235 }
236
237 it = timeMaps.begin();
238 while (it != et) {
239 uint64 tval = i->second->getTime(it->first.c_str());
240 if (tval)
241 removeMessage<uint64>(i->second, it->second, tval);
242 it++;
243 }
244
245 is = stringMaps.begin();
246 while (is != es) {
247 std::string sval = i->second->getString(is->first.c_str());
248 removeMessage<std::string>(i->second, is->second, sval);
249 is++;
250 }
251
252 ii = integerMaps.begin();
253 while (ii != ei) {
254 int64 ival;
255 if (i->second->getInt(ii->first.c_str(), ival))
256 removeMessage<int64>(i->second, ii->second, ival);
257 ii++;
258 }
259
260 ifl = floatMaps.begin();
261 while (ifl != efl) {
262 float64 fval;
263 if (i->second->getDouble(ifl->first.c_str(), fval))
264 removeMessage<float64>(i->second, ifl->second, fval);
265 ifl++;
266 }
267 // delete message itself
268 delete(i->second);
269 // remove entry
270 eolMap.erase(i++);
271 }
272 else
273 break;
274 }
275
276 return true;
277}
278
279
280
281
282
283
284
285
286
287
288
289bool MessageIndex::addMessageToTime(DataMessage* msg) {
290 uint64 val;
291 std::map<std::string, std::multimap<uint64, DataMessage*> >::iterator i = timeMaps.begin(), e = timeMaps.end();
292 while (i != e) {
293 if (val = msg->getTime(i->first.c_str()))
294 i->second.insert(Time_Pair(val, msg));
295 i++;
296 }
297 return true;
298}
299
300bool MessageIndex::addMessageToString(DataMessage* msg) {
301 const char* val;
302 std::map<std::string, std::multimap<std::string, DataMessage*> >::iterator i = stringMaps.begin(), e = stringMaps.end();
303 while (i != e) {
304 if (val = msg->getString(i->first.c_str()))
305 i->second.insert(String_Pair(val, msg));
306 i++;
307 }
308 return true;
309}
310
311bool MessageIndex::addMessageToInteger(DataMessage* msg) {
312 int64 val;
313 std::map<std::string, std::multimap<int64, DataMessage*> >::iterator i = integerMaps.begin(), e = integerMaps.end();
314 while (i != e) {
315 if (msg->getInt(i->first.c_str(), val)) {
316 i->second.insert(Int_Pair(val, msg));
317 // printf("------------------- WB Added %lld to key '%s' %s\n", val, i->first.c_str(), PrintTimeString(msg->getCreatedTime()).c_str());
318 }
319 i++;
320 }
321 return true;
322}
323
324bool MessageIndex::addMessageToFloat(DataMessage* msg) {
325 float64 val;
326 std::map<std::string, std::multimap<float64, DataMessage*> >::iterator i = floatMaps.begin(), e = floatMaps.end();
327 while (i != e) {
328 if (msg->getDouble(i->first.c_str(), val))
329 i->second.insert(Float_Pair(val, msg));
330 i++;
331 }
332 return true;
333}
334
335
336
337
338template <typename T>
339bool MessageIndex::deleteIndexKey(const char* key, std::map<std::string, std::multimap<T, DataMessage*> > &maps) {
340
341 std::multimap<T, DataMessage*>* map = &(maps[key]);
342
343 typename std::multimap<T, DataMessage*>::iterator i = map->begin(), e = map->end();
344 while (i != e) {
345 delete(i->second);
346 i++;
347 }
348 maps.erase(key);
349 return true;
350}
351
352template <typename T>
353bool MessageIndex::removeMessage(DataMessage* msg, std::multimap<T, DataMessage*> &map, T &val) {
354 typename std::multimap<T, DataMessage*>::iterator i, e = map.end();
355 i = map.lower_bound(val);
356 while ((i != e) && (i->first == val)) {
357 if (((void*)msg) == ((void*)i->second))
358 map.erase(i++);
359 else
360 i++;
361 }
362 return true;
363}
364
365template <typename T>
366char* MessageIndex::queryMessages(T start, T end, uint32 from, uint32 to, PsyType &type, uint32 maxcount, uint64 maxage, uint32 &size, uint32 &count, std::multimap<T, DataMessage*> &map) {
367 count = size = 0;
368 typename std::multimap<T, DataMessage*>::iterator i1, i2, e = map.end();
369
370 uint64 now = GetTimeNow();
371 uint64 createdAfter = 0;
372 if (maxage)
373 createdAfter = now - maxage;
374
375 if ( (i1 = map.lower_bound(start)) == e) {
376 // printf("queryMessages found (%u) results (of %u)...\n", count, map.size());
377 return NULL;
378 }
379 i2 = map.upper_bound(end);
380
381 uint32 allocSize = 4096;
382 char* result = (char*) malloc(allocSize);
383
384 DataMessage* msg;
385 uint32 msgSize;
386 while ((i1 != i2) && (i1 != e)) {
387 if (msg = i1->second) {
388 // Filters...
389 if (msg->getEOL() < now) {
390 i1++;
391 continue;
392 }
393 if (from && (from != msg->getFrom())) {
394 i1++;
395 continue;
396 }
397 if (to && (to != msg->getTo())) {
398 i1++;
399 continue;
400 }
401 if ((type != NOTYPE) && (!msg->getType().matches(type))) {
402 i1++;
403 continue;
404 }
405 if (maxcount && (count >= maxcount))
406 break;
407 if (createdAfter && (msg->getCreatedTime() < createdAfter)) {
408 i1++;
409 continue;
410 }
411
412 msgSize = i1->second->getSize();
413 while (size + msgSize > allocSize) {
414 allocSize *= 2;
415 void* newmem = realloc(result, allocSize);
416 if (!newmem) {
417 free(result);
418 count = size = 0;
419 return NULL;
420 }
421 result = (char*) newmem;
422 }
423 memcpy(result+size, msg->data, msgSize);
424 size += msgSize;
425 count++;
426 }
427 i1++;
428 }
429 // printf("queryMessages found %u results (of %u)...\n", count, map.size());
430 return result;
431}
432
433template <typename T>
434char* MessageIndex::queryMessages(T start, uint32 from, uint32 to, PsyType &type, uint32 maxcount, uint64 maxage, uint32 &size, uint32 &count, std::multimap<T, DataMessage*> &map) {
435 count = size = 0;
436 typename std::multimap<T, DataMessage*>::iterator i1, i2, e = map.end();
437
438 uint64 now = GetTimeNow();
439 uint64 createdAfter = 0;
440 if (maxage)
441 createdAfter = now - maxage;
442
443 if ( (i1 = map.lower_bound(start)) == e)
444 return NULL;
445
446 uint32 allocSize = 4096;
447 char* result = (char*) malloc(allocSize);
448
449 DataMessage* msg;
450 uint32 msgSize;
451 while (i1 != e) {
452 if (msg = i1->second) {
453 // Filters...
454 if (msg->getEOL() < now) {
455 i1++;
456 continue;
457 }
458 if (from && (from != msg->getFrom())) {
459 i1++;
460 continue;
461 }
462 if (to && (to != msg->getTo())) {
463 i1++;
464 continue;
465 }
466 if ((type != NOTYPE) && (!msg->getType().matches(type))) {
467 i1++;
468 continue;
469 }
470 if (maxcount && (count >= maxcount))
471 break;
472 if (createdAfter && (msg->getCreatedTime() < createdAfter)) {
473 i1++;
474 continue;
475 }
476
477 msgSize = i1->second->getSize();
478 while (size + msgSize > allocSize) {
479 allocSize *= 2;
480 void* newmem = realloc(result, allocSize);
481 if (!newmem) {
482 free(result);
483 count = size = 0;
484 return NULL;
485 }
486 result = (char*) newmem;
487 }
488 memcpy(result+size, msg->data, msgSize);
489 size += msgSize;
490 count++;
491 }
492 i1++;
493 }
494// printf("queryMessages found %u results...\n", count);
495 return result;
496}
497
498template <typename T>
499char* MessageIndex::queryMessages(uint32 from, uint32 to, PsyType &type, uint32 maxcount, uint64 maxage, uint32 &size, uint32 &count, std::multimap<T, DataMessage*> &map) {
500 count = size = 0;
501 typename std::multimap<T, DataMessage*>::reverse_iterator i1, e = map.rend();
502
503 uint64 now = GetTimeNow();
504 uint64 createdAfter = 0;
505 if (maxage)
506 createdAfter = now - maxage;
507
508 if ( (i1 = map.rbegin()) == e) {
509 // printf("queryMessages found (%u) results (of %u)...\n", count, map.size());
510 return NULL;
511 }
512
513 uint32 allocSize = 4096;
514 char* result = (char*) malloc(allocSize);
515
516 DataMessage* msg;
517 uint32 msgSize;
518 while (i1 != e) {
519 if (msg = i1->second) {
520 // Filters...
521 if (msg->getTTL() && msg->getEOL() < now) {
522 i1++;
523 continue;
524 }
525 if (from && (from != msg->getFrom())) {
526 i1++;
527 continue;
528 }
529 if (to && (to != msg->getTo())) {
530 i1++;
531 continue;
532 }
533 if ((type != NOTYPE) && (!msg->getType().matches(type))) {
534 i1++;
535 continue;
536 }
537 if (maxcount && (count >= maxcount))
538 break;
539 if (createdAfter && (msg->getCreatedTime() < createdAfter)) {
540 i1++;
541 continue;
542 }
543
544 msgSize = i1->second->getSize();
545 while (size + msgSize > allocSize) {
546 allocSize *= 2;
547 void* newmem = realloc(result, allocSize);
548 if (!newmem) {
549 free(result);
550 count = size = 0;
551 return NULL;
552 }
553 result = (char*) newmem;
554 }
555 memcpy(result+size, msg->data, msgSize);
556 size += msgSize;
557 count++;
558 }
559 i1++;
560 }
561 // printf("queryMessages found %u results (of %u)...\n", count, map.size());
562 return result;
563}
564
565
567
568 // 1. Setup: build an index and populate it with messages
569 unittest::progress(5, "populate index");
570 MessageIndex* index = new MessageIndex();
571
572 index->addIndexKey("time", INDEX_TIME);
573
574 uint32 n, size, count;
575 int64 val;
576 DataMessage* msg;
577 char* result, *data;
578 RetrieveSpec spec;
579
580 uint64 now = GetTimeNow();
581 uint64 t0 = GetTimeNow();
582 for (n=0; n<100; n++) {
583 msg = new DataMessage();
584 msg->setCreatedTime(now - 99000 + (n*1000));
585 msg->setTTL(1000000);
586 msg->setInt("Count", n);
587 index->addMessage(msg);
588 }
589 double insertUs = (double)(GetTimeNow() - t0);
590 unittest::detail("inserted 100 messages in %.0f us", insertUs);
591 if (insertUs > 0)
592 unittest::metric("insert_throughput", 100.0 / insertUs * 1e6, "msg/s", true);
593
594 // 2. Query by maxage
595 unittest::progress(35, "query by maxage");
596 memset(&spec, 0, sizeof(RetrieveSpec));
597 utils::strcpyavail(spec.key, "time", MAXKEYNAMELEN, true);
598 spec.keytype = INDEX_TIME;
599 spec.maxage = 50000;
600
601 result = index->queryMessages(&spec, size, count);
602 unittest::detail("maxage query returned %u msgs", count);
603 if ( (count < 40) || (count > 50) ) {
604 unittest::fail("MessageIndex test: maxage query didn't return around 50 msgs (got %u)", count);
605 free(result);
606 delete(index);
607 return false;
608 }
609
610 data = result;
611 for (n=0; n<count; n++) {
612 if (GetObjID(data) != DATAMESSAGEID) {
613 unittest::fail("MessageIndex test: maxage query result %u not a DataMessage", n);
614 free(result);
615 delete(index);
616 return false;
617 }
618 msg = new DataMessage(data, true);
619 if (!msg->getInt("Count", val) || (val != 99 - n)) {
620 unittest::fail("MessageIndex test: maxage query ordering mismatch at %u (expected %lld)", n, (int64)(99 - n));
621 free(result);
622 delete(index);
623 delete(msg);
624 return false;
625 }
626 data += msg->getSize();
627 delete(msg);
628 }
629 free(result);
630
631 // 3. Query by maxcount
632 unittest::progress(60, "query by maxcount");
633 memset(&spec, 0, sizeof(RetrieveSpec));
634 utils::strcpyavail(spec.key, "time", MAXKEYNAMELEN, true);
635 spec.keytype = INDEX_TIME;
636 spec.maxcount = 50;
637
638 result = index->queryMessages(&spec, size, count);
639 unittest::detail("maxcount query returned %u msgs", count);
640 if (count != 50) {
641 unittest::fail("MessageIndex test: maxcount query didn't return 50 msgs (got %u)", count);
642 free(result);
643 delete(index);
644 return false;
645 }
646
647 data = result;
648 for (n=0; n<count; n++) {
649 if (GetObjID(data) != DATAMESSAGEID) {
650 unittest::fail("MessageIndex test: maxcount query result %u not a DataMessage", n);
651 free(result);
652 delete(index);
653 return false;
654 }
655 msg = new DataMessage(data, true);
656 if (!msg->getInt("Count", val) || (val != 99 - n)) {
657 unittest::fail("MessageIndex test: maxcount query ordering mismatch at %u (expected %lld)", n, (int64)(99 - n));
658 free(result);
659 delete(index);
660 delete(msg);
661 return false;
662 }
663 data += msg->getSize();
664 delete(msg);
665 }
666 free(result);
667
668 // 4. Time advancement: older messages should age out of the maxage window
669 unittest::progress(85, "age-out window");
670 utils::Sleep(950);
671
672 memset(&spec, 0, sizeof(RetrieveSpec));
673 utils::strcpyavail(spec.key, "time", MAXKEYNAMELEN, true);
674 spec.keytype = INDEX_TIME;
675 spec.maxcount = 100;
676
677 result = index->queryMessages(&spec, size, count);
678 unittest::detail("post-sleep query returned %u msgs", count);
679 if (count > 50) {
680 unittest::fail("MessageIndex test: post-sleep query returned more than 50 msgs (got %u)", count);
681 free(result);
682 delete(index);
683 return false;
684 }
685 free(result);
686
687 delete(index);
688 unittest::progress(100, "done");
689 return true;
690}
691
692
695 "Message index time-based insert and query (maxage, maxcount, age-out)", "core");
696}
697
698
699} // namespace cmlabs
In-memory, multi-key index over DataMessages with TTL-based expiry — the storage engine behind whiteb...
#define DATAMESSAGEID
Definition ObjectIDs.h:75
#define GetObjID(data)
Extract the cid field from a binary object block: the uint32 at byte offset 4 (after the leading size...
Definition ObjectIDs.h:26
#define INDEX_FLOAT
Index the entry as a float64.
#define INDEX_INTEGER
Index the entry as an int64.
#define INDEX_TIME
Index the entry as a time (uint64 microseconds).
#define INDEX_STRING
Index the entry as a string.
static struct PsyType NOTYPE
The empty/unset message type (isValid() == false).
Definition Types.h:436
#define FLOAT64_NOVALUE
Definition Types.h:81
#define INT64_NOVALUE
Definition Types.h:80
Small, dependency-free unit test harness used by all CMSDK object tests.
#define MAXKEYNAMELEN
Definition Utils.h:85
#define stricmp
Definition Utils.h:132
The central Psyclone data container: a self-contained binary message with typed, named user entries.
bool setTTL(uint64 ttl)
setTTL(uint64 ttl)
bool getInt(const char *key, int64 &value)
getInt(const char* key, int64& value)
bool setInt(const char *key, int64 value)
setInt(const char* key, int64 value)
PsyType getType()
getType()
uint32 getSize()
getSize() Get message size Many types of data of any size can be put into a message as user entries; ...
bool setCreatedTime(uint64 t)
setCreatedTime(uint64 t)
uint64 getTime(const char *key)
getTime(const char* key)
uint64 getTTL()
getTTL()
uint64 getEOL()
getEOL()
uint64 getCreatedTime()
getCreatedTime()
bool setFileStorage(const char *dir, uint32 buffertime, const char *indexfilename="index.db")
Enable file-backed storage of indexed messages.
char * queryMessages(RetrieveSpec *spec, uint32 &size, uint32 &count)
Execute a retrieval query and return matching messages as one flat buffer.
static bool UnitTest()
Run the built-in self test.
bool addIndexKey(const char *key, uint8 type)
Start indexing messages by the value of a named user entry.
~MessageIndex()
Destroys the index and deletes all messages it still holds.
bool addMessage(DataMessage *msg)
Add a message to the index; the index TAKES OWNERSHIP of msg and will delete it on expiry.
bool doMaintenance()
Perform periodic housekeeping: evict expired messages (per TTL/EOL) and flush file storage buffers.
bool removeIndexKey(const char *key, uint8 type)
Remove a previously added index key (the index over that entry is dropped; the messages themselves re...
MessageIndex()
Create an empty index with no extra index keys and no file storage.
bool setDefaultTTL(uint64 defaultTTL)
Set the TTL applied to messages that carry none of their own.
static UnitTestRunner & instance()
Access the singleton (created on first use).
void registerTest(const char *name, UnitTestFunc func, const char *description="", const char *category="", bool inDefaultRun=true)
Register a test with the runner.
uint64 GetTimeNow()
Return the current absolute time (µs since year 0) according to the TMC.
Definition PsyTime.cpp:69
#define PSYMINUTE
Definition PsyTime.h:125
int32 GetTimeAgeMS(uint64 t)
Age of a timestamp relative to now, in milliseconds.
Definition PsyTime.cpp:35
bool Sleep(uint32 ms)
Suspend the calling thread.
Definition Utils.cpp:2802
uint32 strcpyavail(char *dst, const char *src, uint32 maxlen, bool copyAvailable)
Bounded strcpy that always NUL-terminates.
Definition Utils.cpp:6056
void fail(const char *fmt,...)
Set an explanatory reason shown on the FAIL line.
void metric(const char *name, double value, const char *unit="", bool higherIsBetter=true)
Record a performance metric.
void detail(const char *fmt,...)
Verbose-only indented diagnostic line (shown only when verbose=1).
void progress(int percent, const char *action)
Report progress with a short description of the current action.
void Register_MessageIndex_Tests()
Specification of a whiteboard retrieval attached to a TriggerSpec.
uint64 endTime
Range end when keytype == INDEX_TIME (microseconds).
char endString[MAXVALUENAMELEN+1]
Range end when keytype == INDEX_STRING.
char startString[MAXVALUENAMELEN+1]
Range start when keytype == INDEX_STRING.
uint64 startTime
Range start when keytype == INDEX_TIME (microseconds).
uint8 keytype
Interpretation of key: INDEX_TIME, INDEX_STRING, INDEX_INTEGER or INDEX_FLOAT.
int64 endInt
Range end when keytype == INDEX_INTEGER.
int64 startInt
Range start when keytype == INDEX_INTEGER.
PsyType type
Message type to match (wildcards allowed).
uint32 maxcount
Maximum number of messages to return (0 = unlimited).
uint32 from
Restrict to messages sent by this component id (0 = any).
float64 startFloat
Range start when keytype == INDEX_FLOAT.
float64 endFloat
Range end when keytype == INDEX_FLOAT.
uint64 maxage
Maximum message age in MICROSECONDS (XML gives ms; parser multiplies by 1000).
char key[MAXKEYNAMELEN+1]
Indexed user entry to range-query on (empty = time/type retrieval only).
uint32 to
Restrict to messages addressed to this component id (0 = any).