大数据架构师避而不谈的3个致命代码陷阱
|
去年十一月,我蹲在三号机房听告警声听出耳鸣,空调滴水正砸在机柜散热格栅上,嗒、嗒、嗒——跟心跳不对频。老张接了个电话,手一直按着耳机左耳,拇指在耳垂上搓了三下才挂。我盯着他搓耳朵那会儿,Hive on Tez那个作业又爆了OOM,不是内存配得低,是它读一个ORC文件时,先反序列化整个Stripe Footer,再校验CRC,可这文件压根没写Footer CRC字段——空指针就卡在ValidateFooter.check()里,不抛异常,不重试,就挂着。这个我敢打包票,我扒过三次源码,最后一次是蹲厕所蹲到腿麻,手机备忘录里还记着第271行的注释:“// TODO: handle missing crc gracefully”,写于2018年。他们叫它“优雅降级”,我叫它“优雅装死”。 你猜架构师怎么讲这个?PPT第38页写着“全链路可观测性”。可你真去查Yarn日志,container-launch.log里只有一行:“Exit code is 143”。143?143是kill -15,是Kubernetes先发SIGTERM,Yarn后杀进程——可谁问过为啥先发?因为TaskAttemptImpl.java里checkHealth()方法里那个isHealthy()调用,返回false之前,它顺手把healthStatus设成“UNHEALTHY”,然后十秒后自动触发kill。而healthStatus是全局缓存的AtomicReference,上一个task刚把它改成“UNKNOWN”,这儿又盖成“UNHEALTHY”,结果下游监控取值永远滞后两轮。我说这话的时候,小李正用牙签剔指甲缝里的泡面渣,头也没抬:“你动它干啥?加个兜底try-catch不就完了?”他剔完左手中指,换右手小指,牙签断了半截,掉进键盘F键缝里。我原以为他懂,结果他连F键下面那个热敏电阻是干啥的都不知道。 第三个坑不带代码,带DDL。上周五下午四点十七分,数仓上线一个新分区表,建表语句最后多敲了个逗号,就那么放在SQL脚本末尾。MySQL 8.0认,Hive 3.1.2也认,Doris 2.0.4居然也认。但凌晨三点,Spark SQL读这张表,执行explain extended,parse阶段直接跪——不是语法报错,是AST构建时,TreeBuilder.visitPropertyList()把那个逗号当成了下一个property的起始分隔符,往后续读两百零七字符,撞上comment标记“-- 分区说明”,于是把整段注释吞成一个StringLiteral节点。后果?Spark UI上Plan Tree里,Filter节点突然多了个“col_name = '-- 分区说明'”,跑了一千八百二十六个task,三百七十二个产出空结果。我截图发群里,王工回:“删掉逗号呗。”他回完秒撤,撤了又发个咖啡emoji。我桌上那盒黄焖鸡盖饭凉透了,汤凝成一层浅褐色膜,筷子横在上面,一端沾着粒没嚼碎的蒜瓣。
文章配图,仅供参考 说到底不是技术不行,是人懒得翻commit log。你看Flink CDC的binlog parser,那个EventParser.deserialize()里有个while(true)循环,靠readByte()等下一位来break——可MySQL服务端偶发网络闪断,readByte()返回-1,它不抛EOFException,也不return null,就继续while,CPU冲到98%,直到Yarn强制杀掉container。这bug修了三轮,第一轮改判-1就throw,第二轮发现MySQL官方文档写“-1表示连接中断”,第三轮……第三轮没人提MR,因为PR作者三个月前离职了,Jira ticket关了,标题叫“[FIX] minor perf improvement”,连复现步骤都没写。这种事,你问架构师?他正在调试K8s sidecar的liveness probe超时阈值,一边敲kubectl describe pod,一边说“我们要对齐SRE SLO标准”。窗外雨还没停,水汽糊了玻璃一半,我擦了一下,手指印留在上面,像一道没闭合的括号。其实我早该想到。2016年在杭州,同一个机房,同一个空调漏水节奏,我们修一个Kafka Consumer Group的rebalance失败,查三天,最后发现是ZooKeeper客户端配置里autoCommitEnable设成了字符串"false"而不是布尔值false——字符串和布尔值在Scala里类型擦除后都是AnyRef,序列化过去ZK,zkCli.sh读出来显示“false”,可ConsumerConfig.fromMap()底层用Java的Boolean.parseBoolean(),传入字符串"false"返回的是false没错,但它的判断逻辑是:只有"true"(忽略大小写)才返回true,其余一律false。所以"false"和"False"甚至"faLse",全被parseBoolean当成true的反面。可文档没写这层,Confluent官网Example里只写“auto.commit.enable: false”,字体是Courier New,看起来就是布尔字面量。这事我没跟任何人提过。现在想想,大概就是因为没人提,才一直留着。 哦对,刚才老张电话里说的是他儿子发烧38.7℃,他老婆抱着孩子刚进急诊室。他挂完电话,把耳机挂回支架上,那支架还是2014年阿里云峰会送的塑料货,左钩已经弯了三十度,每次挂上都往右偏两厘米。我现在正打算把Tez源码里那个check()方法重写成Optional.ofNullable().map().orElseThrow(),但得先找台没装Ansible的测试机——上个月自动化部署把/dev/shm目录清空了,/tmp目录权限设成了1777,导致本地编译器生成的临时object文件被其他用户进程误删,链接时报undefined reference。这种事儿,没法写进架构评审纪要。 (编辑:站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |


程序员必追:本周架构师视角的技术动态速递
后端架构师聚首:日志驱动的架构演进与AI新趋势
二十年测试架构师亲测:高口碑网游平台技术体验报告
专访前端架构师:技术整合视角下的未来规划与趋势
全平台适配:19年虚拟架构师的多端资源优化方案