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

欢迎!请参阅关于页面以了解更多关于这个工作方式的信息。

+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 函数中,它将关键字对象转换为字节序列,现在看起来有了差异。如果在本地部分参数中使用字符串代替关键字,则生成的 uuid 在 Clojure 升级前后是一致的。
这些 uuid 用于下游系统,因此要它们处理这种变化将会相当困难。有没有什么办法能生成与先前的 uuid 完全相同的结果呢?

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

2 答案

0 投票

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

作者:
是的,相同的 Java 版本(openjdk 版 "17.0.2")
0 投票
作者:
编辑了 作者:

在这里重新整理了之前的对话,以备归档——使用关键字作为名称的局部部分将导致 clj-uuid 使用 Java 序列化将对象序列化为字节。

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

我们不保证在版本之间 Clojure 对象的二进制序列化兼容性,因此这里期望它们相同是不正确的。可以在 clj-uuid 中以多种方式处理这些问题,例如,通过为特定于 KeyWord 的 UUIDNameBytes 协议提供自定义序列化( https://github.com/danlentz/clj-uuid/blob/master/src/clj_uuid.clj#L557 ),或者寄希望于将对象流转换为字符串然后再转换为字节等。

我们可能通过将 clojure.lang.Keyword 的 serialversionUID 设置为 1.10 时的值来解决此特定问题(因为这些对象可能是二进制兼容的),我们还将更多考虑这一点。但即使我们这样做,我也建议为 clj-uuid 的局部名称部分使用更稳定的类型(如字符串)。

作者:
谢谢 Alex,说得通。我会在 clj-uuid 上提出一个问题,看看是否有前进的道路。
通过创建类似Keyword和Symbol的Java类(同非transient字段),并将这些类中的serialVersionUID设置为1.10版本,可能解决您的特定问题。我相信您可以在稳定的情况下复制已序列化的字节。然后,您可以在clj-uuid中覆盖协议,以使用这些伪类进行Keyword序列化。您将永远陷入这个破解中,但这可能至少为您提供了一个迁移的路径。或者,如果您有少量keyword命名空间,甚至可以直接用字节编码关键词到字节映射。
感谢Alex,我会试一下。出于好奇,如果Symbol类在1.11.0中没有改变,为什么还需要创建它呢?
Keyword类有一个包含Symbol的字段,因此序列化将包括两个类。
明白了,谢谢。
大家好。是的,我确实希望能针对更广泛的对象类型(尤其是关键字)特别处理稳定字节数组的转换。

为了更好地理解“伪关键字/符号”类的想法,这些类是否可以以相同的方式序列化而无需共享相同的(冲突的)类名?即使我们以相同的方式序列化(.writeObject),我对ObjectOutputStream的阅读却让人失望——它序列化“对象的类,类签名以及所有非瞬时的字段的值”。我想我们可能还需要一个专门的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 并不是很吸引人。
by
Clojure 1.11.1-rc1 已发布 - 请进行测试并反馈!
by
非常感谢,看起来很好!
...