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在关键字(为了改善 arity 异常报告)上做出了一些向后兼容的增量更改,因此关键字之间的二进制序列化在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类(相同非transient字段)并设置其serialVersionUID为1.10版本的serialvers来修复您具体的问题。我相信您然后(稳定地)可以复制序列化的字节。您可以在clj-uuid中覆盖协议以使用这些伪类进行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
顺便说一下,我认为我们将进行1.11.1版本,并将Keyword和ArraySeq的serialVersionUIDs固定回1.10.3版本。以防这有助于决定延迟升级路径。
by
关于uuid的延迟升级路径,对此有什么看法?强制使用不可兼容(但稳定)的序列化作为可选方案?还是默认使用?
by
我认为没有一种很好的标准方式来处理这个问题,除了在库上放置兼容性说明。你可以使用版本条件语句,但所有这些选项都很糟糕,并且会影响性能。
by
Clojure 1.11.1-rc1已发布,请进行测试并反馈!
by
非常感谢,看起来不错!
...