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"

经过一番尝试和错误,发现是local部分参数(即::some-keyword)导致了差异,因为在clj-uuid/v5函数中,关键字对象被转换为字节数组,现在看起来不同了。(如果我用字符串代替关键字作为local部分参数,则生成的UUID在升级Clojure之前和之后都是一致的。)
生成的UUID用于下游系统,因此很难处理这种变化。是否有办法生成与之前完全相同的UUID呢?

关闭时注释: 1.11.1已修复

2 答案

0 投票
#

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

by
是的,使用的Java版本相同(openjdk版本 "17.0.2")
0 投票
by
编辑 by

在此处重述别处的会话以记录在案 - 使用关键字作为名称的局部部分似乎会导致clj-uuid使用Java序列化将对象序列化为字节。

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

我们不保证发行之间Clojure对象的二进制可序列化性,因此这些期望相同的观点是错误的。可以在clj-uuid中通过几种方式解决这个问题,例如,专门在UUIDNameBytes协议中为Keyword提供自定义序列化(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上提出一个问题,看看是否也有进一步发展的途径。
可以通过创建类似于关键词和类似于符号的Java类(相同的非瞬态字段)并在这些类中设置serialVersionUID为1.10系列版本来解决您特定的一个问题。我相信您可以在稳定的情况下复制出序列化字节。然后您可以在clj-uuid中重载协议以使用这些伪类进行关键词序列化。您将永久困在这个黑客技术中,但它可能会为您提供迁移的路径。或者,如果您有少数几个关键词命名空间,您甚至可以硬编码关键词到字节的映射。
感谢Alex,我会试试这个方法。出于好奇,如果1.11.0中没有对其进行更改,为什么还需要创建Symbol类呢?
关键词类有一个字段用于存储符号,因此序列化将包含这两个类。
明白了,谢谢
大家好。是的,我确实希望为更广泛的对象,尤其是关键字,特别设计一种稳定的字节数组转换。

为了更好地理解“伪关键字/符号”类的概念,这些是否可以以相同的方式序列化而无需共享相同的(冲突的)类名?即使我们在相同的方式中序列化(.writeObject),我阅读的ObjectOutputStream https://docs.oracle.com/javase/7/docs/api/java/io/ObjectOutputStream.html看起来很令人失望——它序列化“对象的类,类签名以及所有非瞬态字段的值”。我想我们也许还需要一个专用的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现在可用 - 请测试并回报结果!
非常感谢,看起来很好!
...