diff --git a/src/include/aerospike/as_command.h b/src/include/aerospike/as_command.h
index f26f68c87..ad13a6d0e 100644
--- a/src/include/aerospike/as_command.h
+++ b/src/include/aerospike/as_command.h
@@ -366,8 +366,8 @@ as_command_write_header_write(
uint8_t*
as_command_write_header_read(
uint8_t* cmd, const as_policy_base* policy, as_policy_read_mode_ap read_mode_ap,
- as_policy_read_mode_sc read_mode_sc, uint32_t timeout, uint16_t n_fields, uint16_t n_bins,
- uint8_t read_attr, uint8_t write_attr, uint8_t info_attr
+ as_policy_read_mode_sc read_mode_sc, int read_ttl, uint32_t timeout, uint16_t n_fields,
+ uint16_t n_bins, uint8_t read_attr, uint8_t write_attr, uint8_t info_attr
);
/**
@@ -377,7 +377,8 @@ as_command_write_header_read(
uint8_t*
as_command_write_header_read_header(
uint8_t* cmd, const as_policy_base* policy, as_policy_read_mode_ap read_mode_ap,
- as_policy_read_mode_sc read_mode_sc, uint16_t n_fields, uint16_t n_bins, uint8_t read_attr
+ as_policy_read_mode_sc read_mode_sc, int read_ttl, uint16_t n_fields, uint16_t n_bins,
+ uint8_t read_attr
);
/**
diff --git a/src/include/aerospike/as_policy.h b/src/include/aerospike/as_policy.h
index 61c41abf5..1dc32fabc 100644
--- a/src/include/aerospike/as_policy.h
+++ b/src/include/aerospike/as_policy.h
@@ -576,6 +576,26 @@ typedef struct as_policy_read_s {
*/
as_policy_read_mode_sc read_mode_sc;
+ /**
+ * Determine how record TTL (time to live) is affected on reads. When enabled, the server can
+ * efficiently operate as a read-based LRU cache where the least recently used records are expired.
+ * The value is expressed as a percentage of the TTL sent on the most recent write.
+ *
+ * For example, if the most recent write had a TTL of 10 hours and read_touch_ttl_percent is set to
+ * 80, the next read after 8 hours will result in a touch, resetting the TTL to another 10 hours.
+ *
+ * Values:
+ *
+ * - 0 : Use server config default-read-touch-ttl-pct for the record's namespace/set.
+ * - -1 : Do not reset record TTL on reads.
+ * - 1 - 100 : Reset record TTL on reads when within this percentage of the most recent write TTL.
+ *
+ *
+ *
+ * Default: 0
+ */
+ int read_touch_ttl_percent;
+
/**
* Should raw bytes representing a list or map be deserialized to as_list or as_map.
* Set to false for backup programs that just need access to raw bytes.
@@ -768,8 +788,9 @@ typedef struct as_policy_operate_s {
/**
* The default time-to-live (expiration) of the record in seconds. This field will
- * only be used if "as_operations.ttl" is set to AS_RECORD_CLIENT_DEFAULT_TTL. The
- * as_operations instance is passed in to operate functions along with as_policy_operate.
+ * only be used if one or more of the operations is a write operation and if "as_operations.ttl"
+ * is set to AS_RECORD_CLIENT_DEFAULT_TTL. The as_operations instance is passed in to
+ * operate functions along with as_policy_operate.
*
* There are also special values that can be set in the record ttl:
*
@@ -780,6 +801,26 @@ typedef struct as_policy_operate_s {
*/
uint32_t ttl;
+ /**
+ * Determine how record TTL (time to live) is affected on reads. When enabled, the server can
+ * efficiently operate as a read-based LRU cache where the least recently used records are expired.
+ * The value is expressed as a percentage of the TTL sent on the most recent write.
+ *
+ * For example, if the most recent write had a TTL of 10 hours and read_touch_ttl_percent is set to
+ * 80, the next read after 8 hours will result in a touch, resetting the TTL to another 10 hours.
+ *
+ * Values:
+ *
+ * - 0 : Use server config default-read-touch-ttl-pct for the record's namespace/set.
+ * - -1 : Do not reset record TTL on reads.
+ * - 1 - 100 : Reset record TTL on reads when within this percentage of the most recent write TTL.
+ *
+ * -
+ *
+ * Default: 0
+ */
+ int read_touch_ttl_percent;
+
/**
* Should raw bytes representing a list or map be deserialized to as_list or as_map.
* Set to false for backup programs that just need access to raw bytes.
@@ -887,6 +928,26 @@ typedef struct as_policy_batch_s {
*/
as_policy_read_mode_sc read_mode_sc;
+ /**
+ * Determine how record TTL (time to live) is affected on reads. When enabled, the server can
+ * efficiently operate as a read-based LRU cache where the least recently used records are expired.
+ * The value is expressed as a percentage of the TTL sent on the most recent write.
+ *
+ * For example, if the most recent write had a TTL of 10 hours and read_touch_ttl_percent is set to
+ * 80, the next read after 8 hours will result in a touch, resetting the TTL to another 10 hours.
+ *
+ * Values:
+ *
+ * - 0 : Use server config default-read-touch-ttl-pct for the record's namespace/set.
+ * - -1 : Do not reset record TTL on reads.
+ * - 1 - 100 : Reset record TTL on reads when within this percentage of the most recent write TTL.
+ *
+ * -
+ *
+ * Default: 0
+ */
+ int read_touch_ttl_percent;
+
/**
* Determine if batch commands to each server are run in parallel threads.
*
@@ -987,7 +1048,7 @@ typedef struct as_policy_batch_read_s {
* transaction is ignored. This can be used to eliminate a client/server roundtrip
* in some cases.
*
- * aerospike_destroy() automatically calls as_exp_destroy() on all global default
+ * aerospike_destroy() automatically calls as_exp_destroy() on all global default
* policy filter expression instances. The user is responsible for calling as_exp_destroy()
* on filter expressions when setting temporary transaction policies.
*
@@ -1007,6 +1068,26 @@ typedef struct as_policy_batch_read_s {
*/
as_policy_read_mode_sc read_mode_sc;
+ /**
+ * Determine how record TTL (time to live) is affected on reads. When enabled, the server can
+ * efficiently operate as a read-based LRU cache where the least recently used records are expired.
+ * The value is expressed as a percentage of the TTL sent on the most recent write.
+ *
+ * For example, if the most recent write had a TTL of 10 hours and read_touch_ttl_percent is set to
+ * 80, the next read after 8 hours will result in a touch, resetting the TTL to another 10 hours.
+ *
+ * Values:
+ *
+ * - 0 : Use server config default-read-touch-ttl-pct for the record's namespace/set.
+ * - -1 : Do not reset record TTL on reads.
+ * - 1 - 100 : Reset record TTL on reads when within this percentage of the most recent write TTL.
+ *
+ * -
+ *
+ * Default: 0
+ */
+ int read_touch_ttl_percent;
+
} as_policy_batch_read;
/**
@@ -1019,7 +1100,7 @@ typedef struct as_policy_batch_write_s {
* transaction is ignored. This can be used to eliminate a client/server roundtrip
* in some cases.
*
- * aerospike_destroy() automatically calls as_exp_destroy() on all global default
+ * aerospike_destroy() automatically calls as_exp_destroy() on all global default
* policy filter expression instances. The user is responsible for calling as_exp_destroy()
* on filter expressions when setting temporary transaction policies.
*
@@ -1498,6 +1579,7 @@ as_policy_read_init(as_policy_read* p)
p->replica = AS_POLICY_REPLICA_DEFAULT;
p->read_mode_ap = AS_POLICY_READ_MODE_AP_DEFAULT;
p->read_mode_sc = AS_POLICY_READ_MODE_SC_DEFAULT;
+ p->read_touch_ttl_percent = 0;
p->deserialize = true;
p->async_heap_rec = false;
return p;
@@ -1574,6 +1656,7 @@ as_policy_operate_init(as_policy_operate* p)
p->gen = AS_POLICY_GEN_DEFAULT;
p->exists = AS_POLICY_EXISTS_DEFAULT;
p->ttl = 0; // AS_RECORD_DEFAULT_TTL
+ p->read_touch_ttl_percent = 0;
p->deserialize = true;
p->durable_delete = false;
p->async_heap_rec = false;
@@ -1678,6 +1761,7 @@ as_policy_batch_init(as_policy_batch* p)
p->replica = AS_POLICY_REPLICA_SEQUENCE;
p->read_mode_ap = AS_POLICY_READ_MODE_AP_DEFAULT;
p->read_mode_sc = AS_POLICY_READ_MODE_SC_DEFAULT;
+ p->read_touch_ttl_percent = 0;
p->concurrent = false;
p->allow_inline = true;
p->allow_inline_ssd = false;
@@ -1727,6 +1811,7 @@ as_policy_batch_read_init(as_policy_batch_read* p)
p->filter_exp = NULL;
p->read_mode_ap = AS_POLICY_READ_MODE_AP_DEFAULT;
p->read_mode_sc = AS_POLICY_READ_MODE_SC_DEFAULT;
+ p->read_touch_ttl_percent = 0;
return p;
}
diff --git a/src/main/aerospike/aerospike_batch.c b/src/main/aerospike/aerospike_batch.c
index bb06c4721..7c670e144 100644
--- a/src/main/aerospike/aerospike_batch.c
+++ b/src/main/aerospike/aerospike_batch.c
@@ -38,14 +38,15 @@
// Constants
//---------------------------------
- #define BATCH_MSG_READ 0x0
- #define BATCH_MSG_REPEAT 0x1
- #define BATCH_MSG_INFO 0x2
- #define BATCH_MSG_WRITE 0xe
+#define BATCH_MSG_READ 0x0
+#define BATCH_MSG_REPEAT 0x1
+#define BATCH_MSG_INFO 0x2
+#define BATCH_MSG_GEN 0x4
+#define BATCH_MSG_TTL 0x8
- #define BATCH_TYPE_RECORDS 0
- #define BATCH_TYPE_KEYS 1
- #define BATCH_TYPE_KEYS_NO_CALLBACK 2
+#define BATCH_TYPE_RECORDS 0
+#define BATCH_TYPE_KEYS 1
+#define BATCH_TYPE_KEYS_NO_CALLBACK 2
//---------------------------------
// Types
@@ -537,7 +538,7 @@ as_batch_read_record_size_old(
as_key* key, as_batch_read_record* rec, as_batch_builder* bb, as_error* err
)
{
- bb->size += 6;
+ bb->size += 6; // repeat(1) + info1(1) + n_fields(2) + n_ops(2) = 6
bb->size += as_command_string_field_size(key->ns);
bb->size += as_command_string_field_size(key->set);
@@ -640,8 +641,8 @@ as_batch_header_write_old(
}
p = as_command_write_header_read(p, &policy->base, policy->read_mode_ap,
- policy->read_mode_sc, policy->base.total_timeout, bb->field_count_header, 0,
- bb->read_attr | AS_MSG_INFO1_BATCH_INDEX, 0, 0);
+ policy->read_mode_sc, policy->read_touch_ttl_percent, policy->base.total_timeout,
+ bb->field_count_header, 0, bb->read_attr | AS_MSG_INFO1_BATCH_INDEX, 0, 0);
if (bb->filter_exp) {
p = as_exp_write(bb->filter_exp, p);
@@ -889,7 +890,7 @@ as_batch_write_record_size(
as_key* key, as_batch_write_record* rec, as_batch_builder* bb, as_error* err
)
{
- bb->size += 6; // gen(2) + ttl(4) = 6
+ bb->size += 2; // gen(2) = 2
if (rec->policy) {
if (rec->policy->filter_exp) {
@@ -930,7 +931,7 @@ as_batch_write_record_size(
static void
as_batch_apply_record_size(as_key* key, as_batch_apply_record* rec, as_batch_builder* bb)
{
- bb->size += 6; // gen(2) + ttl(4) = 6
+ bb->size += 2; // gen(2) = 2
if (rec->policy) {
if (rec->policy->filter_exp) {
@@ -957,7 +958,7 @@ as_batch_apply_record_size(as_key* key, as_batch_apply_record* rec, as_batch_bui
static void
as_batch_remove_record_size(as_key* key, as_batch_remove_record* rec, as_batch_builder* bb)
{
- bb->size += 6; // gen(2) + ttl(4) = 6
+ bb->size += 2; // gen(2) = 2
if (rec->policy) {
if (rec->policy->filter_exp) {
@@ -975,7 +976,7 @@ as_batch_record_size(
as_key* key, as_batch_base_record* rec, as_batch_builder* bb, as_error* err
)
{
- bb->size += 8;
+ bb->size += 12; // repeat(1) + info(3) + ttl(4) + n_fields(2) + n_ops(2) = 12
bb->size += as_command_string_field_size(key->ns);
bb->size += as_command_string_field_size(key->set);
@@ -1081,7 +1082,7 @@ as_batch_attr_read_header(as_batch_attr* attr, const as_policy_batch* p)
attr->info_attr = AS_MSG_INFO3_SC_READ_TYPE | AS_MSG_INFO3_SC_READ_RELAX;
break;
}
- attr->ttl = 0;
+ attr->ttl = p->read_touch_ttl_percent;
attr->gen = 0;
attr->has_write = false;
attr->send_key = false;
@@ -1114,7 +1115,7 @@ as_batch_attr_read_row(as_batch_attr* attr, const as_policy_batch_read* p)
attr->info_attr = AS_MSG_INFO3_SC_READ_TYPE | AS_MSG_INFO3_SC_READ_RELAX;
break;
}
- attr->ttl = 0;
+ attr->ttl = p->read_touch_ttl_percent;
attr->gen = 0;
attr->has_write = false;
attr->send_key = false;
@@ -1322,10 +1323,12 @@ as_batch_write_read(
uint8_t* p, as_key* key, as_batch_attr* attr, as_exp* filter, uint16_t n_ops
)
{
- *p++ = BATCH_MSG_INFO;
+ *p++ = (BATCH_MSG_INFO | BATCH_MSG_TTL);
*p++ = attr->read_attr;
*p++ = attr->write_attr;
*p++ = attr->info_attr;
+ *(uint32_t*)p = cf_swap_to_be32(attr->ttl);
+ p += sizeof(uint32_t);
p = as_batch_write_fields_filter(p, key, filter, 0, n_ops);
return p;
}
@@ -1335,7 +1338,7 @@ as_batch_write_write(
uint8_t* p, as_key* key, as_batch_attr* attr, as_exp* filter, uint16_t n_fields, uint16_t n_ops
)
{
- *p++ = BATCH_MSG_WRITE;
+ *p++ = (BATCH_MSG_INFO | BATCH_MSG_GEN | BATCH_MSG_TTL);
*p++ = attr->read_attr;
*p++ = attr->write_attr;
*p++ = attr->info_attr;
@@ -3065,20 +3068,28 @@ as_batch_retry_parse_row(uint8_t* p, uint8_t* type)
{
p += sizeof(uint32_t) + AS_DIGEST_VALUE_SIZE;
- *type = *p++;
+ uint8_t t = *p++;
+ *type = t;
- switch (*type) {
- case BATCH_MSG_REPEAT:
+ if (t == BATCH_MSG_REPEAT) {
return p;
- case BATCH_MSG_READ:
+ }
+
+ if (t == BATCH_MSG_READ) {
p++;
- break;
- case BATCH_MSG_INFO:
- p += 3;
- break;
- case BATCH_MSG_WRITE:
- p += 9;
- break;
+ }
+ else {
+ if (t & BATCH_MSG_INFO) {
+ p += 3;
+ }
+
+ if (t & BATCH_MSG_GEN) {
+ p += 2;
+ }
+
+ if (t & BATCH_MSG_TTL) {
+ p += 4;
+ }
}
uint16_t n_fields = cf_swap_from_be16(*(uint16_t*)p);
diff --git a/src/main/aerospike/aerospike_key.c b/src/main/aerospike/aerospike_key.c
index f7a80f015..951d4010a 100644
--- a/src/main/aerospike/aerospike_key.c
+++ b/src/main/aerospike/aerospike_key.c
@@ -229,7 +229,8 @@ aerospike_key_get(
uint8_t* buf = as_command_buffer_init(size);
uint32_t timeout = as_command_server_timeout(&policy->base);
uint8_t* p = as_command_write_header_read(buf, &policy->base, policy->read_mode_ap,
- policy->read_mode_sc, timeout, n_fields, 0, AS_MSG_INFO1_READ | AS_MSG_INFO1_GET_ALL, 0, 0);
+ policy->read_mode_sc, policy->read_touch_ttl_percent, timeout, n_fields, 0,
+ AS_MSG_INFO1_READ | AS_MSG_INFO1_GET_ALL, 0, 0);
p = as_command_write_key(p, policy->key, key);
p = as_command_write_filter(&policy->base, filter_size, p);
@@ -280,7 +281,8 @@ aerospike_key_get_async(
uint32_t timeout = as_command_server_timeout(&policy->base);
uint8_t* p = as_command_write_header_read(cmd->buf, &policy->base, policy->read_mode_ap,
- policy->read_mode_sc, timeout, n_fields, 0, AS_MSG_INFO1_READ | AS_MSG_INFO1_GET_ALL, 0, 0);
+ policy->read_mode_sc, policy->read_touch_ttl_percent, timeout, n_fields, 0,
+ AS_MSG_INFO1_READ | AS_MSG_INFO1_GET_ALL, 0, 0);
p = as_command_write_key(p, policy->key, key);
p = as_command_write_filter(&policy->base, filter_size, p);
@@ -328,7 +330,8 @@ aerospike_key_select(
uint8_t* buf = as_command_buffer_init(size);
uint32_t timeout = as_command_server_timeout(&policy->base);
uint8_t* p = as_command_write_header_read(buf, &policy->base, policy->read_mode_ap,
- policy->read_mode_sc, timeout, n_fields, nvalues, AS_MSG_INFO1_READ, 0, 0);
+ policy->read_mode_sc, policy->read_touch_ttl_percent, timeout, n_fields, nvalues,
+ AS_MSG_INFO1_READ, 0, 0);
p = as_command_write_key(p, policy->key, key);
p = as_command_write_filter(&policy->base, filter_size, p);
@@ -392,7 +395,8 @@ aerospike_key_select_async(
uint32_t timeout = as_command_server_timeout(&policy->base);
uint8_t* p = as_command_write_header_read(cmd->buf, &policy->base, policy->read_mode_ap,
- policy->read_mode_sc, timeout, n_fields, nvalues, AS_MSG_INFO1_READ, 0, 0);
+ policy->read_mode_sc, policy->read_touch_ttl_percent, timeout, n_fields, nvalues,
+ AS_MSG_INFO1_READ, 0, 0);
p = as_command_write_key(p, policy->key, key);
p = as_command_write_filter(&policy->base, filter_size, p);
@@ -432,7 +436,8 @@ aerospike_key_exists(
uint8_t* buf = as_command_buffer_init(size);
uint8_t* p = as_command_write_header_read_header(buf, &policy->base, policy->read_mode_ap,
- policy->read_mode_sc, n_fields, 0, AS_MSG_INFO1_READ | AS_MSG_INFO1_GET_NOBINDATA);
+ policy->read_mode_sc, policy->read_touch_ttl_percent, n_fields, 0,
+ AS_MSG_INFO1_READ | AS_MSG_INFO1_GET_NOBINDATA);
p = as_command_write_key(p, policy->key, key);
p = as_command_write_filter(&policy->base, filter_size, p);
@@ -482,7 +487,8 @@ aerospike_key_exists_async(
AS_LATENCY_TYPE_READ);
uint8_t* p = as_command_write_header_read_header(cmd->buf, &policy->base, policy->read_mode_ap,
- policy->read_mode_sc, n_fields, 0, AS_MSG_INFO1_READ | AS_MSG_INFO1_GET_NOBINDATA);
+ policy->read_mode_sc, policy->read_touch_ttl_percent, n_fields, 0,
+ AS_MSG_INFO1_READ | AS_MSG_INFO1_GET_NOBINDATA);
p = as_command_write_key(p, policy->key, key);
p = as_command_write_filter(&policy->base, filter_size, p);
@@ -923,7 +929,17 @@ as_operate_write(void* udata, uint8_t* buf)
as_operate* oper = udata;
const as_policy_operate* policy = oper->policy;
const as_operations* ops = oper->ops;
- uint32_t ttl = (ops->ttl == AS_RECORD_CLIENT_DEFAULT_TTL)? policy->ttl : ops->ttl;
+ uint32_t ttl;
+
+ if (oper->write_attr & AS_MSG_INFO2_WRITE) {
+ ttl = (ops->ttl == AS_RECORD_CLIENT_DEFAULT_TTL)? policy->ttl : ops->ttl;
+ }
+ else {
+ // ttl is an unsigned 32 bit integer in the wire protocol, but it still
+ // works if a negative read_touch_ttl_percent is used. The server casts
+ // ttl back to a signed integer when all operations are read operations.
+ ttl = (uint32_t)policy->read_touch_ttl_percent;
+ }
uint8_t* p = as_command_write_header_write(buf, &policy->base, policy->commit_level,
policy->exists, policy->gen, ops->gen, ttl, oper->n_fields,
diff --git a/src/main/aerospike/aerospike_query.c b/src/main/aerospike/aerospike_query.c
index 751952c89..dd8c28b45 100644
--- a/src/main/aerospike/aerospike_query.c
+++ b/src/main/aerospike/aerospike_query.c
@@ -876,7 +876,7 @@ as_query_command_init(
uint8_t info_attr = qb->is_new ? AS_MSG_INFO3_PARTITION_DONE : 0;
p = as_command_write_header_read(cmd, base_policy, AS_POLICY_READ_MODE_AP_ONE,
- AS_POLICY_READ_MODE_SC_SESSION, base_policy->total_timeout, qb->n_fields, qb->n_ops,
+ AS_POLICY_READ_MODE_SC_SESSION, -1, base_policy->total_timeout, qb->n_fields, qb->n_ops,
read_attr, write_attr, info_attr);
}
else if (query->ops) {
diff --git a/src/main/aerospike/aerospike_scan.c b/src/main/aerospike/aerospike_scan.c
index a8a615491..c6520548e 100644
--- a/src/main/aerospike/aerospike_scan.c
+++ b/src/main/aerospike/aerospike_scan.c
@@ -529,7 +529,7 @@ as_scan_command_init(
int info_attr = cluster->has_partition_query? AS_MSG_INFO3_PARTITION_DONE : 0;
p = as_command_write_header_read(cmd, &policy->base, AS_POLICY_READ_MODE_AP_ONE,
- AS_POLICY_READ_MODE_SC_SESSION, policy->base.total_timeout, sb->n_fields, n_ops,
+ AS_POLICY_READ_MODE_SC_SESSION, -1, policy->base.total_timeout, sb->n_fields, n_ops,
read_attr, 0, info_attr);
}
diff --git a/src/main/aerospike/as_command.c b/src/main/aerospike/as_command.c
index 9829e0fe0..ea6f3d8a0 100644
--- a/src/main/aerospike/as_command.c
+++ b/src/main/aerospike/as_command.c
@@ -259,8 +259,8 @@ as_command_write_header_write(
uint8_t*
as_command_write_header_read(
uint8_t* cmd, const as_policy_base* policy, as_policy_read_mode_ap read_mode_ap,
- as_policy_read_mode_sc read_mode_sc, uint32_t timeout, uint16_t n_fields, uint16_t n_bins,
- uint8_t read_attr, uint8_t write_attr, uint8_t info_attr
+ as_policy_read_mode_sc read_mode_sc, int read_ttl, uint32_t timeout, uint16_t n_fields,
+ uint16_t n_bins, uint8_t read_attr, uint8_t write_attr, uint8_t info_attr
)
{
as_command_set_attr_read(read_mode_ap, read_mode_sc, policy->compress, &read_attr,
@@ -270,7 +270,8 @@ as_command_write_header_read(
cmd[9] = read_attr;
cmd[10] = write_attr;
cmd[11] = info_attr;
- memset(&cmd[12], 0, 10);
+ memset(&cmd[12], 0, 6);
+ *(int*)&cmd[18] = cf_swap_to_be32(read_ttl);
*(uint32_t*)&cmd[22] = cf_swap_to_be32(timeout);
*(uint16_t*)&cmd[26] = cf_swap_to_be16(n_fields);
*(uint16_t*)&cmd[28] = cf_swap_to_be16(n_bins);
@@ -280,7 +281,8 @@ as_command_write_header_read(
uint8_t*
as_command_write_header_read_header(
uint8_t* cmd, const as_policy_base* policy, as_policy_read_mode_ap read_mode_ap,
- as_policy_read_mode_sc read_mode_sc, uint16_t n_fields, uint16_t n_bins, uint8_t read_attr
+ as_policy_read_mode_sc read_mode_sc, int read_ttl, uint16_t n_fields, uint16_t n_bins,
+ uint8_t read_attr
)
{
uint8_t info_attr = 0;
@@ -290,7 +292,8 @@ as_command_write_header_read_header(
cmd[9] = read_attr;
cmd[10] = 0;
cmd[11] = info_attr;
- memset(&cmd[12], 0, 10);
+ memset(&cmd[12], 0, 6);
+ *(int*)&cmd[18] = cf_swap_to_be32(read_ttl);
uint32_t timeout = as_command_server_timeout(policy);
*(uint32_t*)&cmd[22] = cf_swap_to_be32(timeout);
*(uint16_t*)&cmd[26] = cf_swap_to_be16(n_fields);
diff --git a/src/test/aerospike_batch/batch.c b/src/test/aerospike_batch/batch.c
index b868bebdb..11ab502dc 100644
--- a/src/test/aerospike_batch/batch.c
+++ b/src/test/aerospike_batch/batch.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2008-2022 Aerospike, Inc.
+ * Copyright 2008-2024 Aerospike, Inc.
*
* Portions may be licensed to Aerospike, Inc. under one or more contributor
* license agreements.
@@ -46,6 +46,7 @@
#define N_KEYS 200
extern aerospike* as;
+extern bool g_has_ttl;
uint32_t num_threads = 0;
pthread_rwlock_t rwlock;
@@ -802,6 +803,55 @@ TEST(batch_remove, "Batch remove")
assert_int_eq(errors, 0);
}
+TEST(batch_reset_read_ttl, "Batch reset read ttl")
+{
+ as_error err;
+ as_status status;
+ uint32_t errors;
+
+ // Define keys
+ as_batch batch;
+ as_batch_inita(&batch, 2);
+ as_key_init_int64(as_batch_keyat(&batch, 0), NAMESPACE, SET, 8888);
+ as_key_init_int64(as_batch_keyat(&batch, 1), NAMESPACE, SET, 8889);
+
+ as_operations ops;
+ as_operations_inita(&ops, 1);
+ as_operations_add_write_int64(&ops, "a", 1);
+ ops.ttl = 2;
+
+ errors = 0;
+ status = aerospike_batch_operate(as, &err, NULL, NULL, &batch, &ops, result_cb, &errors);
+ assert_int_eq(status, AEROSPIKE_OK);
+ assert_int_eq(errors, 0);
+
+ // Read the records before they expire and reset read ttl.
+ as_sleep(1010);
+ as_policy_batch pb;
+ as_policy_batch_init(&pb);
+ pb.read_touch_ttl_percent = 40;
+
+ errors = 0;
+ status = aerospike_batch_exists(as, &err, &pb, &batch, result_cb, &errors);
+ assert_int_eq(status, AEROSPIKE_OK);
+ assert_int_eq(errors, 0);
+
+ // Read the records again, but don't reset read ttl.
+ as_sleep(1010);
+ pb.read_touch_ttl_percent = -1;
+ errors = 0;
+ status = aerospike_batch_exists(as, &err, &pb, &batch, result_cb, &errors);
+ assert_int_eq(status, AEROSPIKE_OK);
+ assert_int_eq(errors, 0);
+
+ // Read the record after it expires, showing it's gone.
+ as_sleep(1500);
+ errors = 0;
+ status = aerospike_batch_exists(as, &err, NULL, &batch, not_exists_cb, &errors);
+ assert_int_eq(status, AEROSPIKE_OK);
+ assert_int_eq(errors, 0);
+}
+
//---------------------------------
// Test Suite
//---------------------------------
@@ -819,4 +869,8 @@ SUITE(batch, "aerospike batch tests")
suite_add(batch_write_complex);
suite_add(batch_write_read_all_bins);
suite_add(batch_remove);
+
+ if (g_has_ttl) {
+ suite_add(batch_reset_read_ttl);
+ }
}
diff --git a/src/test/aerospike_key/key_basics.c b/src/test/aerospike_key/key_basics.c
index da2225a2b..67ac21e24 100644
--- a/src/test/aerospike_key/key_basics.c
+++ b/src/test/aerospike_key/key_basics.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2008-2022 Aerospike, Inc.
+ * Copyright 2008-2024 Aerospike, Inc.
*
* Portions may be licensed to Aerospike, Inc. under one or more contributor
* license agreements.
@@ -27,6 +27,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -40,6 +41,7 @@
extern aerospike* as;
extern bool g_enterprise_server;
+extern bool g_has_ttl;
/******************************************************************************
* MACROS
@@ -822,6 +824,59 @@ TEST(key_basics_write_empty_bin_name, "write empty bin name")
as_record_destroy(prec);
}
+TEST(key_basics_reset_read_ttl, "reset read ttl")
+{
+ // Write initial record.
+ as_key key;
+ as_key_init(&key, NAMESPACE, SET, "rrt");
+
+ // Write record with 2 second ttl.
+ as_record rec;
+ as_record_inita(&rec, 1);
+ as_record_set_str(&rec, "a", "expirevalue");
+ rec.ttl = 2;
+
+ as_error err;
+ as_status status = aerospike_key_put(as, &err, NULL, &key, &rec);
+ assert_int_eq(status, AEROSPIKE_OK);
+
+ // Read the record before it expires and reset read ttl.
+ as_sleep(1000);
+
+ as_policy_read pr;
+ as_policy_read_init(&pr);
+ pr.read_touch_ttl_percent = 40;
+
+ as_record* prec = NULL;
+ status = aerospike_key_get(as, &err, &pr, &key, &prec);
+ assert_int_eq(status, AEROSPIKE_OK);
+
+ char* s = as_record_get_str(prec, "a");
+ assert_not_null(s);
+ assert_string_eq(s, "expirevalue");
+ as_record_destroy(prec);
+
+ // Read the record again, but don't reset read ttl.
+ as_sleep(1000);
+ pr.read_touch_ttl_percent = -1;
+
+ prec = NULL;
+ status = aerospike_key_get(as, &err, &pr, &key, &prec);
+ assert_int_eq(status, AEROSPIKE_OK);
+
+ s = as_record_get_str(prec, "a");
+ assert_not_null(s);
+ assert_string_eq(s, "expirevalue");
+ as_record_destroy(prec);
+
+ // Read the record after it expires, showing it's gone.
+ as_sleep(2000);
+
+ prec = NULL;
+ status = aerospike_key_get(as, &err, NULL, &key, &prec);
+ assert_int_eq(status, AEROSPIKE_ERR_RECORD_NOT_FOUND);
+}
+
/******************************************************************************
* TEST SUITE
*****************************************************************************/
@@ -854,4 +909,8 @@ SUITE(key_basics, "aerospike_key basic tests") {
if (g_enterprise_server) {
suite_add(key_basics_compression);
}
+
+ if (g_has_ttl) {
+ suite_add(key_basics_reset_read_ttl);
+ }
}
diff --git a/src/test/aerospike_key/key_operate.c b/src/test/aerospike_key/key_operate.c
index 4978a611f..a94187324 100644
--- a/src/test/aerospike_key/key_operate.c
+++ b/src/test/aerospike_key/key_operate.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2008-2023 Aerospike, Inc.
+ * Copyright 2008-2024 Aerospike, Inc.
*
* Portions may be licensed to Aerospike, Inc. under one or more contributor
* license agreements.
@@ -29,6 +29,7 @@
#include
#include
#include
+#include
#include "../test.h"
@@ -379,6 +380,65 @@ TEST(key_operate_read_all_bins , "operate read all bins")
as_record_destroy(prec);
}
+TEST(key_operate_reset_read_ttl, "operate reset_read_ttl")
+{
+ // Write initial record.
+ as_key key;
+ as_key_init(&key, NAMESPACE, SET, "oprrttl");
+
+ // Write record with 2 second ttl.
+ as_record rec;
+ as_record_inita(&rec, 1);
+ as_record_set_str(&rec, "a", "expirevalue");
+ rec.ttl = 2;
+
+ as_error err;
+ as_status status = aerospike_key_put(as, &err, NULL, &key, &rec);
+ assert_int_eq(status, AEROSPIKE_OK);
+
+ // Read the record with operate command before it expires and reset read ttl.
+ as_sleep(1000);
+
+ as_policy_operate po;
+ as_policy_operate_init(&po);
+ po.read_touch_ttl_percent = 40;
+
+ as_operations ops;
+ as_operations_inita(&ops, 1);
+ as_operations_add_read(&ops, "a");
+
+ as_record* prec = NULL;
+ status = aerospike_key_operate(as, &err, &po, &key, &ops, &prec);
+ assert_int_eq(status, AEROSPIKE_OK);
+
+ char* s = as_record_get_str(prec, "a");
+ assert_not_null(s);
+ assert_string_eq(s, "expirevalue");
+ as_record_destroy(prec);
+
+ // Read the record again, but don't reset read ttl.
+ as_sleep(1000);
+ po.read_touch_ttl_percent = -1;
+
+ prec = NULL;
+ status = aerospike_key_operate(as, &err, &po, &key, &ops, &prec);
+ assert_int_eq(status, AEROSPIKE_OK);
+
+ s = as_record_get_str(prec, "a");
+ assert_not_null(s);
+ assert_string_eq(s, "expirevalue");
+ as_record_destroy(prec);
+
+ // Read the record after it expires, showing it's gone.
+ as_sleep(2000);
+
+ prec = NULL;
+ status = aerospike_key_operate(as, &err, NULL, &key, &ops, &prec);
+ assert_int_eq(status, AEROSPIKE_ERR_RECORD_NOT_FOUND);
+
+ as_operations_destroy(&ops);
+}
+
/******************************************************************************
* TEST SUITE
*****************************************************************************/
@@ -388,6 +448,7 @@ SUITE(key_operate, "aerospike_key_operate tests")
if (g_has_ttl) {
suite_add(key_operate_touchget);
suite_add(key_operate_gen_equal);
+ suite_add(key_operate_reset_read_ttl);
}
suite_add(key_operate_9);
suite_add(key_operate_float);