diff options
| author | chenzizhan <[email protected]> | 2024-01-05 13:58:24 +0800 |
|---|---|---|
| committer | chenzizhan <[email protected]> | 2024-01-05 13:58:24 +0800 |
| commit | 4d97e5cb6b188a06eaa1fb1d1dd4b762d77e87f2 (patch) | |
| tree | c96fea9865a0a1ce6f62c24ee1af26e6730e4d7a | |
| parent | 185e3bfd2cf0c347ecdb4922390bf718a151c37f (diff) | |
fix: TSG-18335 fieldstat_easy_reset->fseasy_output_and_reset
| -rw-r--r-- | include/fieldstat/fieldstat_easy.h | 78 | ||||
| -rw-r--r-- | src/fieldstat_easy.c | 45 | ||||
| -rw-r--r-- | test/test_easy_fs.cpp | 423 |
3 files changed, 271 insertions, 275 deletions
diff --git a/include/fieldstat/fieldstat_easy.h b/include/fieldstat/fieldstat_easy.h index 8b12780..d49470f 100644 --- a/include/fieldstat/fieldstat_easy.h +++ b/include/fieldstat/fieldstat_easy.h @@ -21,10 +21,6 @@ struct fieldstat_easy *fieldstat_easy_new(int max_thread_num, const char *name, */ void fieldstat_easy_free(struct fieldstat_easy *fse); /* - * reset the instance. Will fail and return -1 if fieldstat_easy_enable_auto_output is called, since the behavior is hard to define. -*/ -int fieldstat_easy_reset(struct fieldstat_easy *fse); -/* * enable auto output. both data of accumulated and delta will be output. * @param output_path: output file path. Should be identical to the one in python config. * @param interval_second: output interval in second. @@ -54,6 +50,12 @@ int fieldstat_easy_register_histogram(struct fieldstat_easy *fse, const char *na void fieldstat_easy_output(struct fieldstat_easy *fse, char **buff, size_t *buff_len); void fieldstat_easy_output_array(struct fieldstat_easy *fse, char ***json_objects, size_t *n_object); /* + Do fieldstat_easy_output_array and then reset the instance. + Will fail and return -1 if fieldstat_easy_enable_auto_output is called, since the behavior is hard to define. Return 0 if success. + The thread will be blocked until the output is finished. +*/ +int fieldstat_easy_output_array_and_reset(struct fieldstat_easy *fse, char ***json_objects, size_t *n_object); +/* * @brief let the value of counter metric of cell_id increase by `increment`. * @param thread_id: thread id. Must be in [0, max_thread_num). * @param metric_id: metric id, previously returned by fieldstat_register_counter. @@ -74,71 +76,3 @@ int fieldstat_easy_histogram_record(struct fieldstat_easy *fse, int thread_id, i #ifdef __cplusplus } #endif - - -/* -关于输出delta: -fs_write; -fs_accumulate; -fs_delta; - -一个在write上register的metric,同样会随merge进入accumulate,delta的metric 名字后面希望跟一个delta,但是因为它是切换过来的,而且最终要加到accumulate上,所以。。。 -delta的形成一定靠reset。 - -输出的时候,acc 和 delta的本来分别在两个json上,但是文件里只有一个json。 -最好提供一个exportPair 的接口,由json聚合。如果输出两个json,我还要读出来再写一套,更恶心了。 - -另外一个方法,干脆给histogram算差值,这样是不是更干净一点?从算法上讲,histogram没道理不能算差。 -然后就变成fs_write, fs_reading. 最终的输出是一个总量。 -这个方法总体很好,但是需要我在exporter上保存metric数据,依赖metric。光这一条就可以直接否了方案。 - -这个easy自己维护一套metric呢?查询到的histogram和counter都merge,然后改名,再merge进来。。。这个改名就特别莫名其妙,输出的东西当然要输出自己来搞。 -算了,调用两遍exporter得了,然后我把一个的加到另外一个上?这样的问题是我要用cjson来解,真的不要,太恶心了。。 - -还是搞个duo吧..? - -不行,因为我真的没办法保证acc 和 delta的cell一样,delta的cell是调用者来搞的, 太容易顺序错了。于是counter history 那套是必须的。 -不过传入delta而不是由exporter保留,这部分还是对的。如果还是想要这个history,那么exporter就还得是有状态的,不然总不能每次都拿delta重建一份。(感觉就重建一份吧) -算了,果然只能这样了。。 - -那么干脆每次都新来一个acc呢?我记录acc,合成acc,delta咋办?也不对。 -得得得得,算delta这个逃不掉了。 - -提供一个fieldstat减法怎么样?首先hll不支持减。另外我不希望动fs4的接口。 - - - ---------------------------------- plan1 --------------------------------- -改动最小,性能影响最大,所有的还都得搞两份 - -instance = new() -register_metric(i,"A"); -intance_delt -register_metric(i_delta, "A_delta"); -counter_incyby(instance, id, 1); -counter_incyby(instance_delta, id, 1); - - -dst = merge(instance); -dst_delta = merge(instance_delta); - -merge(dst, dst_delta) - - - ---------------------------------- plan2 --------------------------------- - // 最莫名其妙,而且merge 改起来容易出问题 -instance_delta = new() -incyby - -instance = merge_and_add_delta(instance_delta) // 新增接口 - - ----------------------------------- plan3 --------------------------------- -// 仅仅需要修改exporter,但是编码量比较大,选择这种。export只有一遍,而merge是每个线程一遍,还是慢export吧 -json_total = exporter_export_duo(acc, delta); // 新增接口 - - - ---------------------------------- plan4 --------------------------------- -enable-delta() -histogram_decode() -histogram_substract() -*/
\ No newline at end of file diff --git a/src/fieldstat_easy.c b/src/fieldstat_easy.c index a3fc148..8be8dbc 100644 --- a/src/fieldstat_easy.c +++ b/src/fieldstat_easy.c @@ -279,7 +279,7 @@ void fieldstat_easy_output(struct fieldstat_easy *fse, char **buff, size_t *buff *buff_len = strlen(*buff); } -// output many json string for accumulated data(many objects vs array) +// output many json string for accumulated data(many objects as elements in an array) void fieldstat_easy_output_array(struct fieldstat_easy *fse, char ***json_objects, size_t *n_object) { struct timeval timestamp = get_current_timestamp(); @@ -289,6 +289,28 @@ void fieldstat_easy_output_array(struct fieldstat_easy *fse, char ***json_object fieldstat_free(dst); } +int fieldstat_easy_output_array_and_reset(struct fieldstat_easy *fse, char ***json_objects, size_t *n_object) +{ + if (fse->output_thread_running) { + printf("fieldstat_easy_output_array_and_reset: reset is not allowed when auto output is enabled.\n"); + return -1; + } + + struct fieldstat *dst = fieldstat_new(); + for (int i = 0; i < fse->max_thread_num; i++) { + pthread_spin_lock(&fse->fsu[i].lock); + fieldstat_merge(dst, fse->fsu[i].active); + fieldstat_reset(fse->fsu[i].active); + pthread_spin_unlock(&fse->fsu[i].lock); + } + + struct timeval timestamp = get_current_timestamp(); + fieldstat_json_exporter_export_array(fse->exporter, dst, ×tamp, json_objects, n_object); + fieldstat_free(dst); + + return 0; +} + int fieldstat_easy_counter_incrby(struct fieldstat_easy *fse, int thread_id, int metric_id, const struct fieldstat_tag *tags, size_t n_tag, long long increment) { if (thread_id < 0) { @@ -320,24 +342,3 @@ int fieldstat_easy_histogram_record(struct fieldstat_easy *fse, int thread_id, i return ret; } - -int fieldstat_easy_reset(struct fieldstat_easy *fse) -{ - // We can't let the user reset operation affects auto passive output, just let it fail. - if (fse->output_thread_running) { - printf("fieldstat_easy_reset: reset is not allowed when auto output is enabled.\n"); - return -1; - } - - for (int i = 0; i < fse->max_thread_num; i++) { - pthread_spin_lock(&fse->fsu[i].lock); - fieldstat_reset(fse->fsu[i].active); - // fsu.read_only: Only for output_to_file. - pthread_spin_unlock(&fse->fsu[i].lock); - } - - // accumulate: Only has data when output_to_file. - // delta: Only for output_to_file. - - return 0; -}
\ No newline at end of file diff --git a/test/test_easy_fs.cpp b/test/test_easy_fs.cpp index d6f9177..cc7a7f7 100644 --- a/test/test_easy_fs.cpp +++ b/test/test_easy_fs.cpp @@ -2,6 +2,7 @@ #include <stub/stub.h> #include <fstream> #include <thread> +#include <regex> #include "utils.hpp" #include "cjson/cJSON.h" @@ -10,209 +11,269 @@ #define FILENAME "./test_easy_fieldstat.json" -TEST(test_easy_fieldstat, new_and_free) -{ - struct fieldstat_easy *fse = fieldstat_easy_new(10, "instance name1231231231", &TEST_TAG_STRING, 1); - fieldstat_easy_free(fse); -} - -TEST(test_easy_fieldstat, output_to_buff) -{ - struct fieldstat_easy *fse = fieldstat_easy_new(10, NULL, &TEST_TAG_STRING, 1); - fieldstat_easy_register_counter(fse, "metric counter"); - fieldstat_easy_counter_incrby(fse, 0, 0, &TEST_TAG_INT, 1, 1); - // read file, should be empty - char *buff = NULL; - size_t buff_len = 0; - fieldstat_easy_output(fse, &buff, &buff_len); - cJSON *root = cJSON_Parse(buff); - cJSON *cell = cJSON_GetArrayItem(root, 0); - cJSON *metric = cJSON_GetObjectItem(cell, "fields"); - long long value = cJSON_GetObjectItem(metric, "metric counter")->valueint; - EXPECT_EQ(value, 1); - cJSON *tags = cJSON_GetObjectItem(cell, "tags"); - EXPECT_EQ(cJSON_GetObjectItem(tags, TEST_TAG_INT.key)->valueint, TEST_TAG_INT.value_longlong); - EXPECT_STREQ(cJSON_GetObjectItem(tags, TEST_TAG_STRING.key)->valuestring, TEST_TAG_STRING.value_str); - - cJSON_Delete(root); - free(buff); - fieldstat_easy_free(fse); -} - -cJSON *read_file() -{ - std::ifstream ifs(FILENAME); - if (!ifs.is_open()) { - return NULL; - } - std::string content((std::istreambuf_iterator<char>(ifs)), (std::istreambuf_iterator<char>())); - printf("content: %s\n", content.c_str()); - cJSON *root = cJSON_Parse(content.c_str()); - ifs.close(); - return root; -} +// TEST(test_easy_fieldstat, new_and_free) +// { +// struct fieldstat_easy *fse = fieldstat_easy_new(10, "instance name1231231231", &TEST_TAG_STRING, 1); +// fieldstat_easy_free(fse); +// } + +// TEST(test_easy_fieldstat, output_to_buff) +// { +// struct fieldstat_easy *fse = fieldstat_easy_new(10, NULL, &TEST_TAG_STRING, 1); +// fieldstat_easy_register_counter(fse, "metric counter"); +// fieldstat_easy_counter_incrby(fse, 0, 0, &TEST_TAG_INT, 1, 1); +// // read file, should be empty +// char *buff = NULL; +// size_t buff_len = 0; +// fieldstat_easy_output(fse, &buff, &buff_len); +// cJSON *root = cJSON_Parse(buff); +// cJSON *cell = cJSON_GetArrayItem(root, 0); +// cJSON *metric = cJSON_GetObjectItem(cell, "fields"); +// long long value = cJSON_GetObjectItem(metric, "metric counter")->valueint; +// EXPECT_EQ(value, 1); +// cJSON *tags = cJSON_GetObjectItem(cell, "tags"); +// EXPECT_EQ(cJSON_GetObjectItem(tags, TEST_TAG_INT.key)->valueint, TEST_TAG_INT.value_longlong); +// EXPECT_STREQ(cJSON_GetObjectItem(tags, TEST_TAG_STRING.key)->valuestring, TEST_TAG_STRING.value_str); + +// cJSON_Delete(root); +// free(buff); +// fieldstat_easy_free(fse); +// } + +// cJSON *read_file() +// { +// std::ifstream ifs(FILENAME); +// if (!ifs.is_open()) { +// return NULL; +// } +// std::string content((std::istreambuf_iterator<char>(ifs)), (std::istreambuf_iterator<char>())); +// printf("content: %s\n", content.c_str()); +// cJSON *root = cJSON_Parse(content.c_str()); +// ifs.close(); +// return root; +// } + +// TEST(test_easy_fieldstat, output_to_file) +// { +// struct fieldstat_easy *fse = fieldstat_easy_new(10, NULL, NULL, 0); +// int counter_id = fieldstat_easy_register_counter(fse, "metric counter"); +// fieldstat_easy_enable_auto_output(fse, FILENAME, 1); +// sleep(3); // long enough to output +// // 1st interval: read file, should be empty +// printf("1st interval\n"); +// cJSON *root = read_file(); +// EXPECT_EQ(cJSON_GetArraySize(root), 0); // is empty and valid +// cJSON_Delete(root); + +// fieldstat_easy_counter_incrby(fse, 0, counter_id, &TEST_TAG_INT, 1, 1); +// fieldstat_easy_counter_incrby(fse, 1, counter_id, &TEST_TAG_INT, 1, 10); +// fieldstat_easy_counter_incrby(fse, 2, counter_id, &TEST_TAG_INT, 1, 100); +// sleep(2); + +// // 2nd interval: merge 3 thread's data, and output + +// printf("2nd interval\n"); +// root = read_file(); +// EXPECT_EQ(cJSON_GetArraySize(root), 1); + +// cJSON *tagged_by_int = cJSON_GetArrayItem(root, 0); +// cJSON *metric = cJSON_GetObjectItem(tagged_by_int, "fields"); +// EXPECT_TRUE(metric != NULL); + +// long long value = cJSON_GetObjectItem(metric, "metric counter")->valueint; +// EXPECT_EQ(value, 111); +// cJSON_Delete(root); + +// // 3nd interval: no new data, just output again +// sleep(1); +// printf("3rd interval\n"); +// root = read_file(); +// EXPECT_EQ(cJSON_GetArraySize(root), 1); +// tagged_by_int = cJSON_GetArrayItem(root, 0); +// metric = cJSON_GetObjectItem(tagged_by_int, "fields"); +// value = cJSON_GetObjectItem(metric, "metric counter")->valueint; +// EXPECT_EQ(value, 111); // should not change +// cJSON_Delete(root); + +// // 4th interval: new data, output again +// fieldstat_easy_counter_incrby(fse, 0, counter_id, &TEST_TAG_DOUBLE, 1, 10086); +// sleep(2); +// printf("4th interval\n"); +// root = read_file(); +// cJSON *tagged_by_double = cJSON_GetArrayItem(root, 1); +// metric = cJSON_GetObjectItem(tagged_by_double, "fields"); +// value = cJSON_GetObjectItem(metric, "metric counter")->valueint; +// EXPECT_EQ(value, 10086); +// cJSON_Delete(root); + +// fieldstat_easy_free(fse); +// remove(FILENAME); +// } + +// TEST(test_easy_fieldstat, empty_tag) +// { +// struct fieldstat_easy *fse = fieldstat_easy_new(10, "ha", NULL, 0); +// int counter_id = fieldstat_easy_register_counter(fse, "metric counter"); +// fieldstat_easy_counter_incrby(fse, 0, counter_id, NULL, 0, 1); +// fieldstat_easy_enable_auto_output(fse, FILENAME, 2); +// sleep(3); // long enough to output, but only once +// fieldstat_easy_free(fse); + +// cJSON *root = read_file(); +// EXPECT_EQ(cJSON_GetArraySize(root), 1); + +// cJSON *tagged_by_int = cJSON_GetArrayItem(root, 0); +// cJSON *metric = cJSON_GetObjectItem(tagged_by_int, "fields"); +// EXPECT_TRUE(metric != NULL); +// cJSON *metric_delta = cJSON_GetObjectItem(tagged_by_int, "fields_delta"); +// EXPECT_TRUE(metric_delta != NULL); + +// long long value = cJSON_GetObjectItem(metric, "metric counter")->valueint; +// EXPECT_EQ(value, 1); +// long long value_delta = cJSON_GetObjectItem(metric_delta, "metric counter")->valueint; +// EXPECT_EQ(value_delta, 1); + +// cJSON_Delete(root); +// remove(FILENAME); +// } + +// extern "C" { +// extern char *fs_easy_output_to_json(struct fieldstat_easy *fs, struct timeval *timestamp); +// } + +// int g_output_count = 0; +// char *fs_easy_output_to_json_stub(struct fieldstat_easy *fs, struct timeval *timestamp) +// { +// g_output_count++; +// return NULL; +// } + +// TEST(test_easy_fieldstat, output_interval_ok) +// { +// Stub stub; +// stub.set(fs_easy_output_to_json, fs_easy_output_to_json_stub); + +// struct fieldstat_easy *fse = fieldstat_easy_new(10, NULL, NULL, 0); +// fieldstat_easy_register_histogram(fse, "metric histogram", 1, 100000, 3); // a pretty time consuming metric +// fieldstat_easy_histogram_record(fse, 0, 0, &TEST_TAG_INT, 1, 1); +// fieldstat_easy_histogram_record(fse, 0, 0, &TEST_TAG_DOUBLE, 1, 10); +// fieldstat_easy_histogram_record(fse, 0, 0, &TEST_TAG_STRING, 1, 110); + +// fieldstat_easy_enable_auto_output(fse, FILENAME, 1); + +// sleep(30); // output 30s/1s = 20 times +// fieldstat_easy_free(fse); + +// EXPECT_TRUE(g_output_count == 30 || g_output_count == 29); // missing only one is ok and expected +// } + +// TEST(test_easy_fieldstat, ensure_data_racing_of_two_output_and_of_incyby) +// { +// const int N_THREADS = 3; +// struct fieldstat_tag global_tags[1]; +// struct fieldstat_tag tmptag; +// tmptag.key = "app id"; +// tmptag.type = TAG_INTEGER; +// tmptag.value_longlong = 1; +// global_tags[0] = tmptag; + +// struct fieldstat_easy *fse = fieldstat_easy_new(N_THREADS, NULL, global_tags, 1); +// int counter_id = fieldstat_easy_register_counter(fse, "incoming bytes"); +// int hdr_id = fieldstat_easy_register_histogram(fse, "delay", 1, 10000, 1); +// fieldstat_easy_enable_auto_output(fse, FILENAME, 1); + +// std::thread threads[N_THREADS]; +// for (int thread_id = 0; thread_id < N_THREADS; thread_id++) { +// threads[thread_id] = std::thread([fse, counter_id, hdr_id, thread_id]() { +// for (int i = 0; i < 1000000; i++) { // loop million times to ensure no core dump +// fieldstat_easy_counter_incrby(fse, thread_id, counter_id, &TEST_TAG_INT, 1, 1); +// fieldstat_easy_histogram_record(fse, thread_id, hdr_id, &TEST_TAG_INT, 1, rand() % 10000); +// usleep(1); // 1us * 1000000 = 1s, just long enough to output +// } +// }); +// } +// for (int i = 0; i < N_THREADS; i++) { +// threads[i].join(); +// } +// fieldstat_easy_free(fse); + +// remove(FILENAME); +// } -TEST(test_easy_fieldstat, output_to_file) +TEST(test_easy_fieldstat, reset) { - struct fieldstat_easy *fse = fieldstat_easy_new(10, NULL, NULL, 0); + struct fieldstat_easy *fse = fieldstat_easy_new(3, NULL, NULL, 0); int counter_id = fieldstat_easy_register_counter(fse, "metric counter"); - fieldstat_easy_enable_auto_output(fse, FILENAME, 1); - sleep(3); // long enough to output - // 1st interval: read file, should be empty - printf("1st interval\n"); - cJSON *root = read_file(); - EXPECT_EQ(cJSON_GetArraySize(root), 0); // is empty and valid - cJSON_Delete(root); - fieldstat_easy_counter_incrby(fse, 0, counter_id, &TEST_TAG_INT, 1, 1); - fieldstat_easy_counter_incrby(fse, 1, counter_id, &TEST_TAG_INT, 1, 10); - fieldstat_easy_counter_incrby(fse, 2, counter_id, &TEST_TAG_INT, 1, 100); - sleep(2); - - // 2nd interval: merge 3 thread's data, and output - - printf("2nd interval\n"); - root = read_file(); - EXPECT_EQ(cJSON_GetArraySize(root), 1); - - cJSON *tagged_by_int = cJSON_GetArrayItem(root, 0); - cJSON *metric = cJSON_GetObjectItem(tagged_by_int, "fields"); - EXPECT_TRUE(metric != NULL); - - long long value = cJSON_GetObjectItem(metric, "metric counter")->valueint; - EXPECT_EQ(value, 111); - cJSON_Delete(root); - - // 3nd interval: no new data, just output again - sleep(1); - printf("3rd interval\n"); - root = read_file(); - EXPECT_EQ(cJSON_GetArraySize(root), 1); - tagged_by_int = cJSON_GetArrayItem(root, 0); - metric = cJSON_GetObjectItem(tagged_by_int, "fields"); - value = cJSON_GetObjectItem(metric, "metric counter")->valueint; - EXPECT_EQ(value, 111); // should not change - cJSON_Delete(root); - - // 4th interval: new data, output again - fieldstat_easy_counter_incrby(fse, 0, counter_id, &TEST_TAG_DOUBLE, 1, 10086); - sleep(2); - printf("4th interval\n"); - root = read_file(); - cJSON *tagged_by_double = cJSON_GetArrayItem(root, 1); - metric = cJSON_GetObjectItem(tagged_by_double, "fields"); - value = cJSON_GetObjectItem(metric, "metric counter")->valueint; - EXPECT_EQ(value, 10086); - cJSON_Delete(root); - - fieldstat_easy_free(fse); - remove(FILENAME); -} - -TEST(test_easy_fieldstat, empty_tag) -{ - struct fieldstat_easy *fse = fieldstat_easy_new(10, "ha", NULL, 0); - int counter_id = fieldstat_easy_register_counter(fse, "metric counter"); - fieldstat_easy_counter_incrby(fse, 0, counter_id, NULL, 0, 1); - fieldstat_easy_enable_auto_output(fse, FILENAME, 2); - sleep(3); // long enough to output, but only once - fieldstat_easy_free(fse); - - cJSON *root = read_file(); - EXPECT_EQ(cJSON_GetArraySize(root), 1); - - cJSON *tagged_by_int = cJSON_GetArrayItem(root, 0); - cJSON *metric = cJSON_GetObjectItem(tagged_by_int, "fields"); - EXPECT_TRUE(metric != NULL); - cJSON *metric_delta = cJSON_GetObjectItem(tagged_by_int, "fields_delta"); - EXPECT_TRUE(metric_delta != NULL); - - long long value = cJSON_GetObjectItem(metric, "metric counter")->valueint; - EXPECT_EQ(value, 1); - long long value_delta = cJSON_GetObjectItem(metric_delta, "metric counter")->valueint; - EXPECT_EQ(value_delta, 1); - - cJSON_Delete(root); - remove(FILENAME); -} - -extern "C" { - extern char *fs_easy_output_to_json(struct fieldstat_easy *fs, struct timeval *timestamp); -} - -int g_output_count = 0; -char *fs_easy_output_to_json_stub(struct fieldstat_easy *fs, struct timeval *timestamp) -{ - g_output_count++; - return NULL; -} - -TEST(test_easy_fieldstat, output_interval_ok) -{ - Stub stub; - stub.set(fs_easy_output_to_json, fs_easy_output_to_json_stub); - - struct fieldstat_easy *fse = fieldstat_easy_new(10, NULL, NULL, 0); - fieldstat_easy_register_histogram(fse, "metric histogram", 1, 100000, 3); // a pretty time consuming metric - fieldstat_easy_histogram_record(fse, 0, 0, &TEST_TAG_INT, 1, 1); - fieldstat_easy_histogram_record(fse, 0, 0, &TEST_TAG_DOUBLE, 1, 10); - fieldstat_easy_histogram_record(fse, 0, 0, &TEST_TAG_STRING, 1, 110); + fieldstat_easy_counter_incrby(fse, 1, counter_id, &TEST_TAG_INT, 1, 1); + + char **objects = NULL; + size_t n_objects = 0; + fieldstat_easy_output_array_and_reset(fse, &objects, &n_objects); + EXPECT_EQ(n_objects, 1); + EXPECT_TRUE(objects != NULL); + free(objects[0]); + free(objects); - fieldstat_easy_enable_auto_output(fse, FILENAME, 1); + fieldstat_easy_output_array_and_reset(fse, &objects, &n_objects); + EXPECT_EQ(n_objects, 0); - sleep(30); // output 30s/1s = 20 times fieldstat_easy_free(fse); - - EXPECT_TRUE(g_output_count == 30 || g_output_count == 29); // missing only one is ok and expected } -TEST(test_easy_fieldstat, ensure_data_racing_of_two_output_and_of_incyby) +// issue: 调用环境为多线程 调用环境为多线程写 , 没有开启后台线程输出 一个后台线程输出 。 而是使用 没有开启fieldstat的后台线程输出。而是自己启用一个线程,使用 ’fieldstat_easy_output_array‘输出,输出周期1s一次 +// 输到数据库中的统计小于实际incrby的调用次数 +TEST(test_easy_fieldstat, accuracy_in_multithread) { - const int N_THREADS = 3; - struct fieldstat_tag global_tags[1]; - struct fieldstat_tag tmptag; - tmptag.key = "app id"; - tmptag.type = TAG_INTEGER; - tmptag.value_longlong = 1; - global_tags[0] = tmptag; - - struct fieldstat_easy *fse = fieldstat_easy_new(N_THREADS, NULL, global_tags, 1); - int counter_id = fieldstat_easy_register_counter(fse, "incoming bytes"); - int hdr_id = fieldstat_easy_register_histogram(fse, "delay", 1, 10000, 1); - fieldstat_easy_enable_auto_output(fse, FILENAME, 1); + const int N_THREADS = 10; + struct fieldstat_easy *fse = fieldstat_easy_new(N_THREADS, NULL, NULL, 0); + int counter_id = fieldstat_easy_register_counter(fse, "hit number"); std::thread threads[N_THREADS]; for (int thread_id = 0; thread_id < N_THREADS; thread_id++) { - threads[thread_id] = std::thread([fse, counter_id, hdr_id, thread_id]() { - for (int i = 0; i < 1000000; i++) { // loop million times to ensure no core dump + threads[thread_id] = std::thread([fse, counter_id, thread_id]() { + for (size_t i = 0; i < 100000ULL; i++) { // 1 million times fieldstat_easy_counter_incrby(fse, thread_id, counter_id, &TEST_TAG_INT, 1, 1); - fieldstat_easy_histogram_record(fse, thread_id, hdr_id, &TEST_TAG_INT, 1, rand() % 10000); - usleep(1); // 1us * 1000000 = 1s, just long enough to output } }); } + + char **objects = NULL; + size_t n_objects = 0; + std::regex pattern(R"("hit number":(\d+))"); + + // main thread output(every 1s) + long long total_hit_number = 0; + for (int i = 0; i < 10; i++) { + sleep(1); + + fieldstat_easy_output_array_and_reset(fse, &objects, &n_objects); + if (n_objects == 0) { + continue; + } + std::string input(objects[0]); + + std::sregex_iterator iter(input.begin(), input.end(), pattern); + std::sregex_iterator end; + if (iter != end) { + for (; iter != end; iter++) { + std::smatch match = *iter; + total_hit_number += std::stoll(match[1]); + } + } else { + printf("no match, %s\n", objects[0]); + } + + free(objects[0]); + free(objects); + } + for (int i = 0; i < N_THREADS; i++) { threads[i].join(); } - fieldstat_easy_free(fse); - remove(FILENAME); -} - -TEST(test_easy_fieldstat, reset) -{ - struct fieldstat_easy *fse = fieldstat_easy_new(3, NULL, NULL, 0); - int counter_id = fieldstat_easy_register_counter(fse, "metric counter"); - fieldstat_easy_counter_incrby(fse, 0, counter_id, &TEST_TAG_INT, 1, 1); - fieldstat_easy_counter_incrby(fse, 1, counter_id, &TEST_TAG_INT, 1, 1); - - fieldstat_easy_reset(fse); - char *buff = NULL; - size_t buff_len = 0; - fieldstat_easy_output(fse, &buff, &buff_len); - EXPECT_STREQ(buff, "[]"); + EXPECT_EQ(total_hit_number, 1000000ULL); // 1 million * 10 threads - free(buff); fieldstat_easy_free(fse); } |
