Expiration value returned is invalid

I am trying to get the ttl of an object using GetHeader. However it seems the expiration returned is different from the one I set.

Here is the code:

package main

import (
	"fmt"
	"github.com/aerospike/aerospike-client-go"
)

func main() {
	policy := aerospike.NewClientPolicy()
	// We need to request prole replicas in order to distribute
	// reads across both master and prole nodes
	policy.RequestProleReplicas = true

	client, err := aerospike.NewClientWithPolicy(policy, "172.28.128.3", 3000)
	if err != nil {
		panic(err)
	}

	writePolicy := aerospike.NewWritePolicy(0, 2)
	key, keyErr := aerospike.NewKey("cache", "test_as_record", "abc")
	if keyErr != nil {
		panic(keyErr)
	}

	putErr := client.Put(writePolicy, key, aerospike.BinMap{
		"bin1": "value1",
	})
	if putErr != nil {
		panic(putErr)
	}

	readPolicy := aerospike.NewPolicy()
	value, headerErr := client.GetHeader(readPolicy, key)
	if headerErr != nil {
		panic(headerErr)
	}

	fmt.Printf("Got Expiration: %d, Gen: %d\n", value.Expiration, value.Generation)
}

This returns

Got Expiration: 4294966404, Gen: 1

I expected it to return 2? From the docs and code it seems this should be actual number of seconds: aerospike-client-go/record.go at master · aerospike/aerospike-client-go · GitHub

The record is expiring fine.

That does not make sense to me. That is TTL = -892 seconds. ???

Server stores TTL as seconds from CITRUSLEAF_EPOCH = 1/1/2010 0:0:0 GMT as record expiration time. TTL handed to client is computed based on current time = server stored TTL expiration value - (current time in secs from 1/1/1970 or unix epoch time - citrusleaf_epoch which is 1,262,304,000)

For example, right now, unix epoch time is (was) 1,503,546,872. If I store a record with ttl = 30 days or 2,592,000 seconds, server will store 2,592,000 + (1,503,546,872 - 1,262,304,000) = 243,834,872

If I query the TTL via a client, such as AQL, AQL will give me: 243,834,872 - (current unix epoch sec - 1,262,304,400) ==> seconds TTL remaining. (AQL uses C Client underneath.)

I am not 100% sure but I believe the server provides the 243,834,872 number, client computes the TTL.

Max TTL limit is 10 years or 315,360,000 secs.

What is your server version? Could be a bug in the client side computation of TTL. Can you repeat with AQL and check? What is the client version?

The documentation says “Expiration” should have seconds to expiry, not the actual expiry timestamp. Although like you mentioned that number is not correct either as a second value or as a timestamp.

I am using vagrant to run Aerospike on a mac. Community server 3.13.0.1. Aerospike Go client is version 1.27.0

If I put a sleep of 3 seconds in the example above, the record does expire and will not be found. I am not sure how to insert using aql with a ttl (and then to check)

Here is how you can insert a records with 30 second TTL

$aql
aql>set record_print_metadata true
aql>set record_ttl 30
aql>insert into test.testset (PK, mybin) values ('k1', 123)
aql>select * from test.testset where PK = 'k1'

Here is what I get:

aql> set record_print_metadata true
aql> set record_ttl 30
aql> insert into test.testset (PK, mybin) values ('k1', 123)
OK, 1 record affected.

aql> select * from test.testset where PK = 'k1'
+-------+-------+-------+
| mybin | {ttl} | {gen} |
+-------+-------+-------+
| 123   | 17    | 1     |
+-------+-------+-------+
1 row in set (0.001 secs)

aql> select * from test.testset where PK = 'k1'
+-------+-------+-------+
| mybin | {ttl} | {gen} |
+-------+-------+-------+
| 123   | 12    | 1     |
+-------+-------+-------+
1 row in set (0.001 secs)

aql> select * from test.testset where PK = 'k1'
+-------+-------+-------+
| mybin | {ttl} | {gen} |
+-------+-------+-------+
| 123   | 9     | 1     |
+-------+-------+-------+
1 row in set (0.000 secs)
1 Like

Yea, that works for me in AQL, so I assume its a client library bug then? Does the above go code work/not work for you?

I don’t know Go… :-(.

You could try with the python client and see - but I am pretty sure that python client also runs C client underneath. best is if you can try the Java client.

you can file a bug for client libraries.

Ok, its weird. After running this in aql, now my Go code is returning the correct expiration.

AFAIK, the only negative values that you will find stored on server for TTL is 2^32 -1 (4,294,967,295)- Live for ever record.

Ok, I am not sure how to recreate this myself now. I tried to change the namespace/set/key. I even restarted asd, and even restarted the vagrant box. It returns 2 (or 3) which is what I expect from the code above.

Try with a longer expiration - more than 2 or 3 seconds - like 60 seconds. If you can identify a repeatable bug, you can file a bug report. Not too many folks use records with 2 or 3 seconds TTL - so could be a bug.

Yeah, we dont either. I was just testing it with that. Good point above using something really silly like 2-3 as the results might be inaccurate. I did notice off by 1 with 60 seconds as well.

Got Expiration: 61, Gen: 1

Anyhow, if I can reproduce this properly I will file a bug. When it was returning invalid values, even an expiration of 500 didnt work. Its interesting it would always return 4294966402 + expiration.

Anyhow moot point since I cant reproduce this anymore.

Thanks a bunch for your help!

I have read in other threads issues with clock skew. Perhaps when you run the server in docker / vagrant - some times there can be significant clock skew and then this TTL situation could possibly mess up. there is another thread on how to ensure the clocks are not skewed. See HLC jump warning in logs - #5 by pgupta

Thanks!! That makes sense. Looking at the code its clear that clock skew can cause these issues.

This topic was automatically closed 6 days after the last reply. New replies are no longer allowed.