Skip to content

Commit ca8c2ed

Browse files
committed
refactor(sync): move NetworkProbe to net module and fix test linkage
- Remove NetworkProbe from sync - Depend on vix::net for connectivity probing - Fix sync tests to link against vix::sync instead of vix_core - Introduce standalone CMakeLists for sync module
1 parent a216b2a commit ca8c2ed

25 files changed

+1900
-0
lines changed

.gitignore

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,14 @@
3939

4040
# debug information files
4141
*.dwo
42+
43+
.vscode/
44+
build/
45+
vix.log
46+
cmd.md
47+
build/
48+
build-ninja/
49+
.vix-test/
50+
.vix_test_inflight/
51+
.vix_test/
52+
.vix_test_perm/

CMakeLists.txt

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
# ====================================================================
2+
# Vix.cpp — Sync Module
3+
# ====================================================================
4+
# Purpose:
5+
# Offline-first synchronization engine for Vix (WAL, outbox, retries,
6+
# sync workers). Builds as STATIC when sources exist, otherwise as a
7+
# header-only INTERFACE target.
8+
#
9+
# Public Targets:
10+
# - vix_sync : The actual library target (STATIC or INTERFACE)
11+
# - vix::sync : Namespaced alias for consumers
12+
#
13+
# Public Dependencies:
14+
# - vix::utils
15+
# - vix::net
16+
#
17+
# Options:
18+
# - VIX_ENABLE_SANITIZERS : Inherit sanitizers from the parent project
19+
#
20+
# Installation/Export:
21+
# Installs into the umbrella export-set `VixTargets`.
22+
# ====================================================================
23+
24+
cmake_minimum_required(VERSION 3.20)
25+
project(vix_sync VERSION 0.1.0 LANGUAGES CXX)
26+
27+
include(GNUInstallDirs)
28+
29+
# ------------------------ Global settings ----------------------------
30+
set(CMAKE_CXX_STANDARD 20)
31+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
32+
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
33+
34+
# ------------------------ Sources discovery --------------------------
35+
file(GLOB_RECURSE SYNC_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp")
36+
37+
# ------------------------ Utils dependency (robust) ------------------
38+
option(VIX_SYNC_FETCH_UTILS "Auto-fetch vix::utils if missing" ON)
39+
40+
if (NOT TARGET vix::utils)
41+
if (EXISTS "${CMAKE_CURRENT_LIST_DIR}/../utils/CMakeLists.txt")
42+
message(STATUS "[sync] Adding utils from umbrella: ../utils")
43+
add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/../utils" "utils")
44+
elseif (VIX_SYNC_FETCH_UTILS)
45+
include(FetchContent)
46+
message(STATUS "[sync] Fetching vix::utils via FetchContent")
47+
FetchContent_Declare(vix_utils
48+
GIT_REPOSITORY https://github.com/vixcpp/utils.git
49+
GIT_TAG dev
50+
)
51+
FetchContent_MakeAvailable(vix_utils)
52+
else()
53+
message(FATAL_ERROR "vix::utils not found. Enable VIX_SYNC_FETCH_UTILS=ON or provide the target before sync.")
54+
endif()
55+
endif()
56+
57+
set(VIX_UTILS_TARGET vix::utils)
58+
if (NOT TARGET ${VIX_UTILS_TARGET} AND TARGET vix::utils)
59+
set(VIX_UTILS_TARGET vix::utils)
60+
endif()
61+
62+
# ------------------------- Net dependency (robust) -------------------
63+
option(VIX_SYNC_FETCH_NET "Auto-fetch vix::net if missing" ON)
64+
65+
if (NOT TARGET vix::net)
66+
if (EXISTS "${CMAKE_CURRENT_LIST_DIR}/../net/CMakeLists.txt")
67+
message(STATUS "[sync] Adding net from umbrella: ../net")
68+
add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/../net" "net")
69+
elseif (VIX_SYNC_FETCH_NET)
70+
include(FetchContent)
71+
message(STATUS "[sync] Fetching vix::net via FetchContent")
72+
FetchContent_Declare(vix_net
73+
GIT_REPOSITORY https://github.com/vixcpp/net.git
74+
GIT_TAG dev
75+
)
76+
FetchContent_MakeAvailable(vix_net)
77+
else()
78+
message(FATAL_ERROR "vix::net not found. Enable VIX_SYNC_FETCH_NET=ON or provide the target before sync.")
79+
endif()
80+
endif()
81+
82+
set(VIX_NET_TARGET vix::net)
83+
if (NOT TARGET ${VIX_NET_TARGET} AND TARGET vix::net)
84+
set(VIX_NET_TARGET vix::net)
85+
endif()
86+
87+
# ============================== STATIC ===============================
88+
if (SYNC_SOURCES)
89+
message(STATUS "[sync] Building STATIC library with detected sources.")
90+
91+
add_library(vix_sync STATIC ${SYNC_SOURCES})
92+
93+
if (TARGET vix_warnings)
94+
message(STATUS "[sync] vix_warnings detected (from umbrella)")
95+
target_link_libraries(vix_sync PUBLIC vix_warnings)
96+
if (VIX_ENABLE_SANITIZERS AND TARGET vix_sanitizers)
97+
target_link_libraries(vix_sync INTERFACE vix_sanitizers)
98+
endif()
99+
else()
100+
message(STATUS "[sync] vix_warnings not found (building sync standalone)")
101+
endif()
102+
103+
add_library(vix::sync ALIAS vix_sync)
104+
target_compile_features(vix_sync PUBLIC cxx_std_20)
105+
106+
target_include_directories(vix_sync
107+
PUBLIC
108+
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
109+
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
110+
)
111+
112+
target_link_libraries(vix_sync
113+
PUBLIC
114+
${VIX_UTILS_TARGET}
115+
${VIX_NET_TARGET}
116+
)
117+
118+
set_target_properties(vix_sync PROPERTIES
119+
OUTPUT_NAME vix_sync
120+
VERSION ${PROJECT_VERSION}
121+
SOVERSION 0
122+
EXPORT_NAME sync
123+
)
124+
125+
install(TARGETS vix_sync
126+
EXPORT VixTargets
127+
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
128+
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
129+
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
130+
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
131+
)
132+
install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
133+
FILES_MATCHING PATTERN "*.hpp" PATTERN "*.h")
134+
135+
# ============================ HEADER-ONLY ============================
136+
else()
137+
message(STATUS "[sync] Building HEADER-ONLY library (no sources).")
138+
139+
add_library(vix_sync INTERFACE)
140+
add_library(vix::sync ALIAS vix_sync)
141+
target_compile_features(vix_sync INTERFACE cxx_std_20)
142+
143+
target_include_directories(vix_sync INTERFACE
144+
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
145+
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
146+
)
147+
148+
target_link_libraries(vix_sync INTERFACE
149+
${VIX_UTILS_TARGET}
150+
${VIX_NET_TARGET}
151+
)
152+
153+
if (VIX_ENABLE_SANITIZERS AND TARGET vix_sanitizers)
154+
target_link_libraries(vix_sync INTERFACE vix_sanitizers)
155+
endif()
156+
157+
install(TARGETS vix_sync
158+
EXPORT VixTargets
159+
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
160+
)
161+
install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
162+
FILES_MATCHING PATTERN "*.hpp" PATTERN "*.h")
163+
endif()
164+
165+
# ----------------------------- Tests ---------------------------------
166+
option(VIX_SYNC_BUILD_TESTS "Build sync module tests" OFF)
167+
168+
if (VIX_SYNC_BUILD_TESTS)
169+
include(CTest)
170+
enable_testing()
171+
add_subdirectory(tests)
172+
endif()
173+
174+
# ----------------------------- Summary -------------------------------
175+
message(STATUS "------------------------------------------------------")
176+
message(STATUS "vix::sync configured (${PROJECT_VERSION})")
177+
if (SYNC_SOURCES)
178+
message(STATUS "Mode: STATIC / sources found")
179+
else()
180+
message(STATUS "Mode: HEADER-ONLY / no sources")
181+
endif()
182+
message(STATUS "Net target: ${VIX_NET_TARGET}")
183+
message(STATUS "Utils target: ${VIX_UTILS_TARGET}")
184+
message(STATUS "Include dir: ${CMAKE_CURRENT_SOURCE_DIR}/include (for <vix/...>)")
185+
message(STATUS "Sanitizers (inherited): ${VIX_ENABLE_SANITIZERS}")
186+
message(STATUS "Build type: ${CMAKE_BUILD_TYPE}")
187+
message(STATUS "------------------------------------------------------")

include/vix/sync/Operation.hpp

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
#pragma once
2+
3+
#include <cstdint>
4+
#include <string>
5+
#include <string_view>
6+
#include <utility>
7+
8+
namespace vix::sync
9+
{
10+
11+
enum class OperationStatus : uint8_t
12+
{
13+
Pending = 0,
14+
InFlight,
15+
Done,
16+
Failed,
17+
PermanentFailed
18+
};
19+
20+
struct Operation
21+
{
22+
std::string id;
23+
std::string kind;
24+
std::string target;
25+
std::string payload;
26+
std::string idempotency_key;
27+
28+
std::int64_t created_at_ms{0};
29+
std::int64_t updated_at_ms{0};
30+
std::uint32_t attempt{0};
31+
std::int64_t next_retry_at_ms{0};
32+
33+
OperationStatus status{OperationStatus::Pending};
34+
std::string last_error;
35+
36+
bool is_done() const noexcept { return status == OperationStatus::Done; }
37+
bool is_pending() const noexcept { return status == OperationStatus::Pending; }
38+
bool is_failed() const noexcept { return status == OperationStatus::Failed; }
39+
40+
void fail(std::string err, std::int64_t now_ms)
41+
{
42+
last_error = std::move(err);
43+
status = OperationStatus::Failed;
44+
updated_at_ms = now_ms;
45+
}
46+
47+
void done(std::int64_t now_ms)
48+
{
49+
status = OperationStatus::Done;
50+
updated_at_ms = now_ms;
51+
last_error.clear();
52+
}
53+
};
54+
55+
} // namespace vix::sync

include/vix/sync/RetryPolicy.hpp

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#pragma once
2+
3+
#include <cstdint>
4+
#include <algorithm>
5+
6+
namespace vix::sync
7+
{
8+
9+
struct RetryPolicy
10+
{
11+
std::uint32_t max_attempts{8};
12+
std::int64_t base_delay_ms{500}; // 0.5s
13+
std::int64_t max_delay_ms{30'000}; // 30s
14+
// Exponential factor: delay = base * (2^attempt)
15+
double factor{2.0};
16+
// 0.0 = none, 0.2 = +/-20%
17+
double jitter_ratio{0.2};
18+
19+
bool can_retry(std::uint32_t attempt) const noexcept
20+
{
21+
return attempt < max_attempts;
22+
}
23+
24+
std::int64_t compute_delay_ms(std::uint32_t attempt) const noexcept
25+
{
26+
// attempt: 0,1,2,... (0 => base)
27+
double d = static_cast<double>(base_delay_ms);
28+
for (std::uint32_t i = 0; i < attempt; ++i)
29+
d *= factor;
30+
31+
auto delay = static_cast<std::int64_t>(d);
32+
return std::clamp(delay, base_delay_ms, max_delay_ms);
33+
}
34+
};
35+
36+
} // namespace vix::sync
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
#pragma once
2+
3+
#include <atomic>
4+
#include <cstdint>
5+
#include <memory>
6+
#include <thread>
7+
#include <vector>
8+
9+
#include <vix/net/NetworkProbe.hpp>
10+
#include <vix/sync/outbox/Outbox.hpp>
11+
#include <vix/sync/engine/SyncWorker.hpp>
12+
13+
namespace vix::sync::engine
14+
{
15+
16+
class SyncEngine
17+
{
18+
public:
19+
struct Config
20+
{
21+
std::size_t worker_count{1};
22+
// Engine loop
23+
std::int64_t idle_sleep_ms{250};
24+
std::int64_t offline_sleep_ms{500};
25+
// Worker
26+
std::size_t batch_limit{25};
27+
std::int64_t inflight_timeout_ms{10'000};
28+
};
29+
30+
SyncEngine(Config cfg,
31+
std::shared_ptr<vix::sync::outbox::Outbox> outbox,
32+
std::shared_ptr<vix::net::NetworkProbe> probe,
33+
std::shared_ptr<ISyncTransport> transport);
34+
35+
~SyncEngine();
36+
// Tick-based (manual driving)
37+
std::size_t tick(std::int64_t now_ms);
38+
// Threaded mode
39+
void start();
40+
void stop();
41+
bool running() const noexcept { return running_.load(); }
42+
43+
private:
44+
void run_loop_();
45+
46+
private:
47+
Config cfg_;
48+
std::shared_ptr<vix::sync::outbox::Outbox> outbox_;
49+
std::shared_ptr<vix::net::NetworkProbe> probe_;
50+
std::shared_ptr<ISyncTransport> transport_;
51+
std::vector<std::unique_ptr<SyncWorker>> workers_;
52+
std::atomic<bool> running_{false};
53+
std::thread thread_;
54+
};
55+
56+
} // namespace vix::sync::engine

0 commit comments

Comments
 (0)