kotlinaerospike

Failed to keep only last key (ordered) in Aerospike Map


I am trying to write facade code that always keep one (in this example, but maybe more keys but keep in desc by order) in a map in Aerospike.

Here is the facade code:

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,

            // Put this version with now time
            MapOperation.put(
                MapPolicy(MapOrder.KEY_ORDERED, MapWriteMode.UPDATE),
                RuidMetadataTablesSchema.LATEST_VERSION_BIN,
                Value.get(version),
                Value.get(time)
                ),
//            // keep only the latest version
            MapOperation.removeByRankRange(
                RuidMetadataTablesSchema.LATEST_VERSION_BIN,
                -2,1,
                MapReturnType.KEY
            )

        )


    }

    private fun getKey(key: String) =
        keyResolver.getKey(key)

}

Here is the test code that showes that it not deleting by rank - it deletes by last

@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>)
    }
}

and it prints the following:

{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}

I expected it to print only 20= always and not override it with old keys


Solution

  • Rank is by order of value (timestamp in your example) in the key-value pair. If value is same, rank is established by key order. Wondering if adding 1 ms sleep in the loop between each entry would give some clarity. i.e. make the timestamp values unique.

    If I am following your code correctly (I reserve the right to be wrong! :-) ) - you insert 20:xx091, then 19:xx114, ( delete 2nd highest rank, deletes 20:xx091), then 18:xx114 (I am suspecting same timestamp value) - so deletes 18:xx114 (same value, then by key order) ... and so on. You were probably able to insert 18: thru 2: with xx114. Then 1:xx115, followed by 0:xx115.

    So since we want to keep only the last Key and since the map is being ordered MapPolicy(MapOrder.KEY_ORDERED, MapWriteMode.UPDATE), then all you need to do is change the operation to removeByIndexRange meaning it will look like this:

    return client.operate(
    
                null,
                key,
    
                // Put this version with now time
                MapOperation.put(
                    MapPolicy(MapOrder.KEY_ORDERED, MapWriteMode.UPDATE),
                    RuidMetadataTablesSchema.LATEST_VERSION_BIN,
                    Value.get(version),
                    Value.get(time)
                    ),
    //            // keep only the latest version
                MapOperation.removeByIndexRange(
                    RuidMetadataTablesSchema.LATEST_VERSION_BIN,
                    -1,1,
                    MapReturnType.INVERTED
                )
    
            )