新的系列视频:从零开始构建资源预定系统

2022-12-05 14:31:05 浏览数 (2)

九月初的小长假,加上 Tubi holiday,四天时间里我做了 9 个视频,一口气把 xdiff 系列弄完。这系列一路发到了十月初。在此期间,我基本无暇打理 B 站视频,只是在视频库存快要见底的时候思考过下一个系列发什么。恰巧最近业余时间都在研究和 postgres 相关的项目:

  • neon:这是个让人眼前一亮的 serverless postgres
  • pgx:这是个用 Rust 创建 postgres extension 的 crates,我还做了一期直播
  • db migration:我自己在研究 db migration 有没有可能做得像 terraform 那样,描述状态,而非描述动作;同时又不给开发者带来太大的心智负担

于是我开始考虑要不要来录制一个和 postgres 相关的项目。前几天我看 postgres 文档,了解 EXCLUDE constraint 时,发现了它结合 RANGE 类型做冲突检查的妙用,顿时想到了很多使用场景,其中之一就是处理资源预定系统。预定系统是很多业务逻辑中都可能涉及到的内容。它可以处理诸如某个资源在某个时间段独占的一系列问题。比如说日程表安排,会议室预定,测试设备的预定,酒店房间预定等等。构建一个预定系统不算太过复杂,但也绝不容易。于是我就琢磨着干脆就以这个主题做下一个系列视频。

在这个系列里,我希望有新的突破 —— 我想尝试着把我从零开始做一个项目的经验,尤其是我思考问题的过程展示出来,这样大家不仅能了解我是如何写代码的,还可以进一步了解我是如何在开发前期是如何做出各种选择的。这个题目对我而言是个全新的课题,我自己也未处理过类似的问题。

所以,基本上你会在视频中看到我从零开始构建一个新项目过程中的所有动作,以及后续添加需求维护项目的一些处理,包括但不限于:

  • 用 excalidraw 做前提需求的分析,以及大致的架构图
  • 项目 git repo 的初始化,包括:pre-commit,CI,以及对 workspace 的思考
  • 撰写 RFC 来思考要解决的问题的技术方案以及选择
  • 系统对外接口的设计
  • 核心数据结构(数据库 schema)的设计
  • 撰写代码过程中的思考,以及对架构的重新审视
  • 撰写代码过程中的不断重构,涉及代码级别, 文件级别,甚至 crate 级别的重构
  • 撰写代码过程中对测试的思考,以及测试的重构
  • 需求变化过程中是如何重构的
  • 我个人对 user-friendly 和 developer-friendly 的思考

那么,目前做好的内容都包括什么?

上个周末我已经录好并剪辑了 9 期内容(12 个小时的裸素材,还不包括因各种原因废弃掉的 3 小时的素材),包括:

  1. 思考需求:这一讲我们主要是前期的需求分析和架构设计。
  2. 设计数据库 schema:这一讲我们构建数据库表和函数,其中会着重介绍 EXCLUDE constraint 的用法。
  3. 系统设计:这一讲我们进一步细化系统层级都有哪些功能。

头三讲基本和 Rust 无关,主要是厘清需求:

以及对 reservation service 有一个基本的架构设计:

  1. 构建 gRPC 接口:这一讲我们撰写描述接口的 proto 文件,并用 build 脚本(使用 tonic-build)进行编译。
  2. 使用 sqlx 做数据库 migration:这一讲我们把数据库设计整理到 migration 中,并确保其正确执行。
  3. 实现预定功能:这一讲我们正式开始实现预定系统的核心逻辑。我们先构建一个 trait 来通盘考虑这个层级的接口,然后实现了 reserve 方法。
  4. 对 sqlx 进行测试:这一讲我们谈谈如何对数据库相关的代码做测试。和数据库打交道的代码,其核心逻辑往往在 SQL 语句中,所以 mock 的意义不大。我们需要使用类似于 sqlx-database-tester 这样的工具,可以帮助我们创建临时数据库,做 migration,运行被测代码,最后销毁临时数据库。
  5. 优雅地返回错误信息。当用户的预定与已有的预定产生冲突时,postgres 会抛出一个 23P01 的错误代码。我们希望当出现这个错误时,我们可以清晰地告诉用户,你的预定和已有的哪个预定产生冲突。这一讲我们会实现大量的 FromStr / TryFrom 结合正则表达式来一步步达到我们的目标。
  6. 完成预定系统的其它功能:这一讲我们完成剩下的主要功能,包括 change_status, update_note, get, delete, query。这一讲的亮点主要是通过实现 FrowRow trait,我们可以把 protobuf 接口中定义的 Reservation 数据结构和数据库中定义的 reservations 表联系起来。
  7. 对预定进行查询(进行中):这一讲我们完成 query 的功能。我会探讨做分页的一些基本思路和方案,然后在查询中支持分页。

除此之外,未来还会涉及哪些内容?

头 10 讲完成后,上面架构图中, Reservation 子模块就暂告一段落。之后我会探讨以下内容:

  • 构建 simulation 脚本,为数据库添加基准测试数据(预计 1 讲)。
  • 处理 performance benchmark,为 Reservation 子模块的核心功能做一个基本的性能测试(预计 2 讲)。
  • 修复 CI 并为性能测试添加 CI 脚本(预计 1 讲)。到目前为止,这个项目的 github action 一直是失败的,原因是我还没有构建 postgres 数据库,而测试对其有依赖。我之所以没有修复它,是想也把它作为一个素材和大家分享。我还希望在打 Tag 时触发性能测试,来了解随着版本的发布,系统的核心性能指标变化情况。类似下图:
  • 构建 Listener 子模块,监听数据库中 reservation 的变更,并通过 channel 将其广播出去(预计 2 讲)。
  • 实现 gRPC service 中所有定义的 RPC(预计 2-3 讲)。

至此,我们的预定系统的核心功能就讲差不多了。还有一些内容,取决于我是否有空:

  • 构建 CLI,让服务的使用对开发者更友好(2-3 讲)
  • 添加对 open-telemetry 的支持(1-2 讲)
  • 刻意进行一次大的需求变更,尝试在已有的系统上进行大规模重构(? 讲)
  • 其它遗漏的部分

为啥我要持续构建这些教程?

如果你是我公众号的长期读者,应该知道,我做视频,和写公众号一样,主要还是出于热爱,出于把自己的知识传播出去带给自己的愉悦感。这种愉悦感分层:一层是单纯的分享的快乐,另一层是在分享过程中我个人能力得到提升的快乐。我还记得在我去年六七月份第一次做 live-coding 的长视频时,除了大纲外,我会预先把要介绍的代码粗糙地写一遍,心里有个数;而在这一年多的坚持下,我已经录制了近百个视频。现在我已经可以做到只要想录,无需准备,随时可以开录。

在我看来,软件开发是一门技术,同时也是一门艺术。学习软件开发,除了通过大量阅读(读书或者读代码)掌握基本知识外,师傅对徒弟的传帮带也是必不可少的。就像学习绘画,从书本中学习,和近距离观摩一位优秀的画家一步步是怎么作画,感受是非常不一样的。如果在这个过程中,画家不仅展示一步步从草稿到作品的详尽过程,还把自己的思路想法都介绍给你,那效果又大为不同。我希望,通过我的视频,可以给大家带来这方面的收益。我个人在学习 Rust 的过程中,从 Jon Gjengset 的视频中学到了很多,所以我也想把这种体验带给大家,也算是 pay it forward 吧。

0 人点赞