summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorchenzizhan <[email protected]>2024-01-05 13:58:24 +0800
committerchenzizhan <[email protected]>2024-01-05 13:58:24 +0800
commit4d97e5cb6b188a06eaa1fb1d1dd4b762d77e87f2 (patch)
treec96fea9865a0a1ce6f62c24ee1af26e6730e4d7a
parent185e3bfd2cf0c347ecdb4922390bf718a151c37f (diff)
fix: TSG-18335 fieldstat_easy_reset->fseasy_output_and_reset
-rw-r--r--include/fieldstat/fieldstat_easy.h78
-rw-r--r--src/fieldstat_easy.c45
-rw-r--r--test/test_easy_fs.cpp423
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, &timestamp, 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);
}