@@ -2,6 +2,7 @@ package structure
2
2
3
3
import (
4
4
"encoding/binary"
5
+ "fmt"
5
6
"github.com/ByteStorage/FlyDB/config"
6
7
"github.com/ByteStorage/FlyDB/engine"
7
8
_const "github.com/ByteStorage/FlyDB/lib/const"
@@ -24,6 +25,7 @@ const maxHashMetaSize = 1 + binary.MaxVarintLen64*6
24
25
type HashStructure struct {
25
26
db * engine.DB
26
27
hashValueType string
28
+ expire int64
27
29
}
28
30
29
31
// NewHashStructure Returns a new NewHashStructure
@@ -56,7 +58,7 @@ func NewHashStructure(options config.Options) (*HashStructure, error) {
56
58
// - The function creates a new HashField containing the field details and encodes it.
57
59
// - The function uses a write batch to efficiently commit changes to the database.
58
60
// - It returns a boolean indicating whether the field was newly created or updated.
59
- func (hs * HashStructure ) HSet (k string , f , v interface {}) (bool , error ) {
61
+ func (hs * HashStructure ) HSet (k string , f , v interface {}, ttl int64 ) (bool , error ) {
60
62
// Convert the parameters to bytes
61
63
key := stringToBytesWithKey (k )
62
64
@@ -114,6 +116,18 @@ func (hs *HashStructure) HSet(k string, f, v interface{}) (bool, error) {
114
116
_ = batch .Put (key , hashMeta .encodeHashMeta ())
115
117
}
116
118
119
+ // Check if the provided TTL is greater than 0
120
+ if ttl > 0 {
121
+ // Calculate the expiration time in nanoseconds
122
+ expirationTime := time .Now ().Add (time .Duration (ttl ) * time .Second ).UnixNano ()
123
+
124
+ // Update the hash metadata with the expiration time
125
+ hashMeta .expire = expirationTime
126
+
127
+ // Put the updated hash metadata in the database
128
+ _ = batch .Put (key , hashMeta .encodeHashMeta ())
129
+ }
130
+
117
131
// Put the field to the database
118
132
_ = batch .Put (hfBuf , value )
119
133
@@ -149,6 +163,12 @@ func (hs *HashStructure) HSet(k string, f, v interface{}) (bool, error) {
149
163
// - The retrieved byte data is converted back to the corresponding data type.
150
164
// - Returns the value corresponding to the field and any possible error.
151
165
func (hs * HashStructure ) HGet (k string , f interface {}) (interface {}, error ) {
166
+ // Determine whether the key has expired
167
+ ttl , _ := hs .TTL (k )
168
+ if ttl == - 1 {
169
+ return nil , _const .ErrKeyIsExpired
170
+ }
171
+
152
172
// Convert the parameters to bytes
153
173
key := stringToBytesWithKey (k )
154
174
@@ -223,6 +243,7 @@ func (hs *HashStructure) HMGet(k string, f ...interface{}) ([]interface{}, error
223
243
// Convert the parameters to bytes
224
244
field , err , _ := interfaceToBytes (fi )
225
245
if err != nil {
246
+ fmt .Println ("err" , err )
226
247
return nil , err
227
248
}
228
249
@@ -928,7 +949,7 @@ func (hs *HashStructure) HMove(source, destination string, f interface{}) (bool,
928
949
//
929
950
// bool: True if the field was set, false otherwise.
930
951
// error: An error if occurred during the operation, or nil on success.
931
- func (hs * HashStructure ) HSetNX (k string , f , v interface {}) (bool , error ) {
952
+ func (hs * HashStructure ) HSetNX (k string , f , v interface {}, ttl int64 ) (bool , error ) {
932
953
// Convert the parameters to bytes
933
954
key := stringToBytesWithKey (k )
934
955
@@ -968,7 +989,7 @@ func (hs *HashStructure) HSetNX(k string, f, v interface{}) (bool, error) {
968
989
// Get the field from the database
969
990
_ , err = hs .db .Get (hfBuf )
970
991
if err != nil && err == _const .ErrKeyNotFound {
971
- _ , err := hs .HSet (k , field , value )
992
+ _ , err := hs .HSet (k , field , value , ttl )
972
993
if err != nil {
973
994
return false , err
974
995
}
@@ -1054,6 +1075,91 @@ func (hs *HashStructure) Keys() ([]string, error) {
1054
1075
return keys , nil
1055
1076
}
1056
1077
1078
+ // TTL returns the time-to-live (TTL) of a key in the hash.
1079
+ // It takes a string key 'k' and returns the remaining TTL in seconds and any possible error.
1080
+ //
1081
+ // Parameters:
1082
+ //
1083
+ // k: The key for which TTL needs to be determined.
1084
+ //
1085
+ // Returns:
1086
+ //
1087
+ // int64: The remaining TTL in seconds. Returns 0 if the key has expired or doesn't exist.
1088
+ // error: An error if occurred during the operation, or nil on success.
1089
+ func (hs * HashStructure ) TTL (k string ) (int64 , error ) {
1090
+ // Check the parameters
1091
+ if len (k ) == 0 {
1092
+ return - 1 , _const .ErrKeyIsEmpty
1093
+ }
1094
+
1095
+ // Find the hash metadata by the given key
1096
+ hashMeta , err := hs .findHashMeta (k , Hash )
1097
+ if err != nil {
1098
+ return - 1 , err
1099
+ }
1100
+
1101
+ ttl := hashMeta .expire / int64 (time .Second ) - time .Now ().UnixNano ()/ int64 (time .Second )
1102
+
1103
+ if hashMeta .expire == 0 {
1104
+ return 0 , nil
1105
+ }
1106
+
1107
+ if ttl <= 0 {
1108
+ return - 1 , _const .ErrKeyIsExpired
1109
+ }
1110
+ return ttl , nil
1111
+ }
1112
+
1113
+ // Size returns the size of a field in the hash as a formatted string.
1114
+ // It takes a string key 'k' and one or more fields 'f' (optional).
1115
+ // It returns a formatted string indicating the size of the field and any possible error.
1116
+ //
1117
+ // Parameters:
1118
+ //
1119
+ // k: The key of the hash table.
1120
+ // f: The field(s) whose size needs to be determined (optional).
1121
+ //
1122
+ // Returns:
1123
+ //
1124
+ // string: A formatted string indicating the size of the field.
1125
+ // error: An error if occurred during the operation, or nil on success.
1126
+ func (hs * HashStructure ) Size (k string , f ... interface {}) (string , error ) {
1127
+ value , err := hs .HMGet (k , f ... )
1128
+ if err != nil {
1129
+ return "" , err
1130
+ }
1131
+
1132
+ var sizeInBytes int
1133
+
1134
+ // Calculate the size of the value
1135
+ for _ , v := range value {
1136
+ toString , err := interfaceToString (v )
1137
+ if err != nil {
1138
+ return "" , err
1139
+ }
1140
+ sizeInBytes += len (toString )
1141
+ }
1142
+
1143
+ // Convert bytes to corresponding units (KB, MB...)
1144
+ const (
1145
+ KB = 1 << 10
1146
+ MB = 1 << 20
1147
+ GB = 1 << 30
1148
+ )
1149
+
1150
+ var size string
1151
+ switch {
1152
+ case sizeInBytes < KB :
1153
+ size = fmt .Sprintf ("%dB" , sizeInBytes )
1154
+ case sizeInBytes < MB :
1155
+ size = fmt .Sprintf ("%.2fKB" , float64 (sizeInBytes )/ KB )
1156
+ case sizeInBytes < GB :
1157
+ size = fmt .Sprintf ("%.2fMB" , float64 (sizeInBytes )/ MB )
1158
+ }
1159
+
1160
+ return size , nil
1161
+ }
1162
+
1057
1163
func isFirstFiveBytesField (data []byte ) bool {
1058
1164
if len (data ) < 5 {
1059
1165
return false
@@ -1086,10 +1192,6 @@ func (hs *HashStructure) findHashMeta(k string, dataType DataStructure) (*HashMe
1086
1192
return nil , ErrInvalidType
1087
1193
}
1088
1194
1089
- // Check the expiration time
1090
- if hashMeta .expire > 0 && hashMeta .expire < time .Now ().UnixNano () {
1091
- exist = false
1092
- }
1093
1195
}
1094
1196
1095
1197
// If the hash metadata is not found, create a new one
0 commit comments