OpenViking 共享临时上传的时间戳目录布局设计与实现:一次根目录列举完成过期清理

发布时间:2026/9/11 14:02:05

OpenViking 共享临时上传的时间戳目录布局设计与实现:一次根目录列举完成过期清理 OpenViking 共享临时上传的时间戳目录布局设计与实现一次根目录列举完成过期清理【免费下载链接】OpenVikingSelf-evolving Context Database for AI Agents. Unify Agent Memory, Knowledge RAG and Skills.项目地址: https://gitcode.com/GitHub_Trending/op/OpenViking共享临时上传shared temp upload是 OpenViking HTTP 服务器在分布式部署下处理本地文件上传的专用通道文件先上传到服务器托管的临时存储换取temp_file_id再由add_resource等 API 消费。本篇文章基于 docs/plans/2026-08-17-shared-temp-upload-timestamp-directory.md 实现计划结合 openviking/server/temp_upload_store.py、openviking/service/user_deletion.py 等源码与测试深入讲解时间戳前缀目录布局的设计动机、实现原理、清理算法与配套测试读完你可以理解 OpenViking 如何在**不依赖文件系统修改时间modTime**的前提下仅凭一次根目录列举就能判定并回收全部过期上传。一、设计动机为什么临时上传需要时间戳目录而非扁平对象在引入本方案之前共享上传在viking://upload根下以扁平的.content/.meta对象形式存放过期清理需要依赖对象本身的修改时间来推断存活时长。这在分布式、对象存储后端上存在两个痛点文件系统修改时间不可靠对象存储的 mtime 语义不统一复制、同步或后端实现差异都会污染它把清理逻辑建立在 mtime 上既不稳健也难以审计清理成本高逐个对象读取属性再判断过期面对大批量上传时扫描开销线性放大。计划文档给出的核心思路docs/plans/2026-08-17-shared-temp-upload-timestamp-directory.md是Store each shared temporary upload in a timestamp-prefixed directory so cleanup can determine expiry from one root listing without filesystem modification times.即将过期时间编码进目录名本身。目录名携带创建时间戳清理时只需要一次根目录列举解析每个一级目录名中的时间戳即可判断是否过期再递归删除过期目录——整个过程零 mtime 依赖。二、目录布局与 upload ID 格式2.1 目录层级共享上传存储在viking://upload内部根下源码常量_SHARED_UPLOAD_ROOT viking://upload见 openviking/server/temp_upload_store.py每次上传产生一个独立目录viking://upload/bucket/leaf/content viking://upload/bucket/leaf/metacontent上传文件本体先写入metaJSON 元数据后写入其存在即标记一次上传完成类似提交标记防止消费者读到半成品这两个对象不属于正常文件浏览面仅供内部使用。路径完全由temp_file_id推导而来消费者无需额外查询即可构造两个 URI。源码中的构造函数openviking/server/temp_upload_store.pydef _shared_content_uri(bucket: str, leaf: str) - str: return f{_SHARED_UPLOAD_ROOT}/{bucket}/{leaf}/content def _shared_meta_uri(bucket: str, leaf: str) - str: return f{_SHARED_UPLOAD_ROOT}/{bucket}/{leaf}/meta2.2 新旧两种 upload ID 格式计划文档最初设想的格式是定宽 Unix 毫秒时间戳 UUID13-digit-ms-uuid。当前仓库实现在此基础上进一步演进为小时分桶格式同时保留对旧格式的完整兼容两者可通过第一段前缀的长度区分openviking/server/temp_upload_store.py格式示例前缀长度解析函数新格式当前布局2026081715-3f2c…(32位hex)10 位YYYYMMDDHHUTC_split_shared_upload_id旧格式legacy 扁平布局1723881600000-3f2c…(32位hex)13 位毫秒时间戳_shared_upload_created_at新 ID 由_new_shared_upload_id()生成openviking/server/temp_upload_store.pydef _new_shared_upload_id() - str: return f{time.strftime(%Y%m%d%H, time.gmtime())}-{uuid.uuid4().hex}其中 10 位小时前缀YYYYMMDDHH同时承担两个职责作为存储分桶bucket共享上传按 UTC 小时归入viking://upload/YYYYMMDDHH/子目录_SHARED_BUCKET_SECONDS 3600见 openviking/server/temp_upload_store.py根目录永远不会退化为一个巨大的扁平目录作为新格式的标记旧格式以 13 位毫秒时间戳开头两种格式按第一段长度即可判别无需额外标记。_split_shared_upload_id()负责把新格式拆成(bucket, leaf)二元组并对合法性做严格校验前缀必须恰好 10 位纯数字、能通过strptime(%Y%m%d%H)解析叶子必须是 32 位小写 hexopenviking/server/temp_upload_store.py。在桶内叶子只使用 UUID hex不重复前缀因此目录路径不会越级嵌套。2.3 对外temp_file_id不变对 API 消费者而言返回的temp_file_id始终是shared_upload_id形式_parse_shared_temp_file_id校验前缀并禁止/、\见 openviking/server/temp_upload_store.py。目录布局是纯内部实现细节同一账号在其存活期内可重复消费该 ID参见 docs/en/api/02-resources.md 的说明。三、保存与解析路径content 先写、meta 后写3.1 保存save入口是TempUploadStore.save_upload()按upload_mode分发到_save_local单机或_save_shared共享见 openviking/server/temp_upload_store.py。_save_shared的核心步骤openviking/server/temp_upload_store.py先把上传流写入本地临时文件并累计字节数超过shared_max_size_bytes立即抛InvalidArgumentError生成新格式upload_id得到temp_file_id fshared_{upload_id}构造content与meta两个 URI元数据写入version、temp_file_id、account、user、original_filename、content_type、file_ext、size、storage_uri等字段openviking/server/temp_upload_store.py先写content再写metawrite_file_bytes之后才write_file保证消费端读取时 meta 一旦可见content 必然已完整落盘写入成功后调度一次异步清理_schedule_shared_cleanup若任一步骤失败递归删除整个viking://upload/bucket/leaf目录并重新抛出不留下孤儿数据finally中清理本地临时文件。VikingFS 写入使用auto_pathlockFalse——共享上传目录从不被并发修改省去不必要的路径锁开销。3.2 解析消费resolveresolve_for_consume()先把temp_file_id拆出shared_前缀openviking/server/temp_upload_store.py_resolve_shared的执行流程openviking/server/temp_upload_store.py读取 meta_read_shared_meta依据 upload_id 自身的格式精确选择一条路径、不做回退探测——新格式读viking://upload/bucket/leaf/meta旧格式读 legacy 扁平路径viking://upload/upload_id/meta_legacy_shared_meta_uri见 openviking/server/temp_upload_store.py校验 metatemp_file_id必须与请求一致、account必须属于当前账号否则抛PermissionDeniedErroropenviking/server/temp_upload_store.py校验content存在读取字节流到本地临时文件返回ResolvedTempUpload。测试test_read_shared_meta_uses_bucket_path_for_new_id与test_read_shared_meta_uses_flat_path_for_legacy_id分别验证了两种格式恰好一次读取、无 fallback 探测的行为tests/server/test_temp_upload_store_async_io.py。四、清理机制一次列举、三组分类、最老优先清理是这套设计的灵魂。_cleanup_shared_uploads()openviking/server/temp_upload_store.py实现时间戳 单次列举的过期判定一次根目录列举vfs.ls(viking://upload, show_all_hiddenTrue, node_limitLS_ALL_NODES, sort_byname, sort_orderasc)按名称升序天然得到最老在前的排列三组独立分类每个一级目录按名称判定归属——YYYYMMDDHH小时桶 → bucket 组过期时刻 bucket_start 3600 ttl_seconds一个桶内最晚上传也已过期时整桶过期13 位毫秒前缀 → legacy 扁平上传组过期时刻 created_at ttl_seconds其余畸形/外来目录 → invalid 组默认跳过仅当cleanup_invalid_dirsTrue时才尽力删除test_cleanup_skips_invalid_dirs_by_default/test_cleanup_removes_invalid_dirs_when_enabled覆盖这两种行为独立的最老优先扫描bucket 与 legacy 两组各自处理删除队首过期项在第一个仍未过期的条目处停止并记录其过期时刻作为due_at——这样一组的大量积压不会饿死另一组测试test_cleanup_buckets_not_starved_by_live_legacy专门验证递归删除_remove_shared_dir用vfs.remove_files(uri, recursiveTrue, auto_pathlockFalse)整目录删除openviking/server/temp_upload_store.py。原始上传从无向量索引记录删除时跳过 vector-store 清理以省去无谓的往返due_at节流清理结果中最小的存活过期时刻被记录到_SHARED_CLEANUP_DUE_AT[account_id]此后在该时刻之前的新请求不会再提交清理任务任一删除失败则清空due_at让下一次请求立即重试测试test_cleanup_remove_failure_clears_due_at_and_stops、test_cleanup_full_page_expired_clears_due_at验证。清理任务通过有界队列maxsize100交给单例后台线程ov-shared-upload-cleanup执行请求路径上只做节流判断与入队_schedule_shared_cleanup见 openviking/server/temp_upload_store.pyTTL 为 0、该账号已有排队任务、或now due_at时直接跳过避免为同一账号堆积重复任务。源码注释明确解释了这些模块级状态_SHARED_CLEANUP_DUE_AT/_SHARED_CLEANUP_PENDING为何必须放在模块作用域而非实例上——TempUploadStore并非单例HTTP 路由与 MCP 端点每次请求都会调用build(...)。五、用户删除联动按 meta 归属定向清理共享上传属于账号级数据用户删除时必须一并回收。UserDeletionService的删除流水线包含多个 stage任务记录、AGFS、向量库、上传、OAuth token、用量审计其中_delete_uploads()负责上传清理openviking/service/user_deletion.py列举viking://upload全部一级目录对每个目录读取其metauri/meta仅当meta.account 目标账号且meta.user 目标用户时才递归删除该目录。对于 meta 不可读的目录则记录 warning 并跳过绝不错删其他账号的数据。该实现与计划文档 Task 3 的目标一致从扫描根级 .meta 对象改为先列目录、再按目录读 meta、按归属定向删除。六、配置参数速查共享上传的行为由TempUploadConfigopenviking/server/config.py控制可在服务端配置中通过server.temp_upload.*路径设置见 docs/en/api/02-resources.md参数类型默认值说明default_modelocal/sharedlocal未显式指定upload_mode时使用的临时上传后端shared_max_size_bytesint512 * 1024 * 1024共享上传单文件大小上限ttl_secondsintge012 * 60 * 60共享上传保留时长0表示关闭 TTL 清理cleanup_invalid_dirsboolFalse是否在清理时删除名称畸形/外来的目录默认关闭以免误删客户端侧HTTP 客户端可通过在ovcli.conf设置upload.mode shared选用共享上传SDK 侧openviking.Config{UploadMode: shared}等效docs/en/api/02-resources.md。未显式指定时default_mode保持local既有客户端行为不变。七、测试与验证从写失败测试到全量回归计划文档采用 TDD 方式逐任务推进测试与验证命令如下# 清理行为Task 1 / Task 3 uv run --active pytest tests/server/test_temp_upload_store_async_io.py -q # 保存与解析路径Task 2 uv run --active pytest tests/server/test_api_resources.py -q -k shared_temp_upload # 聚焦回归 静态检查Task 4 uv run --active pytest tests/test_config_loader.py tests/server/test_temp_upload_store_async_io.py -q uv run --active pytest tests/server/test_api_resources.py -q -k shared_temp_upload uv run --active ruff check openviking/server/temp_upload_store.py openviking/service/user_deletion.py tests/server/test_temp_upload_store_async_io.py tests/server/test_api_resources.py仓库中与本文档对应的测试包括tests/server/test_temp_upload_store_async_io.py覆盖桶/legacy/invalid 三组清理、due_at节流与清除、删除失败重试、新老格式 ID 判别、meta 路径精确读取、事件循环安全_stream_upload_to_local_temp的asyncio.to_thread落盘等tests/server/test_api_resources.pytest_shared_temp_upload_can_be_added_repeatedly同一shared_id可重复消费、test_shared_temp_upload_failed_consume_is_retryable消费失败可重试、test_shared_upload_content_read_rejects_internal_scope内容读取拒绝内部作用域、test_temp_upload_uses_configured_shared_defaultdefault_modeshared生效等端到端断言。计划文档 Task 4 还要求同步更新 docs/en/api/02-resources.md、docs/zh/api/02-resources.md、docs/en/guides/01-configuration.md 与 docs/zh/guides/01-configuration.md在文档中只描述时间戳目录布局 单次列举清理不再引用对象修改时间。八、兼容性与演进要点新旧格式共存legacy 扁平上传在 TTL 内仍可被读取_legacy_shared_meta_uri保留读兼容并被归入 legacy 组单独清理legacy 条目永远不会被当作 invalid 目录删除测试test_cleanup_never_treats_legacy_flat_as_invalid验证。桶粒度过期小时桶的过期以bucket_start 3600 ttl计算语义是桶内最晚上传也已过期因此整桶递归删除是安全的当前小时的桶由于存在存活上传而始终被跳过测试test_cleanup_removes_expired_buckets_and_stops_at_live。不依赖 mtime所有过期判定都来自目录名中的时间戳10 位小时前缀或 13 位毫秒前缀与底层对象存储的修改时间语义完全解耦这正是本设计的核心收益。综上所述这套时间戳前缀目录 单次列举清理方案用目录名承担元数据职责把过期判定从读对象属性降维成解析目录名同时通过小时分桶、最老优先扫描与due_at节流保证了清理在根目录规模增长下的可扩展性是 OpenViking 分布式共享上传链路中一个精巧且自洽的基础设施设计。【免费下载链接】OpenVikingSelf-evolving Context Database for AI Agents. Unify Agent Memory, Knowledge RAG and Skills.项目地址: https://gitcode.com/GitHub_Trending/op/OpenViking创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
延伸阅读

更多相关文章

2026/9/11 14:02:05

MuJoCo肌腱系统完整指南:三步搭出肌肉驱动模型

MuJoCo肌腱系统完整指南:三步搭出肌肉驱动模型 【免费下载链接】mujoco Multi-Joint dynamics with Contact. A general purpose physics simulator. 项目地址: https://gitcode.com/GitHub_Trending/mu/mujoco 想模拟肱二头肌屈臂?只控制关节角度…

2026/9/11 14:02:05

后端开发者如何用AWS大数据技术栈提升竞争力

1. 为什么后端开发者需要掌握AWS大数据技术栈十年前我刚入行时,后端开发者的技术栈还停留在CRUD和基础架构维护。如今在云原生和大数据时代,我发现身边能熟练使用AWS数据服务的中高级开发者,平均薪资比传统后端高出30-45%。这个差距不仅体现在…

2026/9/11 13:57:04

SFTP目录权限问题排查与自动化修复方案

1. SFTP目录权限问题的典型场景 遇到SFTP服务器上的目录无法读取数据时,90%的情况都源于Linux文件系统权限配置不当。上周我就处理了一个生产环境案例:某金融公司的报表系统突然无法通过SFTP获取每日交易数据,运维团队花了3小时才发现是cronj…

2026/9/11 16:12:35

Typst 安装与配置:10 分钟从零到第一个 PDF

Typst 安装与配置:10 分钟从零到第一个 PDF 【免费下载链接】typst A markup-based typesetting system that is powerful and easy to learn. 项目地址: https://gitcode.com/GitHub_Trending/ty/typst 等排版引擎编译了整整 40 秒,只为了改一个…

2026/9/11 16:12:35

Scala课程设计实战:基于滑动窗口与线性回归的交通拥堵预测

简介:一份基于Scala的交通拥堵预测课程设计源码,主要面向计算机相关专业学生、教师以及正在完成课设或大作业的开发者。项目以交通拥堵预测为业务场景,整合Scala编程、数据处理与数据库设计相关知识点,经导师指导评审获得高分&…

2026/9/11 16:12:35

低功耗MCU端侧语音识别:从RT1050到智能穿戴设备

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/11 16:12:35

数字化转型时代下的成长股投资新方法论

1. 项目概述:重新定义成长股投资方法论彼得林奇的"质量成长"投资理念正在经历数字化转型时代的考验。这位传奇基金经理在管理麦哲伦基金期间创造的29%年化收益率纪录,其核心逻辑是寻找具有持续盈利增长能力的优质企业。但当我们把目光投向当今…

2026/9/11 16:07:34

嵌入式调试笔记:2个IO读取4档开关与Modbus float传输解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/10 16:39:38

超人会飞不算本事:系统稳定依赖清晰规则与边界设计

开头先不绕弯子。“#斯坦李吐槽dc 所以超人是无缘无故会飞的嘛哈哈哈哈哈哈哈锤哥真是技术人才啊!#雷神 #复联”这类调侃式短标题,第一波冲击力在于它把两个宇宙的角色塞进同一个吐槽箱里,但细想一下就能发现,它真正碰到的根本不是…

2026/9/10 11:16:38

超人VS蜘蛛侠:拆解超级IP的影响力与传播方法论

把“蜘蛛侠 vs 超人”放在 CSDN 上聊,可能很多人第一反应是走错片场了。但如果把这两个角色看成“两个持续运营了 80 多年的文化产品”,你会发现,这场比较本质上是两个不同 IP 策略的长期结果对比:超人赢在定义了整个超级英雄题材…

2026/9/9 16:31:09

基于CNN的调制信号识别:MATLAB实现时频图分类实战

简介:本资源是一套面向通信工程与信号处理方向学习者、研究者的深度学习实践方案,聚焦调制信号自动检测与识别这一典型无线通信任务,解决传统方法依赖人工特征、低信噪比下性能下降等痛点。压缩包共12个文件(10.73MB)&…

2026/9/10 12:32:02

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/10 15:19:50

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/10 15:49:53

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
咨询二维码