Skip to content

Commit 3cf2f4c

Browse files
committed
perf(ci): Optimize CI pipeline and resolve code quality issues
Optimize CI/CD pipeline with comprehensive caching and improve code quality by resolving all clang-tidy warnings. CI/CD Optimizations: - Add LLVM/Clang 18 installation caching to reduce setup time - Add Homebrew package caching for macOS builds - Improve Docker build cache scoping for better reusability - Optimize macOS workflow to run after Linux tests complete - Remove redundant format checks from macOS workflow Code Quality Improvements: - Fix clang-tidy warnings for unused variables and parameters - Eliminate magic numbers by introducing named constants (kBytesPerMB) - Resolve variable shadowing in RAII guard classes - Fix dead store warnings with NOLINT annotations where appropriate - Add move constructors/operators delete for RAII guards - Improve code formatting consistency Bug Fixes: - Initialize variables to prevent undefined behavior - Add missing #include <memory> in mygram-cli.cpp - Make ExtractAllFilters static as it doesn't use instance state - Fix potential overflow in result sorting with proper casting
1 parent e5bfe3b commit 3cf2f4c

18 files changed

+89
-70
lines changed

.github/workflows/ci.yml

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -70,14 +70,20 @@ jobs:
7070
restore-keys: |
7171
${{ runner.os }}-ccache-
7272
73+
- name: Cache LLVM installation
74+
id: cache-llvm
75+
uses: actions/cache@v4
76+
with:
77+
path: |
78+
/usr/lib/llvm-18
79+
/usr/bin/clang-format-18
80+
/usr/bin/clang-tidy-18
81+
key: ${{ runner.os }}-llvm-18
82+
7383
- name: Install dependencies
7484
run: |
7585
sudo apt-get update
7686
sudo apt-get install -y \
77-
wget \
78-
lsb-release \
79-
software-properties-common \
80-
gnupg \
8187
cmake \
8288
build-essential \
8389
libmysqlclient-dev \
@@ -87,11 +93,16 @@ jobs:
8793
lcov \
8894
ccache
8995
90-
# Install LLVM/Clang 18 for clang-format and clang-tidy
96+
- name: Install LLVM/Clang 18
97+
if: steps.cache-llvm.outputs.cache-hit != 'true'
98+
run: |
9199
wget https://apt.llvm.org/llvm.sh
92100
chmod +x llvm.sh
93101
sudo ./llvm.sh 18
94102
sudo apt-get install -y clang-format-18 clang-tidy-18
103+
104+
- name: Setup LLVM alternatives
105+
run: |
95106
sudo update-alternatives --install /usr/bin/clang-format clang-format /usr/bin/clang-format-18 100
96107
sudo update-alternatives --install /usr/bin/clang-tidy clang-tidy /usr/bin/clang-tidy-18 100
97108
@@ -171,8 +182,8 @@ jobs:
171182
172183
build-and-test-macos:
173184
name: Build and Test (macOS)
174-
needs: changes
175-
if: github.event_name == 'pull_request' && (needs.changes.outputs.src == 'true' || needs.changes.outputs.tests == 'true')
185+
needs: [changes, linux-test-and-coverage]
186+
if: needs.changes.outputs.src == 'true' || needs.changes.outputs.tests == 'true'
176187
runs-on: macos-latest
177188

178189
steps:
@@ -195,6 +206,14 @@ jobs:
195206
restore-keys: |
196207
${{ runner.os }}-ccache-
197208
209+
- name: Cache Homebrew packages
210+
uses: actions/cache@v4
211+
with:
212+
path: ~/Library/Caches/Homebrew
213+
key: ${{ runner.os }}-brew-${{ hashFiles('.github/workflows/ci.yml') }}
214+
restore-keys: |
215+
${{ runner.os }}-brew-
216+
198217
- name: Install dependencies (macOS)
199218
run: |
200219
brew install \
@@ -203,14 +222,8 @@ jobs:
203222
icu4c \
204223
readline \
205224
pkg-config \
206-
llvm@18 \
207225
ccache
208226
209-
- name: Check code formatting
210-
run: |
211-
export PATH="/opt/homebrew/opt/llvm@18/bin:$PATH"
212-
make CLANG_FORMAT=clang-format format-check
213-
214227
- name: Setup ccache
215228
run: |
216229
ccache --set-config=max_size=500M
@@ -264,8 +277,6 @@ jobs:
264277
265278
docker-build:
266279
name: Docker Build Test
267-
needs: changes
268-
if: github.event_name == 'pull_request' && needs.changes.outputs.docker == 'true'
269280
runs-on: ubuntu-latest
270281

271282
steps:
@@ -283,8 +294,8 @@ jobs:
283294
push: false
284295
load: true
285296
tags: mygramdb:test
286-
cache-from: type=gha
287-
cache-to: type=gha,mode=max
297+
cache-from: type=gha,scope=docker-build
298+
cache-to: type=gha,mode=max,scope=docker-build
288299

289300
- name: Test Docker image
290301
run: |

.github/workflows/docker-release.yml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,10 @@ jobs:
6868
push: true
6969
tags: ${{ steps.meta.outputs.tags }}
7070
labels: ${{ steps.meta.outputs.labels }}
71-
cache-from: type=gha
72-
cache-to: type=gha,mode=max
71+
cache-from: |
72+
type=gha,scope=docker-build
73+
type=gha,scope=docker-release
74+
cache-to: type=gha,mode=max,scope=docker-release
7375

7476
- name: Generate release notes
7577
id: release_notes

src/cli/mygram-cli.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
#include <cstring>
1212
#include <iostream>
13+
#include <memory>
1314
#include <sstream>
1415
#include <string>
1516
#include <utility>

src/config/config.cpp

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -571,8 +571,9 @@ Config ParseConfigFromJson(const json& root) {
571571
config.cache.enabled = cache["enabled"].get<bool>();
572572
}
573573
if (cache.contains("max_memory_mb")) {
574+
constexpr size_t kBytesPerMB = 1024 * 1024; // Bytes in one megabyte
574575
int max_memory_mb = cache["max_memory_mb"].get<int>();
575-
config.cache.max_memory_bytes = static_cast<size_t>(max_memory_mb) * 1024 * 1024;
576+
config.cache.max_memory_bytes = static_cast<size_t>(max_memory_mb) * kBytesPerMB;
576577
}
577578
if (cache.contains("min_query_cost_ms")) {
578579
config.cache.min_query_cost_ms = cache["min_query_cost_ms"].get<double>();
@@ -603,9 +604,10 @@ Config ParseConfigFromJson(const json& root) {
603604
if (config.cache.enabled && config.cache.max_memory_bytes > 0) {
604605
auto system_info = utils::GetSystemMemoryInfo();
605606
if (system_info) {
606-
constexpr double kMaxCacheRatio = 0.5; // Maximum 50% of physical memory
607-
uint64_t max_allowed_cache = static_cast<uint64_t>(
608-
static_cast<double>(system_info->total_physical_bytes) * kMaxCacheRatio);
607+
constexpr double kMaxCacheRatio = 0.5; // Maximum 50% of physical memory
608+
constexpr size_t kBytesPerMB = 1024 * 1024; // Bytes in one megabyte
609+
auto max_allowed_cache =
610+
static_cast<uint64_t>(static_cast<double>(system_info->total_physical_bytes) * kMaxCacheRatio);
609611

610612
if (config.cache.max_memory_bytes > max_allowed_cache) {
611613
std::stringstream err_msg;
@@ -614,12 +616,11 @@ Config ParseConfigFromJson(const json& root) {
614616
err_msg << " Physical memory: " << utils::FormatBytes(system_info->total_physical_bytes) << "\n";
615617
err_msg << " Maximum allowed (50% of physical memory): " << utils::FormatBytes(max_allowed_cache) << "\n";
616618
err_msg << " Recommendation:\n";
617-
err_msg << " - Set cache.max_memory_mb to at most "
618-
<< (max_allowed_cache / 1024 / 1024) << " MB\n";
619+
err_msg << " - Set cache.max_memory_mb to at most " << (max_allowed_cache / kBytesPerMB) << " MB\n";
619620
err_msg << " - Consider system memory requirements for index and operations\n";
620621
err_msg << " Example:\n";
621622
err_msg << " cache:\n";
622-
err_msg << " max_memory_mb: " << (max_allowed_cache / 1024 / 1024);
623+
err_msg << " max_memory_mb: " << (max_allowed_cache / kBytesPerMB);
623624
throw std::runtime_error(err_msg.str());
624625
}
625626
} else {

src/config/config.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ struct LoggingConfig {
243243
*/
244244
struct CacheConfig {
245245
bool enabled = true; ///< Enable/disable cache (default: true)
246-
size_t max_memory_bytes = 32 * 1024 * 1024; ///< Maximum cache memory in bytes (default: 32MB) // NOLINT
246+
size_t max_memory_bytes = 32 * 1024 * 1024; ///< Maximum cache memory in bytes (default: 32MB) // NOLINT
247247
double min_query_cost_ms = 10.0; ///< Minimum query cost to cache (default: 10ms) // NOLINT
248248
int ttl_seconds = 3600; ///< Cache entry TTL (default: 1 hour, 0 = no TTL) // NOLINT
249249
std::string invalidation_strategy = "ngram"; ///< Invalidation strategy: "ngram", "table"

src/config/config_help.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,9 +208,10 @@ nlohmann::json ConfigToJson(const Config& config) {
208208
};
209209

210210
// Cache configuration
211+
constexpr size_t kBytesPerMB = 1024 * 1024; // Bytes in one megabyte
211212
json["cache"] = {
212213
{"enabled", config.cache.enabled},
213-
{"max_memory_mb", config.cache.max_memory_bytes / (1024 * 1024)}, // Convert bytes to MB for display
214+
{"max_memory_mb", config.cache.max_memory_bytes / kBytesPerMB}, // Convert bytes to MB for display
214215
{"min_query_cost_ms", config.cache.min_query_cost_ms},
215216
{"ttl_seconds", config.cache.ttl_seconds},
216217
{"invalidation_strategy", config.cache.invalidation_strategy},

src/index/index.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -455,10 +455,12 @@ void Index::Optimize(uint64_t total_docs) {
455455
// RAII guard to ensure flag is cleared
456456
struct OptimizationGuard {
457457
std::atomic<bool>& flag;
458-
explicit OptimizationGuard(std::atomic<bool>& f) : flag(f) {}
458+
explicit OptimizationGuard(std::atomic<bool>& flag_ref) : flag(flag_ref) {}
459459
~OptimizationGuard() { flag = false; }
460460
OptimizationGuard(const OptimizationGuard&) = delete;
461461
OptimizationGuard& operator=(const OptimizationGuard&) = delete;
462+
OptimizationGuard(OptimizationGuard&&) = delete;
463+
OptimizationGuard& operator=(OptimizationGuard&&) = delete;
462464
};
463465
OptimizationGuard guard(is_optimizing_);
464466

src/mysql/binlog_reader.cpp

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -70,14 +70,16 @@ bool BinlogReader::Start() {
7070
struct RunningGuard {
7171
std::atomic<bool>& flag;
7272
bool& success;
73-
explicit RunningGuard(std::atomic<bool>& f, bool& s) : flag(f), success(s) {}
73+
explicit RunningGuard(std::atomic<bool>& flag_ref, bool& success_ref) : flag(flag_ref), success(success_ref) {}
7474
~RunningGuard() {
7575
if (!success) {
7676
flag = false;
7777
}
7878
}
7979
RunningGuard(const RunningGuard&) = delete;
8080
RunningGuard& operator=(const RunningGuard&) = delete;
81+
RunningGuard(RunningGuard&&) = delete;
82+
RunningGuard& operator=(RunningGuard&&) = delete;
8183
};
8284
bool start_success = false;
8385
RunningGuard guard(running_, start_success);
@@ -156,6 +158,7 @@ bool BinlogReader::Start() {
156158
reader_thread_ = std::make_unique<std::thread>(&BinlogReader::ReaderThreadFunc, this);
157159

158160
spdlog::info("Binlog reader started from GTID: {}", current_gtid_);
161+
// NOLINTNEXTLINE(clang-analyzer-deadcode.DeadStores) - Used by RunningGuard destructor
159162
start_success = true; // Mark start as successful
160163
return true;
161164
} catch (const std::exception& e) {
@@ -277,6 +280,7 @@ void BinlogReader::ReaderThreadFunc() {
277280
continue;
278281
}
279282
spdlog::info("[binlog worker] Reconnected successfully");
283+
// NOLINTNEXTLINE(clang-analyzer-deadcode.DeadStores) - Used in next iteration after continue
280284
reconnect_attempt = 0; // Reset delay counter after successful reconnection
281285
continue;
282286
}
@@ -337,6 +341,7 @@ void BinlogReader::ReaderThreadFunc() {
337341
if (!binlog_connection_->Connect()) {
338342
spdlog::error("Failed to reconnect: {}", binlog_connection_->GetLastError());
339343
} else {
344+
// NOLINTNEXTLINE(clang-analyzer-deadcode.DeadStores) - Used in next iteration after continue
340345
reconnect_attempt = 0; // Reset delay counter after successful reconnection
341346
}
342347
continue;
@@ -392,6 +397,7 @@ void BinlogReader::ReaderThreadFunc() {
392397
if (!binlog_connection_->Connect()) {
393398
spdlog::error("Failed to reconnect: {}", binlog_connection_->GetLastError());
394399
} else {
400+
// NOLINTNEXTLINE(clang-analyzer-deadcode.DeadStores) - Used after break exits inner loop
395401
reconnect_attempt = 0; // Reset delay counter after successful reconnection
396402
}
397403
break; // Exit inner loop to retry from outer loop
@@ -527,7 +533,7 @@ bool BinlogReader::EvaluateRequiredFilters(const std::unordered_map<std::string,
527533
}
528534

529535
std::unordered_map<std::string, storage::FilterValue> BinlogReader::ExtractAllFilters(
530-
const RowData& row_data, const config::TableConfig& table_config) const {
536+
const RowData& row_data, const config::TableConfig& table_config) {
531537
std::unordered_map<std::string, storage::FilterValue> all_filters;
532538

533539
// Convert required_filters to FilterConfig format for extraction
@@ -598,7 +604,7 @@ bool BinlogReader::CompareFilterValue(const storage::FilterValue& value, const c
598604
} else if (std::holds_alternative<double>(value)) {
599605
// Float comparison
600606
double val = std::get<double>(value);
601-
double target;
607+
double target = 0.0;
602608
try {
603609
target = std::stod(filter.value);
604610
} catch (const std::exception& e) {
@@ -656,7 +662,7 @@ bool BinlogReader::CompareFilterValue(const storage::FilterValue& value, const c
656662
// For datetime comparison, we need to parse target value
657663
// For now, assume target is numeric (epoch timestamp)
658664
// TODO: Add proper datetime parsing if needed
659-
uint64_t target;
665+
uint64_t target = 0;
660666
try {
661667
target = std::stoull(filter.value);
662668
} catch (const std::exception& e) {
@@ -1087,7 +1093,7 @@ std::optional<BinlogEvent> BinlogReader::ParseBinlogEvent(const unsigned char* b
10871093
event.type = BinlogEventType::UPDATE;
10881094
event.table_name = table_meta->table_name;
10891095
event.primary_key = after_row.primary_key;
1090-
event.text = after_row.text; // New text (after image)
1096+
event.text = after_row.text; // New text (after image)
10911097
event.old_text = before_row.text; // Old text (before image) for index update
10921098
event.gtid = current_gtid_;
10931099

@@ -1488,14 +1494,14 @@ bool BinlogReader::FetchColumnNames(TableMetadata& metadata) {
14881494

14891495
// Cache miss or stale: use SHOW COLUMNS (faster than INFORMATION_SCHEMA)
14901496
// Escape backticks in identifier names
1491-
auto escape_identifier = [](const std::string& id) {
1497+
auto escape_identifier = [](const std::string& identifier) {
14921498
std::string escaped;
1493-
escaped.reserve(id.length());
1494-
for (char c : id) {
1495-
if (c == '`') {
1499+
escaped.reserve(identifier.length());
1500+
for (char chr : identifier) {
1501+
if (chr == '`') {
14961502
escaped += "``"; // Double backtick for escaping
14971503
} else {
1498-
escaped += c;
1504+
escaped += chr;
14991505
}
15001506
}
15011507
return escaped;

src/mysql/binlog_reader.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ struct BinlogEvent {
4848
BinlogEventType type = BinlogEventType::UNKNOWN;
4949
std::string table_name;
5050
std::string primary_key;
51-
std::string text; // Normalized text for INSERT/UPDATE (after image for UPDATE)
51+
std::string text; // Normalized text for INSERT/UPDATE (after image for UPDATE)
5252
std::string old_text; // Before image text for UPDATE events (empty for INSERT/DELETE)
5353
std::unordered_map<std::string, storage::FilterValue> filters;
5454
std::string gtid; // GTID for this event
@@ -254,8 +254,8 @@ class BinlogReader {
254254
* @param table_config Table configuration to use for filter extraction
255255
* @return Map of filter name to FilterValue
256256
*/
257-
std::unordered_map<std::string, storage::FilterValue> ExtractAllFilters(
258-
const RowData& row_data, const config::TableConfig& table_config) const;
257+
static std::unordered_map<std::string, storage::FilterValue> ExtractAllFilters(
258+
const RowData& row_data, const config::TableConfig& table_config);
259259

260260
/**
261261
* @brief Extract all filter columns using default table_config_ (single-table mode)

src/mysql/connection.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -418,7 +418,7 @@ bool Connection::ValidateUniqueColumn(const std::string& database, const std::st
418418
return false;
419419
}
420420

421-
int count;
421+
int count = 0;
422422
try {
423423
count = std::stoi(row[0]);
424424
} catch (const std::invalid_argument& e) {

0 commit comments

Comments
 (0)