【发布时间】:2019-01-07 21:29:08
【问题描述】:
我遇到了 VoyageMongo 问题。我在编辑对象时得到了重复的对象(即更改和保存已持久化的对象),特别是那些覆盖#= 和#hash 的对象。
这是(简化的)案例:我有 UserAccount 类,实例变量为 email、salt(用于密码加密)和 name。这些是#= 和#hash 方法:
= anObject
(self isKindOf: anObject class)
ifFalse: [ ^ false ].
^ self email = anObject email and: [ self salt = anObject salt ]
hash
^ (self salt hash + self email hash) hash
email 和 salt 在创建时设置并且永远不会更改。现在,这是一个小脚本:
UserAccount removeAll.
20 timesRepeat: [ UserAccount new save ].
10 timesRepeat: [ UserAccount selectAll atRandom
name: 'Joe Doe';
save ].
UserAccount selectAll size = 20
这会生成 20 个UserAccounts(#new 在这种情况下创建一个带有随机电子邮件和盐的实例),然后随机选择 10 个并编辑它们的名称。 UserAccount selectAll 的最终大小应该保持在 20,但通常更大,这意味着它正在存储重复项。
可能的罪魁祸首:调试到VOCache,持有缓存对象的WeakKeyDictionary(在reversedObjects var中,对象本身是键)有时无法“命中”现有对象,因为随着键数组的增大,#scanFor: 开始查看不同的点(更具体地说,#startIndexFor:)。发生这种情况时,我可以看到 VOCache 的 reversedObjects 中的对象,但 VOCache>>keyAtValue: 找不到它。
长话短说:
- 是不是我不应该在持久对象中覆盖
#=?或者... - 是不是我的
#hash没有实现好?
或者,当然,我没有看到的任何其他问题:)
非常感谢!
PS:在 Pharo 6.1 和 7 中使用最新的 VoyageMongo 对此进行了测试。
【问题讨论】:
-
方法
keyAtValue:不使用#hash(它是键,而不是值,在Dictionary中得到散列。)所以,它不能是@ 987654349@ 方法。如果您在缓存中看到该对象,keyAtValue:应该会找到它(除非#=已损坏,您的情况似乎并非如此) -
谢谢莱安德罗。这里不直观的问题是 i.v.
reversedObjects是一个WeakKeyDictionary,其中对象是键(将编辑问题以澄清)。因此,VOCache>>keyAtValue:调用WeakKeyDictionary>>#at:ifAbsent:,后者调用#findElementOrNil:然后#scanFor:最后是#startIndexFor:(uff :)) 其中#hash:用于扫描内部键数组。 -
我明白了。在问题中包含的代码中,没有迹象表明更改
name会更改hash,因为哈希依赖于email。你能证实这一点吗?换句话说,您能否确认更改name后,哈希值保持不变? -
等一下!!如果您更改
#hash方法(您在重新实现它时所做的),您必须重新散列WeekKeyDictionary。你做到了吗? -
是的,实际上我每次都使用全新的缓存运行测试,尽可能地隔离案例。关于另一个问题,我只为
#=和#hash使用不可变属性(即在创建时设置并且从不更改)。WeakKeyDictionary几乎不是问题(对于键的查找,我一定有什么不明白的地方),因为它在整个系统中被广泛使用,所以可能是VOCache如何使用它的问题。