diff --git a/hazelcast/protocol/codec/map_message_type.py b/hazelcast/protocol/codec/map_message_type.py index 09e76a5e36..4bfef44322 100644 --- a/hazelcast/protocol/codec/map_message_type.py +++ b/hazelcast/protocol/codec/map_message_type.py @@ -55,3 +55,4 @@ MAP_VALUESWITHPAGINGPREDICATE = 0x0139 MAP_ENTRIESWITHPAGINGPREDICATE = 0x013a MAP_CLEARNEARCACHE = 0x013b +MAP_SETTTL = 0x0149 diff --git a/hazelcast/protocol/codec/map_set_ttl_codec.py b/hazelcast/protocol/codec/map_set_ttl_codec.py new file mode 100644 index 0000000000..e3691c7616 --- /dev/null +++ b/hazelcast/protocol/codec/map_set_ttl_codec.py @@ -0,0 +1,40 @@ +from hazelcast.serialization.bits import * +from hazelcast.protocol.client_message import ClientMessage +from hazelcast.protocol.custom_codec import * +from hazelcast.util import ImmutableLazyDataList +from hazelcast.protocol.codec.map_message_type import * + +REQUEST_TYPE = MAP_SETTTL +RESPONSE_TYPE = 101 +RETRYABLE = False + + +def calculate_size(name, key, ttl): + """ Calculates the request payload size""" + data_size = 0 + data_size += calculate_size_str(name) + data_size += calculate_size_data(key) + data_size += LONG_SIZE_IN_BYTES + return data_size + + +def encode_request(name, key, ttl): + """ Encode request into client_message""" + client_message = ClientMessage(payload_size=calculate_size(name, key, ttl)) + client_message.set_message_type(REQUEST_TYPE) + client_message.set_retryable(RETRYABLE) + client_message.append_str(name) + client_message.append_data(key) + client_message.append_long(ttl) + client_message.update_frame_length() + return client_message + + +def decode_response(client_message, to_object=None): + """ Decode response from client message""" + parameters = dict(response=None) + parameters['response'] = client_message.read_bool() + return parameters + + + diff --git a/hazelcast/proxy/map.py b/hazelcast/proxy/map.py index 057bb61cfc..14ddd457dc 100644 --- a/hazelcast/proxy/map.py +++ b/hazelcast/proxy/map.py @@ -10,10 +10,10 @@ map_is_locked_codec, map_key_set_codec, map_key_set_with_predicate_codec, map_load_all_codec, \ map_load_given_keys_codec, map_lock_codec, map_put_codec, map_put_all_codec, map_put_if_absent_codec, \ map_put_transient_codec, map_size_codec, map_remove_codec, map_remove_if_same_codec, \ - map_remove_entry_listener_codec, map_replace_codec, map_replace_if_same_codec, map_set_codec, map_try_lock_codec, \ - map_try_put_codec, map_try_remove_codec, map_unlock_codec, map_values_codec, map_values_with_predicate_codec, \ - map_add_interceptor_codec, map_execute_on_all_keys_codec, map_execute_on_key_codec, map_execute_on_keys_codec, \ - map_execute_with_predicate_codec, map_add_near_cache_entry_listener_codec + map_remove_entry_listener_codec, map_replace_codec, map_replace_if_same_codec, map_set_codec, map_set_ttl_codec, \ + map_try_lock_codec, map_try_put_codec, map_try_remove_codec, map_unlock_codec, map_values_codec, \ + map_values_with_predicate_codec, map_add_interceptor_codec, map_execute_on_all_keys_codec, map_execute_on_key_codec, \ + map_execute_on_keys_codec, map_execute_with_predicate_codec, map_add_near_cache_entry_listener_codec from hazelcast.proxy.base import Proxy, EntryEvent, EntryEventType, get_entry_listener_flags, MAX_SIZE from hazelcast.util import check_not_none, thread_id, to_millis from hazelcast import six @@ -725,6 +725,21 @@ def set(self, key, value, ttl=-1): value_data = self._to_data(value) return self._set_internal(key_data, value_data, ttl) + def set_ttl(self, key, ttl): + """ + Updates the TTL (time to live) value of the entry specified by the given key with a new TTL value. New TTL + value is valid starting from the time this operation is invoked, not since the time the entry was created. + If the entry does not exist or is already expired, this call has no effect. + + :param key: (object), the key of the map entry. + :param ttl: (int), maximum time for this entry to stay in the map (0 means infinite, + negative means map config default) + """ + check_not_none(key, "key can't be None") + check_not_none(ttl, "ttl can't be None") + key_data = self._to_data(key) + return self._set_ttl_internal(key_data, ttl) + def size(self): """ Returns the number of entries in this map. @@ -861,6 +876,9 @@ def _set_internal(self, key_data, value_data, ttl): return self._encode_invoke_on_key(map_set_codec, key_data, key=key_data, value=value_data, thread_id=thread_id(), ttl=to_millis(ttl)) + def _set_ttl_internal(self, key_data, ttl): + return self._encode_invoke_on_key(map_set_ttl_codec, key_data, key=key_data, ttl=to_millis(ttl)) + def _try_remove_internal(self, key_data, timeout): return self._encode_invoke_on_key(map_try_remove_codec, key_data, key=key_data, thread_id=thread_id(), timeout=to_millis(timeout)) @@ -1007,6 +1025,10 @@ def _set_internal(self, key_data, value_data, ttl): self._invalidate_cache(key_data) return super(MapFeatNearCache, self)._set_internal(key_data, value_data, ttl) + def _set_ttl_internal(self, key_data, ttl): + self._invalidate_cache(key_data) + return super(MapFeatNearCache, self)._set_ttl_internal(key_data, ttl) + def _replace_internal(self, key_data, value_data): self._invalidate_cache(key_data) return super(MapFeatNearCache, self)._replace_internal(key_data, value_data) diff --git a/tests/proxy/map_test.py b/tests/proxy/map_test.py index c05272454f..778112aa23 100644 --- a/tests/proxy/map_test.py +++ b/tests/proxy/map_test.py @@ -437,6 +437,15 @@ def test_set(self): self.map.set("key", "value") self.assertEqual(self.map.get("key"), "value") + + def test_set_ttl(self): + self.map.put("key", "value") + self.map.set_ttl("key", 0.1) + + def evicted(): + self.assertFalse(self.map.contains_key("key")) + + self.assertTrueEventually(evicted, 1) def test_size(self): self._fill_map()