Go 28:进程重启后任务记录能说明什么
上篇给 HTTP 路径上的数据库等待设置期限,仍没有处理进程突然消失的情况。SQL 里留下 RUNNING,只能说明某次领取已提交;进程死后,它不可能自己变成终态。QUEUED 留着原文,倒是可以由后续启动的 worker 再领取。本篇继续修改同一个任务服务,区分这两行的恢复动作。完整累计工程、SHA-256和独立解包步骤可复跑真实子进程实验。 先看进程死时数据库实际保存了什么 测试在隔离的 PostgreSQL 16.15 上启动一个独立 Go 子进程,提供原有 POST /tasks、GET /tasks/{id},worker 数为 1,排队槽位为 1。连续提交 task[0] 和 task[1] 都得到 HTTP 202。第一个 worker 已把 task[0] 从 QUEUED 条件更新为 RUNNING,并进入故意不退出的分析器;第二个仍是 QUEUED。父进程查到这两行后调用 os.Process.Kill,Wait 确认子进程被信号终止。它没有机会执行 service.Close,不能把正常关闭时的等待语义套到这里。 杀死进程后再次从数据库查询,task[0] ...
Go 27:SQL 超时与连接池饱和怎样传回服务
上篇把任务的领取与终态写进数据库。如果连接池的唯一连接被占住,新的查询还没有抵达 PostgreSQL 就会等待。这样的等待不会因为 SQL 语句本身很快而消失。本文沿用累计任务服务及 PostgreSQL 16.15;完整工程包、SHA-256和独立复跑步骤包含正反实验。 先把等待放在可控的位置 database/sql.DB 是连接池,不是单一连接。在本版 sql.DB.SetMaxOpenConns(1) 下,测试先完成建表、插入 held 任务,再用 db.Conn(context.Background()) 借出唯一的池槽位并暂不归还。此时服务处理 GET、POST、取消这三种 HTTP 请求;它们尚未获得连接。Go 1.27.1 的 DB.Conn 文档说明:池满时,调用会等待可用连接或上下文取消。DBStats 的 InUse 和累计 WaitCount/WaitDuration 能帮助辨认本次是在池里等待,但不是数据库服务端的等待计数。 SQL 模式给每个 HTTP 操作建一个 context.WithTimeout(request.Context(), 2*ti...
Kotlin 31:写了 flow,为何任务还没执行
30 的有界队列说明“send 已返回”不等于“任务已完成”。现在要给执行过程加一条进度流:如果函数返回 `Flow`,是不是任务已经启动、状态已经写入 Store?对 `flow {}` 构造器而言,答案可能正好相反:还没有任何人收集,里面的业务步骤便不会运行;同一个流对象被收集两次,步骤还可能执行两遍。要给这条进度链路做决策,先看**生产时刻、执行上下文和异常由谁接住**。 版本依旧是 Kotlin Gradle Plugin/stdlib 2.2.21、Gradle 8.14、JDK 21、language/API 2.2、jvmTarget 21、coroutines core/test 1.10.2(tag SHA 5f8900478a8e20c073145b1608fbc71fe3d7378b)。官方旧入口 https://kotlinlang.org/docs/flow.html 这次只返回 334 字节的 HTML 跳转页,因此实际依据是它指向的 Asynchronous Flow 正文、固定版本的 Flow、flowOn、buffer、catch API。下文的...
Kotlin 30:队列接纳以后,任务算成功了吗
29 的内存索引可以拒绝同一任务的重复完成,仍缺少一个问题:生产者提交任务时,谁来限制待处理项?如果一个 `Channel.send` 返回成功,是**队列已有位置**,还是消费者已将业务状态写成 Succeeded?这两个完成点分不开,服务往往在队列入列时误报成功;消费者故障后,先前的“发送成功”便成了一条没有去向的承诺。 冻结的 Kotlin/JVM 组合不变:编译器/KGP/stdlib 2.2.21、Gradle 8.14、JDK/toolchain 21、language/API 2.2、jvmTarget 21,kotlinx.coroutines core/test 1.10.2,库源码 tag 5f8900478a8e20c073145b1608fbc71fe3d7378b。先核对 官方 Channel 指南 和 Channel、trySend、receiveCatching、close API。后文的排队与丢弃观察来自本工程受控闸门,不是所有持久消息系统的保证。 一个容量为 1 的任务队列 新建的 BoundedTaskQueue(capacity = 1) ...
Kotlin 29:计数原子,为何任务仍被完成两次
28给故障找到了父级和观察者,但还没约束成功路径:两个协程同时想更新同一条任务,都会先读到旧值。即使单个计数器用 `AtomicInteger.incrementAndGet()`,在“先检查任务是否 Running,再写成 Succeeded,最后增加完成次数”的组合里,两个请求仍可能都认为自己完成了这条任务。问题不是协程库的某个例外,而是**读、比较、修改跨越多个对象**时,究竟把哪段操作算作一个不可拆分的业务转移。 本篇仍在冻结的 Kotlin/JVM 2.2.21、Gradle 8.14、JDK/toolchain 21、language/API 2.2、jvmTarget 21、kotlinx.coroutines core/test 1.10.2 上运行,上游协程源码 tag 为 5f8900478a8e20c073145b1608fbc71fe3d7378b。Kotlin 官方共享可变状态指南、Mutex、withLock 及 JDK 21 AtomicInteger提供机制的依据;具体的 1、2 等数字来自本次实验。测试调度器控制的读写交错并不等于跑过真实跨核线程...
Kotlin 28:同一个异常为何有时落在 await,有时到 handler
任务树里有一个子任务抛 IllegalStateException。若它由 launch 启动,错误会走到哪里?换成 async 就会让兄弟任务保持运行吗?25已经验证普通任务树的子故障会清理等待中的兄弟;27验证取消需要保留自己的控制流。现在把故障与拓扑放到同一张图上:普通 coroutineScope 和受监督 supervisorScope 决定兄弟间的关系,launch、async 则影响错误在哪里被观察。不能把两个维度混成“换了 builder 就自动隔离”。 沿用 Kotlin 2.2.21、Gradle 8.14、JDK 21、language/API 2.2、jvmTarget 21、coroutines core/test 1.10.2,库上游 tag SHA 5f8900478a8e20c073145b1608fbc71fe3d7378b。官方异常处理指南与 supervisorScope、launch、async、CoroutineExceptionHandler 官方 API 是设计实验的一手资料。下述断言使用本机 runBlocking、受控 Compl...
Kotlin 27:取消请求何时变成资源退出
任务因超时被取消,调用者收到异常,资源也一定关了吗?26 的真实闸门已经给出一个反例:协程取消时,专用线程上的同步 await() 直到测试线程放闸才退出。现在增加两种更难混的情况:一段计算只有在检查取消时才肯停;一个资源在“已由生产者取得、尚未交给等待的协程”这段间隙碰上取消,到底谁负责关闭? 版本不变:Kotlin Gradle Plugin/编译器/stdlib 2.2.21、Gradle 8.14、JDK/toolchain 21、language/API 2.2、jvmTarget 21,kotlinx.coroutines core/test 1.10.2(源码 tag 5f8900478a8e20c073145b1608fbc71fe3d7378b)。先核对 Kotlin 取消与超时指南、ensureActive、withTimeout、NonCancellable 和 suspendCancellableCoroutine 的官方契约。实际运行结果分别记在真实线程和测试调度器两本账上,不能互相代替。 取消不是 JVM 强行退出正在执行的指令 真实单线程实验各启动...
Kotlin 26:换个 Dispatcher,阻塞还在吗
25 的服务所有者能等待和取消任务树,却不能让一段同步 `CountDownLatch.await()` 自己变成可挂起的等待。如果调用这段代码的协程原本由处理请求的工作线程执行,我们可以把它移到专用 Dispatcher,让调用工作者去处理别的事情。但**阻塞没有消失**:另一个线程仍须等闸门。还得回答两个容易漏掉的问题:提交超过专用工作者容量时谁排队?换线程后原来的 `ThreadLocal` 还在不在? 基线仍为 Kotlin/KGP/stdlib 2.2.21、Gradle 8.14、JDK 21、language/API 2.2、jvmTarget 21、kotlinx.coroutines core/test 1.10.2,库源码 tag 5f8900478a8e20c073145b1608fbc71fe3d7378b。本文查 官方上下文与调度器文档、withContext 和 asContextElement;runInterruptible仅作中断边界的资料,不将其名字当作本篇已验证中断的证据。 把同步步骤明确放进有容量的执行器 累计任务系统新增一个很小的适配入...
Kotlin 25:子任务结束前谁能宣布任务完成
24 的合成步骤把线程是否空出的问题分成了两种等待,但 `suspend` 本身没说启动的子任务由谁等。假如步骤内部 `launch` 一个工作项后立即返回,外层便报告任务成功;子工作项却还在等输入。随后服务关闭或子工作项抛异常,这份“成功”算什么?需要区分**函数主体运行到末尾**、**整棵任务树结束**和**业务记录实际提交**。 本文继续使用 Kotlin 2.2.21、Gradle 8.14、JDK 21、language/API 2.2、jvmTarget 21,协程 core/test 仍是 1.10.2(上游 tag 5f8900478a8e20c073145b1608fbc71fe3d7378b)。依据是 Kotlin 协程基础、异常处理以及 Job、coroutineScope、SupervisorJob 的官方 API。库契约解释为何安排这种测试;“已经发生”的结果只取本工程的受控闸门和输出,不从文档替运行做结论。 先给每次执行一个可等待的范围 executeManagedTask 不再仅调用单个挂起步骤,而是让步骤在 coroutineScope 中执行。...
Kotlin 24:suspend 为何没有自动让线程空出来
任务执行步骤要等待外部结果。把函数前面加上 suspend,是让它有了暂停的可能,还是已经让工作线程在等待期间空出来了?这两件事不能混为一谈。尤其当原先的代码是 Thread.sleep、CountDownLatch.await 或同步文件读取时,改了签名而没改等待方式,调用线程仍然会被占住。 本篇沿用 23 的累计任务工程,但不把文件操作偷偷改称异步 I/O。冻结组合为 Kotlin Gradle Plugin/stdlib 2.2.21、Gradle 8.14、JDK/toolchain 21、language/API 2.2、jvmTarget 21,新增 kotlinx-coroutines-core 与 kotlinx-coroutines-test 1.10.2,上游 tag 指向 5f8900478a8e20c073145b1608fbc71fe3d7378b。实际解析树及构建运行记录在素材工程的 verification/24/;tag 是版本锚点,并不表示本篇已逐行审过调度器实现。Kotlin 协程基础、上下文与调度器、delay API 与 runTest A...









