2024 年 Clojure 状态调查! 中分享您的想法。

欢迎!请参阅 关于 页面以获取关于如何使用本站的更多信息。

+2
Clojure
已关闭

你好,当更新到 Clojure 1.11.0 时,我遇到了以下问题。在我们的应用程序中,我们有一些地方创建 V5(基于名称的,SHA1 哈希)uuid,使用 danlentz/clj-uuid 库。升级后,v5 uuid 的生成出现了不同。一个例子是

(def ^:const +namespace+ #uuid "50d94d91-a1cf-422d-9586-4ddacf6df176")

(clj-uuid/v5 +namespace+ :some-keyword) 

;; Clojure 1.10.3
=> #uuid "d30e9c3c-ced2-534e-a6b8-ecf784fb0785"

;; Clojure 1.11.0
=> #uuid "a16f6719-952a-55b9-b71b-b15dd263665b"

经过一些尝试和错误后,似乎是由于本地部分参数(即 :some-keyword)导致差异,因为在 clj-uuid/v5 函数中,它将关键字对象转换为字节数组,现在看起来不同。(如果我将本地部分参数从关键字改为字符串,则生成的 uuid 在 Clojure 升级前后是一致的。)
生成的 uuid 用于下游系统,并且处理这种变化非常困难。是否有办法生成与我以前的 uuid 完全相同的结果?

已注明“固定在 1.11.1”关闭

2 答案

0

你好。感谢你的报告。为了澄清——测试之间使用的 Java 版本是一样的,对吗?

是的,相同的Java版本(openjdk版本"17.0.2")
0

编辑过

这就是我将与此处的对话重新hash记录的原因——使用关键字作为名称的本地部分似乎会导致clj-uuid使用Java序列化方式将对象序列化为字节。

Clojure 1.11在Keyword(为了改进arity异常报告)中做了一些向后兼容的增量更改,因此1.10和1.11之间的Keyword二进制序列化发生了变化。

我们无法保证不同版本之间Clojure对象的二进制序列化,因此这里期望它们是相同的这种想法是不正确的。在clj-uuid中,如果希望解决这个问题,有几种方法可以处理这种属性——在UUIDNameBytes协议中为Keyword提供特定于自定义的序列化(https://github.com/danlentz/clj-uuid/blob/master/src/clj_uuid.clj#L557),或通过依赖pr将其转换为字符串然后再转换为字节,而不是使用二进制序列化将对象流输出等。

我们有可能通过将clojure.lang.Keyworad的serialversionUID设置为1.10中的值来“修复”这个特定案例(因为这些对象可能是二进制兼容的),我们将会考虑这一点。但是即使如此,我仍然建议为clj-uuid本地名称部分使用更稳定的东西(如字符串)。

谢谢Alex,说得有道理。我将在clj-uuid上提出一个问题,并看看是否有前进的道路。
可能通过创建类似于关键词和符号的Java类(相同的非瞬态字段)并设置它们中的serialVersionUID为1.10的串行版本号来解决您特定的错误。我相信您当时可以(稳定地)复制序列化的字节。然后您可以在clj-uuid中重写协议,使用这些伪类进行关键词序列化。您将永久性地陷入这个漏洞,但这可能至少为您提供了一条迁移的路径。或者,如果您有数量很少的关键词命名空间,甚至可以硬编码关键词到字节映射。
感谢Alex,我会尝试。出于好奇,如果它没有在1.11.0中改变,那么为什么还需要创建Symbol类呢?
关键字类有一个字段包含符号,因此序列化将包括这两个类。
明白了,谢谢
大家好。是的,我确实希望我为更广泛的对象(特别是关键词)编写了专门的稳定字节数组转换。

为了更好地理解“伪关键词/符号”类想法,它们是否可以在不共享相同(冲突)的类名的情况下以相同的方式进行序列化?即使我们在相同的 (.writeObject) 下序列化,我认为我对 ObjectOutputStream 的理解令人失望 —— 它序列化“对象的类、类签名以及所有非短暂字段的值”。我想我们还需要一个专用的 ObjectOutputStream 吧?

我一直在查看各种命名空间关键词的字节数组序列化。可能有可能重新创建它,但至少最初看来,它不如直接添加关键词名称和名称空间的字节数组清晰。
by
是的,我认为它需要相同的类名。在类加载器中加载不同的类(即使是动态类加载器)是可能的,但这已经是一个相当复杂的解决方案。
by
我得到了一个解决方案,但必须复制 clojure.lang.Keyword 类(就像你说的,它确实需要相同的类名),并且添加了 serialVersionUID(其值与 1.10.0 版本的类的自动生成的值相等)。

我开始尝试有一种方法/黑客技术使其具有不同的类名,然后在序列化逻辑中将它重写为“clojure.lang.Keyword”,这样自定义类就只为 uuid 生成而不冲突-但运气不佳。例如,这里https://github.com/openjdk-mirror/jdk7u-jdk/blob/f4d80957e89a19a29bb9f9807d2a28351ed7f7df/src/share/classes/java/io/ObjectStreamClass.java#L708就是它的名称作为 ObjectStreamClass 部分
- ObjectOutputStream 上还有一个 .writeObjectOverride 方法…但重新实现默认的 writeObject 并不太吸引人
通知,我认为我们将进行1.11.1版本更新,并将Keyword和ArraySeq的serialVersionUIDs回退到1.10.3版本。以防这有助于决定延迟升级路径。
就uuid的延迟升级路径而言,对此有什么看法吗? 强制一个不兼容(但稳定)的序列化作为可选方案? 默认方案?
我认为没有好的标准方法来处理这个问题,除了在库中放置兼容性说明。你可以使用版本条件来做事,但这些选项都相当低劣,并且会影响到性能。
Clojure 1.11.1-rc1现在可用 - 请进行测试并报告结果!
非常感谢,看起来不错!
...