4343#include "entry.h"
4444
4545
46+ int hashTypeExpireEntry (entry * entry );
47+
48+ volatileEntryType hashvolatileEntryType = {
49+ .entryGetKey = (sds (* )(const void * entry ))entryGetField ,
50+ .getExpiry = (long long (* )(const void * entry ))entryGetExpiry ,
51+ .expire = hashTypeExpireEntry ,
52+ };
53+
54+ /*-----------------------------------------------------------------------------
55+ * Hash type Expiry API
56+ *----------------------------------------------------------------------------*/
57+
58+ static volatile_set * hashTypeGetVolatileSet (robj * o ) {
59+ serverAssert (o -> encoding == OBJ_ENCODING_HASHTABLE );
60+ return * (volatile_set * * )hashtableMetadata (o -> ptr );
61+ }
62+
63+ void hashTypeFreeVolatileSet (robj * o ) {
64+ volatile_set * set = hashTypeGetVolatileSet (o );
65+ if (set )
66+ freeVolatileSet (set );
67+ }
68+
69+ int hashTypeHasVolatileElements (robj * o ) {
70+ return ((o -> encoding == OBJ_ENCODING_HASHTABLE ) && (hashTypeGetVolatileSet (o ) != NULL ));
71+ }
72+
73+ size_t hashTypeNumVolatileElements (robj * o ) {
74+ if (hashTypeHasVolatileElements (o )) {
75+ return volatileSetNumEntries (hashTypeGetVolatileSet (o ));
76+ }
77+ return 0 ;
78+ }
79+
80+ void hashTypeIgnoreTTL (robj * o , int ignore ) {
81+ if (o -> encoding == OBJ_ENCODING_HASHTABLE ) {
82+ /* prevent placing access function if not needed */
83+ if (!ignore && !hashTypeHasVolatileElements (o )) {
84+ ignore = 0 ;
85+ }
86+ hashtableSetType (o -> ptr , ignore ? & hashHashtableType : & hashWithVolatileItemsHashtableType );
87+ }
88+ }
89+
90+ static volatile_set *
91+ hashTypeGetOrcreateVolatileSet (robj * o ) {
92+ serverAssert (o -> encoding == OBJ_ENCODING_HASHTABLE );
93+ volatile_set * * volatile_set_ref = hashtableMetadata (o -> ptr );
94+ if (* volatile_set_ref == NULL ) {
95+ * volatile_set_ref = createVolatileSet (& hashvolatileEntryType );
96+ /* serves mainly for optimization. Use type which supports access function only when needed. */
97+ hashTypeIgnoreTTL (o , 0 );
98+ }
99+ return * volatile_set_ref ;
100+ }
101+
102+ static void hashTypeDeleteVolatileSet (robj * o ) {
103+ volatile_set * * volatile_set_ref = hashtableMetadata (o -> ptr );
104+ freeVolatileSet (* volatile_set_ref );
105+ * volatile_set_ref = NULL ;
106+ /* serves mainly for optimization. by changing the hashtable type we can avoid extra function call in hashtable access */
107+ hashTypeIgnoreTTL (o , 1 );
108+ }
109+
110+ void hashTypeTrackEntry (robj * o , void * entry ) {
111+ volatile_set * set = hashTypeGetOrcreateVolatileSet (o );
112+ serverAssert (volatileSetAddEntry (set , entry , entryGetExpiry (entry )));
113+ }
114+
115+ void hashTypeUntrackEntry (robj * o , void * entry ) {
116+ if (!entryHasExpiry (entry )) return ;
117+ volatile_set * set = hashTypeGetVolatileSet (o );
118+ debugServerAssert (set );
119+ serverAssert (volatileSetRemoveEntry (set , entry , entryGetExpiry (entry )));
120+ if (volatileSetNumEntries (set ) == 0 ) {
121+ hashTypeDeleteVolatileSet (o );
122+ }
123+ }
124+
125+ static void hashTypeTrackUpdateEntry (robj * o , void * old_entry , void * new_entry , long long old_expiry , long long new_expiry ) {
126+ int old_tracked = (old_entry && old_expiry != EXPIRY_NONE );
127+ int new_tracked = (new_entry && new_expiry != EXPIRY_NONE );
128+ /* If entry was not tracked before and not going to be tracked now, we can simply return */
129+ if (!old_tracked && !new_tracked )
130+ return ;
131+
132+ volatile_set * set = hashTypeGetOrcreateVolatileSet (o );
133+ debugServerAssert (set );
134+
135+ if (old_tracked && !new_tracked )
136+ serverAssert (volatileSetRemoveEntry (set , old_entry , old_expiry ));
137+ else if (new_tracked && !old_tracked )
138+ serverAssert (volatileSetAddEntry (set , new_entry , new_expiry ));
139+ else {
140+ volatile_set * set = hashTypeGetVolatileSet (o );
141+ debugServerAssert (set );
142+ serverAssert (volatileSetUpdateEntry (set , old_entry , new_entry , old_expiry , new_expiry ) == 1 );
143+ }
144+ if (volatileSetNumEntries (set ) == 0 ) {
145+ hashTypeDeleteVolatileSet (o );
146+ }
147+ }
148+
46149int hashTypeExpireEntry (void * entry ) {
47150 // TBD
48151 UNUSED (entry );
49152 return 1 ;
50153}
51154
155+ hashtableElementAccessState hashHashtableTypeAccess (hashtable * ht , void * entry ) {
156+ UNUSED (ht );
157+
158+ if (!canExpireWithFlags (0 , NULL )) return ELEMENT_VALID ;
159+
160+ if (!entryIsExpired (entry )) return ELEMENT_VALID ;
161+
162+ return ELEMENT_INVALID ;
163+ }
52164
53165/*-----------------------------------------------------------------------------
54166 * Hash type API
@@ -281,7 +393,7 @@ int hashTypeSet(robj *o, sds field, sds value, long long expiry, int flags) {
281393
282394 /* We have to ignore the TTL when setting an element. this is mainly in order to be able to update an existing expired
283395 * entry and not have it remain in the hashtable with the same field/value. */
284- objectIgnoreTTL (o , 1 );
396+ hashTypeIgnoreTTL (o , 1 );
285397 hashtablePosition position ;
286398 void * existing ;
287399 if (hashtableFindPositionForInsert (ht , field , & position , & existing )) {
@@ -290,7 +402,7 @@ int hashTypeSet(robj *o, sds field, sds value, long long expiry, int flags) {
290402 hashtableInsertAtPosition (ht , entry , & position );
291403 /* In case an expiry is set on the new entry, we need to track it */
292404 if (expiry != EXPIRY_NONE ) {
293- objectTrackEntry (o , entry );
405+ hashTypeTrackEntry (o , entry );
294406 }
295407 } else {
296408 /* exists: replace value */
@@ -307,11 +419,11 @@ int hashTypeSet(robj *o, sds field, sds value, long long expiry, int flags) {
307419 int replaced = hashtableReplaceReallocatedEntry (ht , existing , new_entry );
308420 serverAssert (replaced );
309421 }
310- objectTrackUpdateEntry (o , existing , new_entry , entry_expiry , expiry );
422+ hashTypeTrackUpdateEntry (o , existing , new_entry , entry_expiry , expiry );
311423
312424 update = is_expired ? 0 : 1 ;
313425 }
314- objectIgnoreTTL (o , 0 );
426+ hashTypeIgnoreTTL (o , 0 );
315427 } else {
316428 serverPanic ("Unknown hash encoding" );
317429 }
@@ -383,7 +495,7 @@ int hashTypeSetExpire(robj *o, sds field, long long expiry, int flag) {
383495 }
384496 }
385497 * entry_ref = entrySetExpiry (current_entry , expiry );
386- objectTrackUpdateEntry (o , current_entry , * entry_ref , current_expire , expiry );
498+ hashTypeTrackUpdateEntry (o , current_entry , * entry_ref , current_expire , expiry );
387499 return 1 ;
388500 }
389501 return -2 ; // we did not find anything to do. return -2
@@ -408,7 +520,7 @@ int hashTypePersist(robj *o, sds field) {
408520 entry * current_entry = * entry_ref ;
409521 long long current_expire = entryGetExpiry (current_entry );
410522 if (current_expire != EXPIRY_NONE ) {
411- objectUntrackEntry (o , current_entry );
523+ hashTypeUntrackEntry (o , current_entry );
412524 * entry_ref = entryUpdate (current_entry , NULL , EXPIRY_NONE );
413525 return 1 ;
414526 }
@@ -441,7 +553,7 @@ int hashTypeDelete(robj *o, sds field) {
441553 void * entry = NULL ;
442554 deleted = hashtablePop (ht , field , & entry );
443555 if (deleted ) {
444- objectUntrackEntry (o , entry );
556+ hashTypeUntrackEntry (o , entry );
445557 entryFree (entry );
446558 }
447559 } else {
@@ -486,7 +598,7 @@ void hashTypeInitVolatileIterator(robj *subject, hashTypeIterator *hi) {
486598 if (hi -> encoding == OBJ_ENCODING_LISTPACK ) {
487599 return ;
488600 } else if (hi -> encoding == OBJ_ENCODING_HASHTABLE ) {
489- volatileSetStart (objectGetVolatileSet (subject ), & hi -> viter );
601+ volatileSetStart (hashTypeGetVolatileSet (subject ), & hi -> viter );
490602 } else {
491603 serverPanic ("Unknown hash encoding" );
492604 }
@@ -701,7 +813,7 @@ robj *hashTypeDup(robj *o) {
701813 entry * entry = entryCreate (field , sdsdup (value ), expiry );
702814 hashtableAdd (ht , entry );
703815 if (expiry != EXPIRY_NONE )
704- objectTrackEntry (hobj , entry );
816+ hashTypeTrackEntry (hobj , entry );
705817 }
706818 hashTypeResetIterator (& hi );
707819 } else {
0 commit comments