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

欢迎!请访问关于页面以了解更多关于如何使用本站的信息。

+2
链接到用户页面 Clojure
关闭

嗨,当我升级到 Clojure 1.11.0 时遇到了以下情况。在我们应用程序中,我们有一些创建 V5 (基于名称的 SHA1 散列) UUIDs 的地方,使用了 danlentz/clj-uuid 库。升级之后,产生的 v5 UUIDs 不同。一个例子是

(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 版本升级前后是一致的。)
生成的 UUIDs 用在下游系统中,处理这种变化将非常困难。有没有可能实现与之前完全相同的 UUIDs 呢?

以如下备注关闭:在 1.11.1 中修复

2 个回答

0
链接到用户页面

你好。感谢你的报告。为了明确,测试之间使用的 Java 版本是不是相同的?

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

只是重新整理了别处的讨论,供记录参考——似乎使用关键字作为名称局部部分会导致 clj-uuid 将对象序列化为字节,使用 Java 序列化。

Clojure 1.11 对关键字进行了一些向后兼容的增量更改(以提高异常报告的准确性),因此关键字之间的二进制序列化在 1.10 和 1.11 之间发生了变化。

我们不保证在不同版本之间二进制序列化 Clojure 对象,因此这里期望它们是相同的这个期望是不正确的。在 clj-uuid 中,有几种方法可以解决这个属性,如果需要的话——通过为 UUIDNameBytes 协议中的关键字提供自定义序列化(https://github.com/danlentz/clj-uuid/blob/master/src/clj_uuid.clj#L557),或者通过依赖于将 pr 返回的字符串转换为字节,而不是二进制序列化到对象流等。

我们可能可以通过将 clojure.lang.Keyword 的 serialversionUID 设置为它在 1.10 中的值来“修复”这个具体案例(因为这些对象可能是二进制兼容的),我们将考虑更多。但即使我们这样做,我也建议使用更稳定的字符串来作为 clj-uuid 的本地名称部分。

/
感谢 Alex,这个解释很合理。我将在 clj-uuid 上提出问题,并查看是否还有其他前进的方法。
可能通过创建类似于Keyword和Symbol的Java类(具有相同的非临时字段)并设置serialVersionUID为1.10版本解决您特定的问题。我相信您随后可以(稳定地)复制序列化的字节。然后可以重写clj-uuid中的协议以使用这些类进行Keyword序列化。您将永远受这个漏洞的影响,但可能至少为您提供了一个迁移的路径。或者,如果您关键字命名空间数量很少,甚至可以将关键字与字节映射硬编码。
感谢Alex,我会试试看。出于好奇,如果1.11.0版本中没有更改Symbol类,为什么还需要创建Symbol类呢?
关键字类有一个包含符号的字段,因此序列化将包含这两个类。
明白了,谢谢。
大家好。是的,我确实希望为更广泛的对象(尤其是关键词)提供一个专门的稳定字节数组转换。

为了更好地理解“伪关键词/符号”类想法,这些序列化是否可以在不共享相同(冲突的)类名的情况下进行?即使我们序列化相同(.writeObject),我还是对ObjectOutputStream的读取感到失望--它序列化“对象类、类签名和所有非transient字段的值”。我想我们需要一个专业的ObjectOutputStream?

我一直在查看不同命名空间的字的序列化。可能有办法重新创建这一过程,但至少从第一眼看来,这并不像直接插入关键词名称和名称空间的字节数那样清晰。
by
是的,我认为它需要相同的类名。在classloader中加载其他类(甚至动态classloader)是可能的,但这已经成为一个相当复杂的解决方案。
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现在可供下载 - 请开始测试并及时反馈!
非常感谢,看起来不错!
...