Aerospike Map无法保留最后一个有序键问题排查
问题描述
我想编写Facade代码,确保Aerospike中的Map始终只保留一个键(当前需求是保留1个,后续可能需要保留多个并按降序排列)。
Facade代码
import com.aerospike.client.Value import com.aerospike.client.cdt.MapOperation import com.aerospike.client.cdt.MapOrder import com.aerospike.client.cdt.MapPolicy import com.aerospike.client.cdt.MapReturnType import com.aerospike.client.cdt.MapWriteMode import com.aerospike.client.query.KeyRecord import com.aerospike.client.reactor.AerospikeReactorClient import reactor.core.publisher.Mono class ForStackOverflow( private val client: AerospikeReactorClient, private val keyResolver: KeyResolver ) { fun updateRuidVersion(key: String, version: Long, time: Long): Mono<KeyRecord> { val key = getKey(key) return client.operate( null, key, // 写入当前版本和时间 MapOperation.put( MapPolicy(MapOrder.KEY_ORDERED, MapWriteMode.UPDATE), RuidMetadataTablesSchema.LATEST_VERSION_BIN, Value.get(version), Value.get(time) ), // 仅保留最新版本 MapOperation.removeByRankRange( RuidMetadataTablesSchema.LATEST_VERSION_BIN, -2,1, MapReturnType.KEY ) ) } private fun getKey(key: String) = keyResolver.getKey(key) }
测试代码
测试代码显示当前操作并未按预期的rank删除元素,反而删除了最后一个键:
@Test fun `trying to keep only last version in table`(){ val client = AerospikeReactorClientBuilder.getAerospikeReactorClient(config) val writerFacade = ForStackOverflow( client, simpleKeyResolver ) val myId = "id-" + System.currentTimeMillis() val versions = (0..20).map {it} val allWrites = versions.reversed().map { version -> writerFacade.updateRuidVersion( myId, version.toLong(), System.currentTimeMillis() ) } allWrites.forEach{ it.block() val res = client.get(simpleKeyResolver.getKey(myId)).block()!! println(res.record.bins[RuidMetadataTablesSchema.LATEST_VERSION_BIN] as TreeMap<Long, Long>) } }
输出结果
{20=1716813009091} {19=1716813009114} {19=1716813009114} {19=1716813009114} {19=1716813009114} {19=1716813009114} {19=1716813009114} {19=1716813009114} {19=1716813009114} {19=1716813009114} {19=1716813009114} {19=1716813009114} {19=1716813009114} {19=1716813009114} {19=1716813009114} {19=1716813009114} {19=1716813009114} {19=1716813009114} {19=1716813009114} {1=1716813009115} {1=1716813009115}
预期结果
我期望Map中始终只保留版本号20对应的键,而非被旧版本的键覆盖。
解决方案
问题出在MapOrder.KEY_ORDERED和removeByRankRange的配合逻辑上:
- 当Map使用
KEY_ORDERED排序时,元素按键的升序排列,键值最小的元素rank为0,最大的元素rank为-1(倒数第一个)。 - 当前调用
removeByRankRange(-2,1),是从倒数第二个元素开始删除1个元素,每次新增后Map会有两个元素,删除后剩下的是键最小的第一个元素,和保留最大键的需求完全相反。
针对需求提供两种解决思路:
方案一:调整删除的rank范围(保持KEY_ORDERED)
因为KEY_ORDERED是升序,最大键在rank=-1的位置,我们需要删除除最后一个元素外的所有元素:
MapOperation.removeByRankRange( RuidMetadataTablesSchema.LATEST_VERSION_BIN, 0, -1, // 从rank 0开始,删除到倒数第二个元素(共删除size-1个) MapReturnType.KEY )
方案二:改用降序排序(适配后续保留多个降序键的需求)
把MapPolicy的排序改为MapOrder.KEY_ORDERED_DESC(键降序),此时最大的键rank为0,要保留1个元素就删除rank>=1的所有元素:
// 修改put操作的MapPolicy MapPolicy(MapOrder.KEY_ORDERED_DESC, MapWriteMode.UPDATE), // 修改删除操作 MapOperation.removeByRankRange( RuidMetadataTablesSchema.LATEST_VERSION_BIN, 1, Int.MAX_VALUE, // 删除rank>=1的所有元素 MapReturnType.KEY )
两种方案都能实现每次写入后只保留键最大的元素,方案二更适配后续保留多个降序排列键的需求——后续要保留N个最大键,只需把删除的起始rank改为N即可。
内容的提问来源于stack exchange,提问作者Ehud Lev
相关产品推荐
相关产品推荐

