Clojure 2024 年调查 中分享您的想法!

欢迎!请查看 关于 页面,了解更多关于如何使用本站的信息。

+2
Clojure
已关闭

大家好,我在升级到 Clojure 1.11.0 时遇到了以下情况。在我们的应用程序中,我们有一些地方使用 danlentz/clj-uuid 库创建 V5(基于名称,SHA1 哈希)uuids。升级后,产生的 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"

经过一番尝试和错误后,我似乎发现是 local part 参数(即 :some-keyword)导致了差异,因为 clj-uuid/v5 函数中将其转换为了字节数组,现在的结果似乎不同。(如果我用字符串而不是关键字作为 local part 参数,则产生的 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 之间发生了变化。

我们没有保证 Between different versions of Clojure objects can be binary serialized, so the expectation that these would be identical is incorrect. 如果需要,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 上提出一个问题,并看看是否也有前进的道路。
可以通过创建类似于关键字和符号的Java类(相同的非瞬态字段)并设置其中的serialVersionUID为1.10序列版本来解决您特定的 issue。我相信您可以然后(稳定地)复制序列化的字节。然后您可以在clj-uuid中重写协议以使用这些伪类进行关键字序列化。您将永远处于此hack之中,但这可能为您迁移至少提供了一条路径。或者,如果您有少量关键字命名空间,甚至可以手动硬编码关键字到字节映射。
Alex您好,我想知道为什么需要创建符号类,如果它在1.11.0版本中没有改变?
关键字类有一个字段用于存储符号,因此序列化将包括这两个类。
知道了,谢谢
大家好。是的,我确实希望通过为更广泛的对象(特别是关键字)特别指定稳定的字节数组转换。

为了更好地理解“伪关键字/符号”类的理念,这些元素是否可以以相同的方式序列化,而不需要共享相同的(冲突的)类名?即使我们以相同的方式序列化(.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不是特别有吸引力
作者
仅供参考,我认为我们可能会推出1.11.1版本,并将Keyword和ArraySeq的serialVersionUIDs回溯到1.10.3版本。以防这有助于决定延迟升级路径。
作者
关于uuid的延迟升级路径,大家对此有何看法?是强迫使用不稳定但不兼容的序列化(作为可选)还是默认采用?
作者
我认为除了在库上放置兼容性说明之外,没有其他好的处理方法。版本条件语句可以做到一些事,但所有这些选项都很糟糕,会影响到性能。
作者
Clojure 1.11.1-rc1 已发布 - 请测试并反馈结果!
作者
非常感谢,看起来不错!
...