星空体育·(中国)官方网站XK SPORT

您当前位置:

星空体育·(中国)官方网站XK SPORT > 设计团队

机械阀门隔膜阀致敬昨晚熬夜改bug的技术团队!连OpenAI也躲不过:为什么几行代码能反复干翻大批软件

发布时间:2024-03-01 浏览次数:

  现在是2024年,昨天Leap Day Bugs又来了,估计又有一些团队迫于压力熬夜改bug了。

  2 月 29 日下午,有消息称禾赛科技激光雷达存在固件 bug,致使凡是用了禾赛激光雷达的车,自动驾驶功能全部歇菜。

  禾赛科技是激光雷达头部企业,其激光雷达交付量成功突破 5 万大关,成为了全球车载激光雷达行业首个单月交付量突破 5 万的公司。对此,有媒体向禾赛科技官方求证,禾赛科技回应称:“有 2 个老款 L4 机械式激光雷达今天出现了软件 bug。目前,问题原因已经找到,我们也跟相关客户都做了深入沟通、并提供了相关解决方案。”

  据称该 bug 是个闰年问题。闰年是指该年有 366 日,即较平常年份多出一日。闰年是为了弥补因人为历法规定的年度天数 365 日和平均回归年的大约 365.24219 日的差距而设立的。多出来的一天为 2 月 29 日。也就是说今年的 3 月 1 日晚了 24 小时,这种情况每四年发生一次。对于开发者来说闰年是一次小考验,它强制要求大家必须在应用程序中考虑少见但不可避免的事件。

  “24 小时”,说长不长,说短也不短,但对程序员来说,这可能是要求他们通宵达旦、爆肝代码的节奏。

  而昨天因日历上的小小变化而造成软件 bug 和中断问题的不止禾赛科技一家。虽然四年前才刚发生过一次,但显然到现在还有很多公司没有做好准备。

  多位网友反馈 OpenAI ChatGPT 3.5 认为“2024-02-29”不是有效日期。由于此问题,至少有一名 OpenAI API 用户在自己的应用程序中遇到了故障:

  “我们有一个通过 API 使用 ChatGPT 的产品,使用的是 3.5 Turbo 版本。我们的查询涉及一些日期。今天它没有像通常那样返回文本,而是一直给出错误。”

  “该问题影响了全国所有无人值守的加油站,因为新西兰所有燃料公司都使用一家技术提供商 Invenco。原因是该系统未处理 2 月 29 日这一日期。在经历了长达一天的闰年故障(刷卡支付机停机了 10 多个小时)之后,全国各地的加油站已重新恢复运行。”

  “我们清楚地知道闰年,”Invenco 首席执行官约翰斯科特 (John Scott) 说。 “过去 20 到 30 年来我们一直在与它们打交道。 ”

  哥伦比亚最大的航空公司打印的机票有误。阿维安卡航空公司 (Avianca) 打印的机票日期为 3/1,而不是 2/29,因为他们的系统没有考虑闰日。一位旅客分享了该航空公司向客户发送的电子邮件:

  “我们通知您,如果您的航班日期为 2024 年 2 月 29 日,您的登机牌上的航班日期可能会存在差异。为了确保您获得正确的信息,请从或我们的应用程序重新下载。”

  印度新发布的智能手表无法显示正确的日期。Fastrack FS1 是印度公司 Fatrack 最近发布的一款智能手表。FS1 型号于 2023 年 3 月发布。有多份报告称该款手表在 2 月 28 日晚 11:59 后不再继续跳动。

  Fastrack 已经承认存在故障,并表示正在努力修复。但显然这个问题花了 8 个小时还没得到解决。

  有用户无法购买 YouTube Premium 订阅。年龄验证逻辑认为他们未满 18 岁,因为他们是在闰日出生的。这位用户发帖称,如果按照 YouTube Premium 计算方法,他们需要等到 70 岁之后才能够购买。

  EA Sports 赛车游戏崩溃了。EA SPORTS WRC(世界拉力锦标赛)是一款拉力赛车游戏,于 2023 年 11 月发布,适用于 Windows、Xbox 和 Playstation。今天这个游戏显然玩不了了:因为它崩溃了。

  鉴于游戏行业比其他大多数公司在游戏质量保证和测试方面投入更多,这次崩溃着实有点让人难以理解。

  EA Sports 建议的解决方法是“将你的系统日期设置为 3 月 1 日 ,或者今天就休息一下!”

  这个解决方案简直是太出乎意料了,但也不是人人都打算忽视这个问题。有些开发者还是在认真修复这个 bug 的,对着这些开发者,我们借用网友的话来说,就是“值得致敬”!

  例如:2012 年微软 Azure 曾遭遇中断,证书到期日期的计算错误致使服务中断达 12 个小时。2010 年索尼 PlayStation 网络中断的根源,正是系统将 2010 年错误识别成了闰年。2008 年微软 Zure 设备集体“变砖”,罪魁祸首就是 12 月 31 日逻辑错误。2008 年微软 Exchange 管理 bug 导致管理员在 2 月 29 日无法执行大部分操作。Lotus 1-2-3 对 1900 年的计算错误,直到 30 多年后的今天也仍是笼罩在微软 Excel 头顶的阴影!

  这些还都是登上头条的大新闻,我们相信肯定还有不计其数的小问题也曾发生,并在不同程度上影响到很多无辜用户和项目开发者。

  闰年 bug 随处可见,但在 C/C++ 代码中惹出的麻烦最大,可能导致应用程序崩溃或者缓冲区溢出(已经构成安全风险)。

  在使用 Win32 API 的 C/C++ 代码当中,SYSTEMTIME 结构成为常见的民用时间表示方式。它会将日期中的各个部分设为不同的字段,具体分隔为年、月、日值(及其他值)。下面来看常见的代码表示:

  上述代码能够顺利运行,不会报出任何错误。但风险在于机械阀门隔膜阀,如果在 2 月 29 日调用代码,则结果值仍将是 2 月 29 日,但结果年很可能并非闰年。例如 2016-02-29 + 1 year = 2017-02-29,而 2017 年根本就没有 2 月 29 号。

  机械阀门隔膜阀

  在最终被作为另一项函数(例如 SystemTimeToFileTime)的参数之前,这个值可能会被传递多次,这会导致函数失败并返回零值。遗憾的是,很多方法都会直接使用上述代码,而根本不对返回值进行检查。这可能会引发无法预测的结果,例如将的 FILETIME 值保留为未初始化状态。

  检查结果是否有效并在必要时进行调整,保证正确向 SYSTEMTIME 添加一年:

  请注意,标准 C++(非 Windows)代码中也可能存在类似的 bug。这里使用 tm 结构替代 SYSTEMTIME,因此具体操作略有不同。该结构中的月份值为 0 到 11,而非 1 到 12,因此二月被标记为 month 1。大家可以调用 _mkgmtime 来生成 time_t 结构,而非 SystemTimeToFileTime。二者的关键区别在于,tm 结构在运行至非闰年的 2 月 29 日不会报错,而是直接生成代表 3 月 1 日的值。因此如果应用软件计划于 2 月 28 日截止,则需要进行调整。

  以上 C 代码可以轻松使用 C# 或者其他语言重写,也可以使用字符串或者其他某种数据类型替换整数。其中的关键,在于我们会声明一个固定大小的数组来保存数据,并假设一年中的每一天在数组中都有相应的单一位置。相信大家已经看出问题了,在闰年中,数组无法给第 366 天(12 月 31 日)留出位置。

  由此产生的后果视编程语言而定。在 C# 中,这会引发 IndexOutOfRangeException 异常。在 C 语言中,除非启用了边界检查编译器选项,否则这会导致缓冲区溢出——具体影响也就可大可小了。JavaScript 开发才倒是不用担心,因为语言会自动添加第 366 个元素。

  闰年 bug 还会造成其他影响,比如影响到上一年 2 月 29 日到次年 3 月 1 日之间的任意数据。这种影响通常体现在数据过滤当中,比如范围查询不会考虑到额外的闰日——假设一年始终只有 365 天,或者假设 2 月始终只有 28 天。我们以下面的 SQL 语句为例:

  这条查询很好,但如果把其中的 @enddate 设定为今天,再把 @startdate 设置为今年再减去 365 天,结果会如何。假设该范围内恰好包含 2 月 29 日闰日,那它就无法涵盖一整年。具体来讲,开始日期少了一天,所以过滤得出的值不正确(假设用户就是想筛出过去一整年的数据)。

  机械阀门隔膜阀

  在评估此类 bug 时,我们首先需要考虑 bug 的实际影响。具体来说,这些值会显示在哪里?如果系统只是每天把平均订单金额更新到仪表板上的图表当中,那造成的影响肯定不会像公司财务报告(比如上报给证券交易委员会的文件)中的当年总销售额那么重要。当然,bug 评估肯定要求大家熟悉应用软件及其用法,所以实际操作还是要由各位灵活调整。

  但这种方法也有其缺陷。仅通过评估年份,是无法确定具体需要添加多少天的。毕竟 endDate 有可能只是 2016-01-01,所以尽管 2016 年是闰年,但只需减去 365 天就能得到 2015-01-01。也就是说,我们还得考虑 2 月 29 日闰日是否被包含在范围之内。如果尝试手动执行,就得使用不少相当复杂的代码。而且跨越的年数越多,具体实现就越麻烦。

  究其根本,中的 TimeSpan(包括其他语言中的相似类型)表示的都是绝对时间,其中“年”和“月”属于民用时间单位。一年或一个月的绝对时间量,将根据开发者描述的年份或月份而有所变化。(夏令时甚至对一天的定义都有浮动,但这就不在本文的讨论范围内了。)

  这里的 AddYears 方法正确实现了所有必要逻辑,可以确定要向未来移动多少天,或者在取负值时代表向过去移动多少天。

  这里的问题前文已经提到了。如果今天是闰年的 2 月 29 日,则结果值将为 3 月 1 日——可能有影响,也可能没啥影响。毕竟对于其他所有日期来说,结果都跟原始值处于同一个月内。但请注意,如果你的应用软件对月底和月初非常敏感,那就不行。

  这里大家可以使用以下函数在 JavaScript 正确添加年份,而无需调用完整库:

  这就实现了添加年份,之后会检查是否发生了转至三月的情况。如果发生,则做出调整。再次强调,千万不要具体计算需要添加的天数来解决问题——那更容易出错,除非你真的很有经验、清醒地知道自己在干什么。

  弄错了闰年算法。闰年绝对不是固定每四年一次,对于不能被 100 整除的年份才是每四年一次,能被 400 整除的除外。也就是说,1900 年并不是闰年。

  为每个月使用天数数组,其中二月只有 28 天。使用此类数组时机械阀门隔膜阀,必须考虑闰年的第 29 天。更好的办法当然是为闰年创建一套跟平年不同的数组,而一步到位的答案则是直接使用 API(如果可行),尽量别自己亲自计算。

  针对闰年为代码创建分支,但没有测试所有代码路径。例如,Zune bug 的代码顶部就有一个 ISleapYear(year)分支,但微软显然从来没测试过该分支。

  使用单独的年、月和日值,但却不对其进行验证。例如,我们可能有一个带有单独下拉菜单控件的 UI,用于选定每个组件。只测试某个日期在特定月份内是否有效还不够,我们还得把年份也考虑进来。

  直接使用一年的平均天数,比如日期数学中的 365.25 天或者 365.2425 天。虽然这在科学上比较准确,但却根本不适合民用时间惯例。毕竟大多数用例根本就不在乎日期的值取到小数点后几位。如果我们只需要一个近似值倒是没问题,但结果中的具体日期还是可能出错。

  确保进行充分的单元测试,并且了解如何正确“模拟时钟”(我们会在下一节中具体讲解)。

  如果有一组工具可以针对现有代码运行,并指出哪里存在闰年 bug,那可就太棒了!但遗憾的是,我们还没听说过这样的工具。唯一能想到的,也就是简单的字符串搜索或者正则表达式搜索了。

  .NET 真正需要的是一套全面的 Roslyn 分析器,它可以捕捉常见的日期 / 时间 bug,包括闰年、时区、夏令时、解析等。

  同样的道理也适用于 C++、Javascript 和其他编程语言——大家都需要,但就是没有。

  为什么不把时间快进到下一个闰日,看看结果如何?在某些系统上,这样确实可行。但其同样存在一些问题。

  我们的单元测试可能仍然无法捕捉到所有问题。除非大家手动查看整个应用软件的每个屏幕和每份报告,否则很可能发现不了数据过滤 bug。没发现的 bug 就是雷,早晚会炸。

  这可能会带来一种虚假的安全感,认为快进后没问题,那到时候也不会有问题。这种问题不止出现过一次,只有客户们在 2 月 29 日或者 3 月 1 日疯狂打电话投诉时,你才会意识到自己太傻太天真。

  很多系统都必须使用域服务器进行身份验证,或者使用其他时间敏感的身份验证方案。所以这里要提醒大家,Kerberos 协议有着严格的时间同步要求,默认容差必须在 5 分钟之内。另外还有 SSL 证书、代码签名证书和一系列其他安全机制,它们全都依赖于时钟。所以如果试图谎报时间,系统就会报错。

  这也是许多可靠系统中的常见模式。再次强调,用于显示当前真实时间的系统时钟绝不可随意使用。应用程序的逻辑永远不该直接调用 DateTime.Now、DateTime.UtcNow、new Datte()、GetSystemTime 或者编程语言中任何同类项来获取当前日期和时间。

  相反,我们应该将时钟视为一项服务(在 DDD 领域驱动设计意义上);而且跟任何服务一样,大家必须有办法模拟时钟。

  机械阀门隔膜阀

  创建一个从 IClock 实现的 FakeClock 类,该类接受固定值作为构造函数参数,且其中 GetCurrentTime 仅返回该固定值。

  在应用程序逻辑中,应仅依赖于 IClock 接口,且通常由构造函数进行注入。

  上面这一系列步骤听起来有点麻烦,但只要顺利完成,大家就能感受到它的优势所在。这意味着当前日期和时间都是依赖项,这也是保证所有代码都能受测试覆盖的唯一方法。

  这里我们没有提供具体代码,因为在不同的编程语言中肯定有不同的实现,但思路和模式应该是共通的。另外,Noda Time 中已经提供非常好找实现,它在主程序集中直接提供 IClock 和 SystemClock,在 NodeTime.Testing 程序集中则提供 FakeClock。所以如果大家实在担心闰年 bug,不妨直接使用 Noda Time。

  闰年虽然不像当初的千年虫那样搞得举世震惊,但它无疑也是个刺头、而且每隔几年就跑出来恶心人。过去四年间大家写了多少代码?敢保证一切都符合标准吗?现在请花点时间扫描并测试自己的代码,没准会发现有些您没意识到的隐患就潜伏在阴影当中。

联系地址:广东省广州市天河区88号

联系电话:400-123-4567

E-mail:admin@eyoucms.com

服务热线:13800000000

扫一扫,关注XK星空体育

Copyright © 2012-2024 星空体育·(中国)官方网站XK SPORT 版权所有 Powered by EyouCms

网站地图 txt地图