rocksdb-dedup 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- rocksdb_dedup-0.1.0/PKG-INFO +184 -0
- rocksdb_dedup-0.1.0/README.md +157 -0
- rocksdb_dedup-0.1.0/pyproject.toml +47 -0
- rocksdb_dedup-0.1.0/setup.cfg +4 -0
- rocksdb_dedup-0.1.0/src/dedup_client/__init__.py +22 -0
- rocksdb_dedup-0.1.0/src/dedup_client/client.py +307 -0
- rocksdb_dedup-0.1.0/src/dedup_client/dedup_pb2.py +75 -0
- rocksdb_dedup-0.1.0/src/dedup_client/dedup_pb2_grpc.py +435 -0
- rocksdb_dedup-0.1.0/src/dedup_client/py.typed +0 -0
- rocksdb_dedup-0.1.0/src/dedup_client/sharded.py +460 -0
- rocksdb_dedup-0.1.0/src/rocksdb_dedup.egg-info/PKG-INFO +184 -0
- rocksdb_dedup-0.1.0/src/rocksdb_dedup.egg-info/SOURCES.txt +15 -0
- rocksdb_dedup-0.1.0/src/rocksdb_dedup.egg-info/dependency_links.txt +1 -0
- rocksdb_dedup-0.1.0/src/rocksdb_dedup.egg-info/requires.txt +5 -0
- rocksdb_dedup-0.1.0/src/rocksdb_dedup.egg-info/top_level.txt +1 -0
- rocksdb_dedup-0.1.0/tests/test_integration.py +339 -0
- rocksdb_dedup-0.1.0/tests/test_sharded.py +220 -0
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: rocksdb-dedup
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: 基于 RocksDB 与布隆过滤器的高性能去重服务 Python 客户端
|
|
5
|
+
Author: Dedup Team
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/your-org/rocksdb-dedup
|
|
8
|
+
Project-URL: Repository, https://github.com/your-org/rocksdb-dedup
|
|
9
|
+
Keywords: rocksdb,bloom-filter,dedup,grpc,爬虫去重
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
21
|
+
Requires-Python: >=3.8
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
Requires-Dist: grpcio>=1.60.0
|
|
24
|
+
Provides-Extra: dev
|
|
25
|
+
Requires-Dist: grpcio-tools>=1.60.0; extra == "dev"
|
|
26
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
27
|
+
|
|
28
|
+
# rocksdb-dedup
|
|
29
|
+
|
|
30
|
+
基于 RocksDB + 布隆过滤器的高性能持久化去重服务 Python 客户端。
|
|
31
|
+
|
|
32
|
+
## 特性
|
|
33
|
+
|
|
34
|
+
- 原子 `check_and_set` 操作,保证去重准确性
|
|
35
|
+
- 批量操作接口,高 QPS 场景下减少 RPC 开销
|
|
36
|
+
- 多租户 namespace 隔离
|
|
37
|
+
- 支持过期自动清理
|
|
38
|
+
- 布隆过滤器自动扩容
|
|
39
|
+
- 预生成 gRPC 代码,零编译步骤,开箱即用
|
|
40
|
+
|
|
41
|
+
## 安装
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
pip install rocksdb-dedup
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
或从源码安装:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
pip install clients/python
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
> 注意:运行时只依赖 `grpcio`,不需要安装 `grpcio-tools` 或 protoc 编译器。
|
|
54
|
+
|
|
55
|
+
## 快速开始
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
from dedup_client import DedupClient
|
|
59
|
+
import time
|
|
60
|
+
|
|
61
|
+
# 连接服务
|
|
62
|
+
with DedupClient("localhost:50051") as client:
|
|
63
|
+
ns = "spider_news"
|
|
64
|
+
expire_at = int(time.time() * 1000) + 30 * 86400 * 1000 # 30天后过期
|
|
65
|
+
|
|
66
|
+
# 核心接口:检查并写入
|
|
67
|
+
result = client.check_and_set(
|
|
68
|
+
namespace=ns,
|
|
69
|
+
key=b"https://example.com/page/1",
|
|
70
|
+
expire_at_ms=expire_at,
|
|
71
|
+
metadata=b'{"depth": 3, "source": "seed"}',
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
if result == "inserted":
|
|
75
|
+
print("新 URL,开始抓取")
|
|
76
|
+
elif result == "duplicate":
|
|
77
|
+
print("重复 URL,跳过")
|
|
78
|
+
elif result == "expired_and_replaced":
|
|
79
|
+
print("已过期,重新抓取")
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## API 列表
|
|
83
|
+
|
|
84
|
+
| 方法 | 说明 |
|
|
85
|
+
|---|---|
|
|
86
|
+
| `check_and_set(namespace, key, expire_at_ms, metadata)` | 原子检查并写入 |
|
|
87
|
+
| `batch_check_and_set(items)` | 批量检查并写入 |
|
|
88
|
+
| `exists(namespace, key)` | 仅检查是否存在 |
|
|
89
|
+
| `batch_exists(namespace, keys)` | 批量检查存在性 |
|
|
90
|
+
| `get(namespace, key)` | 获取详细信息 |
|
|
91
|
+
| `delete(namespace, key)` | 删除 key |
|
|
92
|
+
| `batch_delete(namespace, keys)` | 批量删除 |
|
|
93
|
+
| `stats(namespace)` | 获取服务统计 |
|
|
94
|
+
|
|
95
|
+
### 批量操作示例
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
# 批量检查并写入
|
|
99
|
+
items = [
|
|
100
|
+
("spider_news", b"https://example.com/1", expire_at, None),
|
|
101
|
+
("spider_news", b"https://example.com/2", expire_at, b"meta2"),
|
|
102
|
+
("spider_news", b"https://example.com/3", expire_at, None),
|
|
103
|
+
]
|
|
104
|
+
results = client.batch_check_and_set(items)
|
|
105
|
+
# results: ["inserted", "inserted", "inserted"]
|
|
106
|
+
|
|
107
|
+
# 批量检查存在性
|
|
108
|
+
keys = [b"https://example.com/1", b"https://example.com/999"]
|
|
109
|
+
exists_list = client.batch_exists("spider_news", keys)
|
|
110
|
+
# exists_list: [True, False]
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### 服务状态
|
|
114
|
+
|
|
115
|
+
```python
|
|
116
|
+
stats = client.stats()
|
|
117
|
+
print(f"总 key 数: {stats['total_keys']:,}")
|
|
118
|
+
print(f"布隆容量: {stats['bloom_capacity']:,}")
|
|
119
|
+
print(f"误判率: {stats['bloom_fpp']:.4%}")
|
|
120
|
+
print(f"QPS: {stats['qps_estimate']:,}")
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## 配置选项
|
|
124
|
+
|
|
125
|
+
### 超时设置
|
|
126
|
+
|
|
127
|
+
```python
|
|
128
|
+
# 全局默认超时
|
|
129
|
+
client = DedupClient("localhost:50051", timeout=10.0)
|
|
130
|
+
|
|
131
|
+
# 单次调用也可以通过修改 client.timeout 临时调整
|
|
132
|
+
client.timeout = 30.0
|
|
133
|
+
client.stats()
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## 开发
|
|
137
|
+
|
|
138
|
+
### 重新生成 gRPC 代码
|
|
139
|
+
|
|
140
|
+
仅在修改了 `proto/dedup.proto` 时需要:
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
pip install -e ".[dev]"
|
|
144
|
+
python -m grpc_tools.protoc -I../../proto --python_out=./src/dedup_client --grpc_python_out=./src/dedup_client ../../proto/dedup.proto
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
生成后需要手动将 `dedup_pb2_grpc.py` 中的绝对导入改为相对导入:
|
|
148
|
+
|
|
149
|
+
```python
|
|
150
|
+
# 将 import dedup_pb2 as dedup__pb2
|
|
151
|
+
# 改为 from . import dedup_pb2 as dedup__pb2
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### 运行测试
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
# 先启动服务端
|
|
158
|
+
cargo run -- serve --addr 0.0.0.0:50051 --db-path ./data/dedup
|
|
159
|
+
|
|
160
|
+
# 再运行集成测试
|
|
161
|
+
python -m pytest tests/
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## 项目结构
|
|
165
|
+
|
|
166
|
+
```
|
|
167
|
+
clients/python/
|
|
168
|
+
├── pyproject.toml # 打包配置
|
|
169
|
+
├── README.md # 本文档
|
|
170
|
+
├── src/
|
|
171
|
+
│ └── dedup_client/ # Python 包
|
|
172
|
+
│ ├── __init__.py # 导出 DedupClient
|
|
173
|
+
│ ├── client.py # 客户端实现
|
|
174
|
+
│ ├── dedup_pb2.py # 预生成的 proto 消息定义
|
|
175
|
+
│ ├── dedup_pb2_grpc.py # 预生成的 gRPC 存根
|
|
176
|
+
│ └── py.typed # PEP 561 类型标记
|
|
177
|
+
└── tests/ # 集成测试
|
|
178
|
+
├── test_integration.py
|
|
179
|
+
└── test_restart.py
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
## License
|
|
183
|
+
|
|
184
|
+
MIT
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# rocksdb-dedup
|
|
2
|
+
|
|
3
|
+
基于 RocksDB + 布隆过滤器的高性能持久化去重服务 Python 客户端。
|
|
4
|
+
|
|
5
|
+
## 特性
|
|
6
|
+
|
|
7
|
+
- 原子 `check_and_set` 操作,保证去重准确性
|
|
8
|
+
- 批量操作接口,高 QPS 场景下减少 RPC 开销
|
|
9
|
+
- 多租户 namespace 隔离
|
|
10
|
+
- 支持过期自动清理
|
|
11
|
+
- 布隆过滤器自动扩容
|
|
12
|
+
- 预生成 gRPC 代码,零编译步骤,开箱即用
|
|
13
|
+
|
|
14
|
+
## 安装
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pip install rocksdb-dedup
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
或从源码安装:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
pip install clients/python
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
> 注意:运行时只依赖 `grpcio`,不需要安装 `grpcio-tools` 或 protoc 编译器。
|
|
27
|
+
|
|
28
|
+
## 快速开始
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
from dedup_client import DedupClient
|
|
32
|
+
import time
|
|
33
|
+
|
|
34
|
+
# 连接服务
|
|
35
|
+
with DedupClient("localhost:50051") as client:
|
|
36
|
+
ns = "spider_news"
|
|
37
|
+
expire_at = int(time.time() * 1000) + 30 * 86400 * 1000 # 30天后过期
|
|
38
|
+
|
|
39
|
+
# 核心接口:检查并写入
|
|
40
|
+
result = client.check_and_set(
|
|
41
|
+
namespace=ns,
|
|
42
|
+
key=b"https://example.com/page/1",
|
|
43
|
+
expire_at_ms=expire_at,
|
|
44
|
+
metadata=b'{"depth": 3, "source": "seed"}',
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
if result == "inserted":
|
|
48
|
+
print("新 URL,开始抓取")
|
|
49
|
+
elif result == "duplicate":
|
|
50
|
+
print("重复 URL,跳过")
|
|
51
|
+
elif result == "expired_and_replaced":
|
|
52
|
+
print("已过期,重新抓取")
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## API 列表
|
|
56
|
+
|
|
57
|
+
| 方法 | 说明 |
|
|
58
|
+
|---|---|
|
|
59
|
+
| `check_and_set(namespace, key, expire_at_ms, metadata)` | 原子检查并写入 |
|
|
60
|
+
| `batch_check_and_set(items)` | 批量检查并写入 |
|
|
61
|
+
| `exists(namespace, key)` | 仅检查是否存在 |
|
|
62
|
+
| `batch_exists(namespace, keys)` | 批量检查存在性 |
|
|
63
|
+
| `get(namespace, key)` | 获取详细信息 |
|
|
64
|
+
| `delete(namespace, key)` | 删除 key |
|
|
65
|
+
| `batch_delete(namespace, keys)` | 批量删除 |
|
|
66
|
+
| `stats(namespace)` | 获取服务统计 |
|
|
67
|
+
|
|
68
|
+
### 批量操作示例
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
# 批量检查并写入
|
|
72
|
+
items = [
|
|
73
|
+
("spider_news", b"https://example.com/1", expire_at, None),
|
|
74
|
+
("spider_news", b"https://example.com/2", expire_at, b"meta2"),
|
|
75
|
+
("spider_news", b"https://example.com/3", expire_at, None),
|
|
76
|
+
]
|
|
77
|
+
results = client.batch_check_and_set(items)
|
|
78
|
+
# results: ["inserted", "inserted", "inserted"]
|
|
79
|
+
|
|
80
|
+
# 批量检查存在性
|
|
81
|
+
keys = [b"https://example.com/1", b"https://example.com/999"]
|
|
82
|
+
exists_list = client.batch_exists("spider_news", keys)
|
|
83
|
+
# exists_list: [True, False]
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### 服务状态
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
stats = client.stats()
|
|
90
|
+
print(f"总 key 数: {stats['total_keys']:,}")
|
|
91
|
+
print(f"布隆容量: {stats['bloom_capacity']:,}")
|
|
92
|
+
print(f"误判率: {stats['bloom_fpp']:.4%}")
|
|
93
|
+
print(f"QPS: {stats['qps_estimate']:,}")
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## 配置选项
|
|
97
|
+
|
|
98
|
+
### 超时设置
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
# 全局默认超时
|
|
102
|
+
client = DedupClient("localhost:50051", timeout=10.0)
|
|
103
|
+
|
|
104
|
+
# 单次调用也可以通过修改 client.timeout 临时调整
|
|
105
|
+
client.timeout = 30.0
|
|
106
|
+
client.stats()
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## 开发
|
|
110
|
+
|
|
111
|
+
### 重新生成 gRPC 代码
|
|
112
|
+
|
|
113
|
+
仅在修改了 `proto/dedup.proto` 时需要:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
pip install -e ".[dev]"
|
|
117
|
+
python -m grpc_tools.protoc -I../../proto --python_out=./src/dedup_client --grpc_python_out=./src/dedup_client ../../proto/dedup.proto
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
生成后需要手动将 `dedup_pb2_grpc.py` 中的绝对导入改为相对导入:
|
|
121
|
+
|
|
122
|
+
```python
|
|
123
|
+
# 将 import dedup_pb2 as dedup__pb2
|
|
124
|
+
# 改为 from . import dedup_pb2 as dedup__pb2
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### 运行测试
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
# 先启动服务端
|
|
131
|
+
cargo run -- serve --addr 0.0.0.0:50051 --db-path ./data/dedup
|
|
132
|
+
|
|
133
|
+
# 再运行集成测试
|
|
134
|
+
python -m pytest tests/
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## 项目结构
|
|
138
|
+
|
|
139
|
+
```
|
|
140
|
+
clients/python/
|
|
141
|
+
├── pyproject.toml # 打包配置
|
|
142
|
+
├── README.md # 本文档
|
|
143
|
+
├── src/
|
|
144
|
+
│ └── dedup_client/ # Python 包
|
|
145
|
+
│ ├── __init__.py # 导出 DedupClient
|
|
146
|
+
│ ├── client.py # 客户端实现
|
|
147
|
+
│ ├── dedup_pb2.py # 预生成的 proto 消息定义
|
|
148
|
+
│ ├── dedup_pb2_grpc.py # 预生成的 gRPC 存根
|
|
149
|
+
│ └── py.typed # PEP 561 类型标记
|
|
150
|
+
└── tests/ # 集成测试
|
|
151
|
+
├── test_integration.py
|
|
152
|
+
└── test_restart.py
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## License
|
|
156
|
+
|
|
157
|
+
MIT
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "rocksdb-dedup"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "基于 RocksDB 与布隆过滤器的高性能去重服务 Python 客户端"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.8"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "Dedup Team" },
|
|
14
|
+
]
|
|
15
|
+
keywords = ["rocksdb", "bloom-filter", "dedup", "grpc", "爬虫去重"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 4 - Beta",
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"License :: OSI Approved :: MIT License",
|
|
20
|
+
"Programming Language :: Python :: 3",
|
|
21
|
+
"Programming Language :: Python :: 3.8",
|
|
22
|
+
"Programming Language :: Python :: 3.9",
|
|
23
|
+
"Programming Language :: Python :: 3.10",
|
|
24
|
+
"Programming Language :: Python :: 3.11",
|
|
25
|
+
"Programming Language :: Python :: 3.12",
|
|
26
|
+
"Programming Language :: Python :: 3.13",
|
|
27
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
28
|
+
]
|
|
29
|
+
dependencies = [
|
|
30
|
+
"grpcio>=1.60.0",
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
[project.optional-dependencies]
|
|
34
|
+
dev = [
|
|
35
|
+
"grpcio-tools>=1.60.0",
|
|
36
|
+
"pytest>=7.0",
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
[project.urls]
|
|
40
|
+
Homepage = "https://github.com/your-org/rocksdb-dedup"
|
|
41
|
+
Repository = "https://github.com/your-org/rocksdb-dedup"
|
|
42
|
+
|
|
43
|
+
[tool.setuptools.packages.find]
|
|
44
|
+
where = ["src"]
|
|
45
|
+
|
|
46
|
+
[tool.setuptools.package-data]
|
|
47
|
+
dedup_client = ["py.typed"]
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""
|
|
2
|
+
RocksDB 去重服务 Python 客户端库
|
|
3
|
+
|
|
4
|
+
基于 RocksDB + 布隆过滤器的高并发持久化去重引擎的 Python 客户端。
|
|
5
|
+
|
|
6
|
+
快速开始:
|
|
7
|
+
from dedup_client import DedupClient
|
|
8
|
+
|
|
9
|
+
with DedupClient("localhost:50051") as client:
|
|
10
|
+
result = client.check_and_set(
|
|
11
|
+
namespace="spider_news",
|
|
12
|
+
key=b"https://example.com/page/1",
|
|
13
|
+
expire_at_ms=1735689600000,
|
|
14
|
+
)
|
|
15
|
+
print(result) # "inserted" / "duplicate" / "expired_and_replaced"
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from .client import DedupClient
|
|
19
|
+
from .sharded import ShardedDedupClient, ConsistentHashRing
|
|
20
|
+
|
|
21
|
+
__all__ = ["DedupClient", "ShardedDedupClient", "ConsistentHashRing"]
|
|
22
|
+
__version__ = "0.2.0"
|