她盯著这行字看了很久。字迹还是很工整,但比中午那杯写得更慢——笔画的起落处有轻微的停顿,像一个人在写每一个字之前都犹豫了一下。
她认得这个字迹。
三年前他在纸条上写“别太累了”,就是这个字迹。一模一样的工整,一模一样的慢,一模一样的在每一个笔画结束的地方有一个微小的、不仔细看就看不出来的停顿。
她以前觉得那是他的习惯。现在她觉得那不是习惯,是他在写每一个字之前都在想——这句话该不该说。
她把咖啡杯放在桌上,没有扔掉。
前三天她把每一杯咖啡都扔了。美式的、拿铁的,全部扔进茶水间的垃圾桶,杯底残留的咖啡液在垃圾袋里渗出来,把纸巾盒的边缘染成浅褐色。她没有多想,扔了就扔了,像删掉一个不再需要的临时文件。
但这一杯她没有扔。
她把它放在桌上,放在键盘的左边,和笔记本并排。杯壁上那行字朝著她的方向,她坐下来的时候第一眼就能看到。
“别用工作逃避。”
她不知道他在写这句话的时候在想什么。也许在想她这三天的加班记录,也许在想何悦在群组里发的那些消息,也许在想她今天在会议室里说“我不喜欢别人帮我买咖啡”的时候,语气里那种刻意的生硬。
也许他什么都没想,只是写了。
她坐在工位上,看著那行字。电脑萤幕已经暗了,办公区的灯也关了大半,只有她头顶这一盏还亮著。窗外的城市灯光透过玻璃照进来,在桌上投下一片橘黄色的光。那杯咖啡的杯壁上凝著一层细密的水珠,在灯光下亮晶晶的,像一段刚刚被写入的数据——新鲜的,还没有被覆盖过的。
她伸手碰了一下杯壁。凉的。已经放了很久了。
但她觉得那行字还是温的。
她不知道自己在想什么。她应该走的,已经十一点多了,明天还要早起。但她坐在那里,看著那行字,动不了。不是身体动不了,是某个她说不清楚的东西动不了。
三年了。
三年来她把所有的东西都控制得很好。工作、生活、情绪,每一个变量都在可预测的范围内波动,从来没有超出过阈值。她以为自己已经修好了那个漏洞——那个会让她心软、会让她犹豫、会让她想要依赖别人的漏洞。
但现在她看著这行字,发现那个漏洞从来没有被修好过。
它只是被压住了。被工作压住,被理性压住,被“我不需要”这四个字压住。现在有人把它上面的东西一层一层地拿掉,不是用蛮力,是用一杯一杯的咖啡,一行一行的小字,一句一句的“不用现在回答我”。
她不知道该怎么办。
她不能说“好”,因为说了好就意味著承认她需要他。
她不能说“不好”,因为说了不好就意味著她在乎。
她什么都不能说。她只能坐著,看著那行字,像一个卡在死循环里的进程——没有输入,没有输出,没有终止条件,只能一直跑下去,直到系统崩溃或者被人为杀掉。
何悦不知道从哪里冒出来的,端著一杯水走过来,看到程司白坐在工位上发呆。
“程姐?你还没走?”
程司白回过神。“马上走。”
何悦走过来,目光落在桌上的咖啡杯上。她看到了杯壁上那行字,又看了看程司白的表情,嘴角翘了一下,但这次没有压下去。
“这杯咖啡看起来不错。”何悦说。
“嗯。”
“谁买的?”
程司白没有回答。她把咖啡杯拿起来,站起来,拎起公事包。
“走了。”她说。
“程姐。”何悦叫住她。
程司白转头。
何悦站在那里,手里端著水杯,表情难得认真。“我觉得他说得对。”
“什么?”
“别用工作逃避。”何悦说完这句,转身走了,脚步轻快得像一只偷到了鱼的猫。
程司白站在工位旁边,手里还端著那杯凉掉的咖啡。她低头看了一眼杯壁上的字,犹豫了一下,没有扔掉。
她把咖啡杯放进公事包的侧袋里,拉上拉链。杯子在袋子里晃了一下,发出一声轻微的碰撞声,然后安静了。
她走出办公区的时候走廊上的感应灯亮了。脚步声在大理石地面上响起,一二一二,节奏和往常一样均匀。
但如果有人仔细听,会发现今天的节奏比前三天慢了一些。
不是很多。只是慢了一点点。
一点点,像一个参数被微调了零点零几的权重因子——变化太小,监控系统不会报警,日志不会记录,大多数人不会注意到。
但系统内部知道。
参数变了。
电梯到了一楼,门打开。她走出大楼,夜风吹过来,比前几天更凉了一些。她站在门口,抬头看了一眼十五楼。
灯灭了。
他走了。
她不知道他是什么时候走的。也许是放完那杯咖啡之后就走了,也许是在她看著那行字发呆的时候走的。她不知道。
但她知道他明天还会来。还会带咖啡。还会在杯壁上写字。还会说“不用现在回答我”。
她不知道他会坚持多久。一天,一周,一个月,还是更久。
她也不知道自己会撑多久。
地铁进站的时候风从隧道里吹出来,把她的头发吹乱了。她站在月台上,公事包的侧袋里那杯咖啡随著列车进站的震动轻轻晃了一下。她伸手按了一下袋口,把它稳住。
列车门打开,她走进去,找了一个靠窗的位置坐下。
公事包放在膝盖上,她的手指搭在拉链上,没有拉开,只是放在那里。隔著一层尼龙布,她能感觉到那个杯子的轮廓——圆的,硬的,杯壁上有一行字。
“别用工作逃避。”
她在心里把这六个字念了一遍。
然后列车启动了,窗外的灯光开始向后退,一盏一盏的,越来越快。她在玻璃窗上看到自己的倒影——头发被风吹乱了,外套的领口竖起来,下巴埋在里面。眼睛下面有两道浅浅的阴影,是这三天留下来的。
她看起来很累。
但她不想睡。因为她知道如果现在闭上眼睛,她会梦到一些不该梦到的东西。比如一杯拿铁,比如一件深蓝色的外套,比如一条写著“好,不打扰了”的消息。
比如一个人在巷子里说:“你妈说得不对。”
她把目光从倒影上移开,转向窗外。隧道里是黑的,什么都看不到。只有列车行进的声音,轰轰轰轰,像一段没有旋律的白噪音。
她在这片白噪音里坐了三站地铁。
到站的时候她站起来,拎著公事包走出车厢。上楼梯的时候公事包晃了一下,里面的杯子又发出一声轻微的碰撞声。她停了一下,把包带往肩上拢了拢,确保杯子不会掉出来。
出站的时候风很大,她把领口又往上拉了拉。街上人很少,路灯把她的影子拉得很长,投在空荡荡的人行道上。
她走得很慢。
比平时慢了大概十分钟到家。进门的时候她没有开灯,换了鞋,把公事包放在沙发上,然后去厨房倒了一杯水。
客厅里很暗,窗外的路灯透过窗帘照进来,在地板上投下一片橘黄色的光。她坐在沙发上,喝了一口水,然后打开公事包的侧袋,把那杯咖啡拿出来。
杯子已经彻底凉了。杯壁上的水珠凝成了一片,摸起来湿漉漉的。那行字还在,墨水被水珠洇开了一点,边缘变得模糊,但每一个字都还能认出来。
她拿著杯子,坐在沙发上,在黑暗里看著那行字。
看了大概五分钟。
然后她把杯子放在茶几上,站起来,走进浴室洗脸。水很凉,扑在脸上的时候她打了个激灵,整个人清醒了一些。她抬头看著镜子里的自己,眼睛下面的阴影比在地铁上看到的时候更深了,头发乱七八糟的,领口歪在一边。
她看起来像一个刚刚从崩溃边缘恢复过来的系统——还在运行,但日志里全是警告。
她关掉水龙头,用毛巾擦干脸,走出浴室。经过茶几的时候她看了一眼那杯咖啡,犹豫了一下,没有扔。
她走进卧室,关上门,躺在床上。
天花板是白的,什么都没有。窗外的路灯透过窗帘在天花板上投下一片模糊的光斑,像一个失焦的镜头。
她闭上眼睛。
脑子里还是那行字。
“别用工作逃避。”
她在想,如果不工作,她应该做什么。
她应该想清楚那句话。
她应该想清楚他在巷子里说的那句话——“两个人不一定更差”。
她应该想清楚自己在巷子里没有说完的那句话——“我需要的是——”。
需要的是什么?
她不知道。她真的不知道。
她只知道她不想再像这三天一样了。不想每天加班到凌晨,不想把自己埋在程式码里,不想假装自己不需要任何东西。
但她也不知道除了这样,还能怎样。
她翻了一个身,把脸埋在枕头里。枕头很软,有一股洗衣液的味道,和那天在他外套上闻到的柠檬和雪松不一样。她在外套上闻到的味道更淡,更远,像一个信号很弱的广播电台,你要很认真地听才能听清楚它在播什么。
她在听。
她一直都在听。
只是她不想承认。
两周测试期的最后一天,程司白早上六点就到公司了。
她需要在大规模评审会之前把所有的数据再验证一遍。八千次测试,十四个边缘场景,三种网络条件,每一个数字她都要确认。监控面板上的曲线在过去的两周里被她看了无数遍,蓝色的代表Raft变体,绿色的代表Paxos,两条线在不同的坐标轴上交叉、分离、再交叉,像两条在博弈中寻找平衡的函数。
她把最后一组数据录入表格的时候,手指在键盘上停了一下。Raft变体在吞吐量上领先百分之十八,延迟低了百分之十二,但在极端网络条件下的稳定性波动比Paxos大了百分之七。Paxos在稳定性上胜出,但在高并发场景下的性能瓶颈明显,活锁概率在十七个节点同时提案时达到了百分之二点三。
没有完胜。两种方案都有不可忽视的优点和缺点。
她合上笔记本,闭了一下眼睛。这就够了。不需要完胜,只需要数据能说话。
评审会在上午十点,会议室一——公司最大的一间会议室,能坐二十个人。程司白到的时候里面已经坐了十几个,架构组的人坐在左边,业务中台的人坐在右边,中间空著一排椅子,像一条无形的边界线。唐嘉树坐在会议桌的中间位置,面前放著一杯茶和两份打印好的测试报告。
何悦坐在程司白旁边,手里握著笔,笔记本已经翻到了新的一页。她在桌子底下给程司白竖了一个大拇指,程司白没有看到,她正在把U盘连接到投影仪上。
裴衍之是最后一个进来的。他手里拎著笔记型电脑,穿著一件白色的衬衫,袖口挽到小臂中段。他在右边的第一个位置坐下,打开电脑,萤幕上是他的测试报告——封面页上用加粗的字体写著“Paxos一致性方案测试报告·第二周”。
唐嘉树看了一眼投影幕上程司白的报告封面,又看了一眼裴衍之的萤幕。“开始吧。”
程司白站起来,走到投影幕前。她今天穿了一件黑色的西装,头发挽得很整齐,翻页笔握在手里,指尖因为用力而微微发白。
“两周测试,总共八千次边缘场景模拟,覆盖网络分区、节点崩溃、日志不一致、快照恢复、成员变更五个维度。”她点开第一页,萤幕上出现一张对比表,“Raft变体的平均吞吐量每秒一万四千八百笔交易,Paxos每秒一万两千一百笔。Raft领先百分之十八。”
她点到第二页。“平均延迟,Raft两百三十毫秒,Paxos两百九十毫秒。Raft领先百分之十二。”
第三页。“在网络抖动幅度超过百分之三十的极端条件下,Raft的吞吐量下降到每秒一万两千笔,Paxos下降到每秒一万一千五百笔。Raft仍然领先,但差距缩小到百分之四。同时,Raft的稳定性波动幅度为百分之十二,Paxos为百分之七。Paxos在稳定性上领先。”
她把翻页笔放在桌上。“结论:两种方案在性能指标上各有优劣。Raft在吞吐量和延迟上表现更好,Paxos在稳定性上更有优势。没有明显的胜者。”
会议室里安静了几秒。有人翻动纸页的声音,有人低声交谈了几句。唐嘉树点了点头,看向裴衍之。
“你的意见呢?”
裴衍之站起来,走到投影幕前。他没有用简报,而是直接打开了自己的测试报告,翻到最后一页的总结部分。
“我基本同意程司白的数据。”他说,“但有几点需要补充。”
他点开一张图表,上面是一条曲线——Paxos在并发提案数从三增加到十七的过程中,活锁概率的变化。曲线在提案数超过十之后开始陡峭上升,在十七的时候达到百分之二点三。
“这是Paxos的活锁概率曲线。百分之二点三在理论上可以接受,但在实际生产环境中,这个数字会被放大。因为业务中台的高峰期并发提案数不是十七,而是七十。在七十个节点同时提案的情况下,活锁概率会超过百分之十。”
他看向程司白。“你的Raft方案在同样的场景下,领导者选举的频率会增加百分之三十,但不会出现活锁。所以在高并发场景下,Raft的实际可用性可能更高。”
程司白愣了一下。她没有想到他会在评审会上帮她的方案说话。
“但是——”裴衍之点到下一张图表,曲线的走势完全不同,“Paxos在节点故障恢复的场景下,数据同步的耗时比Raft短百分之四十。因为Paxos不需要重新选举领导者,只需要在现有的提案者中达成共识。在频繁出现节点故障的边缘网络环境中,Paxos的恢复速度更快。”
他合上电脑。“所以我的结论和程司白一样。两种方案各有优劣,取舍取决于业务场景的侧重点。如果侧重高并发下的吞吐量,选Raft。如果侧重边缘网络下的稳定性,选Paxos。”
会议室里又安静了几秒。何悦在笔记本上写了一行字,然后把本子转过来给程司白看。上面写著:“裴哥在帮你说话耶。”
程司白没有看。她正看著裴衍之。他站在投影幕前,白色的衬衫在灯光下有点刺眼,表情平静,平静得像一个在陈述客观事实的旁观者。
但他说的话不是客观事实。他说的是一种取舍——把高并发场景的优势让给她的方案,把边缘网络的优势留给自己。这不是一个中立的技术判断,这是一个有立场的选择。
唐嘉树喝了一口茶。“两种方案各有优劣,那就需要业务场景来做决定了。目前项目的主要痛点是什么?”
业务方的代表举了手。“高并发。我们预估的峰值流量是每秒一万五千笔交易,延迟不能超过三百毫秒。Raft的数据更符合我们的需求。”
架构组的人点头。业务中台的人没有说话,都在看裴衍之。
裴衍之的脸上没有任何表情。“我理解业务方的需求。但边缘网络的稳定性同样重要,我们的用户分布在全国各地,网络条件参差不齐。如果只考虑高并发场景,忽略边缘网络的体验,会丢失至少百分之十五的用户。”
“百分之十五的用户是基于什么数据得出的?”程司白问。
“去年全年的网络监控数据。百分之十五的用户处于网络延迟超过两百毫秒的地区。”
“这些用户的活跃度呢?”
“比平均低百分之二十。”
“所以他们本来就不是核心用户群体。”
“他们不是核心用户群体,是因为网络条件限制了他们的使用体验。如果我们改善边缘网络的支持,他们的活跃度可能会提升。”
“改善边缘网络的支持可以用CDN和边缘节点来解决,不需要在核心共识算法上妥协。”
“CDN和边缘节点只能解决网络延迟问题,不能解决节点故障问题。当边缘节点本身出现故障的时候,你的Raft方案需要重新选举领导者,恢复时间比Paxos多百分之四十。”
“百分之四十的恢复时间对应的是多少毫秒?”
“在边缘网络条件下,大概八百毫秒。”
“八百毫秒的恢复时间,用户感知不到。”
“用户感知不到?你点一个按钮等零点八秒没有反应,你会做什么?”
程司白没有回答。她知道他接下来要说什么——你会刷新页面,重复提交,产生脏数据。这句话他在第一周的会议上就说过。
但她没有想到他接下来说的是另外一句话。
“程司白,我知道你的Raft方案在高并发场景下表现更好。但你有没有想过,上线之后出了问题,谁来背锅?”
会议室里的气氛骤然紧绷。何悦的笔停在纸面上,不敢动。陆维安低下头,假装在看报告。
程司白看著裴衍之。他的表情还是很平静,但她听到了他声音里的某种东西——不是质问,不是挑衅,是一种被她压了很久之后终于翻涌上来的、压不住的东西。
“方案是我们共同决策的,出了问题共同承担。”她说。
“共同承担?”裴衍之的声音忽然低了下去,低到只有会议室前面几排的人能听清楚,“你什么时候学会共同承担了?”
程司白的手指在翻页笔上攥紧了。她知道他在说什么。他不是在说方案,他是在说三年前。三年前她选择了一个人的承担——一个人做决定,一个人分手,一个人消失。她没有给他共同承担的机会。
“裴衍之,这里是评审会。”她的声音很平,平得像一面结了冰的湖。
“我知道。”裴衍之把目光从她脸上移开,转向业务方代表,“我的意见是,两种方案都需要进一步的评估。建议再给一周时间,做一次联合压力测试,模拟真实的业务场景。”
业务方代表看了看唐嘉树。唐嘉树没有表态,只是端起茶杯又喝了一口。
“先休息十五分钟。”唐嘉树说。
会议室里的人陆续站起来。何悦拉著陆维安快步走了出去,架构组和业务中台的人也各自散开。程司白收拾东西的时候,裴衍之已经走出了会议室。
她犹豫了三秒,拿起手机跟了出去。
走廊上没什么人。裴衍之站在茶水间门口,手里端著一杯水,没有喝。他看到程司白走过来的时候,肩膀微微绷紧了一下。
“你刚才在会议上说的话,什么意思?”程司白站在他面前,隔著大概一米的距离。
“哪句?”
“共同承担那句。”
裴衍之没有立刻回答。他把水杯放在旁边的桌上,转过身面对她。
“程司白,你是不是又发现了我的方案有什么问题?”
“什么?”
“你每次私下找我谈,都是因为你发现了我的方案有问题。上次是Paxos的边缘场景Bug,这次又是什么?”他的声音很平,但每个字都说得很快,像一段被压缩过的音频,“你可以直接在会上说。不用私下找我。”
程司白看著他,忽然明白了。他以为她又要“技术霸凌”——像上次一样,在会议上保留意见,私下找他,然后一条一条地指出他的方案漏洞。他以为这是她的策略,先给他希望,再让他难堪。
“你觉得我在针对你?”她说。
“我没有这么说。”
“你的语气在说。”
裴衍之沉默了。走廊上的感应灯灭了一盏,光线暗了一些,他的脸一半在亮处一半在暗处,表情看不清楚。
“裴衍之,你的Paxos方案在活锁概率曲线那部分有一个计算错误。”程司白说,“你把并发提案数从三到十七的曲线拟合成了线性,但实际是指数增长。在提案数超过三十的时候,活锁概率不是百分之十,是百分之十七。”
裴衍之的手指在裤缝边攥了一下。
“我本来想在会上说的。”程司白继续说,“但我没有。”
“为什么?”
“因为你的结论是对的。Paxos在高并发场景下确实有活锁风险,需要优化。但你的计算方法错了,如果我在会上指出来,所有人会觉得你的整个报告都有问题。”
裴衍之看著她。走廊上的另一盏感应灯也灭了,光线更暗了一些,但他的眼睛很亮。
“所以你私下找我,是为了——”
“我只是不想你在上线后出问题。”程司白打断他,“你的方案如果基于错误的计算上线,活锁概率会被严重低估。到时候出了故障,你是业务中台的负责人,你要背锅。”
裴衍之愣住了。
他站在那里,手垂在身侧,手指微微张开又合上,像一个在等待输入的系统——空转,闲置,没有任务可以执行。
“你不想我出问题?”他问。声音很轻,轻到差点被走廊尽头传来的脚步声盖过去。
“对。”程司白的声音很确定,确定得像一段编译通过的代码,没有任何语法错误,没有任何歧义,“因为你是业务中台的负责人,出了问题你要背锅。”
她说完这句话,转身走了。脚步声在大理石地面上响起,一二一二,节奏和往常一样均匀。
裴衍之站在茶水间门口,看著她的背影消失在走廊转角。
他听到她说的最后那句话了。每一个字都听得很清楚。
“因为你是业务中台的负责人,出了问题你要背锅。”
这是一个非常合理的、非常专业的、非常程司白式的理由。她不想让他出问题,不是因为他是裴衍之,是因为他是业务中台的负责人。就这么简单。
但他听到了这句台词后面的东西。
就像她那天在巷子里说“烟不是好东西”的时候,他在那五个字里听到了“你可以找我”。就像她在会议室里说“谢谢”的时候,他在那两个字里听到了“我还记得”。就像她在电梯里说“好久不见”的时候,他在那四个字里听到了“我没有忘记”。
她在用一种不会被拒绝的方式,表达一种她不敢直接表达的东西。
她不想他出问题。
不是因为他是业务中台的负责人。
是因为他是裴衍之。
他靠在墙上,闭了一下眼睛。胸口有什么东西在动,很慢,很重,像一个被唤醒之后还在热身的进程——还没有完全启动,但已经开始占用资源了。
十五分钟的休息时间结束了。会议室里的人陆续回来,程司白已经坐在她的位置上,笔记本翻开到新的一页,表情平静,平静得像刚才在走廊上的对话从来没有发生过。
裴衍之走进来的时候,她没有抬头。但他看到她握笔的手指动了一下——不是写字,是用力,用力到指节微微发白。
他坐下来,打开电脑。
唐嘉树清了清嗓子。“继续吧。刚才讨论到哪了?联合压力测试。”
“我同意做联合压力测试。”程司白说,“但不需要一周。三天够了。”
裴衍之看了她一眼。“三天够跑完所有场景?”
“够。我这边的测试脚本已经写好了,只需要调整几个参数就能对接你的业务层数据。”
“那我也调整一下我的测试框架,统一输入输出的格式。”
“好。”
两个人同时低下头,在笔记本上记录。会议室里安静了一会儿,只有笔尖划过纸面的声音和空调的嗡嗡声。
唐嘉树看了看程司白,又看了看裴衍之。“那就这样。三天之后,给最终方案。”
会议结束的时候,程司白站起来准备走。裴衍之叫住了她。
“程司白。”
她转头。
“你刚才说的那个计算错误,具体在哪一页?”
程司白走回来,在他的报告上翻了几页,指著其中一张图表。“这里。你的拟合曲线用了线性回归,但活锁概率的增长是指数级的。你应该用对数坐标。”
裴衍之看著那张图表,点了点头。“你说得对。我改一下。”
“嗯。”
程司白转身走了。这一次她走得比平时快了一些,脚步声在走廊上响起,节奏还是均匀的,但每一步都踩得很重,像一个在压抑什么东西的人——她把所有的力气都用在了脚步上,这样就不会用到别的地方。
裴衍之坐在会议室里,没有走。他看著那张图表,手指在键盘边缘敲了一下。
“你不想我出问题。”
这句话在他脑子里转了一圈又一圈,像一个无法终止的循环。他知道她不只是在说方案。他也知道她不会承认她还在说别的东西。
但他听到了。
这就够了。