btw 我们用回滚来恢复服务的方式也很有意思,虽然 db 在回滚代码后就恢复了,但是真正起作用的不是代码回滚了,而是滚动部署时,旧 db connection 被断开了,释放了锁。
阴差阳错,其实重启也能起到断开 db conn 的作用。重启和回滚,两大恢复服务的利器,哈哈😄
突然想到,我们找解决方案一般都用错误日志/症状搜索。这次的错误日志 (lock error) 和症状(100%cpu、high timeout:spinlock ) 挺独特的,从这个角度写文章,或许也可以帮到网友
好几年前就免费开放了~
学一门语言很大程度上是学它的 library 用法。如果你学 ruby 是为了写 web 应用,可以看这本五星好评的 rails 教程 https://www.railstutorial.org/
换 good_job,频率限制也是开源的 https://github.com/bensheldon/good_job#activejob-concurrency
good_job 是 Postgres-based 的异步任务框架,还是建议使用 sidekiq
让 sidekiq 任务一个一个执行
把这句话换成串行
,应该会更容易搜索答案。有两种方式
如果不同平台的 job 可以并行执行,那我们可以对该 queue 做个分区
platform_name = 'A'
queue = "platform" + "_" + hash_algorithm(platform_name) % 3
MyJob.set(queue: queue).perform_async(params)
# worker1
:concurrency: 1
:queues:
- ["platform_0", 1]
# worker2
:concurrency: 1
:queues:
- ["platform_1", 1]
# worker3
:concurrency: 1
:queues:
- ["platform_2", 1]
涉及到索引的创建,当表数据量比较大时,一定要避开业务高峰期
对于 PG,避开业务高峰的最大原因是 drop/create index 需要锁住整张表,会阻塞住读写操作。(文档)。如果在生产对一张核心表做这个操作,数据库读写会马上阻塞,然后 API 服务请求队列开始堆积,用户端立即无法响应(泪。。)
最佳实践是使用 concurrently 选项,这样的话就不会阻塞读写操作 e.g.
remove_index :notifications, name: :non_uniq_index_notifications_on_order_id, column: :order_id,
algorithm: :concurrently
强烈推荐使用 strong_migrations,可以防止数据库高风险动作,免费雇了一位 DBA
是的,多谢提醒,并发编程得时刻惦记着锁👍。但是对于几乎并发操作的场景,应该可以忽略吧
考的应该是线程间如何通信,有以下方式
# inner-thread communication by memory
$status = :pending
def done?
$status == :done
end
def done!
puts "done!"
$status = :done
end
def main
thread_a = Thread.new do
loop do
sleep(0.5)
if rand(100) < 10
done!
puts 'exit thread_a'
break
end
end
end
thread_c = Thread.new do
loop do
if done?
puts 'exit thread_c'
break
end
sleep(0.5)
done! if rand(100) < 10
end
end
threads = [thread_a, thread_c]
threads.each(&:join)
end
main
我以前也认为绝大部分项目都很小,拧拧螺丝就好了,没必要学这些造火箭知识。后来接触相对大规模的项目后,才发现这类知识真的挺有用。(因为你的代码能影响很多用户,所以觉得有用)如果你等到需要时再学,要么临时抱佛脚,要么机会让给别人。
ruby 社区也有大项目的 💪
十分宝贵的分账经验🙏。之前只是从研发的角度去看聚合支付,以为它仅仅是节省了研发成本,原来聚合支付提供的分账功能才是关键功能👍
大三暑假时在佛山外包公司找到一份 Android 实习,做了几个月后刚好另一个 Ruby 后端实习生走了,我便有机会参与用 Ruby 写服务端。 因为我的大学专业不是计算机,在这段实习经历每天都学到很多新知识,以及非常基础、实用的知识:前后端工程化(搭建、部署)、数据库基本操作、linux 基本维护。。。
外包公司呆了一段时间后,工作内容逐渐重复,之后去了广州做 Ruby 后端。不过接触到的项目仍然小项目,吞吐量不大,也没啥生产问题能解决,常常是一个项目上线,它的生命周期就结束了。 不过庆幸的是,还是能学到一些东西:其他 Ruby 项目是如何搭建、维护的,跟我以前的维护 ruby 项目有哪些不同。
在即将厌倦当时那份工作时,前外包上司恰好内推我到深圳的 Letote。这是我觉得维护的第一个真正有意义的项目:被真实用户使用、业务有收入。这个感觉太好了!
呆了一段时间后,工作内容又逐渐重复了,恰好前 Letote 同事内推我去深圳另一家 Fintech 公司做 Ruby 后端。这是我第一次维护一个超多用户、高吞吐量的项目。 金融类项目的稳定性要求远远高于其他项目,在处理问题时,学到了非常多知识:
因为高吞吐量、稳定性要求高,在这些公司还有非常多问题待解决、待学习。
回顾我这点经历,最值得庆幸的是每个地方我都能学到东西以及多谢前同事的内推~我仍在 Ruby 旅程上~
“哪些字段能让你快速过滤掉大量无关数据”
换句话说,也就是这个查询条件的区分度足够高 (selectivity)。带有 unique index 的 column 区分度最高,因为表里每条记录的值都不一样,即使是有几百万行的表,通过 B Tree 索引,只需要几次 IO 就能找到对应的记录。高区分度 => 查询范围小 => 快 🚀
通过 pg_stats 看各个字段的值的分布情况。比如一个枚举字段 status
,各个值的出现频率是 ({done: 0.9, failed: 0.08, pending: 0.02}
),那 status = pending
就是一个高区分度的查询条件,因为它能过滤掉 99.8% 的数据。
你甚至可以给一个高区分度的查询条件建一个partial index,进一步减少查询范围
一般每个编程语言或者流行框架都有一个 awesome list repo,可以在这些 list 找到流行、开源的项目。
比如这个 awesome rails:https://github.com/gramantin/awesome-rails#open-source-rails-apps
我也尝试了 $ sudo bundle lock --add-platform x86_64-linux
执行这句命令后,你本地的 Gemfile.lock 会有改变。你有没有提交这个修改到 git 仓库并且推送到远端?如果你没把这修改送到远端,那你下一次使用 cap 部署时还是用着旧的 Gemfile.lock 文件,导致 PLATFORMS 不含 x86_64-linux
15% VS Code + 85% RubyMine
RubyMine 的优势
VS Code 的优势
因为 Rack 不支持 HTTP2,基于 Rack 的 Ruby Server 都无法直接使用 HTTP2。
虽然 Rack 不支持,但是一般一个 HTTP Request 不是直接发送到 Rails Server(比如 puma)的,在 Rails Server 前面会有负载均衡器比如 Nginx、AWS Load Balances(ALB)。或者像 RubyChina 这样使用 Cloudflare。
Nginx、ALB、CLoudflare 都支持 HTTP2。这样的话,用户到负载均衡器之间的 HTTP 请求就能使用 HTTP2 的功能了,比如复用 TCP 连接。
user -> Load Balancer -> Rails
http2 http1.1
感谢分享
一个电商小程序,有一些分润和代理的逻辑,没有复杂的促销,单纯下单配送。
请问大佬后端提供了多少个 API?有多少个 test case?
3 天就能交付 C 端和 B 端(前后端),膜拜膜拜
我后悔“没有给 redis 配置 retry delay 时间,当 redis 响应时间抖动一下,所有 redis client 都立即重试,导致 redis cpu usage 涨至 +90% ”
我后悔“没有配置 rack-timeout 的 wait_timeout,当 db 短暂地出现问题,导致所有 API 响应慢,puma 的 request queue 开始疯狂堆积,每个 API response time 越来越慢”
db 恢复后,也只能通过重启所有 containers 来清理 request queue
我后悔“没有给每条 SQL 加上 API 备注,当 DB 出现问题时,无法快速排查这些 top sql 或者 slow sql 来自哪个 API/Job”
marginalia gem 可以给每条 sql 加备注,rails 7 就内建了~
我后悔“打印日志时上下文信息太少了,无法排查问题”
# bad
logger.error("vendor API failed")
# good
logger.error({user_id: id, msg: "vendor API failed", response: santized_vendor_api_response})
RubyChina 应该也需要介绍工具的文章,这类文章第一步就是让读者能快速在本地运行工具,接着在折腾该工具的其他功能。
我喜欢 rails tutorial 和 railscast 的原因就是它们提供了手把手教程,可以让我很快在本地把功能跑起来。
PS
的确可以删除常见命令的输出,比如:rails new demo
, rails g scaffold
, yarn install
这两句话放到一起会不会好点?看到第一句话时我在想前面什么时候启动了 container,怎么拿到 container id。
WebSocket 的地址中的 b6bb364f6d06 是 container 的 ID。
.
$ docker run --name test -d -it debian
趁午休在本地把 webssh 跑起来了,玩了一下,很酷。
官方功能,看私人 repo“感觉”更加安全了。
在 repo 页面点击 .
,就能进入 web 版 vs code。
体验了创建分支、改文件、提交、创建 pr、浏览 pr、评审 pr,十分流畅。更重要的是还支持 vs code 的快捷键!
彩蛋:在 pr 页面也可以通过点击.
进入 vs code 模式,评审大 pr 的时候实在是太方便了
现在 GitHub 的正确打开姿势是这个 :https://github.dev/pingcap/activerecord-tidb-adapter (官方)
期待这些话题