You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 06:45:59