Skip to content

Commit 5da20fc

Browse files
committed
feat(meta-client): add cache crate for databend-meta service
Crate `databend-common-meta-cache` provide a process-wide in memory cache for databend-meta service. It stores a continous range of key values locally and watches the updates on a remote databend-meta and continously apply new changes to local store. The cache may be obsolete if the connection to the datbend-meta service is unstable. Although in the background it keep trying to re-connecting. This cache requires at least databend-meta 1.2.677; if the server is older the cache returns a `Unsupported` error when accessing it. Usage: ```rust let client = MetaGrpcClient::try_create(/*..*/); let cache = Cache::new( client, "your/cache/key/space/in/meta/service", "your-app-name-for-logging", ).await; let value = cache.try_get("key").await?; ```
1 parent 33dd91f commit 5da20fc

15 files changed

+1361
-0
lines changed

Cargo.lock

+17
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+2
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ members = [
9292
"src/meta/app",
9393
"src/meta/app-types",
9494
"src/meta/binaries",
95+
"src/meta/cache",
9596
"src/meta/client",
9697
"src/meta/control",
9798
"src/meta/ee",
@@ -141,6 +142,7 @@ databend-common-management = { path = "src/query/management" }
141142
databend-common-meta-api = { path = "src/meta/api" }
142143
databend-common-meta-app = { path = "src/meta/app" }
143144
databend-common-meta-app-types = { path = "src/meta/app-types" }
145+
databend-common-meta-cache = { path = "src/meta/cache" }
144146
databend-common-meta-client = { path = "src/meta/client" }
145147
databend-common-meta-control = { path = "src/meta/control" }
146148
databend-common-meta-embedded = { path = "src/meta/embedded" }

src/meta/cache/Cargo.toml

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
[package]
2+
name = "databend-common-meta-cache"
3+
description = """
4+
A distributed cache implementation that:
5+
- Maintains a local view of data stored in the meta-service
6+
- Automatically synchronizes with the meta-service via watch API
7+
- Provides safe concurrent access with two-level locking
8+
- Handles connection failures with automatic recovery
9+
- Ensures data consistency through sequence number tracking
10+
"""
11+
version = { workspace = true }
12+
authors = { workspace = true }
13+
license = { workspace = true }
14+
publish = { workspace = true }
15+
edition = { workspace = true }
16+
17+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
18+
19+
[lib]
20+
doctest = false
21+
test = true
22+
23+
[dependencies]
24+
databend-common-base = { workspace = true }
25+
databend-common-meta-client = { workspace = true }
26+
databend-common-meta-types = { workspace = true }
27+
futures = { workspace = true }
28+
log = { workspace = true }
29+
thiserror = { workspace = true }
30+
tokio = { workspace = true }
31+
tonic = { workspace = true }
32+
33+
[dev-dependencies]
34+
anyhow = { workspace = true }
35+
pretty_assertions = { workspace = true }
36+
37+
[lints]
38+
workspace = true

src/meta/cache/README.md

+94
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
# Databend Common Meta Cache
2+
3+
A distributed cache implementation based on meta-service, providing reliable resource management and data synchronization across distributed systems.
4+
5+
6+
## Features
7+
8+
- **Automatic Synchronization**: Background watcher task keeps local cache in sync with meta-service
9+
- **Concurrency Control**: Two-level concurrency control mechanism for safe access
10+
- **Event-based Updates**: Real-time updates through meta-service watch API
11+
- **Safe Reconnection**: Automatic recovery from connection failures with state consistency
12+
13+
## Key Components
14+
15+
### Cache Structure
16+
17+
```text
18+
<prefix>/foo
19+
<prefix>/..
20+
<prefix>/..
21+
```
22+
23+
- `<prefix>`: User-defined string to identify a cache instance
24+
25+
### Main Types
26+
27+
- `Cache`: The main entry point for cache operations
28+
- Provides safe access to cached data
29+
- `CacheData`: Internal data structure holding the cached values
30+
- `EventWatcher`: Background task that watches for changes in meta-service
31+
- Handles synchronization with meta-service
32+
33+
## Usage
34+
35+
```rust
36+
let client = MetaGrpcClient::try_create(/*..*/);
37+
let cache = Cache::new(
38+
client,
39+
"your/cache/key/space/in/meta/service",
40+
"your-app-name-for-logging",
41+
).await;
42+
43+
// Access cached data
44+
cache.try_access(|c: &CacheData| {
45+
println!("last-seq:{}", c.last_seq);
46+
println!("all data: {:?}", c.data);
47+
}).await?;
48+
49+
// Get a specific value
50+
let value = cache.try_get("key").await?;
51+
52+
// List all entries under a prefix
53+
let entries = cache.try_list_dir("prefix").await?;
54+
```
55+
56+
## Concurrency Control
57+
58+
The cache employs a two-level concurrency control mechanism:
59+
60+
1. **Internal Lock (Mutex)**: Protects concurrent access between user operations and the background cache updater. This lock is held briefly during each operation.
61+
62+
2. **External Lock (Method Design)**: Public methods require `&mut self` even for read-only operations. This prevents concurrent access to the cache instance from multiple call sites. External synchronization should be implemented by the caller if needed.
63+
64+
This design intentionally separates concerns:
65+
- The internal lock handles short-term, fine-grained synchronization with the updater
66+
- The external lock requirement (`&mut self`) enables longer-duration access patterns without blocking the background updater unnecessarily
67+
68+
Note that despite requiring `&mut self`, all operations are logically read-only with respect to the cache's public API.
69+
70+
## Initialization Process
71+
72+
When a `Cache` is created, it goes through the following steps:
73+
74+
1. Creates a new instance with specified prefix and context
75+
2. Spawns a background task to watch for key-value changes
76+
3. Establishes a watch stream to meta-service
77+
4. Fetches and processes initial data
78+
5. Waits for the cache to be fully initialized before returning
79+
6. Maintains continuous synchronization
80+
81+
The initialization is complete only when the cache has received a full copy of the data from meta-service, ensuring users see a consistent view of the data.
82+
83+
## Error Handling
84+
85+
The cache implements robust error handling:
86+
87+
- Connection failures are automatically retried in the background
88+
- Background watcher task automatically recovers from errors
89+
- Users are shielded from transient errors through the abstraction
90+
- The cache ensures data consistency by tracking sequence numbers
91+
92+
## License
93+
94+
This project is licensed under the Apache License 2.0 - see the LICENSE file for details.

0 commit comments

Comments
 (0)