first commit
This commit is contained in:
+21
@@ -0,0 +1,21 @@
|
|||||||
|
# Python-generated files
|
||||||
|
__pycache__/
|
||||||
|
*.py[oc]
|
||||||
|
build/
|
||||||
|
dist/
|
||||||
|
wheels/
|
||||||
|
*.egg-info
|
||||||
|
|
||||||
|
# Virtual environments
|
||||||
|
.venv
|
||||||
|
output.md
|
||||||
|
.arts
|
||||||
|
uv.lock
|
||||||
|
cache/
|
||||||
|
|
||||||
|
# IDE files
|
||||||
|
.idea/
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
# test files
|
||||||
|
.pytest_cache/
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
3.13
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
[project]
|
||||||
|
authors = [
|
||||||
|
{name = "chakcy", email = "947105045@qq.com"},
|
||||||
|
]
|
||||||
|
classifiers = [
|
||||||
|
"Development Status :: 4 - Beta",
|
||||||
|
"Intended Audience :: Developers",
|
||||||
|
"License :: OSI Approved :: MIT License",
|
||||||
|
"Programming Language :: Python :: 3.13",
|
||||||
|
"Topic :: System :: Distributed Computing",
|
||||||
|
"Typing :: Typed",
|
||||||
|
"Operating System :: Microsoft :: Windows",
|
||||||
|
"Operating System :: POSIX :: Linux",
|
||||||
|
]
|
||||||
|
dependencies = []
|
||||||
|
description = "Add your description here"
|
||||||
|
name = "queue-sqlite"
|
||||||
|
readme = "README.md"
|
||||||
|
requires-python = ">=3.13"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
|
[[tool.uv.index]]
|
||||||
|
default = true
|
||||||
|
url = "https://pypi.tuna.tsinghua.edu.cn/simple"
|
||||||
|
|
||||||
|
[build-system]
|
||||||
|
build-backend = "hatchling.build"
|
||||||
|
requires = ["hatchling"]
|
||||||
|
|
||||||
|
[dependency-groups]
|
||||||
|
dev = [
|
||||||
|
"maturin>=1.9.2",
|
||||||
|
"psutil>=7.0.0",
|
||||||
|
"pytest>=8.4.1",
|
||||||
|
]
|
||||||
|
|
||||||
|
[tool.hatch.build.targets.wheel]
|
||||||
|
packages = ["src/queue_sqlite"]
|
||||||
|
platforms = ["windows_x86_64", "linux_x86_64"]
|
||||||
|
|
||||||
|
[tool.hatch.build]
|
||||||
|
artifacts = [
|
||||||
|
"src/queue_sqlite/core/*.pyd",
|
||||||
|
]
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
# 需要遍历的目录
|
||||||
|
root_dir = "./"
|
||||||
|
# 遍历目录
|
||||||
|
for dirpath, dirnames, filenames in os.walk(root_dir):
|
||||||
|
if "__pycache__" in dirnames:
|
||||||
|
# 获取 __pycache__ 目录的全路径
|
||||||
|
pycache_dir = os.path.join(dirpath, "__pycache__")
|
||||||
|
# 删除目录
|
||||||
|
shutil.rmtree(pycache_dir)
|
||||||
|
print(f"Removed: {pycache_dir}")
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
def generate_markdown_from_py_files(directory, output_file):
|
||||||
|
with open(output_file, 'w', encoding='utf-8') as md_file:
|
||||||
|
for root, dirs, files in os.walk(directory):
|
||||||
|
# 排除 venv 目录
|
||||||
|
dirs[:] = [d for d in dirs if d != '.venv']
|
||||||
|
dirs[:] = [d for d in dirs if d != '.vscode']
|
||||||
|
dirs[:] = [d for d in dirs if d != 'scripts']
|
||||||
|
dirs[:] = [d for d in dirs if d != 'build']
|
||||||
|
for file in files:
|
||||||
|
if file.endswith('.py') or file.endswith('.rs'):
|
||||||
|
file_path = os.path.join(root, file)
|
||||||
|
md_file.write(f'`{file_path}`\n')
|
||||||
|
md_file.write('```python\n')
|
||||||
|
with open(file_path, 'r', encoding='utf-8') as py_file:
|
||||||
|
md_file.write(py_file.read())
|
||||||
|
md_file.write('\n```\n\n')
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
# 指定目录和输出文件名
|
||||||
|
target_directory = sys.argv[1] # 替换为你的目标目录
|
||||||
|
output_markdown_file = 'output.md' # 输出的 Markdown 文件名
|
||||||
|
|
||||||
|
generate_markdown_from_py_files(target_directory, output_markdown_file)
|
||||||
|
print(f'Markdown 文件已生成:{output_markdown_file}')
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
/target
|
||||||
|
|
||||||
|
# Byte-compiled / optimized / DLL files
|
||||||
|
__pycache__/
|
||||||
|
.pytest_cache/
|
||||||
|
*.py[cod]
|
||||||
|
|
||||||
|
# C extensions
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Distribution / packaging
|
||||||
|
.Python
|
||||||
|
.venv/
|
||||||
|
env/
|
||||||
|
bin/
|
||||||
|
build/
|
||||||
|
develop-eggs/
|
||||||
|
dist/
|
||||||
|
eggs/
|
||||||
|
lib/
|
||||||
|
lib64/
|
||||||
|
parts/
|
||||||
|
sdist/
|
||||||
|
var/
|
||||||
|
include/
|
||||||
|
man/
|
||||||
|
venv/
|
||||||
|
*.egg-info/
|
||||||
|
.installed.cfg
|
||||||
|
*.egg
|
||||||
|
|
||||||
|
# Installer logs
|
||||||
|
pip-log.txt
|
||||||
|
pip-delete-this-directory.txt
|
||||||
|
pip-selfcheck.json
|
||||||
|
|
||||||
|
# Unit test / coverage reports
|
||||||
|
htmlcov/
|
||||||
|
.tox/
|
||||||
|
.coverage
|
||||||
|
.cache
|
||||||
|
nosetests.xml
|
||||||
|
coverage.xml
|
||||||
|
|
||||||
|
# Translations
|
||||||
|
*.mo
|
||||||
|
|
||||||
|
# Mr Developer
|
||||||
|
.mr.developer.cfg
|
||||||
|
.project
|
||||||
|
.pydevproject
|
||||||
|
|
||||||
|
# Rope
|
||||||
|
.ropeproject
|
||||||
|
|
||||||
|
# Django stuff:
|
||||||
|
*.log
|
||||||
|
*.pot
|
||||||
|
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# Sphinx documentation
|
||||||
|
docs/_build/
|
||||||
|
|
||||||
|
# PyCharm
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# VSCode
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
# Pyenv
|
||||||
|
.python-version
|
||||||
Generated
+748
@@ -0,0 +1,748 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ahash"
|
||||||
|
version = "0.8.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"once_cell",
|
||||||
|
"version_check",
|
||||||
|
"zerocopy",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "android-tzdata"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "android_system_properties"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "autocfg"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "2.9.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bumpalo"
|
||||||
|
version = "3.19.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cc"
|
||||||
|
version = "1.2.31"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c3a42d84bb6b69d3a8b3eaacf0d88f179e1929695e1ad012b6cf64d9caaa5fd2"
|
||||||
|
dependencies = [
|
||||||
|
"shlex",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "chrono"
|
||||||
|
version = "0.4.41"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d"
|
||||||
|
dependencies = [
|
||||||
|
"android-tzdata",
|
||||||
|
"iana-time-zone",
|
||||||
|
"js-sys",
|
||||||
|
"num-traits",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"windows-link",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "core"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"chrono",
|
||||||
|
"pyo3",
|
||||||
|
"r2d2",
|
||||||
|
"r2d2_sqlite",
|
||||||
|
"rusqlite",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "core-foundation-sys"
|
||||||
|
version = "0.8.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fallible-iterator"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fallible-streaming-iterator"
|
||||||
|
version = "0.1.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getrandom"
|
||||||
|
version = "0.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"r-efi",
|
||||||
|
"wasi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.14.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
|
||||||
|
dependencies = [
|
||||||
|
"ahash",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashlink"
|
||||||
|
version = "0.9.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af"
|
||||||
|
dependencies = [
|
||||||
|
"hashbrown",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "heck"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "iana-time-zone"
|
||||||
|
version = "0.1.63"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8"
|
||||||
|
dependencies = [
|
||||||
|
"android_system_properties",
|
||||||
|
"core-foundation-sys",
|
||||||
|
"iana-time-zone-haiku",
|
||||||
|
"js-sys",
|
||||||
|
"log",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"windows-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "iana-time-zone-haiku"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "indoc"
|
||||||
|
version = "2.0.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "js-sys"
|
||||||
|
version = "0.3.77"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f"
|
||||||
|
dependencies = [
|
||||||
|
"once_cell",
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.174"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libsqlite3-sys"
|
||||||
|
version = "0.30.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"pkg-config",
|
||||||
|
"vcpkg",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lock_api"
|
||||||
|
version = "0.4.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"scopeguard",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "log"
|
||||||
|
version = "0.4.27"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memoffset"
|
||||||
|
version = "0.9.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-traits"
|
||||||
|
version = "0.2.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell"
|
||||||
|
version = "1.21.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "parking_lot"
|
||||||
|
version = "0.12.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13"
|
||||||
|
dependencies = [
|
||||||
|
"lock_api",
|
||||||
|
"parking_lot_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "parking_lot_core"
|
||||||
|
version = "0.9.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"redox_syscall",
|
||||||
|
"smallvec",
|
||||||
|
"windows-targets",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pkg-config"
|
||||||
|
version = "0.3.32"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "portable-atomic"
|
||||||
|
version = "1.11.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ppv-lite86"
|
||||||
|
version = "0.2.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
|
||||||
|
dependencies = [
|
||||||
|
"zerocopy",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.95"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pyo3"
|
||||||
|
version = "0.25.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8970a78afe0628a3e3430376fc5fd76b6b45c4d43360ffd6cdd40bdde72b682a"
|
||||||
|
dependencies = [
|
||||||
|
"indoc",
|
||||||
|
"libc",
|
||||||
|
"memoffset",
|
||||||
|
"once_cell",
|
||||||
|
"portable-atomic",
|
||||||
|
"pyo3-build-config",
|
||||||
|
"pyo3-ffi",
|
||||||
|
"pyo3-macros",
|
||||||
|
"unindent",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pyo3-build-config"
|
||||||
|
version = "0.25.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "458eb0c55e7ece017adeba38f2248ff3ac615e53660d7c71a238d7d2a01c7598"
|
||||||
|
dependencies = [
|
||||||
|
"once_cell",
|
||||||
|
"target-lexicon",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pyo3-ffi"
|
||||||
|
version = "0.25.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7114fe5457c61b276ab77c5055f206295b812608083644a5c5b2640c3102565c"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"pyo3-build-config",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pyo3-macros"
|
||||||
|
version = "0.25.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a8725c0a622b374d6cb051d11a0983786448f7785336139c3c94f5aa6bef7e50"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"pyo3-macros-backend",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pyo3-macros-backend"
|
||||||
|
version = "0.25.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4109984c22491085343c05b0dbc54ddc405c3cf7b4374fc533f5c3313a572ccc"
|
||||||
|
dependencies = [
|
||||||
|
"heck",
|
||||||
|
"proc-macro2",
|
||||||
|
"pyo3-build-config",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.40"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "r-efi"
|
||||||
|
version = "5.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "r2d2"
|
||||||
|
version = "0.8.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "51de85fb3fb6524929c8a2eb85e6b6d363de4e8c48f9e2c2eac4944abc181c93"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"parking_lot",
|
||||||
|
"scheduled-thread-pool",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "r2d2_sqlite"
|
||||||
|
version = "0.25.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "eb14dba8247a6a15b7fdbc7d389e2e6f03ee9f184f87117706d509c092dfe846"
|
||||||
|
dependencies = [
|
||||||
|
"r2d2",
|
||||||
|
"rusqlite",
|
||||||
|
"uuid",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand"
|
||||||
|
version = "0.9.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
|
||||||
|
dependencies = [
|
||||||
|
"rand_chacha",
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_chacha"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
|
||||||
|
dependencies = [
|
||||||
|
"ppv-lite86",
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.9.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "redox_syscall"
|
||||||
|
version = "0.5.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rusqlite"
|
||||||
|
version = "0.32.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7753b721174eb8ff87a9a0e799e2d7bc3749323e773db92e0984debb00019d6e"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"fallible-iterator",
|
||||||
|
"fallible-streaming-iterator",
|
||||||
|
"hashlink",
|
||||||
|
"libsqlite3-sys",
|
||||||
|
"smallvec",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustversion"
|
||||||
|
version = "1.0.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "scheduled-thread-pool"
|
||||||
|
version = "0.2.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3cbc66816425a074528352f5789333ecff06ca41b36b0b0efdfbb29edc391a19"
|
||||||
|
dependencies = [
|
||||||
|
"parking_lot",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "scopeguard"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "shlex"
|
||||||
|
version = "1.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "smallvec"
|
||||||
|
version = "1.15.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.104"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "target-lexicon"
|
||||||
|
version = "0.13.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e502f78cdbb8ba4718f566c418c52bc729126ffd16baee5baa718cf25dd5a69a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unindent"
|
||||||
|
version = "0.2.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7264e107f553ccae879d21fbea1d6724ac785e8c3bfc762137959b5802826ef3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "uuid"
|
||||||
|
version = "1.17.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom",
|
||||||
|
"js-sys",
|
||||||
|
"rand",
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "vcpkg"
|
||||||
|
version = "0.2.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "version_check"
|
||||||
|
version = "0.9.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasi"
|
||||||
|
version = "0.14.2+wasi-0.2.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
|
||||||
|
dependencies = [
|
||||||
|
"wit-bindgen-rt",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen"
|
||||||
|
version = "0.2.100"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"once_cell",
|
||||||
|
"rustversion",
|
||||||
|
"wasm-bindgen-macro",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-backend"
|
||||||
|
version = "0.2.100"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6"
|
||||||
|
dependencies = [
|
||||||
|
"bumpalo",
|
||||||
|
"log",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"wasm-bindgen-shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-macro"
|
||||||
|
version = "0.2.100"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407"
|
||||||
|
dependencies = [
|
||||||
|
"quote",
|
||||||
|
"wasm-bindgen-macro-support",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-macro-support"
|
||||||
|
version = "0.2.100"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"wasm-bindgen-backend",
|
||||||
|
"wasm-bindgen-shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-shared"
|
||||||
|
version = "0.2.100"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-core"
|
||||||
|
version = "0.61.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3"
|
||||||
|
dependencies = [
|
||||||
|
"windows-implement",
|
||||||
|
"windows-interface",
|
||||||
|
"windows-link",
|
||||||
|
"windows-result",
|
||||||
|
"windows-strings",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-implement"
|
||||||
|
version = "0.60.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-interface"
|
||||||
|
version = "0.59.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-link"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-result"
|
||||||
|
version = "0.3.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6"
|
||||||
|
dependencies = [
|
||||||
|
"windows-link",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-strings"
|
||||||
|
version = "0.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57"
|
||||||
|
dependencies = [
|
||||||
|
"windows-link",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-targets"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||||
|
dependencies = [
|
||||||
|
"windows_aarch64_gnullvm",
|
||||||
|
"windows_aarch64_msvc",
|
||||||
|
"windows_i686_gnu",
|
||||||
|
"windows_i686_gnullvm",
|
||||||
|
"windows_i686_msvc",
|
||||||
|
"windows_x86_64_gnu",
|
||||||
|
"windows_x86_64_gnullvm",
|
||||||
|
"windows_x86_64_msvc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_gnullvm"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_msvc"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnu"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnullvm"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_msvc"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnu"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnullvm"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_msvc"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wit-bindgen-rt"
|
||||||
|
version = "0.39.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerocopy"
|
||||||
|
version = "0.8.26"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f"
|
||||||
|
dependencies = [
|
||||||
|
"zerocopy-derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerocopy-derive"
|
||||||
|
version = "0.8.26"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
[package]
|
||||||
|
name = "core"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
[lib]
|
||||||
|
name = "core"
|
||||||
|
crate-type = ["cdylib"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
chrono = "0.4.41"
|
||||||
|
pyo3 = "0.25.0"
|
||||||
|
r2d2 = "0.8"
|
||||||
|
r2d2_sqlite = "0.25"
|
||||||
|
rusqlite = { version = "0.32", features = ["bundled"] }
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
[build-system]
|
||||||
|
requires = ["maturin>=1.9,<2.0"]
|
||||||
|
build-backend = "maturin"
|
||||||
|
|
||||||
|
[project]
|
||||||
|
name = "core"
|
||||||
|
requires-python = ">=3.8"
|
||||||
|
classifiers = [
|
||||||
|
"Programming Language :: Rust",
|
||||||
|
"Programming Language :: Python :: Implementation :: CPython",
|
||||||
|
"Programming Language :: Python :: Implementation :: PyPy",
|
||||||
|
]
|
||||||
|
dynamic = ["version"]
|
||||||
|
[tool.maturin]
|
||||||
|
features = ["pyo3/extension-module"]
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
mod queue_operation;
|
||||||
|
mod task_mounter;
|
||||||
|
|
||||||
|
use pyo3::prelude::*;
|
||||||
|
use queue_operation::QueueOperation;
|
||||||
|
use task_mounter::TaskMounter; // 导入结构体 // 导入结构体
|
||||||
|
|
||||||
|
#[pymodule]
|
||||||
|
fn core(_py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> {
|
||||||
|
m.add_class::<TaskMounter>()?;
|
||||||
|
m.add_class::<QueueOperation>()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -0,0 +1,507 @@
|
|||||||
|
use chrono::{DateTime, Duration, Utc};
|
||||||
|
use pyo3::prelude::*;
|
||||||
|
use pyo3::types::*;
|
||||||
|
use r2d2::Pool;
|
||||||
|
use r2d2_sqlite::SqliteConnectionManager;
|
||||||
|
use rusqlite::params;
|
||||||
|
|
||||||
|
#[pyclass]
|
||||||
|
pub struct QueueOperation {
|
||||||
|
pool: Pool<SqliteConnectionManager>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pymethods]
|
||||||
|
impl QueueOperation {
|
||||||
|
#[new]
|
||||||
|
pub fn new(queue_path: String) -> Self {
|
||||||
|
let manager = SqliteConnectionManager::file(queue_path.as_str());
|
||||||
|
let pool = Pool::new(manager).unwrap();
|
||||||
|
QueueOperation { pool }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init_db(&self) -> PyResult<()> {
|
||||||
|
let conn = self
|
||||||
|
.pool
|
||||||
|
.get()
|
||||||
|
.map_err(|e| PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(e.to_string()))?;
|
||||||
|
conn.execute(
|
||||||
|
"
|
||||||
|
CREATE TABLE IF NOT EXISTS messages (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
type TEXT NOT NULL,
|
||||||
|
status INTEGER NOT NULL,
|
||||||
|
content TEXT NOT NULL,
|
||||||
|
createtime DATETIME NOT NULL,
|
||||||
|
updatetime DATETIME NOT NULL,
|
||||||
|
result TEXT DEFAULT NULL,
|
||||||
|
priority INTEGER NOT NULL,
|
||||||
|
source TEXT NOT NULL,
|
||||||
|
destination TEXT NOT NULL,
|
||||||
|
retry_count INTEGER NOT NULL,
|
||||||
|
expire_time DATETIME DEFAULT NULL,
|
||||||
|
tags TEXT DEFAULT NULL,
|
||||||
|
metadata TEXT DEFAULT NULL,
|
||||||
|
is_deleted INTEGER NOT NULL DEFAULT 0
|
||||||
|
);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_status ON messages(status);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_priority ON messages(priority);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_dequeue ON messages(
|
||||||
|
status,
|
||||||
|
priority DESC,
|
||||||
|
createtime ASC
|
||||||
|
) WHERE is_deleted = 0 AND status = 0;
|
||||||
|
",
|
||||||
|
params![],
|
||||||
|
)
|
||||||
|
.map_err(|e| PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(e.to_string()))?;
|
||||||
|
let pragmas = [
|
||||||
|
"PRAGMA journal_mode=WAL",
|
||||||
|
"PRAGMA synchronous=NORMAL",
|
||||||
|
"PRAGMA cache_size=-20000",
|
||||||
|
"PRAGMA mmap_size=1073741824",
|
||||||
|
"PRAGMA temp_store=MEMORY",
|
||||||
|
"PRAGMA busy_timeout=5000",
|
||||||
|
];
|
||||||
|
for pragma in pragmas.iter() {
|
||||||
|
let mut stmt = conn
|
||||||
|
.prepare(pragma)
|
||||||
|
.map_err(|e| PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(e.to_string()))?;
|
||||||
|
|
||||||
|
let mut rows = stmt
|
||||||
|
.query([])
|
||||||
|
.map_err(|e| PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(e.to_string()))?;
|
||||||
|
|
||||||
|
while let Some(_) = rows
|
||||||
|
.next()
|
||||||
|
.map_err(|e| PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(e.to_string()))?
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn enqueue<'py>(
|
||||||
|
&self,
|
||||||
|
py: Python<'py>,
|
||||||
|
message: &Bound<'py, PyDict>,
|
||||||
|
) -> PyResult<Bound<'py, PyString>> {
|
||||||
|
let mut conn = self
|
||||||
|
.pool
|
||||||
|
.get()
|
||||||
|
.map_err(|e| PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(e.to_string()))?;
|
||||||
|
|
||||||
|
let get_field = |key: &str| -> PyResult<_> {
|
||||||
|
message.get_item(key)?.ok_or_else(|| {
|
||||||
|
PyErr::new::<pyo3::exceptions::PyKeyError, _>(format!("{} not found", key))
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
let id = get_field("id")?.extract::<String>()?;
|
||||||
|
let type_ = get_field("type")?.extract::<String>()?;
|
||||||
|
let status = get_field("status")?.extract::<i32>()?;
|
||||||
|
let content = get_field("content")?.extract::<String>()?;
|
||||||
|
let createtime = get_field("createtime")?.extract::<String>()?;
|
||||||
|
let updatetime = get_field("updatetime")?.extract::<String>()?;
|
||||||
|
let result = get_field("result")?.extract::<String>()?;
|
||||||
|
let priority = get_field("priority")?.extract::<i32>()?;
|
||||||
|
let source = get_field("source")?.extract::<String>()?;
|
||||||
|
let destination = get_field("destination")?.extract::<String>()?;
|
||||||
|
let retry_count = get_field("retry_count")?.extract::<i32>()?;
|
||||||
|
let expire_time = get_field("expire_time")?.extract::<String>()?;
|
||||||
|
let tags = get_field("tags")?.extract::<String>()?;
|
||||||
|
let metadata = get_field("metadata")?.extract::<String>()?;
|
||||||
|
|
||||||
|
let tx = conn
|
||||||
|
.transaction()
|
||||||
|
.map_err(|e| PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(e.to_string()))?;
|
||||||
|
tx.execute(
|
||||||
|
"
|
||||||
|
INSERT INTO messages (
|
||||||
|
id, type, status, content, createtime,
|
||||||
|
updatetime, result, priority, source,
|
||||||
|
destination, retry_count, expire_time, tags, metadata
|
||||||
|
) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14)
|
||||||
|
",
|
||||||
|
params![
|
||||||
|
id,
|
||||||
|
type_,
|
||||||
|
status,
|
||||||
|
content,
|
||||||
|
createtime,
|
||||||
|
updatetime,
|
||||||
|
result,
|
||||||
|
priority,
|
||||||
|
source,
|
||||||
|
destination,
|
||||||
|
retry_count,
|
||||||
|
expire_time,
|
||||||
|
tags,
|
||||||
|
metadata
|
||||||
|
],
|
||||||
|
)
|
||||||
|
.map_err(|e| PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(e.to_string()))?;
|
||||||
|
tx.commit()
|
||||||
|
.map_err(|e| PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(e.to_string()))?;
|
||||||
|
Ok(Bound::from(PyString::new(py, &id)))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dequeue<'py>(&self, py: Python<'py>, size: i32) -> PyResult<Bound<'py, PyList>> {
|
||||||
|
let conn = self
|
||||||
|
.pool
|
||||||
|
.get()
|
||||||
|
.map_err(|e| PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(e.to_string()))?;
|
||||||
|
let now: DateTime<Utc> = Utc::now();
|
||||||
|
let iso_format = now.to_rfc3339();
|
||||||
|
|
||||||
|
// Fixed SQL query with proper subquery alias
|
||||||
|
let mut stmt = conn
|
||||||
|
.prepare(
|
||||||
|
"UPDATE messages
|
||||||
|
SET status = 1
|
||||||
|
WHERE id IN (
|
||||||
|
SELECT id
|
||||||
|
FROM (
|
||||||
|
SELECT id, ROW_NUMBER() OVER (ORDER BY priority DESC, createtime ASC) AS rn
|
||||||
|
FROM messages
|
||||||
|
WHERE is_deleted = 0
|
||||||
|
AND status = 0
|
||||||
|
AND (expire_time IS NULL OR expire_time > ?1)
|
||||||
|
) AS sub
|
||||||
|
WHERE rn <= ?2
|
||||||
|
)
|
||||||
|
RETURNING *;",
|
||||||
|
)
|
||||||
|
.map_err(|e| PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(e.to_string()))?;
|
||||||
|
|
||||||
|
let rows = PyList::empty(py);
|
||||||
|
let mapped_rows = stmt
|
||||||
|
.query_map(params![iso_format, size], |row| {
|
||||||
|
let dict = PyDict::new(py);
|
||||||
|
|
||||||
|
// Helper to convert PyErr to rusqlite::Error
|
||||||
|
let to_rusqlite_error =
|
||||||
|
|e: PyErr| rusqlite::Error::ToSqlConversionFailure(Box::new(e));
|
||||||
|
|
||||||
|
// Extract and set each field with proper error handling
|
||||||
|
dict.set_item("id", row.get::<_, String>("id")?)
|
||||||
|
.map_err(to_rusqlite_error)?;
|
||||||
|
|
||||||
|
dict.set_item("type", row.get::<_, String>("type")?)
|
||||||
|
.map_err(to_rusqlite_error)?;
|
||||||
|
|
||||||
|
dict.set_item("status", row.get::<_, i32>("status")?)
|
||||||
|
.map_err(to_rusqlite_error)?;
|
||||||
|
|
||||||
|
dict.set_item("content", row.get::<_, String>("content")?)
|
||||||
|
.map_err(to_rusqlite_error)?;
|
||||||
|
|
||||||
|
dict.set_item("createtime", row.get::<_, String>("createtime")?)
|
||||||
|
.map_err(to_rusqlite_error)?;
|
||||||
|
|
||||||
|
dict.set_item("updatetime", row.get::<_, String>("updatetime")?)
|
||||||
|
.map_err(to_rusqlite_error)?;
|
||||||
|
|
||||||
|
dict.set_item("result", row.get::<_, Option<String>>("result")?)
|
||||||
|
.map_err(to_rusqlite_error)?;
|
||||||
|
|
||||||
|
dict.set_item("priority", row.get::<_, i32>("priority")?)
|
||||||
|
.map_err(to_rusqlite_error)?;
|
||||||
|
|
||||||
|
dict.set_item("source", row.get::<_, Option<String>>("source")?)
|
||||||
|
.map_err(to_rusqlite_error)?;
|
||||||
|
|
||||||
|
dict.set_item("destination", row.get::<_, Option<String>>("destination")?)
|
||||||
|
.map_err(to_rusqlite_error)?;
|
||||||
|
|
||||||
|
dict.set_item("retry_count", row.get::<_, i32>("retry_count")?)
|
||||||
|
.map_err(to_rusqlite_error)?;
|
||||||
|
|
||||||
|
dict.set_item("expire_time", row.get::<_, Option<String>>("expire_time")?)
|
||||||
|
.map_err(to_rusqlite_error)?;
|
||||||
|
|
||||||
|
dict.set_item("tags", row.get::<_, Option<String>>("tags")?)
|
||||||
|
.map_err(to_rusqlite_error)?;
|
||||||
|
|
||||||
|
dict.set_item("metadata", row.get::<_, Option<String>>("metadata")?)
|
||||||
|
.map_err(to_rusqlite_error)?;
|
||||||
|
|
||||||
|
// Return the dictionary as Py<PyDict>
|
||||||
|
Ok(dict)
|
||||||
|
})
|
||||||
|
.map_err(|e| PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(e.to_string()))?;
|
||||||
|
|
||||||
|
for row in mapped_rows {
|
||||||
|
let dict =
|
||||||
|
row.map_err(|e| PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(e.to_string()))?;
|
||||||
|
rows.append(dict)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(rows.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_queue_length(&self) -> PyResult<i32> {
|
||||||
|
let conn = self
|
||||||
|
.pool
|
||||||
|
.get()
|
||||||
|
.map_err(|e| PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(e.to_string()))?;
|
||||||
|
let now = Utc::now().to_rfc3339();
|
||||||
|
|
||||||
|
// Using query_row for single result handling
|
||||||
|
let count: i32 = conn
|
||||||
|
.query_row(
|
||||||
|
"SELECT COUNT(*) FROM messages
|
||||||
|
WHERE is_deleted = 0
|
||||||
|
AND status = ?1
|
||||||
|
AND (expire_time IS NULL OR expire_time > ?2)",
|
||||||
|
params![0, now],
|
||||||
|
|row| row.get(0),
|
||||||
|
)
|
||||||
|
.map_err(|e| PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(e.to_string()))?;
|
||||||
|
|
||||||
|
Ok(count)
|
||||||
|
}
|
||||||
|
pub fn get_completed_messages<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyList>> {
|
||||||
|
let conn = self
|
||||||
|
.pool
|
||||||
|
.get()
|
||||||
|
.map_err(|e| PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(e.to_string()))?;
|
||||||
|
let mut stmt = conn
|
||||||
|
.prepare(
|
||||||
|
"SELECT
|
||||||
|
*
|
||||||
|
FROM
|
||||||
|
messages
|
||||||
|
WHERE
|
||||||
|
is_deleted = 0
|
||||||
|
AND (status = ?1 OR status = ?2)",
|
||||||
|
)
|
||||||
|
.map_err(|e| PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(e.to_string()))?;
|
||||||
|
let rows = PyList::empty(py);
|
||||||
|
let mapped_rows = stmt
|
||||||
|
.query_map(params![2, 3], |row| {
|
||||||
|
let dict = PyDict::new(py);
|
||||||
|
|
||||||
|
// Helper to convert PyErr to rusqlite::Error
|
||||||
|
let to_rusqlite_error =
|
||||||
|
|e: PyErr| rusqlite::Error::ToSqlConversionFailure(Box::new(e));
|
||||||
|
|
||||||
|
// Extract and set each field with proper error handling
|
||||||
|
dict.set_item("id", row.get::<_, String>("id")?)
|
||||||
|
.map_err(to_rusqlite_error)?;
|
||||||
|
|
||||||
|
dict.set_item("type", row.get::<_, String>("type")?)
|
||||||
|
.map_err(to_rusqlite_error)?;
|
||||||
|
|
||||||
|
dict.set_item("status", row.get::<_, i32>("status")?)
|
||||||
|
.map_err(to_rusqlite_error)?;
|
||||||
|
|
||||||
|
dict.set_item("content", row.get::<_, String>("content")?)
|
||||||
|
.map_err(to_rusqlite_error)?;
|
||||||
|
|
||||||
|
dict.set_item("createtime", row.get::<_, String>("createtime")?)
|
||||||
|
.map_err(to_rusqlite_error)?;
|
||||||
|
|
||||||
|
dict.set_item("updatetime", row.get::<_, String>("updatetime")?)
|
||||||
|
.map_err(to_rusqlite_error)?;
|
||||||
|
|
||||||
|
dict.set_item("result", row.get::<_, Option<String>>("result")?)
|
||||||
|
.map_err(to_rusqlite_error)?;
|
||||||
|
|
||||||
|
dict.set_item("priority", row.get::<_, i32>("priority")?)
|
||||||
|
.map_err(to_rusqlite_error)?;
|
||||||
|
|
||||||
|
dict.set_item("source", row.get::<_, Option<String>>("source")?)
|
||||||
|
.map_err(to_rusqlite_error)?;
|
||||||
|
|
||||||
|
dict.set_item("destination", row.get::<_, Option<String>>("destination")?)
|
||||||
|
.map_err(to_rusqlite_error)?;
|
||||||
|
|
||||||
|
dict.set_item("retry_count", row.get::<_, i32>("retry_count")?)
|
||||||
|
.map_err(to_rusqlite_error)?;
|
||||||
|
|
||||||
|
dict.set_item("expire_time", row.get::<_, Option<String>>("expire_time")?)
|
||||||
|
.map_err(to_rusqlite_error)?;
|
||||||
|
|
||||||
|
dict.set_item("tags", row.get::<_, Option<String>>("tags")?)
|
||||||
|
.map_err(to_rusqlite_error)?;
|
||||||
|
|
||||||
|
dict.set_item("metadata", row.get::<_, Option<String>>("metadata")?)
|
||||||
|
.map_err(to_rusqlite_error)?;
|
||||||
|
|
||||||
|
// Return the dictionary as Py<PyDict>
|
||||||
|
Ok(dict)
|
||||||
|
})
|
||||||
|
.map_err(|e| PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(e.to_string()))?;
|
||||||
|
|
||||||
|
for row in mapped_rows {
|
||||||
|
let dict =
|
||||||
|
row.map_err(|e| PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(e.to_string()))?;
|
||||||
|
rows.append(dict)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(rows.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_result<'py>(&self, py: Python<'py>, id: String) -> PyResult<Bound<'py, PyDict>> {
|
||||||
|
let conn = self
|
||||||
|
.pool
|
||||||
|
.get()
|
||||||
|
.map_err(|e| PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(e.to_string()))?;
|
||||||
|
let mut stmt = conn
|
||||||
|
.prepare(
|
||||||
|
"SELECT result, status FROM messages WHERE id = ?1 AND (status = ?2 OR status = ?3)",
|
||||||
|
)
|
||||||
|
.map_err(|e| PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(e.to_string()))?;
|
||||||
|
let mut mapped_rows = stmt
|
||||||
|
.query_map(params![&id, 2, 3], |row| {
|
||||||
|
let dict = PyDict::new(py);
|
||||||
|
|
||||||
|
// Helper to convert PyErr to rusqlite::Error
|
||||||
|
let to_rusqlite_error =
|
||||||
|
|e: PyErr| rusqlite::Error::ToSqlConversionFailure(Box::new(e));
|
||||||
|
|
||||||
|
// Extract and set each field with proper error handling
|
||||||
|
dict.set_item("id", row.get::<_, String>("id")?)
|
||||||
|
.map_err(to_rusqlite_error)?;
|
||||||
|
|
||||||
|
dict.set_item("type", row.get::<_, String>("type")?)
|
||||||
|
.map_err(to_rusqlite_error)?;
|
||||||
|
|
||||||
|
dict.set_item("status", row.get::<_, i32>("status")?)
|
||||||
|
.map_err(to_rusqlite_error)?;
|
||||||
|
|
||||||
|
dict.set_item("content", row.get::<_, String>("content")?)
|
||||||
|
.map_err(to_rusqlite_error)?;
|
||||||
|
|
||||||
|
dict.set_item("createtime", row.get::<_, String>("createtime")?)
|
||||||
|
.map_err(to_rusqlite_error)?;
|
||||||
|
|
||||||
|
dict.set_item("updatetime", row.get::<_, String>("updatetime")?)
|
||||||
|
.map_err(to_rusqlite_error)?;
|
||||||
|
|
||||||
|
dict.set_item("result", row.get::<_, Option<String>>("result")?)
|
||||||
|
.map_err(to_rusqlite_error)?;
|
||||||
|
|
||||||
|
dict.set_item("priority", row.get::<_, i32>("priority")?)
|
||||||
|
.map_err(to_rusqlite_error)?;
|
||||||
|
|
||||||
|
dict.set_item("source", row.get::<_, Option<String>>("source")?)
|
||||||
|
.map_err(to_rusqlite_error)?;
|
||||||
|
|
||||||
|
dict.set_item("destination", row.get::<_, Option<String>>("destination")?)
|
||||||
|
.map_err(to_rusqlite_error)?;
|
||||||
|
|
||||||
|
dict.set_item("retry_count", row.get::<_, i32>("retry_count")?)
|
||||||
|
.map_err(to_rusqlite_error)?;
|
||||||
|
|
||||||
|
dict.set_item("expire_time", row.get::<_, Option<String>>("expire_time")?)
|
||||||
|
.map_err(to_rusqlite_error)?;
|
||||||
|
|
||||||
|
dict.set_item("tags", row.get::<_, Option<String>>("tags")?)
|
||||||
|
.map_err(to_rusqlite_error)?;
|
||||||
|
|
||||||
|
dict.set_item("metadata", row.get::<_, Option<String>>("metadata")?)
|
||||||
|
.map_err(to_rusqlite_error)?;
|
||||||
|
|
||||||
|
// Return the dictionary as Py<PyDict>
|
||||||
|
Ok(dict)
|
||||||
|
})
|
||||||
|
.map_err(|e| PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(e.to_string()))?;
|
||||||
|
|
||||||
|
let dict = mapped_rows
|
||||||
|
.next()
|
||||||
|
.transpose()
|
||||||
|
.map_err(|e| PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(e.to_string()))?;
|
||||||
|
|
||||||
|
Ok(dict.unwrap().into())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_status(&self, id: String, status: i32) -> PyResult<()> {
|
||||||
|
let conn = self
|
||||||
|
.pool
|
||||||
|
.get()
|
||||||
|
.map_err(|e| PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(e.to_string()))?;
|
||||||
|
conn.execute(
|
||||||
|
"
|
||||||
|
UPDATE messages SET status = ?1 WHERE id = ?2
|
||||||
|
",
|
||||||
|
params![status, id],
|
||||||
|
)
|
||||||
|
.map_err(|e| PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(e.to_string()))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_result(&self, id: String, result: String) -> PyResult<()> {
|
||||||
|
let conn = self
|
||||||
|
.pool
|
||||||
|
.get()
|
||||||
|
.map_err(|e| PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(e.to_string()))?;
|
||||||
|
conn.execute(
|
||||||
|
"
|
||||||
|
UPDATE messages SET result = ?1 WHERE id = ?2
|
||||||
|
",
|
||||||
|
params![result, id],
|
||||||
|
)
|
||||||
|
.map_err(|e| PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(e.to_string()))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn delete_message(&self, id: String) -> PyResult<()> {
|
||||||
|
let conn = self
|
||||||
|
.pool
|
||||||
|
.get()
|
||||||
|
.map_err(|e| PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(e.to_string()))?;
|
||||||
|
conn.execute(
|
||||||
|
"
|
||||||
|
UPDATE messages SET is_deleted = 1 WHERE id = ?1
|
||||||
|
",
|
||||||
|
params![id],
|
||||||
|
)
|
||||||
|
.map_err(|e| PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(e.to_string()))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clean_expired_messages(&self) -> PyResult<()> {
|
||||||
|
let now: DateTime<Utc> = Utc::now();
|
||||||
|
let iso_format = now.to_rfc3339();
|
||||||
|
let conn = self
|
||||||
|
.pool
|
||||||
|
.get()
|
||||||
|
.map_err(|e| PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(e.to_string()))?;
|
||||||
|
conn.execute(
|
||||||
|
"
|
||||||
|
UPDATE messages
|
||||||
|
SET is_deleted = 1
|
||||||
|
WHERE is_deleted = 0
|
||||||
|
AND expire_time IS NOT NULL
|
||||||
|
AND expire_time < ?1
|
||||||
|
",
|
||||||
|
params![iso_format],
|
||||||
|
)
|
||||||
|
.map_err(|e| PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(e.to_string()))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clean_old_messages(&self, days: i64) -> PyResult<()> {
|
||||||
|
// 获取七天前的时间
|
||||||
|
let now: DateTime<Utc> = Utc::now();
|
||||||
|
let mut old_time = now - Duration::days(days);
|
||||||
|
let iso_format = old_time.to_rfc3339();
|
||||||
|
let conn = self
|
||||||
|
.pool
|
||||||
|
.get()
|
||||||
|
.map_err(|e| PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(e.to_string()))?;
|
||||||
|
conn.execute(
|
||||||
|
"
|
||||||
|
UPDATE messages
|
||||||
|
SET is_deleted = 1
|
||||||
|
WHERE is_deleted = 0
|
||||||
|
AND status IN (?1, ?2)
|
||||||
|
AND updatetime < ?3
|
||||||
|
",
|
||||||
|
params![2, 3, iso_format],
|
||||||
|
)
|
||||||
|
.map_err(|e| PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(e.to_string()))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
use pyo3::prelude::*;
|
||||||
|
|
||||||
|
#[pyclass]
|
||||||
|
pub struct TaskMounter {
|
||||||
|
obj: Py<PyAny>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pymethods]
|
||||||
|
impl TaskMounter {
|
||||||
|
#[new]
|
||||||
|
fn new(obj: Py<PyAny>) -> Self {
|
||||||
|
TaskMounter { obj }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_task_list(&self, py: Python<'_>) -> PyResult<()> {
|
||||||
|
let obj = self.obj.bind(py);
|
||||||
|
let result_obj = obj.call_method("get_task_list", (), None)?;
|
||||||
|
println!("Method result: {:?}", result_obj);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_task_function(&self, py: Python<'_>, task_name: &str) -> PyResult<PyObject> {
|
||||||
|
let obj = self.obj.bind(py);
|
||||||
|
let result = obj.call_method1("get_task_function", (task_name,))?;
|
||||||
|
Ok(result.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
from .message_priority import MessagePriority
|
||||||
|
from .message_status import MessageStatus
|
||||||
|
from .message_type import MessageType
|
||||||
|
|
||||||
|
__all__ = ["MessagePriority", "MessageStatus", "MessageType"]
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
from enum import IntEnum
|
||||||
|
|
||||||
|
|
||||||
|
class MessagePriority(IntEnum):
|
||||||
|
LOW = 0
|
||||||
|
NORMAL = 1
|
||||||
|
HIGH = 2
|
||||||
|
URGENT = 3
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
from enum import IntEnum
|
||||||
|
|
||||||
|
|
||||||
|
class MessageStatus(IntEnum):
|
||||||
|
PENDING = 0
|
||||||
|
PROCESSING = 1
|
||||||
|
COMPLETED = 2
|
||||||
|
FAILED = 3
|
||||||
|
RETRYING = 4
|
||||||
|
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
|
||||||
|
class MessageType(Enum):
|
||||||
|
NOTIFICATION = "notification"
|
||||||
|
SYSTEM = "system"
|
||||||
|
TASK = "task"
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
from ..task_cycle import TaskMounter
|
||||||
|
from typing import Callable, List
|
||||||
|
from ..constant import MessageStatus
|
||||||
|
|
||||||
|
class core:
|
||||||
|
class QueueOperation:
|
||||||
|
def __init__(self, db_path: str):
|
||||||
|
...
|
||||||
|
|
||||||
|
def init_db(self):
|
||||||
|
...
|
||||||
|
|
||||||
|
def enqueue(self, message_dict: dict) -> str:
|
||||||
|
...
|
||||||
|
|
||||||
|
def dequeue(self, size: int = 1) -> List[dict]:
|
||||||
|
...
|
||||||
|
|
||||||
|
def get_queue_length(self) -> int:
|
||||||
|
...
|
||||||
|
|
||||||
|
def get_completed_messages(self) -> List[dict]:
|
||||||
|
...
|
||||||
|
|
||||||
|
def get_result(self, message_id: str):
|
||||||
|
...
|
||||||
|
|
||||||
|
def update_status(self, message_id: str, status: MessageStatus):
|
||||||
|
...
|
||||||
|
|
||||||
|
def update_result(self, message_id: str, result: str):
|
||||||
|
...
|
||||||
|
|
||||||
|
def delete_message(self, message_id: str):
|
||||||
|
...
|
||||||
|
|
||||||
|
def clean_old_messages(self, days: int):
|
||||||
|
...
|
||||||
|
|
||||||
|
def clean_expired_messages(self):
|
||||||
|
...
|
||||||
|
|
||||||
|
class TaskMounter:
|
||||||
|
def __init__(self, task_class: type[TaskMounter]):
|
||||||
|
...
|
||||||
|
|
||||||
|
def get_task_list(self) -> List[str]:
|
||||||
|
...
|
||||||
|
|
||||||
|
def get_task_function(self, name: str) -> Callable:
|
||||||
|
...
|
||||||
|
|
||||||
Binary file not shown.
@@ -0,0 +1,3 @@
|
|||||||
|
from .message_item import MessageItem
|
||||||
|
|
||||||
|
__all__ = ["MessageItem"]
|
||||||
@@ -0,0 +1,224 @@
|
|||||||
|
from dataclasses import dataclass, field
|
||||||
|
from typing import Optional, Dict, Any
|
||||||
|
from datetime import datetime
|
||||||
|
from ..constant import MessageStatus, MessagePriority, MessageType
|
||||||
|
import json
|
||||||
|
from enum import Enum
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class MessageItem:
|
||||||
|
# 必需字段
|
||||||
|
content: dict = field(
|
||||||
|
metadata={"description": "消息内容,包含具体的任务数据或信息"}
|
||||||
|
)
|
||||||
|
|
||||||
|
# 可选字段
|
||||||
|
status: MessageStatus = field(
|
||||||
|
default=MessageStatus.PENDING,
|
||||||
|
metadata={"description": "消息状态:PENDING(待处理)、PROCESSING(处理中)、COMPLETED(已完成)、FAILED(失败)"}
|
||||||
|
)
|
||||||
|
type: MessageType = field(
|
||||||
|
default=MessageType.TASK,
|
||||||
|
metadata={"description": "消息类型:TASK(任务)、NOTIFICATION(通知)、RESPONSE(响应)"}
|
||||||
|
)
|
||||||
|
createtime: datetime = field(
|
||||||
|
default_factory=datetime.now, # type: ignore
|
||||||
|
metadata={"description": "消息创建时间"}
|
||||||
|
)
|
||||||
|
updatetime: datetime = field(
|
||||||
|
default_factory=datetime.now, # type: ignore
|
||||||
|
metadata={"description": "消息最后更新时间"}
|
||||||
|
)
|
||||||
|
result: dict = field(
|
||||||
|
default_factory=dict,
|
||||||
|
metadata={"description": "消息处理结果,存储任务执行后的输出数据"}
|
||||||
|
)
|
||||||
|
id: str = field(
|
||||||
|
default_factory=lambda: str(uuid.uuid4()),
|
||||||
|
metadata={"description": "消息唯一标识符"}
|
||||||
|
)
|
||||||
|
priority: MessagePriority = field(
|
||||||
|
default=MessagePriority.NORMAL,
|
||||||
|
metadata={"description": "消息优先级:LOW(低)、NORMAL(普通)、HIGH(高)、URGENT(紧急)"}
|
||||||
|
)
|
||||||
|
source: str = field(
|
||||||
|
default="client",
|
||||||
|
metadata={"description": "消息来源,标识发送方"}
|
||||||
|
)
|
||||||
|
destination: str = field(
|
||||||
|
default="client",
|
||||||
|
metadata={"description": "消息目标,标识接收方(即task函数名)"}
|
||||||
|
)
|
||||||
|
retry_count: int = field(
|
||||||
|
default=0,
|
||||||
|
metadata={"description": "重试次数,记录消息处理失败后的重试次数"}
|
||||||
|
)
|
||||||
|
expire_time: Optional[datetime] = field(
|
||||||
|
default=None,
|
||||||
|
metadata={"description": "消息过期时间,超过此时间消息将不再处理"}
|
||||||
|
)
|
||||||
|
tags: Optional[str] = field(
|
||||||
|
default=None,
|
||||||
|
metadata={"description": "消息标签,用于分类和筛选"}
|
||||||
|
)
|
||||||
|
metadata: dict = field(
|
||||||
|
default_factory=dict,
|
||||||
|
metadata={"description": "额外元数据,存储自定义的扩展信息"}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls, data: Dict[str, Any]) -> "MessageItem":
|
||||||
|
"""从字典创建消息对象"""
|
||||||
|
# 处理日期时间字段
|
||||||
|
datetime_fields = ['createtime', 'updatetime', 'expire_time']
|
||||||
|
for field in datetime_fields:
|
||||||
|
if field in data:
|
||||||
|
if data[field] == 'null':
|
||||||
|
data[field] = None
|
||||||
|
continue
|
||||||
|
if isinstance(data[field], str):
|
||||||
|
data[field] = datetime.fromisoformat(data[field])
|
||||||
|
elif isinstance(data[field], datetime):
|
||||||
|
continue
|
||||||
|
elif data[field] is None:
|
||||||
|
data[field] = None
|
||||||
|
else:
|
||||||
|
raise ValueError(f"请使用 'str' 或 'datetime' 类型设置 {field} 字段")
|
||||||
|
|
||||||
|
# 处理枚举字段
|
||||||
|
if 'type' in data:
|
||||||
|
if isinstance(data['type'], str):
|
||||||
|
data['type'] = MessageType(data['type']) # 使用枚举名称
|
||||||
|
elif isinstance(data['type'], MessageType):
|
||||||
|
pass # 已经是枚举类型
|
||||||
|
else:
|
||||||
|
raise ValueError(f"请使用 'str' 或 'MessageType' 类型设置 type 字段")
|
||||||
|
|
||||||
|
if 'status' in data:
|
||||||
|
if isinstance(data['status'], str):
|
||||||
|
data['status'] = MessageStatus(data['status']) # 使用枚举名称
|
||||||
|
elif isinstance(data['status'], int):
|
||||||
|
data['status'] = MessageStatus(data['status']) # 使用枚举值
|
||||||
|
elif isinstance(data['status'], MessageStatus):
|
||||||
|
pass # 已经是枚举类型
|
||||||
|
else:
|
||||||
|
raise ValueError(f"Invalid message status: {data['status']}")
|
||||||
|
|
||||||
|
if 'priority' in data:
|
||||||
|
if isinstance(data['priority'], (int, str)):
|
||||||
|
data['priority'] = MessagePriority(data['priority']) # type: ignore
|
||||||
|
elif isinstance(data['priority'], MessagePriority):
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
raise ValueError("请使用 'int' 或 'str' 类型设置 priority 字段")
|
||||||
|
|
||||||
|
return cls(**data)
|
||||||
|
|
||||||
|
def to_dict(self) -> Dict[str, Any]:
|
||||||
|
"""转换为字典
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dict[str, Any]: 消息数据字典
|
||||||
|
"""
|
||||||
|
return {
|
||||||
|
'id': self.id,
|
||||||
|
'type': self.type.value,
|
||||||
|
'status': self.status.value,
|
||||||
|
'content': self.content,
|
||||||
|
'createtime': self.createtime.isoformat(),
|
||||||
|
'updatetime': self.updatetime.isoformat(),
|
||||||
|
'priority': self.priority.value,
|
||||||
|
'source': self.source,
|
||||||
|
'retry_count': self.retry_count,
|
||||||
|
'expire_time': self.expire_time.isoformat() if self.expire_time else None,
|
||||||
|
'tags': self.tags,
|
||||||
|
'metadata': self.metadata,
|
||||||
|
'result': self.result,
|
||||||
|
"destination": self.destination
|
||||||
|
}
|
||||||
|
|
||||||
|
def to_dict_by_core(self) -> Dict[str, Any]:
|
||||||
|
"""转换为核心库需要的字典格式"""
|
||||||
|
return {
|
||||||
|
'id': self.id,
|
||||||
|
'type': self.type.value,
|
||||||
|
'status': self.status.value,
|
||||||
|
'content': json.dumps(self.content),
|
||||||
|
'createtime': self.createtime.isoformat(),
|
||||||
|
'updatetime': self.updatetime.isoformat(),
|
||||||
|
'priority': self.priority.value,
|
||||||
|
'source': self.source,
|
||||||
|
'retry_count': self.retry_count,
|
||||||
|
'expire_time': self.expire_time.isoformat() if self.expire_time else 'null',
|
||||||
|
'tags': self.tags if self.tags else 'null',
|
||||||
|
'metadata': json.dumps(self.metadata),
|
||||||
|
'result': json.dumps(self.result),
|
||||||
|
"destination": self.destination
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def to_json(self) -> str:
|
||||||
|
"""转换为json字符串"""
|
||||||
|
# 将枚举类型转换为字符串
|
||||||
|
data = self.to_dict()
|
||||||
|
for key, value in data.items():
|
||||||
|
if isinstance(value, Enum):
|
||||||
|
data[key] = value.value
|
||||||
|
elif isinstance(value, datetime):
|
||||||
|
data[key] = value.timestamp()
|
||||||
|
return json.dumps(data)
|
||||||
|
|
||||||
|
def is_expired(self) -> bool:
|
||||||
|
"""检查消息是否过期
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: 是否过期
|
||||||
|
"""
|
||||||
|
if self.expire_time is None:
|
||||||
|
return False
|
||||||
|
return datetime.now() > self.expire_time
|
||||||
|
|
||||||
|
def can_retry(self) -> bool:
|
||||||
|
"""检查消息是否可以重试
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: 是否可以重试
|
||||||
|
"""
|
||||||
|
return self.retry_count < 3
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_field_descriptions(cls) -> Dict[str, str]:
|
||||||
|
"""获取所有字段的描述信息
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dict[str, str]: 字段名到描述信息的映射
|
||||||
|
"""
|
||||||
|
from dataclasses import fields
|
||||||
|
|
||||||
|
descriptions = {}
|
||||||
|
for field_info in fields(cls):
|
||||||
|
if 'description' in field_info.metadata:
|
||||||
|
descriptions[field_info.name] = field_info.metadata['description']
|
||||||
|
else:
|
||||||
|
descriptions[field_info.name] = "无描述信息"
|
||||||
|
|
||||||
|
return descriptions
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def print_field_info(cls):
|
||||||
|
"""打印所有字段的信息,包括类型、默认值和描述"""
|
||||||
|
from dataclasses import fields
|
||||||
|
|
||||||
|
print(f"=== {cls.__name__} 字段信息 ===")
|
||||||
|
for field_info in fields(cls):
|
||||||
|
print(f"字段名: {field_info.name}")
|
||||||
|
print(f"类型: {field_info.type}")
|
||||||
|
print(f"默认值: {field_info.default}")
|
||||||
|
if field_info.default_factory is not None:
|
||||||
|
print(f"默认工厂: {field_info.default_factory}")
|
||||||
|
if 'description' in field_info.metadata:
|
||||||
|
print(f"描述: {field_info.metadata['description']}")
|
||||||
|
print("-" * 50)
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
from .queue_operation import QueueOperation
|
||||||
|
|
||||||
|
__all__ = ["QueueOperation"]
|
||||||
@@ -0,0 +1,125 @@
|
|||||||
|
import os
|
||||||
|
import json
|
||||||
|
import hashlib
|
||||||
|
from typing import Tuple, Union, List
|
||||||
|
import threading
|
||||||
|
import random
|
||||||
|
from ..core import core
|
||||||
|
from ..constant import MessageStatus
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class QueueOperation():
|
||||||
|
def __init__(self, shard_num: int = 4, queue_name: str = "default"):
|
||||||
|
self.shard_num = shard_num
|
||||||
|
self.db_dir = os.path.join("cache", queue_name)
|
||||||
|
self.shard_connections = threading.local()
|
||||||
|
if not os.path.exists(self.db_dir):
|
||||||
|
os.makedirs(self.db_dir)
|
||||||
|
self.init_shards()
|
||||||
|
|
||||||
|
def _get_shard_index(self, message_id: str) -> int:
|
||||||
|
"""计算消息的分片索引"""
|
||||||
|
hash_obj = hashlib.sha256(message_id.encode())
|
||||||
|
return int(hash_obj.hexdigest(), 16) % self.shard_num
|
||||||
|
|
||||||
|
def _get_shard_path(self, shard_index: int) -> str:
|
||||||
|
"""获取分片数据库路径"""
|
||||||
|
return os.path.join(self.db_dir, f"queue_shard_{shard_index}.db")
|
||||||
|
|
||||||
|
def _get_shard_conn(self, shard_index: int) -> core.QueueOperation:
|
||||||
|
"""获取分片数据库连接(线程安全)"""
|
||||||
|
if not hasattr(self.shard_connections, "shards"):
|
||||||
|
self.shard_connections.shards = {}
|
||||||
|
|
||||||
|
if shard_index not in self.shard_connections.shards:
|
||||||
|
db_path = self._get_shard_path(shard_index)
|
||||||
|
conn = core.QueueOperation(db_path)
|
||||||
|
self.shard_connections.shards[shard_index] = conn
|
||||||
|
|
||||||
|
return self.shard_connections.shards[shard_index]
|
||||||
|
|
||||||
|
def init_shards(self):
|
||||||
|
"""初始化分片数据库"""
|
||||||
|
for i in range(self.shard_num):
|
||||||
|
conn = self._get_shard_conn(i)
|
||||||
|
conn.init_db()
|
||||||
|
|
||||||
|
# 入队
|
||||||
|
def enqueue(self, message: dict) -> str:
|
||||||
|
shard_index = self._get_shard_index(message["id"])
|
||||||
|
conn = self._get_shard_conn(shard_index)
|
||||||
|
conn.enqueue(message)
|
||||||
|
return message["id"]
|
||||||
|
|
||||||
|
def dequeue(self, size: int = 1) -> List[dict]:
|
||||||
|
messages = []
|
||||||
|
collected = 0
|
||||||
|
|
||||||
|
# 随机轮询分片顺序
|
||||||
|
shard_order = list(range(self.shard_num))
|
||||||
|
random.shuffle(shard_order)
|
||||||
|
|
||||||
|
for shard_index in shard_order:
|
||||||
|
if collected >= size:
|
||||||
|
break
|
||||||
|
|
||||||
|
conn = self._get_shard_conn(shard_index)
|
||||||
|
shard_messages = conn.dequeue(size - collected)
|
||||||
|
messages.extend(shard_messages)
|
||||||
|
collected += len(shard_messages)
|
||||||
|
|
||||||
|
return messages
|
||||||
|
|
||||||
|
# 获取队列长度
|
||||||
|
def get_queue_length(self) -> int:
|
||||||
|
"""获取队列中待处理消息的数量
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
int: 待处理消息数量
|
||||||
|
"""
|
||||||
|
total = 0
|
||||||
|
for i in range(self.shard_num):
|
||||||
|
conn = self._get_shard_conn(i)
|
||||||
|
total += conn.get_queue_length()
|
||||||
|
return total
|
||||||
|
|
||||||
|
# 获取完成/失败的消息
|
||||||
|
def get_completed_messages(self) -> List[dict]:
|
||||||
|
messages = []
|
||||||
|
for i in range(self.shard_num):
|
||||||
|
conn = self._get_shard_conn(i)
|
||||||
|
messages.extend(conn.get_completed_messages())
|
||||||
|
|
||||||
|
return messages
|
||||||
|
|
||||||
|
# 获取消息结果
|
||||||
|
def get_result(self, message_id: str) -> Tuple[bool, Union[str, dict]]:
|
||||||
|
conn = self._get_shard_conn(self._get_shard_index(message_id))
|
||||||
|
return conn.get_result(message_id)
|
||||||
|
|
||||||
|
# 更新消息状态
|
||||||
|
def update_status(self, message_id: str, status: MessageStatus):
|
||||||
|
conn = self._get_shard_conn(self._get_shard_index(message_id))
|
||||||
|
conn.update_status(message_id, status)
|
||||||
|
|
||||||
|
# 更新消息结果
|
||||||
|
def update_result(self, message_id: str, result: dict):
|
||||||
|
conn = self._get_shard_conn(self._get_shard_index(message_id))
|
||||||
|
conn.update_result(message_id, json.dumps(result))
|
||||||
|
|
||||||
|
# 删除消息
|
||||||
|
def delete_message(self, message_id: str):
|
||||||
|
conn = self._get_shard_conn(self._get_shard_index(message_id))
|
||||||
|
conn.delete_message(message_id)
|
||||||
|
|
||||||
|
# 清理7天前的已完成/失败消息
|
||||||
|
def clean_old_messages(self, shard_index: int, days: int = 7):
|
||||||
|
conn = self._get_shard_conn(shard_index)
|
||||||
|
conn.clean_old_messages(days)
|
||||||
|
|
||||||
|
# 清理过期但未处理的消息
|
||||||
|
def clean_expired_messages(self, shard_index: int):
|
||||||
|
conn = self._get_shard_conn(shard_index)
|
||||||
|
conn.clean_expired_messages()
|
||||||
|
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
from .receive_scheduler import ReceiveScheduler
|
||||||
|
from .task_scheduler import TaskScheduler
|
||||||
|
from ..model import MessageItem
|
||||||
|
from typing import Callable
|
||||||
|
from ..queue_operation import QueueOperation
|
||||||
|
from .cleanup_scheduler import CleanupScheduler
|
||||||
|
|
||||||
|
class QueueScheduler:
|
||||||
|
def __init__(self, receive_thread_num=1, task_thread_num=1, shard_num=4):
|
||||||
|
self.queue_operation = QueueOperation(shard_num)
|
||||||
|
self.receive_scheduler = ReceiveScheduler(self.queue_operation, receive_thread_num)
|
||||||
|
self.task_scheduler = TaskScheduler(self.queue_operation, task_thread_num)
|
||||||
|
self.cleanup_scheduler = CleanupScheduler(self.queue_operation)
|
||||||
|
|
||||||
|
def send_message(self, message: MessageItem, callback: Callable):
|
||||||
|
self.receive_scheduler.send_message(message, callback)
|
||||||
|
|
||||||
|
def start_queue_scheduler(self):
|
||||||
|
self.receive_scheduler.start_receive_thread()
|
||||||
|
self.task_scheduler.start_task_thread()
|
||||||
|
self.cleanup_scheduler.start_cleanup()
|
||||||
|
|
||||||
|
def stop_queue_scheduler(self):
|
||||||
|
self.receive_scheduler.stop_receive_thread()
|
||||||
|
self.task_scheduler.stop_task_thread()
|
||||||
|
self.cleanup_scheduler.stop_cleanup()
|
||||||
|
# self.queue_operation.close_all_connections()
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"QueueScheduler"
|
||||||
|
]
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
# 新建清理调度器
|
||||||
|
import threading
|
||||||
|
import time
|
||||||
|
from ..queue_operation import QueueOperation
|
||||||
|
|
||||||
|
|
||||||
|
class CleanupScheduler:
|
||||||
|
def __init__(self, queue_operation: QueueOperation, interval_minutes=60):
|
||||||
|
self.queue_operation = queue_operation
|
||||||
|
self.interval = interval_minutes * 60 # 转换为秒
|
||||||
|
self.is_running = False
|
||||||
|
self.cleanup_thread = None
|
||||||
|
|
||||||
|
def cleanup_expired_messages(self):
|
||||||
|
"""清理过期消息"""
|
||||||
|
while self.is_running:
|
||||||
|
try:
|
||||||
|
# 清理7天前的已完成/失败消息
|
||||||
|
for i in range(self.queue_operation.shard_num):
|
||||||
|
self.queue_operation.clean_old_messages(i, 7)
|
||||||
|
|
||||||
|
# 清理过期但未处理的消息
|
||||||
|
for i in range(self.queue_operation.shard_num):
|
||||||
|
self.queue_operation.clean_expired_messages(i)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"清理消息错误: {str(e)}")
|
||||||
|
|
||||||
|
# 休眠等待下次清理
|
||||||
|
for _ in range(self.interval):
|
||||||
|
if not self.is_running:
|
||||||
|
break
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
def start_cleanup(self):
|
||||||
|
if self.is_running:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.is_running = True
|
||||||
|
self.cleanup_thread = threading.Thread(target=self.cleanup_expired_messages, daemon=True)
|
||||||
|
self.cleanup_thread.start()
|
||||||
|
|
||||||
|
def stop_cleanup(self):
|
||||||
|
if not self.is_running:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.is_running = False
|
||||||
|
if self.cleanup_thread and self.cleanup_thread.is_alive():
|
||||||
|
self.cleanup_thread.join(timeout=2.0)
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
from ..queue_operation import QueueOperation
|
||||||
|
from ..model import MessageItem
|
||||||
|
from typing import Callable
|
||||||
|
from concurrent.futures import ThreadPoolExecutor
|
||||||
|
import threading
|
||||||
|
import time
|
||||||
|
|
||||||
|
class ReceiveScheduler:
|
||||||
|
def __init__(self, queue_operation: QueueOperation, receive_thread_num: int = 1):
|
||||||
|
self.callbacks = dict()
|
||||||
|
self.receive_thread_num = receive_thread_num
|
||||||
|
self.is_running = False
|
||||||
|
self.executor = ThreadPoolExecutor(max_workers=receive_thread_num) # 并行执行回调
|
||||||
|
self.lock = threading.Lock()
|
||||||
|
self.queue_operation = queue_operation
|
||||||
|
self.receive_thread = None # 单一轮询线程
|
||||||
|
|
||||||
|
def send_message(self, message: MessageItem, callback: Callable = None): # type: ignore
|
||||||
|
if callback is None:
|
||||||
|
callback = lambda message: print(message)
|
||||||
|
# self.queue_operation.enqueue(message.to_dict())
|
||||||
|
self.queue_operation.enqueue(message.to_dict_by_core())
|
||||||
|
with self.lock:
|
||||||
|
self.callbacks[message.id] = callback
|
||||||
|
|
||||||
|
def receive_message(self):
|
||||||
|
"""单一轮询线程,并行执行回调"""
|
||||||
|
|
||||||
|
while self.is_running:
|
||||||
|
message_list = self.queue_operation.get_completed_messages()
|
||||||
|
if message_list:
|
||||||
|
for message in message_list:
|
||||||
|
message = MessageItem.from_dict(message)
|
||||||
|
callback = None
|
||||||
|
with self.lock:
|
||||||
|
callback = self.callbacks.pop(message.id, None)
|
||||||
|
|
||||||
|
if callback:
|
||||||
|
# 使用线程池并行执行回调
|
||||||
|
self.executor.submit(self._safe_callback, callback, message)
|
||||||
|
else:
|
||||||
|
time.sleep(0.1) # 适当休眠避免CPU空转
|
||||||
|
|
||||||
|
def _safe_callback(self, callback, message):
|
||||||
|
"""安全执行回调函数"""
|
||||||
|
try:
|
||||||
|
callback(message)
|
||||||
|
# with ThreadPoolExecutor(max_workers=1) as executor:
|
||||||
|
# future = executor.submit(callback, message)
|
||||||
|
# future.result(timeout=5.0)
|
||||||
|
# except TimeoutError:
|
||||||
|
# print(f"回调执行超时: {message.id}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"回调执行错误: {str(e)}")
|
||||||
|
finally:
|
||||||
|
self.queue_operation.delete_message(message.id)
|
||||||
|
|
||||||
|
def start_receive_thread(self):
|
||||||
|
if self.is_running:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.is_running = True
|
||||||
|
# 创建单一轮询线程
|
||||||
|
self.receive_thread = threading.Thread(target=self.receive_message, daemon=True)
|
||||||
|
self.receive_thread.start()
|
||||||
|
|
||||||
|
def stop_receive_thread(self):
|
||||||
|
if not self.is_running:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.is_running = False
|
||||||
|
# 等待轮询线程结束
|
||||||
|
if self.receive_thread and self.receive_thread.is_alive():
|
||||||
|
self.receive_thread.join(timeout=2.0)
|
||||||
|
|
||||||
|
# 关闭线程池
|
||||||
|
self.executor.shutdown(wait=True)
|
||||||
|
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
from ..queue_operation import QueueOperation
|
||||||
|
from ..model import MessageItem
|
||||||
|
from ..constant import MessageStatus
|
||||||
|
from ..task_cycle.task_cycle import TaskCycle
|
||||||
|
from ..task_cycle.task_mounter import TaskMounter
|
||||||
|
from concurrent.futures import ThreadPoolExecutor
|
||||||
|
from datetime import datetime
|
||||||
|
import time
|
||||||
|
import threading
|
||||||
|
|
||||||
|
|
||||||
|
class TaskScheduler:
|
||||||
|
def __init__(self, queue_operation: QueueOperation, task_thread_num: int = 1):
|
||||||
|
self.task_thread_num = task_thread_num
|
||||||
|
self.is_running = False
|
||||||
|
self.executor = ThreadPoolExecutor(max_workers=task_thread_num) # 并行执行任务
|
||||||
|
self.queue_operation = queue_operation
|
||||||
|
self.task_thread = None # 单一轮询线程
|
||||||
|
|
||||||
|
def _process_message(self, message: MessageItem):
|
||||||
|
"""处理单个消息"""
|
||||||
|
try:
|
||||||
|
if message.destination == "client":
|
||||||
|
message.status = MessageStatus.COMPLETED
|
||||||
|
message.result = {"result": "success"}
|
||||||
|
message.updatetime = datetime.now()
|
||||||
|
return message
|
||||||
|
|
||||||
|
task_function = TaskMounter.get_task_function(message.destination)
|
||||||
|
task_cycle = TaskCycle(message, task_function)
|
||||||
|
task_cycle.run()
|
||||||
|
|
||||||
|
message.status = task_cycle.get_task_status() # type: ignore
|
||||||
|
if message.status == MessageStatus.FAILED:
|
||||||
|
message.result = {"error": task_cycle.get_task_error()}
|
||||||
|
else:
|
||||||
|
message.result = task_cycle.get_task_result() # type: ignore
|
||||||
|
message.updatetime = datetime.now()
|
||||||
|
except Exception as e:
|
||||||
|
message.status = MessageStatus.FAILED
|
||||||
|
message.result = {"error": str(e)}
|
||||||
|
message.updatetime = datetime.now()
|
||||||
|
|
||||||
|
return message
|
||||||
|
|
||||||
|
def _update_result(self, message):
|
||||||
|
"""更新任务结果到数据库"""
|
||||||
|
try:
|
||||||
|
self.queue_operation.update_result(message.id, message.result)
|
||||||
|
self.queue_operation.update_status(message.id, message.status)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"任务结果更新失败: {str(e)}")
|
||||||
|
|
||||||
|
def task_callback(self):
|
||||||
|
"""单一轮询线程,并行执行任务"""
|
||||||
|
while self.is_running:
|
||||||
|
try:
|
||||||
|
message_list = self.queue_operation.dequeue(size=self.task_thread_num * 2)
|
||||||
|
if message_list:
|
||||||
|
# 并行处理所有获取到的消息
|
||||||
|
for message in message_list:
|
||||||
|
self.executor.submit(
|
||||||
|
lambda m: self._update_result(self._process_message(m)),
|
||||||
|
MessageItem.from_dict(message)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
time.sleep(0.1) # 适当休眠
|
||||||
|
except Exception as e:
|
||||||
|
print(f"任务调度错误: {str(e)}")
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
def start_task_thread(self):
|
||||||
|
if self.is_running:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.is_running = True
|
||||||
|
# 创建单一轮询线程
|
||||||
|
self.task_thread = threading.Thread(target=self.task_callback, daemon=True)
|
||||||
|
self.task_thread.start()
|
||||||
|
|
||||||
|
def stop_task_thread(self):
|
||||||
|
if not self.is_running:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.is_running = False
|
||||||
|
# 等待轮询线程结束
|
||||||
|
if self.task_thread and self.task_thread.is_alive():
|
||||||
|
self.task_thread.join(timeout=2.0)
|
||||||
|
|
||||||
|
# 关闭线程池
|
||||||
|
self.executor.shutdown(wait=True)
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
from .task_mounter import TaskMounter
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"TaskMounter"
|
||||||
|
]
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
from ..model import MessageItem
|
||||||
|
from typing import Callable, Optional
|
||||||
|
from ..constant import MessageStatus
|
||||||
|
|
||||||
|
class TaskCycle:
|
||||||
|
def __init__(self, message_item: MessageItem, callback: Optional[Callable]):
|
||||||
|
self.message_item = message_item
|
||||||
|
self.callback = callback
|
||||||
|
self.task_result = None
|
||||||
|
self.task_status = None
|
||||||
|
self.task_error = None
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
try:
|
||||||
|
task_result = self.callback(self.message_item) # type: ignore
|
||||||
|
except Exception as e:
|
||||||
|
self.task_result = None
|
||||||
|
self.task_status = MessageStatus.FAILED
|
||||||
|
self.task_error = str(e)
|
||||||
|
else:
|
||||||
|
self.task_result = task_result # type: ignore
|
||||||
|
self.task_status = MessageStatus.COMPLETED
|
||||||
|
self.task_error = None
|
||||||
|
|
||||||
|
def get_task_result(self):
|
||||||
|
return self.task_result
|
||||||
|
|
||||||
|
def get_task_status(self):
|
||||||
|
return self.task_status
|
||||||
|
|
||||||
|
def get_task_error(self):
|
||||||
|
return self.task_error
|
||||||
|
|
||||||
|
def get_task_message_item(self):
|
||||||
|
return self.message_item
|
||||||
|
|
||||||
|
def get_task_callback(self):
|
||||||
|
return self.callback
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
from typing import Callable, List
|
||||||
|
|
||||||
|
|
||||||
|
class TaskMounter:
|
||||||
|
@classmethod
|
||||||
|
def mount_task(cls, function: Callable):
|
||||||
|
setattr(cls, function.__name__, function)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def task(meta: dict = {}):
|
||||||
|
"""带参数的装饰器"""
|
||||||
|
def decorator(function: Callable):
|
||||||
|
# 使用自定义名称或函数原名
|
||||||
|
function.meta = meta # type: ignore
|
||||||
|
setattr(TaskMounter, function.__name__, function)
|
||||||
|
return function
|
||||||
|
return decorator
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_task_function(cls, name: str):
|
||||||
|
return getattr(cls, name, None)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_task_list(cls) -> List[str]:
|
||||||
|
"""获取所有挂载的任务函数名称列表"""
|
||||||
|
task_list = []
|
||||||
|
|
||||||
|
# 遍历类属性字典
|
||||||
|
for attr_name, attr_value in vars(cls).items():
|
||||||
|
# 过滤条件:
|
||||||
|
# 1. 必须是可调用对象(函数)
|
||||||
|
# 2. 不是类自带的特殊方法(非双下划线开头)
|
||||||
|
# 3. 不是类方法本身(如 mount_task, get_task_list 等)
|
||||||
|
if (
|
||||||
|
callable(attr_value)
|
||||||
|
and not attr_name.startswith("__")
|
||||||
|
and attr_name not in ["mount_task", "task", "get_task_function", "get_task_list"]
|
||||||
|
):
|
||||||
|
task_list.append(attr_name)
|
||||||
|
|
||||||
|
return task_list
|
||||||
|
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
from .example import example
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"example"
|
||||||
|
]
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
from queue_sqlite.task_cycle.task_mounter import TaskMounter
|
||||||
|
from queue_sqlite.model import MessageItem
|
||||||
|
|
||||||
|
|
||||||
|
@TaskMounter.task(meta={"task_name": "test"})
|
||||||
|
def example(message_item: MessageItem):
|
||||||
|
def fibonacci_generator():
|
||||||
|
a, b = 0, 1
|
||||||
|
while True:
|
||||||
|
yield a
|
||||||
|
a, b = b, a + b
|
||||||
|
|
||||||
|
# 示例:获取前10项
|
||||||
|
fib = fibonacci_generator()
|
||||||
|
message_item.result = {"fibonacci": [next(fib) for _ in range(500)]}
|
||||||
|
return message_item.to_json()
|
||||||
|
|
||||||
|
|
||||||
|
# 输出:[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
from queue_sqlite.core import core
|
||||||
|
from queue_sqlite.task_cycle import TaskMounter
|
||||||
|
from queue_sqlite.model import MessageItem
|
||||||
|
|
||||||
|
|
||||||
|
class TestCore:
|
||||||
|
queue_operation = core.QueueOperation("./cache/test.db")
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def test_task_mounter(cls):
|
||||||
|
task_mounter = core.TaskMounter(TaskMounter)
|
||||||
|
task_mounter.get_task_list()
|
||||||
|
print(task_mounter.get_task_function("task")({"num": 1})(lambda x: x+1).meta["num"])
|
||||||
|
from tasks import example
|
||||||
|
task_mounter.get_task_list()
|
||||||
|
print(task_mounter.get_task_function("<lambda>"))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def test_queue_operation_init_db(cls):
|
||||||
|
cls.queue_operation.init_db()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def test_queue_operation_enqueue(cls):
|
||||||
|
message_item = MessageItem(content={"num": 1}, destination="test")
|
||||||
|
cls.queue_operation.enqueue(message_item.to_dict_by_core())
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def test_queue_operation_dequeue(cls):
|
||||||
|
message_item = MessageItem(content={"num": 1}, destination="test")
|
||||||
|
cls.queue_operation.enqueue(message_item.to_dict_by_core())
|
||||||
|
print(cls.queue_operation.dequeue(1))
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
from queue_sqlite.model import MessageItem
|
||||||
|
|
||||||
|
|
||||||
|
class TestMessageItem:
|
||||||
|
messageItem = MessageItem(content={"num": 1}, destination="test")
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def test_to_dict(cls):
|
||||||
|
print(cls.messageItem.to_dict())
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def test_to_json(cls):
|
||||||
|
print(cls.messageItem.to_json())
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def test_from_dict(cls):
|
||||||
|
messageItem = MessageItem.from_dict(cls.messageItem.to_dict())
|
||||||
|
print(messageItem.to_dict())
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
from queue_sqlite.model import MessageItem
|
||||||
|
from queue_sqlite.scheduler import QueueScheduler
|
||||||
|
import time
|
||||||
|
from tasks import *
|
||||||
|
import threading
|
||||||
|
import psutil
|
||||||
|
|
||||||
|
|
||||||
|
class TestStress:
|
||||||
|
|
||||||
|
callback_counter = 0
|
||||||
|
lock = threading.Lock()
|
||||||
|
is_monitoring = False
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def monitor_resources(cls):
|
||||||
|
"""监控资源使用情况"""
|
||||||
|
while cls.is_monitoring:
|
||||||
|
cpu = psutil.cpu_percent()
|
||||||
|
memory = psutil.virtual_memory().percent
|
||||||
|
print(f"CPU使用率: {cpu}%, 内存使用率: {memory}%")
|
||||||
|
time.sleep(0.5)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _callback(cls, message_item: MessageItem):
|
||||||
|
with cls.lock:
|
||||||
|
cls.callback_counter += 1
|
||||||
|
# print(f"callback: {message_item}")
|
||||||
|
# print(f"callback: {message_item.id}")
|
||||||
|
# print(f"callback: {message_item.expire_time}")
|
||||||
|
# print(f"callback: {message_item.tags}")
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def test_stress(cls):
|
||||||
|
TASK_NUM = 1000
|
||||||
|
messages = []
|
||||||
|
for i in range(TASK_NUM): # 增加任务数量
|
||||||
|
message_item = MessageItem(content={"num": i}, destination="example")
|
||||||
|
messages.append(message_item)
|
||||||
|
|
||||||
|
cls.is_monitoring = True
|
||||||
|
monitor_thread = threading.Thread(target=cls.monitor_resources, daemon=True)
|
||||||
|
monitor_thread.start()
|
||||||
|
queue_scheduler = QueueScheduler(receive_thread_num=4, task_thread_num=8, shard_num=12)
|
||||||
|
|
||||||
|
# threads = []
|
||||||
|
# for message_item in messages:
|
||||||
|
# t = threading.Thread(target=queue_scheduler.send_message, args=(message_item, cls.test_callback))
|
||||||
|
# threads.append(t)
|
||||||
|
|
||||||
|
# 添加性能计时
|
||||||
|
start_time = time.perf_counter()
|
||||||
|
|
||||||
|
queue_scheduler.start_queue_scheduler()
|
||||||
|
|
||||||
|
for message_item in messages: # 增加任务数量
|
||||||
|
message_item: MessageItem
|
||||||
|
queue_scheduler.send_message(message_item, cls._callback)
|
||||||
|
|
||||||
|
# # 多线程发送任务
|
||||||
|
# for t in threads:
|
||||||
|
# t.start()
|
||||||
|
|
||||||
|
# # 动态等待任务完成
|
||||||
|
start_wait = time.time()
|
||||||
|
while (queue_scheduler.queue_operation.get_queue_length() > 0 or cls.callback_counter < TASK_NUM): # 30秒超时
|
||||||
|
time.sleep(0.5)
|
||||||
|
print(f"当前队列长度:{queue_scheduler.queue_operation.get_queue_length()}")
|
||||||
|
print(f"当前回调数量:{cls.callback_counter}")
|
||||||
|
|
||||||
|
queue_scheduler.stop_queue_scheduler()
|
||||||
|
|
||||||
|
print(f"当前队列长度:{queue_scheduler.queue_operation.get_queue_length()}")
|
||||||
|
print(f"测试函数:斐波那契数列")
|
||||||
|
print(f"次数:{TASK_NUM}")
|
||||||
|
print(f"总耗时: {time.perf_counter() - start_time:.2f}秒")
|
||||||
|
|
||||||
|
cls.is_monitoring = False
|
||||||
|
monitor_thread.join(timeout=1)
|
||||||
|
|
||||||
Reference in New Issue
Block a user