diff --git a/HISTORY b/HISTORY index c8b6c57..b611584 100644 --- a/HISTORY +++ b/HISTORY @@ -1,5 +1,5 @@ -Version 6.05 2019-12-20 +Version 6.05 2019-12-21 * fdfs_trackerd and fdfs_storaged print the server version in usage. you can execute fdfs_trackerd or fdfs_storaged without parameters to show the server version @@ -10,6 +10,7 @@ Version 6.05 2019-12-20 * support backup binlog file when truncate trunk binlog, the config item in tracker.conf: trunk_binlog_max_backups * support alignment size for trunk space allocation + * support merge free trunk spaces Version 6.04 2019-12-05 * storage_report_ip_changed ignore result EEXIST diff --git a/conf/tracker.conf b/conf/tracker.conf index b4e511b..26e677e 100644 --- a/conf/tracker.conf +++ b/conf/tracker.conf @@ -161,6 +161,11 @@ slot_max_size = 1MB # fragmentation, but the more space is wasted. trunk_alloc_alignment_size = 512 +# if merge contiguous free spaces of trunk file +# default value is false +# since V6.05 +trunk_free_space_merge = true + # the trunk file size, should >= 4MB # default value is 64MB # since V3.00 diff --git a/storage/storage_param_getter.c b/storage/storage_param_getter.c index a01185b..62ebd3c 100644 --- a/storage/storage_param_getter.c +++ b/storage/storage_param_getter.c @@ -142,6 +142,8 @@ int storage_get_params_from_tracker() "trunk_init_check_occupying", &iniContext, false); g_trunk_init_reload_from_binlog = iniGetBoolValue(NULL, "trunk_init_reload_from_binlog", &iniContext, false); + g_trunk_free_space_merge = iniGetBoolValue(NULL, + "trunk_free_space_merge", &iniContext, false); g_trunk_compress_binlog_min_interval = iniGetIntValue(NULL, "trunk_compress_binlog_min_interval", &iniContext, 0); g_trunk_compress_binlog_interval = iniGetIntValue(NULL, @@ -203,6 +205,7 @@ int storage_get_params_from_tracker() "trunk_create_file_space_threshold=%d GB, " "trunk_init_check_occupying=%d, " "trunk_init_reload_from_binlog=%d, " + "trunk_free_space_merge=%d, " "trunk_compress_binlog_min_interval=%d, " "trunk_compress_binlog_interval=%d, " "trunk_compress_binlog_time_base=%02d:%02d, " @@ -224,6 +227,7 @@ int storage_get_params_from_tracker() (int)(g_trunk_create_file_space_threshold / (FDFS_ONE_MB * 1024)), g_trunk_init_check_occupying, g_trunk_init_reload_from_binlog, + g_trunk_free_space_merge, g_trunk_compress_binlog_min_interval, g_trunk_compress_binlog_interval, g_trunk_compress_binlog_time_base.hour, diff --git a/storage/trunk_mgr/trunk_mem.c b/storage/trunk_mgr/trunk_mem.c index 22bb90f..ed16591 100644 --- a/storage/trunk_mgr/trunk_mem.c +++ b/storage/trunk_mgr/trunk_mem.c @@ -64,6 +64,7 @@ bool g_if_trunker_self = false; bool g_trunk_create_file_advance = false; bool g_trunk_init_check_occupying = false; bool g_trunk_init_reload_from_binlog = false; +bool g_trunk_free_space_merge = false; int g_trunk_binlog_compress_stage = STORAGE_TRUNK_COMPRESS_STAGE_NONE; int64_t g_trunk_total_free_space = 0; int64_t g_trunk_create_file_space_threshold = 0; @@ -349,54 +350,141 @@ static int64_t storage_trunk_get_binlog_size() return stat_buf.st_size; } +struct trunk_info_array { + FDFSTrunkFullInfo **trunks; + int count; + int alloc; +}; + struct walk_callback_args { int fd; char buff[16 * 1024]; char temp_trunk_filename[MAX_PATH_SIZE]; char *pCurrent; + + struct trunk_info_array trunk_array; //for space combine }; -static int tree_walk_callback(void *data, void *args) +static int trunk_alloc_trunk_array( + struct trunk_info_array *trunk_array) +{ + int bytes; + FDFSTrunkFullInfo **trunks; + int alloc; + + if (trunk_array->alloc == 0) + { + alloc = 64 * 1024; + } + else + { + alloc = trunk_array->alloc * 2; + } + + bytes = sizeof(FDFSTrunkFullInfo *) * alloc; + trunks = (FDFSTrunkFullInfo **)malloc(bytes); + if (trunks == NULL) + { + logError("file: "__FILE__", line: %d, " + "malloc %d bytes fail", __LINE__, bytes); + return ENOMEM; + } + + if (trunk_array->count > 0) + { + memcpy(trunks, trunk_array->trunks, + sizeof(FDFSTrunkFullInfo *) * + trunk_array->count); + } + + if (trunk_array->trunks != NULL) + { + free(trunk_array->trunks); + } + + trunk_array->trunks = trunks; + trunk_array->alloc = alloc; + return 0; +} + +static int save_one_trunk(struct walk_callback_args *pCallbackArgs, + FDFSTrunkFullInfo *pTrunkInfo) +{ + int len; + int result; + + len = sprintf(pCallbackArgs->pCurrent, + "%d %c %d %d %d %d %d %d\n", + (int)g_current_time, TRUNK_OP_TYPE_ADD_SPACE, + pTrunkInfo->path.store_path_index, + pTrunkInfo->path.sub_path_high, + pTrunkInfo->path.sub_path_low, + pTrunkInfo->file.id, + pTrunkInfo->file.offset, + pTrunkInfo->file.size); + pCallbackArgs->pCurrent += len; + if (pCallbackArgs->pCurrent - pCallbackArgs->buff > + sizeof(pCallbackArgs->buff) - 128) + { + if (fc_safe_write(pCallbackArgs->fd, pCallbackArgs->buff, + pCallbackArgs->pCurrent - pCallbackArgs->buff) + != pCallbackArgs->pCurrent - pCallbackArgs->buff) + { + result = errno != 0 ? errno : EIO; + logError("file: "__FILE__", line: %d, " + "write to file %s fail, " + "errno: %d, error info: %s", __LINE__, + pCallbackArgs->temp_trunk_filename, + result, STRERROR(result)); + return result; + } + + pCallbackArgs->pCurrent = pCallbackArgs->buff; + } + + return 0; +} + +static int tree_walk_callback_to_file(void *data, void *args) { struct walk_callback_args *pCallbackArgs; - FDFSTrunkFullInfo *pTrunkInfo; FDFSTrunkNode *pCurrent; - int len; int result; pCallbackArgs = (struct walk_callback_args *)args; pCurrent = ((FDFSTrunkSlot *)data)->head; while (pCurrent != NULL) { - pTrunkInfo = &pCurrent->trunk; - len = sprintf(pCallbackArgs->pCurrent, - "%d %c %d %d %d %d %d %d\n", - (int)g_current_time, TRUNK_OP_TYPE_ADD_SPACE, - pTrunkInfo->path.store_path_index, - pTrunkInfo->path.sub_path_high, - pTrunkInfo->path.sub_path_low, - pTrunkInfo->file.id, - pTrunkInfo->file.offset, - pTrunkInfo->file.size); - pCallbackArgs->pCurrent += len; - if (pCallbackArgs->pCurrent - pCallbackArgs->buff > - sizeof(pCallbackArgs->buff) - 128) - { - if (fc_safe_write(pCallbackArgs->fd, pCallbackArgs->buff, - pCallbackArgs->pCurrent - pCallbackArgs->buff) - != pCallbackArgs->pCurrent - pCallbackArgs->buff) - { - result = errno != 0 ? errno : EIO; - logError("file: "__FILE__", line: %d, " - "write to file %s fail, " - "errno: %d, error info: %s", __LINE__, - pCallbackArgs->temp_trunk_filename, - result, STRERROR(result)); - return result; - } + if ((result=save_one_trunk(pCallbackArgs, &pCurrent->trunk)) != 0) + { + return result; + } + pCurrent = pCurrent->next; + } - pCallbackArgs->pCurrent = pCallbackArgs->buff; - } + return 0; +} + +static int tree_walk_callback_to_list(void *data, void *args) +{ + struct walk_callback_args *pCallbackArgs; + FDFSTrunkNode *pCurrent; + int result; + + pCallbackArgs = (struct walk_callback_args *)args; + pCurrent = ((FDFSTrunkSlot *)data)->head; + while (pCurrent != NULL) + { + if (pCallbackArgs->trunk_array.count >= pCallbackArgs->trunk_array.alloc) + { + if ((result=trunk_alloc_trunk_array( + &pCallbackArgs->trunk_array)) != 0) + { + return result; + } + } + pCallbackArgs->trunk_array.trunks[pCallbackArgs->trunk_array. + count++] = &pCurrent->trunk; pCurrent = pCurrent->next; } @@ -404,6 +492,145 @@ static int tree_walk_callback(void *data, void *args) return 0; } +static int trunk_compare_id_offset(const void *p1, const void *p2) +{ + FDFSTrunkFullInfo *pTrunkInfo1; + FDFSTrunkFullInfo *pTrunkInfo2; + int result; + + pTrunkInfo1 = *((FDFSTrunkFullInfo **)p1); + pTrunkInfo2 = *((FDFSTrunkFullInfo **)p2); + + result = memcmp(&(pTrunkInfo1->path), &(pTrunkInfo2->path), + sizeof(FDFSTrunkPathInfo)); + if (result != 0) + { + return result; + } + + result = pTrunkInfo1->file.id - pTrunkInfo2->file.id; + if (result != 0) + { + return result; + } + + return pTrunkInfo1->file.offset - pTrunkInfo2->file.offset; +} + +static int trunk_compare_path_and_id(const FDFSTrunkFullInfo *pTrunkInfo1, + const FDFSTrunkFullInfo *pTrunkInfo2) +{ + int result; + + result = memcmp(&(pTrunkInfo1->path), &(pTrunkInfo2->path), + sizeof(FDFSTrunkPathInfo)); + if (result != 0) + { + return result; + } + + return pTrunkInfo1->file.id - pTrunkInfo2->file.id; +} + +static int trunk_save_merged_spaces(struct walk_callback_args *pCallbackArgs) +{ + FDFSTrunkFullInfo **ppTrunkInfo; + FDFSTrunkFullInfo **ppEnd; + FDFSTrunkFullInfo **previous; + FDFSTrunkFullInfo **ppMergeFirst; + FDFSTrunkFullInfo trunk_info; + FDFSTrunkFullInfo *pTrunkInfo; + int merge_count; + int merged_trunk_count; + int64_t total_size; + int64_t merged_size; + int result; + + if (pCallbackArgs->trunk_array.count == 0) + { + return 0; + } + + qsort(pCallbackArgs->trunk_array.trunks, pCallbackArgs->trunk_array. + count, sizeof(FDFSTrunkFullInfo *), trunk_compare_id_offset); + + merge_count = 0; + merged_trunk_count = 0; + merged_size = 0; + ppEnd = pCallbackArgs->trunk_array.trunks + + pCallbackArgs->trunk_array.count; + ppTrunkInfo = pCallbackArgs->trunk_array.trunks; + ppMergeFirst = ppTrunkInfo; + total_size = (*ppTrunkInfo)->file.size; + while (++ppTrunkInfo < ppEnd) + { + total_size += (*ppTrunkInfo)->file.size; + previous = ppTrunkInfo - 1; + if (trunk_compare_path_and_id(*previous, *ppTrunkInfo) == 0 && + (*previous)->file.offset + (*previous)->file.size == + (*ppTrunkInfo)->file.offset) + { + continue; + } + + if (ppTrunkInfo - ppMergeFirst == 1) + { + pTrunkInfo = *ppMergeFirst; + } + else + { + pTrunkInfo = &trunk_info; + memcpy(pTrunkInfo, *ppMergeFirst, sizeof(FDFSTrunkFullInfo)); + pTrunkInfo->file.size = (*ppTrunkInfo)->file.offset - + (*ppMergeFirst)->file.offset; + + merge_count++; + merged_size += pTrunkInfo->file.size; + merged_trunk_count += ppTrunkInfo - ppMergeFirst; + } + + if ((result=save_one_trunk(pCallbackArgs, pTrunkInfo)) != 0) + { + return result; + } + + ppMergeFirst = ppTrunkInfo; + ppTrunkInfo++; + } + + if (ppEnd - ppMergeFirst == 1) + { + pTrunkInfo = *ppMergeFirst; + } + else + { + FDFSTrunkFullInfo **ppLast; + + pTrunkInfo = &trunk_info; + ppLast = ppEnd - 1; + memcpy(pTrunkInfo, *ppMergeFirst, sizeof(FDFSTrunkFullInfo)); + pTrunkInfo->file.size = (*ppLast)->file.offset - + (*ppMergeFirst)->file.offset + (*ppLast)->file.size; + + merge_count++; + merged_size += pTrunkInfo->file.size; + merged_trunk_count += ppEnd - ppMergeFirst; + } + + if ((result=save_one_trunk(pCallbackArgs, pTrunkInfo)) != 0) + { + return result; + } + + logInfo("file: "__FILE__", line: %d, " + "trunk count: %d, total_size: %"PRId64", merge count: %d, " + "merged trunk count: %d, merged size: %"PRId64, __LINE__, + pCallbackArgs->trunk_array.count, total_size, + merge_count, merged_trunk_count, merged_size); + + return 0; +} + static int do_save_trunk_data() { int64_t trunk_binlog_size; @@ -445,14 +672,32 @@ static int do_save_trunk_data() pthread_mutex_lock(&trunk_mem_lock); for (i=0; i 0 && result == 0) { diff --git a/storage/trunk_mgr/trunk_mem.h b/storage/trunk_mgr/trunk_mem.h index e7f55db..48409a0 100644 --- a/storage/trunk_mgr/trunk_mem.h +++ b/storage/trunk_mgr/trunk_mem.h @@ -57,6 +57,7 @@ extern bool g_if_use_trunk_file; //if use trunk file extern bool g_trunk_create_file_advance; extern bool g_trunk_init_check_occupying; extern bool g_trunk_init_reload_from_binlog; +extern bool g_trunk_free_space_merge; extern int g_trunk_binlog_compress_stage; extern bool g_if_trunker_self; //if am i trunk server extern int64_t g_trunk_create_file_space_threshold; diff --git a/tracker/tracker_func.c b/tracker/tracker_func.c index fb7d660..00d9824 100644 --- a/tracker/tracker_func.c +++ b/tracker/tracker_func.c @@ -598,6 +598,9 @@ int tracker_load_from_conf_file(const char *filename, \ g_trunk_init_reload_from_binlog = iniGetBoolValue(NULL, "trunk_init_reload_from_binlog", &iniContext, false); + g_trunk_free_space_merge = iniGetBoolValue(NULL, + "trunk_free_space_merge", &iniContext, false); + if ((result=tracker_load_storage_id_info( filename, &iniContext)) != 0) { @@ -775,6 +778,7 @@ int tracker_load_from_conf_file(const char *filename, \ "trunk_create_file_space_threshold=%d GB, " "trunk_init_check_occupying=%d, " "trunk_init_reload_from_binlog=%d, " + "trunk_free_space_merge=%d, " "trunk_compress_binlog_min_interval=%d, " "trunk_compress_binlog_interval=%d, " "trunk_compress_binlog_time_base=%02d:%02d, " @@ -818,6 +822,7 @@ int tracker_load_from_conf_file(const char *filename, \ (int)(g_trunk_create_file_space_threshold / (FDFS_ONE_MB * 1024)), g_trunk_init_check_occupying, g_trunk_init_reload_from_binlog, + g_trunk_free_space_merge, g_trunk_compress_binlog_min_interval, g_trunk_compress_binlog_interval, g_trunk_compress_binlog_time_base.hour, diff --git a/tracker/tracker_global.c b/tracker/tracker_global.c index 96c3689..bd19da4 100644 --- a/tracker/tracker_global.c +++ b/tracker/tracker_global.c @@ -51,6 +51,7 @@ bool g_if_use_trunk_file = false; //if use trunk file bool g_trunk_create_file_advance = false; bool g_trunk_init_check_occupying = false; bool g_trunk_init_reload_from_binlog = false; +bool g_trunk_free_space_merge = false; int g_slot_min_size = 256; //slot min size, such as 256 bytes int g_slot_max_size = 16 * 1024 * 1024; //slot max size, such as 16MB int g_trunk_file_size = 64 * 1024 * 1024; //the trunk file size, such as 64MB diff --git a/tracker/tracker_global.h b/tracker/tracker_global.h index 812bdef..cfd489f 100644 --- a/tracker/tracker_global.h +++ b/tracker/tracker_global.h @@ -75,6 +75,7 @@ extern bool g_if_use_trunk_file; //if use trunk file extern bool g_trunk_create_file_advance; extern bool g_trunk_init_check_occupying; extern bool g_trunk_init_reload_from_binlog; +extern bool g_trunk_free_space_merge; extern int g_slot_min_size; //slot min size, such as 256 bytes extern int g_slot_max_size; //slot max size, such as 16MB extern int g_trunk_file_size; //the trunk file size, such as 64MB diff --git a/tracker/tracker_service.c b/tracker/tracker_service.c index 793ec61..f98d8fb 100644 --- a/tracker/tracker_service.c +++ b/tracker/tracker_service.c @@ -695,6 +695,7 @@ static int tracker_deal_parameter_req(struct fast_task_info *pTask) "trunk_create_file_space_threshold=%"PRId64"\n" "trunk_init_check_occupying=%d\n" "trunk_init_reload_from_binlog=%d\n" + "trunk_free_space_merge=%d\n" "trunk_compress_binlog_min_interval=%d\n" "trunk_compress_binlog_interval=%d\n" "trunk_compress_binlog_time_base=%02d:%02d\n" @@ -716,6 +717,7 @@ static int tracker_deal_parameter_req(struct fast_task_info *pTask) g_trunk_create_file_space_threshold, g_trunk_init_check_occupying, g_trunk_init_reload_from_binlog, + g_trunk_free_space_merge, g_trunk_compress_binlog_min_interval, g_trunk_compress_binlog_interval, g_trunk_compress_binlog_time_base.hour,