Repository navigation
Expand file tree
/
Copy pathMakefile
More file actions
655 lines (578 loc) · 28.2 KB
/
Copy pathMakefile
File metadata and controls
655 lines (578 loc) · 28.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
# OCache Makefile
# Variables
UNAME_S := $(shell uname -s)
UNAME_M := $(shell uname -m)
# Version information (can be overridden)
VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo "dev")
COMMIT := $(shell git rev-parse --short HEAD 2>/dev/null || echo "unknown")
BUILD_TIME := $(shell date -u '+%Y-%m-%dT%H:%M:%SZ')
# Allow custom RocksDB installation path
ROCKSDB_PREFIX ?= /usr/local
# Platform-specific settings
ifeq ($(UNAME_S),Darwin)
# macOS (both Intel and Apple Silicon)
BREW_PREFIX := $(shell brew --prefix 2>/dev/null || echo "/usr/local")
# Check if custom RocksDB exists, otherwise use brew
ifeq ($(wildcard $(ROCKSDB_PREFIX)/include/rocksdb/c.h),)
CGO_CFLAGS := -I$(BREW_PREFIX)/include
CGO_LDFLAGS := -L$(BREW_PREFIX)/lib
else
CGO_CFLAGS := -I$(ROCKSDB_PREFIX)/include
CGO_LDFLAGS := -L$(ROCKSDB_PREFIX)/lib -lrocksdb -lstdc++ -lm -lz -lbz2 -lsnappy -llz4 -lzstd
endif
else ifeq ($(UNAME_S),Linux)
# Linux - prioritize custom RocksDB if available
ifeq ($(wildcard $(ROCKSDB_PREFIX)/include/rocksdb/c.h),)
# Fallback to system paths
CGO_CFLAGS := -I/usr/include -I/usr/local/include
ifeq ($(UNAME_M),x86_64)
CGO_LDFLAGS := -L/usr/lib -L/usr/lib/x86_64-linux-gnu -L/usr/lib64 -L/usr/local/lib
else ifeq ($(UNAME_M),aarch64)
CGO_LDFLAGS := -L/usr/lib -L/usr/lib/aarch64-linux-gnu -L/usr/lib64 -L/usr/local/lib
else ifeq ($(UNAME_M),arm64)
CGO_LDFLAGS := -L/usr/lib -L/usr/lib/aarch64-linux-gnu -L/usr/lib64 -L/usr/local/lib
else
# Generic Linux fallback
CGO_LDFLAGS := -L/usr/lib -L/usr/lib64 -L/usr/local/lib
endif
else
# Use custom RocksDB installation
CGO_CFLAGS := -I$(ROCKSDB_PREFIX)/include
CGO_LDFLAGS := -L$(ROCKSDB_PREFIX)/lib -lrocksdb -lstdc++ -lm -lz -lbz2 -lsnappy -llz4 -lzstd
endif
endif
# Build targets
.PHONY: all
all: build build-cli
# Version ldflags for injecting build info
VERSION_LDFLAGS := -X main.Version=$(VERSION) -X main.Commit=$(COMMIT) -X main.BuildTime=$(BUILD_TIME)
# Static build configuration
STATIC_BUILD ?= false
# WITH_JEMALLOC links jemalloc (bundled in the jemalloc rocksdb-static artifact)
# as the process allocator to cap RocksDB off-heap RSS (issue #176). Linux static
# builds only — jemalloc is not bundled for macOS and the RSS problem is a
# glibc-arena/static-link concern. Requires the `jemalloc` rocksdb-static artifact
# (contains lib/libjemalloc.a).
WITH_JEMALLOC ?= false
ifeq ($(STATIC_BUILD),true)
# Use static RocksDB from artifact
ROCKSDB_STATIC_DIR ?= $(shell pwd)/rocksdb-static/artifact
CGO_CFLAGS := -I$(ROCKSDB_STATIC_DIR)/include
ifeq ($(UNAME_S),Darwin)
# On macOS, also include Homebrew paths for compression libraries.
# macOS doesn't support fully static binaries (only RocksDB is static) and
# bundles no jemalloc, so WITH_JEMALLOC is ignored here.
BREW_PREFIX := $(shell brew --prefix 2>/dev/null || echo "/usr/local")
CGO_LDFLAGS := -L$(ROCKSDB_STATIC_DIR)/lib -L$(BREW_PREFIX)/lib -lrocksdb -lstdc++ -lm -lz -lbz2 -lsnappy -llz4 -lzstd -pthread
LDFLAGS := -ldflags "-s -w $(VERSION_LDFLAGS)"
else
ifeq ($(WITH_JEMALLOC),true)
# -ljemalloc is placed AFTER -lrocksdb so jemalloc's malloc resolves
# RocksDB's allocations, making jemalloc the process allocator. In a fully
# static glibc binary the linker may also pull glibc's malloc.o (for its
# internal aliases); --allow-multiple-definition keeps the first (jemalloc)
# definition instead of erroring. See issue #176.
CGO_LDFLAGS := -L$(ROCKSDB_STATIC_DIR)/lib -lrocksdb -lstdc++ -lm -lz -lbz2 -lsnappy -llz4 -lzstd -ljemalloc -ldl -pthread
LDFLAGS := -ldflags "-linkmode external -extldflags '-static -Wl,--allow-multiple-definition' $(VERSION_LDFLAGS)"
else
CGO_LDFLAGS := -L$(ROCKSDB_STATIC_DIR)/lib -lrocksdb -lstdc++ -lm -lz -lbz2 -lsnappy -llz4 -lzstd -pthread
LDFLAGS := -ldflags "-linkmode external -extldflags '-static' $(VERSION_LDFLAGS)"
endif
endif
else
# Non-static builds still get version info
LDFLAGS := -ldflags "$(VERSION_LDFLAGS)"
endif
.PHONY: build
build: proto
CGO_ENABLED=1 CGO_CFLAGS="$(CGO_CFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" go build $(LDFLAGS) -o ocache ./server/
.PHONY: build-static
build-static:
STATIC_BUILD=true $(MAKE) build
.PHONY: build-cli
build-cli:
ifeq ($(STATIC_BUILD),true)
CGO_ENABLED=0 go build -ldflags "-s -w $(VERSION_LDFLAGS)" -o ocachecli ./client/cmd/
else
CGO_ENABLED=0 go build -ldflags "$(VERSION_LDFLAGS)" -o ocachecli ./client/cmd/
endif
.PHONY: proto
proto: proto-api proto-storage proto-cluster
.PHONY: proto-api
proto-api:
protoc -I ./proto \
-I ./proto/google \
-I . \
--go_out=paths=source_relative:./proto \
--go-grpc_out=paths=source_relative:./proto \
--grpc-gateway_out=paths=source_relative:./proto \
proto/cache.proto
.PHONY: proto-storage
proto-storage:
protoc -I ./storage/proto \
--go_out=paths=source_relative:./storage/proto \
storage/proto/storage.proto
.PHONY: proto-cluster
proto-cluster:
protoc -I ./coordinator/proto \
--go_out=paths=source_relative:./coordinator/proto \
--go-grpc_out=paths=source_relative:./coordinator/proto \
coordinator/proto/cluster.proto
# Installation
.PHONY: install-deps
install-deps: install-protoc install-protoc-plugins
.PHONY: install-rocksdb
install-rocksdb:
ifeq ($(UNAME_S),Darwin)
@echo "Installing RocksDB on macOS..."
@if ! command -v brew &> /dev/null; then \
echo "Homebrew is required but not installed. Please install it first."; \
exit 1; \
fi
brew install rocksdb
else ifeq ($(UNAME_S),Linux)
@echo "Installing RocksDB on Linux..."
@if command -v apt-get &> /dev/null; then \
sudo apt-get update && sudo apt-get install -y librocksdb-dev libsnappy-dev liblz4-dev libzstd-dev zlib1g-dev; \
elif command -v yum &> /dev/null; then \
sudo yum install -y rocksdb-devel snappy-devel lz4-devel libzstd-devel zlib-devel; \
elif command -v dnf &> /dev/null; then \
sudo dnf install -y rocksdb-devel snappy-devel lz4-devel libzstd-devel zlib-devel; \
else \
echo "Unsupported Linux distribution. Please install RocksDB manually."; \
exit 1; \
fi
else
@echo "Unsupported platform: $(UNAME_S)"
@exit 1
endif
.PHONY: install-rocksdb-from-source
install-rocksdb-from-source:
@echo "Installing RocksDB from source..."
@./scripts/install-rocksdb.sh
.PHONY: build-rocksdb-static
build-rocksdb-static:
@echo "Building RocksDB static library..."
@./scripts/build-rocksdb-static.sh
.PHONY: install-protoc
install-protoc:
ifeq ($(UNAME_S),Darwin)
@echo "Installing protoc on macOS..."
@if ! command -v brew &> /dev/null; then \
echo "Homebrew is required but not installed. Please install it first."; \
exit 1; \
fi
brew install protobuf
else ifeq ($(UNAME_S),Linux)
@echo "Installing protoc on Linux..."
@if command -v apt-get &> /dev/null; then \
sudo apt-get update && sudo apt-get install -y protobuf-compiler; \
elif command -v yum &> /dev/null; then \
sudo yum install -y protobuf-compiler; \
elif command -v dnf &> /dev/null; then \
sudo dnf install -y protobuf-compiler; \
else \
echo "Unsupported Linux distribution. Please install protoc manually."; \
exit 1; \
fi
else
@echo "Unsupported platform: $(UNAME_S)"
@exit 1
endif
.PHONY: install-protoc-plugins
install-protoc-plugins:
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
go install github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway@latest
# Run targets
.PHONY: run
run: build
./ocache -disk /tmp/ocache
.PHONY: run-verbose
run-verbose: build
./ocache -disk /tmp/ocache -v
.PHONY: bench
bench: build build-cli run-background
./ocachecli --addr localhost:9000 bench
@$(MAKE) stop
# Compile integration benchmarks once so they can be sampled without rebuilding.
# Set INTEGRATION_BENCH_BINARY to the desired output path.
#
# NOTE: -timeout is a run-time flag, so it cannot be baked in here; a compiled
# test binary has NO default timeout (go test's 10m default is injected at run
# time). Whoever runs the binary must pass -test.timeout (e.g. -test.timeout=300s,
# matching test-integration) or a hung benchmark blocks forever.
INTEGRATION_BENCH_BINARY ?=
.PHONY: bench-integration
bench-integration: proto
@test -n "$(INTEGRATION_BENCH_BINARY)" || { echo "INTEGRATION_BENCH_BINARY is required"; exit 1; }
@mkdir -p "$(dir $(INTEGRATION_BENCH_BINARY))"
@cd tests/integration && CGO_CFLAGS="$(CGO_CFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" go test $(LDFLAGS) -c -o "$(INTEGRATION_BENCH_BINARY)" .
# Compile the compaction/serving benchmark once so paired runs do not include
# Go compilation. PERFLOOP_BUILD_OUTPUT_DIR is supplied by the benchmark runner.
PERFLOOP_BUILD_OUTPUT_DIR ?=
.PHONY: build-bench-compaction-serving-reads
build-bench-compaction-serving-reads: proto
@test -n "$(PERFLOOP_BUILD_OUTPUT_DIR)" || { echo "PERFLOOP_BUILD_OUTPUT_DIR is required"; exit 1; }
@mkdir -p "$(PERFLOOP_BUILD_OUTPUT_DIR)"
@cd server && CGO_ENABLED=1 CGO_CFLAGS="$(CGO_CFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" go test $(LDFLAGS) -tags=ocache_benchmark -c -o "$(PERFLOOP_BUILD_OUTPUT_DIR)/compaction-serving-reads.test" .
# Compile the cleaner reconciliation benchmark once so paired runs measure the
# scheduled cleanup-loop tick without Go compilation. BENCH_OUTPUT is supplied
# by the benchmark runner.
BENCH_OUTPUT ?=
.PHONY: build-storage-bench
build-storage-bench: proto
@test -n "$(BENCH_OUTPUT)" || { echo "BENCH_OUTPUT is required"; exit 1; }
@mkdir -p "$(dir $(BENCH_OUTPUT))"
@cd storage && CGO_ENABLED=1 CGO_CFLAGS="$(CGO_CFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" go test $(LDFLAGS) -c -o "$(BENCH_OUTPUT)" .
# Compile the ring heartbeat benchmark once so paired runs do not include Go
# compilation. PERFLOOP_BUILD_OUTPUT_DIR is supplied by the benchmark runner.
.PHONY: build-bench-ring-heartbeat
build-bench-ring-heartbeat:
@test -n "$(PERFLOOP_BUILD_OUTPUT_DIR)" || { echo "PERFLOOP_BUILD_OUTPUT_DIR is required"; exit 1; }
@mkdir -p "$(PERFLOOP_BUILD_OUTPUT_DIR)"
@cd coordinator && go test $(LDFLAGS) -c -o "$(PERFLOOP_BUILD_OUTPUT_DIR)/ring-heartbeat.test" ./ring
# Compile the topology RPC benchmark once so paired runs do not include Go
# compilation. PERFLOOP_BUILD_OUTPUT_DIR is supplied by the benchmark runner.
.PHONY: build-bench-cache-topology-rpc
build-bench-cache-topology-rpc:
@test -n "$(PERFLOOP_BUILD_OUTPUT_DIR)" || { echo "PERFLOOP_BUILD_OUTPUT_DIR is required"; exit 1; }
@mkdir -p "$(PERFLOOP_BUILD_OUTPUT_DIR)"
@cd server && go test $(LDFLAGS) -tags=ocache_topology_benchmark -c -o "$(PERFLOOP_BUILD_OUTPUT_DIR)/cache-topology-rpc.test" ./service
# Compile the CacheService delete benchmark once so paired runs do not include
# Go compilation. PERFLOOP_BUILD_OUTPUT_DIR is supplied by the benchmark runner.
.PHONY: build-bench-cache-service-delete
build-bench-cache-service-delete: proto
@test -n "$(PERFLOOP_BUILD_OUTPUT_DIR)" || { echo "PERFLOOP_BUILD_OUTPUT_DIR is required"; exit 1; }
@mkdir -p "$(PERFLOOP_BUILD_OUTPUT_DIR)"
@cd server && CGO_ENABLED=1 CGO_CFLAGS="$(CGO_CFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" go test $(LDFLAGS) -c -o "$(PERFLOOP_BUILD_OUTPUT_DIR)/cache-service-delete.test" ./service
# Compile the CacheService ListWithValues benchmark once so paired runs do not
# include Go compilation. PERFLOOP_BUILD_OUTPUT_DIR is supplied by the benchmark runner.
.PHONY: build-bench-cache-service-list-with-values
build-bench-cache-service-list-with-values: proto
@test -n "$(PERFLOOP_BUILD_OUTPUT_DIR)" || { echo "PERFLOOP_BUILD_OUTPUT_DIR is required"; exit 1; }
@mkdir -p "$(PERFLOOP_BUILD_OUTPUT_DIR)"
@cd server && CGO_ENABLED=1 CGO_CFLAGS="$(CGO_CFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" go test $(LDFLAGS) -c -o "$(PERFLOOP_BUILD_OUTPUT_DIR)/cache-service-list-with-values.test" ./service
.PHONY: run-background
run-background:
@echo "Starting ocache in background..."
@./ocache -disk /tmp/ocache > ocache.log 2>&1 & echo $$! > ocache.pid
@sleep 2
.PHONY: stop
stop:
@if [ -f ocache.pid ]; then \
kill `cat ocache.pid` 2>/dev/null || true; \
rm -f ocache.pid; \
echo "Stopped ocache"; \
fi
# Testing targets
# Allow specifying specific tests with TEST variable (e.g., make test TEST=TestMyFunction)
# or with TESTRUN for pattern matching (e.g., make test TESTRUN=MyFunction)
TEST ?=
TESTRUN ?=
TESTFLAGS := $(if $(TEST),-run $(TEST),$(if $(TESTRUN),-run $(TESTRUN),))
.PHONY: test
test: test-server test-storage test-client test-coordinator test-embedded
.PHONY: test-all
test-all: test-server test-storage test-client test-coordinator test-embedded test-integration test-e2e
.PHONY: test-server
test-server: proto
@echo "Running server tests..."
$(if $(TEST)$(TESTRUN),@echo "Filter: $(if $(TEST),$(TEST),$(TESTRUN))",)
@cd server && CGO_CFLAGS="$(CGO_CFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" go test $(LDFLAGS) -v -timeout 60s $(TESTFLAGS) ./...
.PHONY: test-storage
test-storage: proto
@echo "Running storage tests..."
$(if $(TEST)$(TESTRUN),@echo "Filter: $(if $(TEST),$(TEST),$(TESTRUN))",)
@cd storage && CGO_CFLAGS="$(CGO_CFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" go test $(LDFLAGS) -v -timeout 60s $(TESTFLAGS) ./...
.PHONY: test-storage-wire
test-storage-wire: proto
@cd storage && CGO_CFLAGS="$(CGO_CFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" go test $(LDFLAGS) -v -timeout 60s -run '^TestValueMessage' .
.PHONY: test-storage-reconcile
test-storage-reconcile: proto
@cd storage && CGO_CFLAGS="$(CGO_CFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" go test $(LDFLAGS) -v -timeout 60s -run '^TestCleaner' .
.PHONY: test-client
test-client: proto
@echo "Running client tests..."
$(if $(TEST)$(TESTRUN),@echo "Filter: $(if $(TEST),$(TEST),$(TESTRUN))",)
@cd client && go test -v -timeout 30s $(TESTFLAGS) ./...
.PHONY: test-coordinator
test-coordinator: proto
@echo "Running coordinator tests..."
$(if $(TEST)$(TESTRUN),@echo "Filter: $(if $(TEST),$(TEST),$(TESTRUN))",)
@cd coordinator && go test -v -timeout 30s $(TESTFLAGS) ./...
.PHONY: test-embedded
test-embedded: proto
@echo "Running embedded tests..."
$(if $(TEST)$(TESTRUN),@echo "Filter: $(if $(TEST),$(TEST),$(TESTRUN))",)
@cd embedded && CGO_CFLAGS="$(CGO_CFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" go test $(LDFLAGS) -v -timeout 60s $(TESTFLAGS) ./...
.PHONY: test-race
test-race: proto
@echo "Running race tests for server..."
$(if $(TEST)$(TESTRUN),@echo "Filter: $(if $(TEST),$(TEST),$(TESTRUN))",)
@cd server && CGO_CFLAGS="$(CGO_CFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" go test $(LDFLAGS) -race -v -timeout 60s $(TESTFLAGS) ./...
@echo "Running race tests for storage..."
@cd storage && CGO_CFLAGS="$(CGO_CFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" go test $(LDFLAGS) -race -v -timeout 60s $(TESTFLAGS) ./...
@echo "Running race tests for coordinator..."
@cd coordinator && go test -race -v -timeout 30s $(TESTFLAGS) ./...
@echo "Running race tests for client..."
@cd client && go test -race -v -timeout 30s $(TESTFLAGS) ./...
@echo "Running race tests for embedded..."
@cd embedded && CGO_CFLAGS="$(CGO_CFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" go test $(LDFLAGS) -race -v -timeout 60s $(TESTFLAGS) ./...
.PHONY: test-coverage
test-coverage: proto
@echo "Running coverage tests for server..."
$(if $(TEST)$(TESTRUN),@echo "Filter: $(if $(TEST),$(TEST),$(TESTRUN))",)
@cd server && CGO_CFLAGS="$(CGO_CFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" go test $(LDFLAGS) -coverprofile=../coverage-server.out -timeout 60s $(TESTFLAGS) ./...
@echo "Running coverage tests for storage..."
@cd storage && CGO_CFLAGS="$(CGO_CFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" go test $(LDFLAGS) -coverprofile=../coverage-storage.out -timeout 60s $(TESTFLAGS) ./...
@echo "Running coverage tests for client..."
@cd client && go test -coverprofile=../coverage-client.out -timeout 30s $(TESTFLAGS) ./...
@echo "Running coverage tests for coordinator..."
@cd coordinator && go test -coverprofile=../coverage-coordinator.out -timeout 30s $(TESTFLAGS) ./...
@echo "Running coverage tests for embedded..."
@cd embedded && CGO_CFLAGS="$(CGO_CFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" go test $(LDFLAGS) -coverprofile=../coverage-embedded.out -timeout 60s $(TESTFLAGS) ./...
@echo "Combining coverage reports..."
@echo "mode: set" > coverage.out
@tail -n +2 coverage-server.out >> coverage.out 2>/dev/null || true
@tail -n +2 coverage-storage.out >> coverage.out 2>/dev/null || true
@tail -n +2 coverage-client.out >> coverage.out 2>/dev/null || true
@tail -n +2 coverage-coordinator.out >> coverage.out 2>/dev/null || true
@tail -n +2 coverage-embedded.out >> coverage.out 2>/dev/null || true
@rm -f coverage-*.out
@go tool cover -html=coverage.out -o coverage.html
@echo "Coverage report generated at coverage.html"
.PHONY: test-e2e
test-e2e: build build-cli
@echo "Running all E2E tests..."
@$(MAKE) test-e2e-concurrent
@$(MAKE) test-e2e-storage-layers
@$(MAKE) test-e2e-ttl
@$(MAKE) test-e2e-eviction
@$(MAKE) test-e2e-cas
@$(MAKE) test-e2e-compaction
@$(MAKE) test-e2e-recompaction
@$(MAKE) test-e2e-data-validation
@$(MAKE) test-e2e-cluster
.PHONY: test-e2e-concurrent
test-e2e-concurrent: build build-cli
@echo "Running concurrent operations E2E test..."
./tests/e2e/concurrent_ops_test.sh
.PHONY: test-e2e-storage-layers
test-e2e-storage-layers: build build-cli
@echo "Running storage layers E2E test..."
./tests/e2e/storage_layers_test.sh
.PHONY: test-e2e-ttl
test-e2e-ttl: build build-cli
@echo "Running TTL functionality E2E test..."
./tests/e2e/ttl_cleaner_test.sh
.PHONY: test-e2e-eviction
test-e2e-eviction: build build-cli
@echo "Running eviction E2E test (LRU + FIFO)..."
./tests/e2e/eviction_test.sh
.PHONY: test-e2e-cas
test-e2e-cas: build build-cli
@echo "Running CAS (conditional-ops) E2E test..."
./tests/e2e/cas_test.sh
.PHONY: test-e2e-compaction
test-e2e-compaction: build build-cli
@echo "Running compaction E2E test..."
./tests/e2e/compaction_test.sh
.PHONY: test-e2e-recompaction
test-e2e-recompaction: build build-cli
@echo "Running recompaction E2E test..."
./tests/e2e/recompaction_test.sh
.PHONY: test-e2e-data-validation
test-e2e-data-validation: build build-cli
@echo "Running comprehensive data validation E2E test..."
./tests/e2e/data_validation_test.sh
.PHONY: test-e2e-cluster
test-e2e-cluster: build build-cli
@echo "Running cluster mode concurrent operations E2E test..."
./tests/e2e/cluster_concurrent_ops_test.sh
.PHONY: test-integration
test-integration: proto
@echo "Running integration tests..."
$(if $(TEST)$(TESTRUN),@echo "Filter: $(if $(TEST),$(TEST),$(TESTRUN))",)
@cd tests/integration && CGO_CFLAGS="$(CGO_CFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" go test $(LDFLAGS) -v -timeout 300s $(TESTFLAGS) ./...
.PHONY: test-integration-short
test-integration-short: proto
@echo "Running integration tests (short mode)..."
$(if $(TEST)$(TESTRUN),@echo "Filter: $(if $(TEST),$(TEST),$(TESTRUN))",)
@cd tests/integration && CGO_CFLAGS="$(CGO_CFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" go test $(LDFLAGS) -v -short -timeout 30s $(TESTFLAGS) ./...
.PHONY: test-integration-race
test-integration-race: proto
@echo "Running integration tests with race detector..."
$(if $(TEST)$(TESTRUN),@echo "Filter: $(if $(TEST),$(TEST),$(TESTRUN))",)
@cd tests/integration && CGO_CFLAGS="$(CGO_CFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" go test $(LDFLAGS) -race -v -timeout 180s $(TESTFLAGS) ./...
.PHONY: test-integration-coverage
test-integration-coverage: proto
@echo "Running integration tests with coverage..."
$(if $(TEST)$(TESTRUN),@echo "Filter: $(if $(TEST),$(TEST),$(TESTRUN))",)
@cd tests/integration && CGO_CFLAGS="$(CGO_CFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" go test $(LDFLAGS) -coverprofile=../../coverage-integration.out -timeout 300s $(TESTFLAGS) ./...
@go tool cover -html=coverage-integration.out -o coverage-integration.html
@echo "Integration test coverage report generated at coverage-integration.html"
.PHONY: test-integration-objects
test-integration-objects:
@echo "Running small, medium, and large objects integration tests..."
$(if $(TEST)$(TESTRUN),@echo "Filter: $(if $(TEST),$(TEST),$(TESTRUN))",)
@cd tests/integration && CGO_CFLAGS="$(CGO_CFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" go test $(LDFLAGS) -v -run $(if $(TEST)$(TESTRUN),$(if $(TEST),$(TEST),$(TESTRUN)),TestIntegration_Objects) -timeout 120s ./...
.PHONY: test-integration-compaction
test-integration-compaction:
@echo "Running compaction integration tests..."
$(if $(TEST)$(TESTRUN),@echo "Filter: $(if $(TEST),$(TEST),$(TESTRUN))",)
@cd tests/integration && CGO_CFLAGS="$(CGO_CFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" go test $(LDFLAGS) -v -run $(if $(TEST)$(TESTRUN),$(if $(TEST),$(TEST),$(TESTRUN)),TestIntegration_Compaction) -timeout 300s ./...
.PHONY: test-integration-cas
test-integration-cas:
@echo "Running CAS integration tests..."
$(if $(TEST)$(TESTRUN),@echo "Filter: $(if $(TEST),$(TEST),$(TESTRUN))",)
@cd tests/integration && CGO_CFLAGS="$(CGO_CFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" go test $(LDFLAGS) -v -run $(if $(TEST)$(TESTRUN),$(if $(TEST),$(TEST),$(TESTRUN)),CAS) -timeout 300s ./...
.PHONY: test-integration-cleaner
test-integration-cleaner:
@echo "Running cleaner integration tests (TTL and LRU)..."
$(if $(TEST)$(TESTRUN),@echo "Filter: $(if $(TEST),$(TEST),$(TESTRUN))",)
@cd tests/integration && CGO_CFLAGS="$(CGO_CFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" go test $(LDFLAGS) -v -run $(if $(TEST)$(TESTRUN),$(if $(TEST),$(TEST),$(TESTRUN)),TestIntegration_Cleaner) -timeout 120s ./...
.PHONY: test-integration-workflow
test-integration-workflow:
@echo "Running workflow integration tests..."
$(if $(TEST)$(TESTRUN),@echo "Filter: $(if $(TEST),$(TEST),$(TESTRUN))",)
@cd tests/integration && CGO_CFLAGS="$(CGO_CFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" go test $(LDFLAGS) -v -run $(if $(TEST)$(TESTRUN),$(if $(TEST),$(TEST),$(TESTRUN)),TestIntegration_Workflow) -timeout 300s ./...
.PHONY: test-integration-coordinator
test-integration-coordinator:
@echo "Running coordinator integration tests..."
$(if $(TEST)$(TESTRUN),@echo "Filter: $(if $(TEST),$(TEST),$(TESTRUN))",)
@cd tests/integration && CGO_CFLAGS="$(CGO_CFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" go test $(LDFLAGS) -v -run $(if $(TEST)$(TESTRUN),$(if $(TEST),$(TEST),$(TESTRUN)),TestIntegration_Coordinator) -timeout 600s ./...
.PHONY: test-integration-cluster
test-integration-cluster:
@echo "Running cluster integration tests..."
$(if $(TEST)$(TESTRUN),@echo "Filter: $(if $(TEST),$(TEST),$(TESTRUN))",)
@cd tests/integration && CGO_CFLAGS="$(CGO_CFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" go test $(LDFLAGS) -v -run $(if $(TEST)$(TESTRUN),$(if $(TEST),$(TEST),$(TESTRUN)),TestIntegration_Cluster) -timeout 600s ./...
# Code quality targets
.PHONY: lint
lint:
@echo "Running go vet..."
@cd server && CGO_CFLAGS="$(CGO_CFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" go vet $(LDFLAGS) ./...
@cd client && go vet ./...
@cd embedded && CGO_CFLAGS="$(CGO_CFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" go vet $(LDFLAGS) ./...
@echo "Running gofmt..."
@gofmt -l -d $$(find . -name '*.go' -not -path './proto/*')
.PHONY: license-check
license-check:
@echo "Checking dependency licenses..."
@CGO_CFLAGS="$(CGO_CFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" bash scripts/check-licenses.sh
.PHONY: tidy
tidy:
@echo "Running go mod tidy..."
@go work sync
@cd server && go mod tidy
@cd client && go mod tidy
@cd proto && go mod tidy
@cd coordinator && go mod tidy
@cd coordinator/proto && go mod tidy
@cd storage && go mod tidy
@cd common && go mod tidy
@cd tests/integration && go mod tidy
.PHONY: lint-ci
lint-ci:
@echo "Running gofmt..."
@gofmt -l -d $$(find . -name '*.go' -not -path './proto/*')
@echo "Running go mod tidy check..."
@go work sync
@cd server && go mod tidy
@cd client && go mod tidy
@cd proto && go mod tidy
@git diff --exit-code go.mod go.sum || (echo "go.mod or go.sum is not tidy" && exit 1)
.PHONY: lint-fix
lint-fix:
@echo "Fixing formatting issues..."
@gofmt -w $$(find . -name '*.go' -not -path './proto/*')
@echo "Running go mod tidy..."
@go work sync
@cd server && go mod tidy
@cd client && go mod tidy
@cd proto && go mod tidy
.PHONY: vet
vet:
@cd server && CGO_CFLAGS="$(CGO_CFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" go vet $(LDFLAGS) ./...
@cd client && go vet ./...
@cd embedded && CGO_CFLAGS="$(CGO_CFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" go vet $(LDFLAGS) ./...
.PHONY: fmt
fmt:
@gofmt -w $$(find . -name '*.go' -not -path './proto/*')
.PHONY: fmt-check
fmt-check:
@gofmt -l $$(find . -name '*.go' -not -path './proto/*')
.PHONY: check
check: fmt-check vet test
@echo "All checks passed!"
# Clean targets
.PHONY: clean
clean:
rm -f ocache ocachecli ocache.log ocache.pid
rm -f coverage.out coverage.html coverage-integration.out coverage-integration.html
rm -rf /tmp/ocache /tmp/ocache-demo /tmp/ocache-integration-test-*
# Help target
.PHONY: help
help:
@echo "OCache Makefile targets:"
@echo ""
@echo "Build targets:"
@echo " all - Build both server and CLI"
@echo " build - Build the server"
@echo " build-static - Build the server with static RocksDB"
@echo " build-cli - Build the CLI client"
@echo " proto - Generate Go code from protobuf"
@echo ""
@echo "Test targets:"
@echo " test - Run unit tests (server and client)"
@echo " test-all - Run all tests (unit and integration)"
@echo " test-server - Run server tests only"
@echo " test-client - Run client tests only"
@echo " test-coordinator - Run coordinator tests only"
@echo " test-race - Run tests with race detector"
@echo " test-coverage - Run tests with coverage report"
@echo " test-e2e - Run end-to-end tests"
@echo " test-e2e-cluster - Run cluster mode E2E tests"
@echo " test-integration - Run integration tests (storage layer)"
@echo " test-integration-short - Run integration tests in short mode"
@echo " test-integration-objects - Run small, medium, and large objects integration tests"
@echo " test-integration-compaction - Run compaction integration tests"
@echo " test-integration-cas - Run CAS (conditional-ops) integration tests"
@echo " test-integration-cleaner - Run cleaner integration tests (TTL and LRU)"
@echo " test-integration-workflow - Run cross-component integration tests"
@echo " test-integration-coordinator - Run coordinator/cluster integration tests"
@echo " test-integration-cluster - Run cluster integration tests"
@echo " test-integration-race - Run integration tests with race detector"
@echo " test-integration-coverage - Run integration tests with coverage"
@echo " bench - Run benchmarks"
@echo " bench-integration - Compile integration benchmarks (set INTEGRATION_BENCH_BINARY)"
@echo " build-bench-compaction-serving-reads - Compile the compaction/serving benchmark (set PERFLOOP_BUILD_OUTPUT_DIR)"
@echo " build-bench-ring-heartbeat - Compile the ring heartbeat benchmark (set PERFLOOP_BUILD_OUTPUT_DIR)"
@echo " build-bench-cache-topology-rpc - Compile the CacheService topology RPC benchmark (set PERFLOOP_BUILD_OUTPUT_DIR)"
@echo " build-bench-cache-service-delete - Compile the CacheService delete benchmark (set PERFLOOP_BUILD_OUTPUT_DIR)"
@echo " build-bench-cache-service-list-with-values - Compile the CacheService ListWithValues benchmark (set PERFLOOP_BUILD_OUTPUT_DIR)"
@echo ""
@echo " To run specific tests, use TEST or TESTRUN variable:"
@echo " make test TEST=TestMyFunction - Run exact test name"
@echo " make test TESTRUN=MyFunction - Run tests matching pattern"
@echo " make test-server TEST=TestStorage - Run specific server test"
@echo ""
@echo "Code quality targets:"
@echo " lint - Run linters (vet, gofmt check, mod tidy)"
@echo " lint-fix - Fix linting issues"
@echo " vet - Run go vet"
@echo " fmt - Format code with gofmt"
@echo " fmt-check - Check code formatting"
@echo " check - Run all quality checks (fmt, vet, test)"
@echo ""
@echo "Run targets:"
@echo " run - Run the server with default options"
@echo " run-verbose - Run the server with verbose logging"
@echo ""
@echo "Other targets:"
@echo " install-deps - Install dependencies (RocksDB)"
@echo " install-protoc-plugins - Install protoc Go plugins"
@echo " build-rocksdb-static - Build RocksDB static library"
@echo " clean - Remove built binaries and generated files"
@echo " help - Show this help message"
.DEFAULT_GOAL := help