在年初的一篇关于商业开源的博文当中,我介绍了在开发商业软件的过程中,衍生出开源公共软件库的模式。在那篇博文里面,我只是简单罗列了相关开源库的名字及一句话总结。近期,我会结合商业开源实践的最新进展,对其中一些案例做详细展开。

首先介绍的是 Cronexpr[1],一个小巧的 Rust Crontab 解析库。其背后的开源模式,我称之为“涓滴开源”,即将商业软件依赖的微小但完整的功能模块开源出来,供其他人使用。

ScopeDB 的实际需求

开源软件的源头活水是有人需要

“只是为了好玩”(Just For Fun)固然可以成为某人一时的动力,但是开源项目能够长期持续维护,肯定是因为有用户长期使用,倒推上游保持更新。作为互动的另一环,如果上游不再更新,用户也会逐渐流失。

Cronexpr 的需求来源于我和几位伙伴从去年开始开发的 ScopeDB 云数据库[2]。ScopeDB 是一个商业数据库,构建在云计算弹性且廉价的资源之上,通过一致的接口体验和查询语言将原本叠床架屋的数据流水线,简化为从应用直接写入 ScopeDB 后即可执行任意查询。在实现 ScopeDB 的过程中,我们发现类似数据保留策略的执行、简单的物化视图构建,以及数据存储的整理,都可以用定时后台作业的模型来建模:

CREATE JOB archive_table_tSCHEDULE = '4 2 * * * Asia/Shanghai'NODEGROUP = 'background'
ASDELETE FROM tWHERE created_at < NOW() - INTERVAL '720 hours';

可以看到,这里在后台作业的调度周期的时候,用的是形如 4 2 * * * Asia/Shanghai 的 Cron 表达式。这也是定时后台作业常用的调度逻辑定义方案了。

于是,为了支持解析 Cron 表达式,并在 ScopeDB 调度后台作业的逻辑当中嵌入 Cron 表达式的计算逻辑,我第一时间想到的就是寻找一个现成的开源库来解决需求。

Cronexpr 的诞生

很快,我就找到了 Rust 生态里两个看起来不错的 Cron 表达式软件库:

  • croner[3]

  • saffron[4]

其中 croner 看起来是独立开发者发布的软件库,不过当时已经发布了 2.x 版本,今天再看已经发布了 3.x 版本,看起来作者是对软件的成熟度比较有信心的。saffron 虽然只发了一个 0.1.0 版本,但是它是 Cloudflare 出品,且看起来接口也比较正经,应该也相对成熟。

不过,这两者都不支持带时区信息的 Cron 表达式,这对用户体验来说有比较大的差别。同时,这两者对 Cron 表达式解析时的一些实现细节和扩展,都有奇特的“村规”。最后,由于这两个库实现时间较早,它们所采用的时间库是旧的 chrono 库,而不是现在更可靠的 jiff 库。至于其他 Cron 表达式相关的开源库,要么是一看就不靠谱,要么是完全实现成 Unix 下的 Cron 程序,自带不能去掉的命令执行功能,然而 ScopeDB 只需要能解析 Cron 表达式即可。

因此,考虑到解析 Cron 表达式并不复杂,且即使使用 croner 或 saffron 也需要对其进行一些修改以满足 ScopeDB 的需求,我决定自己实现一个 Cron 表达式解析库。

Cronexpr 由此诞生。它的原型接口非常简单:

let crontab = cronexpr::parse_crontab("2 4 * * * Asia/Shanghai").unwrap();// case 0. match timestamp
assert!(crontab.matches("2024-09-24T04:02:00+08:00").unwrap());
assert!(!crontab.matches("2024-09-24T04:01:00+08:00").unwrap());// case 1. find next timestamp with timezone
assert_eq!(crontab.find_next("2024-09-24T10:06:52+08:00").unwrap().to_string(),"2024-09-25T04:02:00+08:00[Asia/Shanghai]"
);

实现的细节就不展开了,这里讨论一下对依赖的选用。

Cronexpr 的依赖只有两个,一个是上面提到的 jiff 库,用来处理时间戳相关的逻辑,另一个是 winnow 库,用来处理 Cron 表达式的解析。

选用 jiff 的道理非常简单,它是目前最可靠的 Rust 日期时间库,对时区、夏令时、各种日期计算都有很好的支持。历法的制定实际上是一种话语权的争夺,历法与日期变更的计算充满了人类世界的不靠谱特质,能把里面各种烂坑填好的库不可多得。jiff 库的作者有长期良好的声誉,他是 Golang 生态当中 toml 库的作者,也是 Rust Team 的官方成员,是 Rust Regex 库的作者,也还创作过 ripgrep 和 csv 等高质量开源库。

选用 winnow 的理由就相对随机。其实 Rust 生态里比较有名的 Parser Combinator 是 nom 库。但是当时 ScopeDB 已经用过 nom 库来解析 ScopeQL 了,我感觉使用的过程中有一些不爽的点。正好当时 nom 一年多没发新版本了,我想着 winnow 号称是它的积极维护的分支,维护者是 Rust Team 的活跃成员,或许可以试试。实际情况是也没有好到哪里去,而且我写的时候不是完全按组合子的味道写的,最终结果有点半手写半组合子的风格,可能还不如纯手写。不过好在 Cron 表达式的结构非常固定,而且可见的迭代需求不多,大致知道怎么回事,代码一直能看懂就差不多了。客观来说,最初采用 winnow 还是节省了不少 while-if-match 式的样板代码。

文档与测试

由于 Cronexpr 的逻辑相对简单,经历过大约两周的迭代后,所有接口和实现基本就已经稳定了。这期间主要修正接口和实现的反馈,来自于 ScopeDB 的实用情况,以及参考现有软件库的接口设计方式。例如,我就是在看到 croner 和 saffron 的接口设计后,才想到可以做一个 iter_after 的接口。

同时,由于 Cron 表达式有参考实现,而且在开发的过程中,我遇到了很多具体“村规”的理解和处理,叠加上当时受到 jiff 库详实文档的启发,我把 Cronexpr 开发过程当中所有的设计理念跟概念都用文档的形式记录了下来。后来发布到 Hacker News 上的时候,也有读者回应称从文档里了解到许多此前不知道的 Cron 表达式的细节。

详实的 Cronexpr 文档

除此以外,实现一个成熟的功能,也很容易找到前人写过的测试集。Cronexpr 最适合的测试模式,当然是所谓的快照测试(Snapshot Testing),即把解析和匹配 Cron 表达式的返回值作为快照记录下来,在迭代过程中保证这些返回值总是一致。因为返回值的文本,尤其是报错时的文本经常相对冗长,所以用手写 assert_eq! 的方式可能会难以维护。我用 insta[5] 工具维护了近一百个测试结果的快照。

基于 insta 的快照测试

顺带一提,ScopeDB 也重度使用 insta 做快照测试,包括 ScopeQL 的 Parsing 测试,以及端到端的类 sqllogictest 的测试。实际效果跟 sqllogictest 基本一样,而且能够跟 Rust 原生的功能做更紧密且可定制化的集成,不用依赖一个新的 DSL 跟每次需要新功能就要对 DSL 做扩展开发。

实际效果与现状

ScopeDB 从第一个测试版本开始就支持 CREATE JOB 功能。

Cronexpr 首先被用在 CREATE JOB 的执行上,即在创建后台作业时先校验 Cron 表达式是否是合法的。随后,ScopeDB 的 server 进程本身会启动一个线程,实时监督哪些 JOB 已经到了需要再次调度的时刻,并触发 JOB 执行。虽然涉及的代码行数不多,但却是 ScopeDB 能够高效运维的核心能力之一。

目前,Cronexpr 已经发布了 1.0 版本,除了偶尔跟进一下依赖库的版本,平常基本没有什么需要再做开发的地方。我能想到的迭代需求,可能还有以下几个:

  1. 有开发者提出过给 Cronexpr 做一套 Fuzz 测试。我觉得没啥必要,因为实际的 case 非常有限,目前基本都枚举完了。但是如果有人做了一个不错的 Fuzz 方案,可以拿 Cronexpr 做实验。

  2. 把 winnow 的依赖去掉。这样,继我在写作本文之前把 thiserror 依赖去掉以后,Cronexpr 就可以仅依赖不可能去掉的 jiff 库,尽可能的减少不必要的依赖。如前所述,winnow 在 Cron 表达式这样一个语法非常固定的场景里作用有限。

  3. 有人可能想要支持可选的秒级、年份级 Cron 表达式。不过这些都是比较小众的需求,我在自己有实际需求之前就不实现了。

涓滴开源与开源的可持续性

在 ScopeDB 的开发过程中,不只有 Cronexpr 一个开源库诞生。上面提到的 server 进程本身会启动一系列后台线程,这些后台线程的调度就是由 Fastimer[6] 库支持的。此外,ScopeDB 使用的 Fastrace[7] + Logforth[8] 方案,实际上是当前 Rust 生态非常先进且可靠的一套日志追踪方案。这些案例或许我在后续文章中还会展开。

要说跟 Cronexpr 相似的,应当是 StackSafe[9] 这个用于避免递归调用和递归数据结构栈溢出的小公共库。库作者 Andy 老师还写了一篇博文[10]介绍其设计理念跟使用方式。

其他还有一些体量不大的软件库,比如 Mea 和 Fastpool 等等。但是它们设计到我很想吐槽的 Async Rust 生态,所以可能会单独写几篇文章讨论它们的情况。

回到开源项目的可持续性上来,这些项目的源头活水,首先就是被 ScopeDB 商业产品所需求。在开源以后,例如 Cronexpr、Fastrace 和 Logforth 等软件库,也逐渐有了第三方的下游依赖。这些新的用户为我们开源的项目提供了宝贵的反馈,有些帮我们提前解决了将要遇到的问题,有些帮助我们更好的设计接口。用户反馈本身也是对工程师编写软件的正向反馈:看到自己写作的软件能够帮到更多的人,被更多的人认可,是一件非常开心的事情。

因此,涓滴开源是一种可持续的开源模式。开源开发者通过商业软件已经实现了经济可持续,将开发商业软件过程中,微小但完整的功能模块,且其本身不产生商业价值,反而最好开源寻求同行评审,以公共库的形式开源发布出来,这是商业开源生产软件的其中一种形式。

这个世界上这样产生的开源软件有很多,包括前文提到的 Cloudflare 开源的 saffron 也属于此类。只要公司不做禁止,这类软件就可自由生长;而如果企业能够稍加引导,这些软件就能成为技术品牌影响力的一部分,其价值将在长时间跨度上不断为企业带来正面的回报。

参考资料

[1] 

Cronexpr: https://crates.io/crates/cronexpr

[2] 

ScopeDB 云数据库: https://www.scopedb.io/

[3] 

croner: https://docs.rs/croner/latest/croner/

[4] 

saffron: https://docs.rs/saffron/latest/saffron/

[5] 

insta: https://insta.rs/

[6] 

Fastimer: https://github.com/fast/fastimer

[7] 

Fastrace: https://github.com/fast/fastrace

[8] 

Logforth: https://github.com/fast/logforth

[9] 

StackSafe: https://github.com/fast/stacksafe

[10] 

博文: https://fast.github.io/blog/stacksafe-taming-recursion-in-rust-without-stack-overflow/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/92080.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/92080.shtml
英文地址,请注明出处:http://en.pswp.cn/pingmian/92080.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

完整的登陆学生管理系统(配置数据库)

目录 要求 思路 1. 登录模块&#xff08;LoginFrame.java&#xff09; 2. 学生信息管理模块&#xff08;StudentFrame.java&#xff09; 3. 数据层&#xff08;StudentDAO.java&#xff09; 4. 业务层&#xff08;StudentService.java / UserService.java&#xff09; 5…

译 | 在 Python 中从头开始构建 Qwen-3 MoE

文章出自&#xff1a;基于 2个Expert 的 MoE 架构分步指南 本篇适合 MoE 架构初学者。文章亮点在于详细拆解 Qwen 3 MoE 架构&#xff0c;并用简单代码从零实现 MoE 路由器、RMSNorm 等核心组件&#xff0c;便于理解内部原理。 该方法适用于需部署高性能、高效率大模型&#x…

Spring Boot + ShardingSphere 分库分表实战

&#x1f680;Spring Boot ShardingSphere 实战&#xff1a;分库分表&#xff0c;性能暴增的终极指南&#xff01; ✅ 适用场景&#xff1a;千万级大表、高并发、读写分离场景 ✅ 核心框架&#xff1a;Spring Boot 3.x ShardingSphere-JDBC 5.4.1 ✅ 数据库&#xff1a;MySQL…

MaxKB 使用 MCP 连接 Oracle (免安装 cx_Oracle 和 Oracle Instant Client)

一、背景 安装cx_Oracle包和Oracle Instant Client来操作数据库&#xff0c;比较繁琐同时容易冲突&#xff0c;不同的 Oracle 版本都需要安装不同的插件。这篇文章将介绍使用 MCP 协议的连接方法。 二、操作步骤 1、使用 1Panel 安装 DBhub a) 数据库类型选择 Oracle 类型。…

基于Python的超声波OFDM数字通信链路设计与实现

基于Python的超声波OFDM数字通信链路设计与实现 摘要 本文详细介绍了使用Python实现的超声波OFDM(正交频分复用)数字通信链路系统。该系统能够在标准音响设备上运行&#xff0c;利用高于15kHz的超声波频段进行数据传输&#xff0c;采用48kHz采样率。文章涵盖了从OFDM基本原理、…

滑动窗口相关题目

近些年来&#xff0c;我国防沙治沙取得显著成果。某沙漠新种植N棵胡杨&#xff08;编号1-N&#xff09;&#xff0c;排成一排。一个月后&#xff0c;有M棵胡杨未能成活。现可补种胡杨K棵&#xff0c;请问如何补种&#xff08;只能补种&#xff0c;不能新种&#xff09;&#xf…

Java 工具类的“活化石”:Apache Commons 核心用法、性能陷阱与现代替代方案

在上一篇文章中&#xff0c;我们回顾了 Apache Commons 的经典组件。但作为 Java 世界中资历最老、影响最深远的工具库&#xff0c;它的价值远不止于此。许多开发者可能只使用了它 10% 的功能&#xff0c;却忽略了另外 80% 能极大提升代码质量的“隐藏宝石”。本文将提供一个更…

数据结构——图及其C++实现 多源最短路径 FloydWarshall算法

目录 一、前言 二、算法思想 三、代码实现 四、测试 五、源码 一、前言 前两篇学习的Dijkstra算法和Bellman-Ford算法都是用来求解图的单源最短路径&#xff0c;即从图中指定的一个源点出发到图中其他任意顶点的最短路径。Dijkstra算法不能求解带有负权重的图的最短路径&…

解决微软应用商店 (Microsoft store) 打不开,无网络连接的问题!

很多小伙伴都会遇见微软应用商店 (Microsoft store)打开后出现无网络的问题&#xff0c;一般出现这种问题基本都是因为你的电脑安装了某些银行的网银工具&#xff0c;因为网银工具为了安全会关闭Internet 选项中的最新版本的TLS协议&#xff0c;而微软商店又需要最新的TLS协议才…

Android—服务+通知=>前台服务

文章目录1、Android服务1.1、定义1.2、基本用法1.2.1、定义一个服务1.2.2、服务注册1.2.3、启动和停止服务1.2.4、活动和服务进行通信1.3、带绑定的服务示例1.3.1、定义服务类1.3.2、客户端&#xff08;Activity&#xff09;绑定与交互​1.3.3、AndroidManifest.xml 注册​1.3.…

从基础功能到自主决策, Agent 开发进阶路怎么走

Agent 开发进阶路线大纲基础功能实现核心模块构建环境感知&#xff1a;传感器数据处理&#xff08;视觉、语音、文本等输入&#xff09;基础动作控制&#xff1a;API调用、硬件驱动、简单反馈机制状态管理&#xff1a;有限状态机&#xff08;FSM&#xff09;或行为树&#xff0…

《动手学深度学习》读书笔记—9.6编码器-解码器架构

本文记录了自己在阅读《动手学深度学习》时的一些思考&#xff0c;仅用来作为作者本人的学习笔记&#xff0c;不存在商业用途。 正如我们在9.5机器翻译中所讨论的&#xff0c;机器翻译是序列转换模型的一个核心问题&#xff0c;其输入和输出都是长度可变的序列。为了处理这种类…

DocBench:面向大模型文档阅读系统的评估基准与数据集分析

本文由「大千AI助手」原创发布&#xff0c;专注用真话讲AI&#xff0c;回归技术本质。拒绝神话或妖魔化。搜索「大千AI助手」关注我&#xff0c;一起撕掉过度包装&#xff0c;学习真实的AI技术&#xff01; 一、数据集概述与核心目标 DocBench 是由研究团队于2024年提出的首个…

Python高级排序技术:非原生可比对象的自定义排序策略详解

引言&#xff1a;超越原生比较操作的排序挑战在Python数据处理中&#xff0c;我们经常需要处理不原生支持比较操作的对象。根据2024年《Python开发者生态系统报告》&#xff0c;在大型项目中&#xff0c;开发者平均需处理28%的自定义对象排序需求&#xff0c;这些对象包括&…

低代码系统的技术深度:超越“可视化操作”的架构与实现挑战

在很多非开发者眼中&#xff0c;低代码平台似乎只是简化流程、快速搭建页面的工具。然而&#xff0c;在真实的企业级应用中&#xff0c;低代码系统必须面对高并发请求、复杂业务规则、多角色权限、跨系统集成与持续演进等一系列工程挑战。高效交付&#xff08;Rapid Delivery&a…

【NLP舆情分析】基于python微博舆情分析可视化系统(flask+pandas+echarts) 视频教程 - 词云图-微博评论词云图实现

大家好&#xff0c;我是java1234_小锋老师&#xff0c;最近写了一套【NLP舆情分析】基于python微博舆情分析可视化系统(flaskpandasecharts)视频教程&#xff0c;持续更新中&#xff0c;计划月底更新完&#xff0c;感谢支持。今天讲解词云图-微博评论词云图实现 视频在线地址&…

Webpack核心技能:Webpack安装配置与模块化

一、webpack 的安装和使用1. webpack 简介webpack 是基于模块化的打包 (构建)工具&#xff0c;它把一切视为模块&#xff08;包括 JS、CSS、图片等资源文件&#xff09;。工作原理&#xff1a;以开发时态的入口模块为起点递归分析所有依赖关系经过压缩、合并等处理最终生成运行…

数据结构---二级指针(应用场景)、内核链表、栈(系统栈、实现方式)、队列(实现方式、应用)

一、二级指针的应用场景1、在被调函数中&#xff0c;想要修改主调函数中的指针变量&#xff0c;需要传递该指针变量的地址&#xff0c;形参用二级指针接收。2、指针数组的数组名是一个二级指针&#xff0c;指针数组的数组名作为参数传递时&#xff0c;可用二级指针接收。指针数…

NodeJs学习日志(1):windows安装使用node.js 安装express,suquelize,sqlite,nodemon

windows安装使用node.js 安装express&#xff0c;suquelize&#xff0c;sqlite 系统是win10&#xff0c;默认已经安装好nodejs与npm包名作用expressWeb应用框架suquelize数据库ORMsqlite数据库nodemon代码热重载安装express 添加express生成器 npm add express-generator4安装e…

Cervantes:面向渗透测试人员和红队的开源协作平台

Cervantes 是一个专为渗透测试人员和红队打造的开源协作平台。它提供了一个集中式工作区&#xff0c;用于集中管理项目、客户端、漏洞和报告。通过简化数据组织和团队协调&#xff0c;它有助于减少规划和执行渗透测试所需的时间和复杂性。 作为 OWASP 旗下的开源解决方案&…