2024 Year of Clojure Survey 中分享您的想法!

欢迎!请参阅 关于 页面以获取更多有关本网站如何工作的信息。

+2
Clojure
已关闭

你好,我在升级到 Clojure 1.11.0 时遇到了以下问题。在我们的应用中,我们在一些地方使用 danlentz/clj-uuid 库创建了 V5 (基于名称,SHA1 哈希) 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 函数中,它将关键字对象转换为字节数组,这个字节数组现在似乎不同。(如果我将局部参数从关键字改为字符串,则在 Clojure 升级前后产生的 uuid 一致。)
产生的 uuid 用于下游系统,要他们处理这种变化会非常困难。有没有办法让我生成与之前完全相同的 uuid?

已关闭,备注为:在 1.11.1 中修复

2 回答

0

你好。感谢你的报告。只是为了澄清——测试之间使用的 Java 版本是否相同?

by
是的,Java版本(openjdk版本 "17.0.2")相同。
0
by
编辑了问题 by

这里只是将其他地方的对话重新整理,以供记录 - 使用名称局部部分的键作为关键词似乎会导致clj-uuid使用Java序列化将对象序列化为字节。

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

我们不保证在发布之间Clojure对象的二进制可序列化性,因此预期这些会是相同的这一想法是不正确的。可以通过以下几种方法在clj-uuid中处理此属性:通过对Keyword在UUIDNameBytes协议中提供特定定制序列化(https://github.com/danlentz/clj-uuid/blob/master/src/clj_uuid.clj#L557),或者依靠pr将其转换为字符串然后转换为字节,而不是直接进行二进制序列化到对象流等。

我们可能会更改这个特定的clojure.lang.Keyword的serialversionUID的值(它以前在1.10中)来“修复”这种情况(因为这些对象可能是二进制兼容的),我们会进一步考虑这个问题。但即使我们这样做,我也建议使用更稳定的字符串等对于clj-uuid的局部名称部分。

by
谢谢Alex,这个解释很合理。我将会在clj-uuid上提出一个问题,并看看那里是否有 forwards。 
by
可能可以通过创建类似 Keyword 和 Symbol 的 Java 类(相同的不持久字段)来解决您特定的 issue,并将它们中的 serialVersionUID 设置为 1.10 的 serialvers。我相信您(稳定地)可以复制序列化的字节。然后,您可以在 clj-uuid 中覆盖协议以使用这些伪类进行 Keyword 序列化。您将永远固定的这个故障,但它可能为您至少提供一条迁移的途径。或者,如果您的关键字命名空间数量很少,您甚至可以硬编码关键字到字节的映射。
by
谢谢 Alex,我会试试。出于好奇,如果 1.11.0 中没有更改,为什么需要创建 Symbol 类呢?
by
Keyword 类有一个字段用于存储 Symbol,因此序列化将包括这两个类。
by
明白了,谢谢。
大家好。是的,我确实希望为更广泛的对象,特别是关键字,专门处理稳定的字节数组转换。

为了更好地理解“假关键字/符号”类的想法,这些是否可以以相同的方式序列化,而不共享相同的(冲突的)类名?即使我们在ObjectOutputStream中以相同的方式进行序列化(.writeObject),但我觉得对其的解读非常令人失望-----它序列化“对象的类,类签名以及所有非瞬态字段的值”。我相信我们可能还需要一个专门的ObjectOutputStream?

我一直在查看各种命名空间关键字的字节数组序列化。这可能是可能的,但至少从第一眼看上去,这与直接插入关键字名称和名称空间的字节数更不清楚。
我认为它需要相同的类名。但在类加载器(甚至动态类加载器)中加载不同的类是可能的,但这将是一个相当复杂的解决方案。
我实现了一些功能,但需要复制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 现已推出 - 请进行测试并反馈!
非常感谢,看上去不错!
...