Skip to content

Commit f7df273

Browse files
committed
add examples
1 parent f0fbfea commit f7df273

7 files changed

Lines changed: 750 additions & 0 deletions

File tree

examples/README.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Sync examples
2+
This folder is a living playground for Vix.cpp offline-first sync.
3+
4+
Run with your usual workflow (example):
5+
6+
- vix run examples/sync/outbox_smoke.cpp
7+
- vix run examples/sync/outbox_permanent_fail.cpp
8+
- vix run examples/sync/inflight_timeout.cpp
9+
10+
Each example writes its own local outbox file under a .vix_example_sync_* folder.
11+
12+
Run:
13+
14+
- vix run examples/sync/vix_sync_showcase.cpp
15+
16+
It creates local files under:
17+
18+
- .vix_example_sync_smoke/
19+
- .vix_example_sync_offline/
20+
- .vix_example_sync_retry/
21+
- .vix_example_sync_permfail/
22+
- .vix_example_sync_inflight/

examples/fake_http_transport.cpp

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/**
2+
*
3+
* @file fake_http_transport.hpp
4+
* @author Gaspard Kirira
5+
*
6+
* Copyright 2025, Gaspard Kirira.
7+
* All rights reserved.
8+
* https://github.com/vixcpp/vix
9+
*
10+
* Use of this source code is governed by a MIT license
11+
* that can be found in the License file.
12+
*
13+
* Vix.cpp
14+
*
15+
*/
16+
#ifndef VIX_EXAMPLES_SYNC_FAKE_HTTP_TRANSPORT_HPP
17+
#define VIX_EXAMPLES_SYNC_FAKE_HTTP_TRANSPORT_HPP
18+
19+
#include <string>
20+
#include <unordered_map>
21+
#include <utility>
22+
23+
#include <vix/sync/engine/SyncWorker.hpp>
24+
25+
namespace vix::sync::engine
26+
{
27+
// Fake transport: rule-based outcomes by operation kind/target/id
28+
class FakeHttpTransport final : public ISyncTransport
29+
{
30+
public:
31+
struct Rule
32+
{
33+
bool ok{true};
34+
bool retryable{true};
35+
std::string error{"simulated failure"};
36+
};
37+
38+
void setDefault(Rule r) { def_ = std::move(r); }
39+
40+
void setRuleForKind(std::string kind, Rule r)
41+
{
42+
by_kind_[std::move(kind)] = std::move(r);
43+
}
44+
45+
void setRuleForTarget(std::string target, Rule r)
46+
{
47+
by_target_[std::move(target)] = std::move(r);
48+
}
49+
50+
std::size_t callCount() const noexcept { return calls_; }
51+
52+
SendResult send(const vix::sync::Operation &op) override
53+
{
54+
++calls_;
55+
56+
if (auto it = by_target_.find(op.target); it != by_target_.end())
57+
{
58+
return toResult(it->second);
59+
}
60+
61+
if (auto it = by_kind_.find(op.kind); it != by_kind_.end())
62+
{
63+
return toResult(it->second);
64+
}
65+
66+
return toResult(def_);
67+
}
68+
69+
private:
70+
static SendResult toResult(const Rule &r)
71+
{
72+
SendResult res;
73+
res.ok = r.ok;
74+
res.retryable = r.retryable;
75+
res.error = r.ok ? "" : r.error;
76+
return res;
77+
}
78+
79+
private:
80+
Rule def_{};
81+
std::unordered_map<std::string, Rule> by_kind_;
82+
std::unordered_map<std::string, Rule> by_target_;
83+
std::size_t calls_{0};
84+
};
85+
86+
} // namespace vix::sync::engine
87+
88+
#endif

examples/fake_transport.hpp

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/**
2+
*
3+
* @file fake_transport.hpp
4+
* @author Gaspard Kirira
5+
*
6+
* Vix.cpp - Sync examples
7+
*
8+
*/
9+
#ifndef VIX_EXAMPLES_SYNC_FAKE_TRANSPORT_HPP
10+
#define VIX_EXAMPLES_SYNC_FAKE_TRANSPORT_HPP
11+
12+
#include <string>
13+
#include <unordered_map>
14+
#include <utility>
15+
16+
#include <vix/sync/engine/SyncWorker.hpp>
17+
18+
namespace vix::sync::engine
19+
{
20+
class FakeTransport final : public ISyncTransport
21+
{
22+
public:
23+
struct Rule
24+
{
25+
bool ok{true};
26+
bool retryable{true};
27+
std::string error{"simulated failure"};
28+
};
29+
30+
void setDefault(Rule r) { def_ = std::move(r); }
31+
32+
void setRuleForKind(std::string kind, Rule r)
33+
{
34+
by_kind_[std::move(kind)] = std::move(r);
35+
}
36+
37+
void setRuleForTarget(std::string target, Rule r)
38+
{
39+
by_target_[std::move(target)] = std::move(r);
40+
}
41+
42+
std::size_t callCount() const noexcept { return calls_; }
43+
44+
SendResult send(const vix::sync::Operation &op) override
45+
{
46+
++calls_;
47+
48+
if (auto it = by_target_.find(op.target); it != by_target_.end())
49+
return toResult(it->second);
50+
51+
if (auto it = by_kind_.find(op.kind); it != by_kind_.end())
52+
return toResult(it->second);
53+
54+
return toResult(def_);
55+
}
56+
57+
private:
58+
static SendResult toResult(const Rule &r)
59+
{
60+
SendResult res;
61+
res.ok = r.ok;
62+
res.retryable = r.retryable;
63+
res.error = r.ok ? "" : r.error;
64+
return res;
65+
}
66+
67+
private:
68+
Rule def_{};
69+
std::unordered_map<std::string, Rule> by_kind_;
70+
std::unordered_map<std::string, Rule> by_target_;
71+
std::size_t calls_{0};
72+
};
73+
74+
} // namespace vix::sync::engine
75+
76+
#endif

examples/inflight_timeout.cpp

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
/**
2+
*
3+
* @file inflight_timeout.cpp
4+
* @author Gaspard Kirira
5+
*
6+
* Vix.cpp
7+
*
8+
*/
9+
#include <cassert>
10+
#include <chrono>
11+
#include <filesystem>
12+
#include <iostream>
13+
#include <memory>
14+
15+
#include <vix/net/NetworkProbe.hpp>
16+
#include <vix/sync/engine/SyncEngine.hpp>
17+
#include <vix/sync/outbox/FileOutboxStore.hpp>
18+
#include <vix/sync/outbox/Outbox.hpp>
19+
20+
#include "fake_http_transport.hpp"
21+
22+
static std::int64_t now_ms()
23+
{
24+
using namespace std::chrono;
25+
return duration_cast<milliseconds>(steady_clock::now().time_since_epoch()).count();
26+
}
27+
28+
static void reset_dir(const std::filesystem::path &dir)
29+
{
30+
std::error_code ec;
31+
std::filesystem::remove_all(dir, ec);
32+
std::filesystem::create_directories(dir, ec);
33+
}
34+
35+
static int run_inflight_timeout()
36+
{
37+
using namespace vix::sync;
38+
using namespace vix::sync::outbox;
39+
using namespace vix::sync::engine;
40+
41+
const std::filesystem::path dir = "./.vix_example_sync_inflight";
42+
reset_dir(dir);
43+
44+
auto store = std::make_shared<FileOutboxStore>(FileOutboxStore::Config{
45+
.file_path = dir / "outbox.json",
46+
.pretty_json = true,
47+
.fsync_on_write = false});
48+
49+
auto outbox = std::make_shared<Outbox>(
50+
Outbox::Config{.owner = "example-sync-inflight"},
51+
store);
52+
53+
auto probe = std::make_shared<vix::net::NetworkProbe>(
54+
vix::net::NetworkProbe::Config{},
55+
[]
56+
{ return true; });
57+
58+
auto transport = std::make_shared<FakeHttpTransport>();
59+
transport->setDefault({.ok = true});
60+
61+
SyncEngine::Config ecfg;
62+
ecfg.worker_count = 1;
63+
ecfg.batch_limit = 10;
64+
ecfg.idle_sleep_ms = 0;
65+
ecfg.offline_sleep_ms = 0;
66+
ecfg.inflight_timeout_ms = 50;
67+
68+
SyncEngine engine(ecfg, outbox, probe, transport);
69+
70+
Operation op;
71+
op.kind = "http.post";
72+
op.target = "/api/messages";
73+
op.payload = R"({"text":"hello offline"})";
74+
75+
const auto t0 = now_ms();
76+
const auto id = outbox->enqueue(op, t0);
77+
78+
const bool claimed = outbox->claim(id, t0);
79+
assert(claimed);
80+
81+
{
82+
const auto saved = store->get(id);
83+
assert(saved.has_value());
84+
assert(saved->status == OperationStatus::InFlight);
85+
}
86+
87+
const auto t1 = t0 + 60;
88+
engine.tick(t1);
89+
90+
{
91+
const auto saved = store->get(id);
92+
assert(saved.has_value());
93+
assert(saved->status != OperationStatus::InFlight);
94+
assert(saved->status == OperationStatus::Failed || saved->status == OperationStatus::Done);
95+
}
96+
97+
engine.tick(t1 + 1);
98+
99+
const auto final = store->get(id);
100+
assert(final.has_value());
101+
assert(final->status == OperationStatus::Done);
102+
103+
assert(transport->callCount() >= 1);
104+
105+
std::cout << "[sync] OK: inflight timeout requeues stuck ops and they complete\n";
106+
return 0;
107+
}
108+
109+
int main()
110+
{
111+
return run_inflight_timeout();
112+
}

0 commit comments

Comments
 (0)