snowflake changes from global static variables to one instance per thread
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
add_library(snowflake snowflake.cpp)
|
||||
target_include_directories(snowflake PUBLIC ${CMAKE_CURRENT_LIST_DIR})
|
||||
target_include_directories(snowflake PUBLIC ${CMAKE_SOURCE_DIR}/src/utils)
|
||||
target_include_directories(snowflake PUBLIC ${CMAKE_SOURCE_DIR}/include/stellar)
|
||||
target_link_libraries(snowflake core)
|
||||
target_link_libraries(snowflake log)
|
||||
|
||||
add_subdirectory(test)
|
||||
@@ -1,57 +1,61 @@
|
||||
#include <time.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "log.h"
|
||||
#include "utils.h"
|
||||
#include "stellar.h"
|
||||
#include "snowflake.h"
|
||||
|
||||
#define snowflake_LOG_ERROR(format, ...) LOG_ERROR("snowflake", format, ##__VA_ARGS__)
|
||||
#define snowflake_LOG_DEBUG(format, ...) LOG_DEBUG("snowflake", format, ##__VA_ARGS__)
|
||||
#define SNOWFLAKE_LOG_ERROR(format, ...) LOG_ERROR("snowflake", format, ##__VA_ARGS__)
|
||||
|
||||
struct snowflake
|
||||
{
|
||||
uint8_t snowflake_base; // 5bit [0, 31]
|
||||
uint8_t snowflake_offset; // 7bit [0, 127]
|
||||
uint64_t device_id; // 12bit [0, 4095] ( base << 7 | offset )
|
||||
uint64_t thread[MAX_THREAD_NUM];
|
||||
uint8_t worker_base_id; // 5 bit [0, 31]
|
||||
uint8_t worker_offset_id; // 7 bit [0, 127]
|
||||
uint64_t thread_id; // limit 256
|
||||
uint64_t device_id; // 12 bit [0, 4095] ( base << 7 | offset )
|
||||
uint64_t sequence;
|
||||
};
|
||||
|
||||
struct snowflake g_snowflake = {};
|
||||
|
||||
// return 0: success
|
||||
// return -1: failed
|
||||
int snowflake_id_init(const struct snowflake_options *opts)
|
||||
struct snowflake *snowflake_new(uint16_t thread_id, uint8_t worker_base_id, uint8_t worker_offset_id)
|
||||
{
|
||||
memset(&g_snowflake, 0, sizeof(struct snowflake));
|
||||
|
||||
if (opts == NULL)
|
||||
if (thread_id > 255)
|
||||
{
|
||||
snowflake_LOG_ERROR("opts is NULL");
|
||||
return -1;
|
||||
SNOWFLAKE_LOG_ERROR("thread_id %u is invalid, range [0, 255]", thread_id);
|
||||
return NULL;
|
||||
}
|
||||
if (worker_base_id > 31)
|
||||
{
|
||||
SNOWFLAKE_LOG_ERROR("worker_base_id %u is invalid, range [0, 31]", worker_base_id);
|
||||
return NULL;
|
||||
}
|
||||
if (worker_offset_id > 127)
|
||||
{
|
||||
SNOWFLAKE_LOG_ERROR("worker_offset_id %u is invalid, range [0, 127]", worker_offset_id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (opts->snowflake_base > 31)
|
||||
struct snowflake *sf = (struct snowflake *)calloc(1, sizeof(struct snowflake));
|
||||
if (sf == NULL)
|
||||
{
|
||||
snowflake_LOG_ERROR("snowflake_base %u is invalid, range [0, 31]", opts->snowflake_base);
|
||||
return -1;
|
||||
SNOWFLAKE_LOG_ERROR("calloc snowflake failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (opts->snowflake_offset > 127)
|
||||
sf->worker_base_id = worker_base_id;
|
||||
sf->worker_offset_id = worker_offset_id;
|
||||
sf->thread_id = thread_id;
|
||||
sf->device_id = ((worker_base_id << 7) | worker_offset_id) & 0xFFF;
|
||||
sf->sequence = 0;
|
||||
|
||||
return sf;
|
||||
}
|
||||
|
||||
void snowflake_free(struct snowflake *sf)
|
||||
{
|
||||
if (sf != NULL)
|
||||
{
|
||||
snowflake_LOG_ERROR("snowflake_offset %u is invalid, range [0, 127]", opts->snowflake_offset);
|
||||
return -1;
|
||||
free(sf);
|
||||
sf = NULL;
|
||||
}
|
||||
|
||||
g_snowflake.snowflake_base = opts->snowflake_base;
|
||||
g_snowflake.snowflake_offset = opts->snowflake_offset;
|
||||
g_snowflake.device_id = ((g_snowflake.snowflake_base << 7) | g_snowflake.snowflake_offset) & 0xFFF;
|
||||
|
||||
snowflake_LOG_DEBUG("snowflake_base: %u, snowflake_offset: %u, device_id: %u",
|
||||
g_snowflake.snowflake_base,
|
||||
g_snowflake.snowflake_offset,
|
||||
g_snowflake.device_id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -61,20 +65,41 @@ int snowflake_id_init(const struct snowflake_options *opts)
|
||||
* | 1bit | 12bit device_id | 8bit thread_id | 28bit timestamp in sec | 15bit sequence per thread |
|
||||
* +------+------------------+----------------+------------------------+---------------------------+
|
||||
*/
|
||||
uint64_t snowflake_id_generate(uint64_t now_sec)
|
||||
{
|
||||
|
||||
#define MAX_ID_PER_THREAD (32768)
|
||||
#define MAX_ID_BASE_TIME (268435456L)
|
||||
|
||||
uint64_t thr_idx = (uint16_t)stellar_get_current_thread_index();
|
||||
uint64_t snowflake_generate(struct snowflake *sf, uint64_t now_sec)
|
||||
{
|
||||
if (sf == NULL)
|
||||
{
|
||||
SNOWFLAKE_LOG_ERROR("snowflake is NULL");
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t global_id = 0;
|
||||
uint64_t id_per_thread = (g_snowflake.thread[thr_idx]++) % MAX_ID_PER_THREAD;
|
||||
uint64_t id = 0;
|
||||
uint64_t id_per_thread = (sf->sequence++) % MAX_ID_PER_THREAD;
|
||||
uint64_t id_base_time = now_sec % MAX_ID_BASE_TIME;
|
||||
global_id = (g_snowflake.device_id << 51) |
|
||||
(thr_idx << 43) |
|
||||
(id_base_time << 15) |
|
||||
(id_per_thread);
|
||||
id = (sf->device_id << 51) |
|
||||
(sf->thread_id << 43) |
|
||||
(id_base_time << 15) |
|
||||
(id_per_thread);
|
||||
|
||||
return global_id;
|
||||
return id;
|
||||
}
|
||||
|
||||
void snowflake_deserialize(uint64_t id, struct snowflake_meta *meta)
|
||||
{
|
||||
if (meta == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
meta->sequence = id & 0x7FFF;
|
||||
meta->time_cycle = MAX_ID_BASE_TIME;
|
||||
meta->time_relative = (id >> 15) & 0xFFFFFFF;
|
||||
meta->thread_id = (id >> 43) & 0xFF;
|
||||
meta->device_id = (id >> 51) & 0xFFF;
|
||||
meta->worker_base_id = (meta->device_id >> 7) & 0x1F;
|
||||
meta->worker_offset_id = meta->device_id & 0x7F;
|
||||
}
|
||||
@@ -7,16 +7,24 @@ extern "C"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
struct snowflake_options
|
||||
struct snowflake;
|
||||
struct snowflake *snowflake_new(uint16_t thread_id, uint8_t worker_base_id, uint8_t worker_offset_id);
|
||||
void snowflake_free(struct snowflake *sf);
|
||||
|
||||
struct snowflake_meta
|
||||
{
|
||||
uint8_t snowflake_base;
|
||||
uint8_t snowflake_offset;
|
||||
uint64_t sequence;
|
||||
uint64_t time_cycle;
|
||||
uint64_t time_relative;
|
||||
uint16_t thread_id;
|
||||
uint16_t device_id;
|
||||
|
||||
uint8_t worker_base_id;
|
||||
uint8_t worker_offset_id;
|
||||
};
|
||||
|
||||
// return 0: success
|
||||
// return -1: failed
|
||||
int snowflake_id_init(const struct snowflake_options *opts);
|
||||
uint64_t snowflake_id_generate(uint64_t now_sec);
|
||||
uint64_t snowflake_generate(struct snowflake *sf, uint64_t now_sec);
|
||||
void snowflake_deserialize(uint64_t id, struct snowflake_meta *meta);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
8
src/snowflake/test/CMakeLists.txt
Normal file
8
src/snowflake/test/CMakeLists.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
add_executable(gtest_snowflake gtest_snowflake.cpp)
|
||||
target_link_libraries(gtest_snowflake snowflake gtest)
|
||||
|
||||
add_executable(snowflake_tool snowflake_tool.cpp)
|
||||
target_link_libraries(snowflake_tool snowflake)
|
||||
|
||||
include(GoogleTest)
|
||||
gtest_discover_tests(gtest_snowflake)
|
||||
48
src/snowflake/test/gtest_snowflake.cpp
Normal file
48
src/snowflake/test/gtest_snowflake.cpp
Normal file
@@ -0,0 +1,48 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "snowflake.h"
|
||||
|
||||
TEST(SNOWFLAKE, GENERATE)
|
||||
{
|
||||
uint16_t thread_id = 1;
|
||||
uint8_t worker_base_id = 2;
|
||||
uint8_t worker_offset_id = 3;
|
||||
|
||||
struct snowflake *sf = snowflake_new(thread_id, worker_base_id, worker_offset_id);
|
||||
EXPECT_TRUE(sf != NULL);
|
||||
EXPECT_TRUE(snowflake_generate(sf, 1000) != 0);
|
||||
snowflake_free(sf);
|
||||
}
|
||||
|
||||
TEST(SNOWFLAKE, DESERIALIZE)
|
||||
{
|
||||
uint16_t thread_id = 1;
|
||||
uint8_t worker_base_id = 2;
|
||||
uint8_t worker_offset_id = 3;
|
||||
|
||||
struct snowflake *sf = snowflake_new(thread_id, worker_base_id, worker_offset_id);
|
||||
EXPECT_TRUE(sf != NULL);
|
||||
|
||||
for (uint64_t i = 0; i < 5; i++)
|
||||
{
|
||||
uint64_t id = snowflake_generate(sf, i + 1000);
|
||||
EXPECT_TRUE(id != 0);
|
||||
|
||||
struct snowflake_meta meta = {};
|
||||
snowflake_deserialize(id, &meta);
|
||||
EXPECT_EQ(meta.sequence, i);
|
||||
EXPECT_EQ(meta.time_relative, i + 1000);
|
||||
EXPECT_EQ(meta.thread_id, thread_id);
|
||||
EXPECT_EQ(meta.device_id, (worker_base_id << 7) | worker_offset_id);
|
||||
EXPECT_EQ(meta.worker_base_id, worker_base_id);
|
||||
EXPECT_EQ(meta.worker_offset_id, worker_offset_id);
|
||||
}
|
||||
|
||||
snowflake_free(sf);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
38
src/snowflake/test/snowflake_tool.cpp
Normal file
38
src/snowflake/test/snowflake_tool.cpp
Normal file
@@ -0,0 +1,38 @@
|
||||
#include <time.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "snowflake.h"
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
if (argc != 2)
|
||||
{
|
||||
printf("Usage: %s <id>\n", argv[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint64_t id = atoll(argv[1]);
|
||||
struct snowflake_meta meta = {};
|
||||
snowflake_deserialize(id, &meta);
|
||||
|
||||
printf("id : %lu\n", id);
|
||||
printf("base_id : %u\n", meta.worker_base_id);
|
||||
printf("offset_id : %u\n", meta.worker_offset_id);
|
||||
printf("device_id : %u\n", meta.device_id);
|
||||
printf("thread_id : %u\n", meta.thread_id);
|
||||
printf("time_cycle : %lu sec (%lu year)\n", meta.time_cycle, meta.time_cycle / 3600 / 24 / 365);
|
||||
printf("time_relative : %lu sec\n", meta.time_relative);
|
||||
printf("sequence : %lu\n", meta.sequence);
|
||||
|
||||
for (int i = 6; i < 10; i++)
|
||||
{
|
||||
char buff[64];
|
||||
time_t tt = meta.time_cycle * i + meta.time_relative;
|
||||
struct tm *tm = localtime(&tt);
|
||||
strftime(buff, sizeof(buff), "%Y-%m-%d %H:%M:%S", tm);
|
||||
printf("\ttime_cycle * %d + time_relative => %s\n", i, buff);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user