Skip to content

Commit fff4475

Browse files
committed
add ttl and size in hash structure(#263)
1 parent 4707304 commit fff4475

File tree

3 files changed

+210
-53
lines changed

3 files changed

+210
-53
lines changed

lib/const/errors.go

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ var (
66
ErrKeyIsEmpty = errors.New("KeyEmptyError : the key is empty")
77
ErrIndexUpdateFailed = errors.New("IndexUpdateFailError : failed to update index")
88
ErrKeyNotFound = errors.New("KeyNotFoundError : key is not found in database")
9+
ErrKeyIsExpired = errors.New("KeyIsExpiredError : key is expired")
910
ErrDataFailNotFound = errors.New("DataFailNotFoundError : data file is not found")
1011
ErrDataDirectoryCorrupted = errors.New("DataDirectoryCorruptedError : the databases directory maybe corrupted")
1112
ErrExceedMaxBatchNum = errors.New("ExceedMaxBatchNumError : exceed the max batch num")

structure/hash.go

+109-7
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package structure
22

33
import (
44
"encoding/binary"
5+
"fmt"
56
"github.com/ByteStorage/FlyDB/config"
67
"github.com/ByteStorage/FlyDB/engine"
78
_const "github.com/ByteStorage/FlyDB/lib/const"
@@ -24,6 +25,7 @@ const maxHashMetaSize = 1 + binary.MaxVarintLen64*6
2425
type HashStructure struct {
2526
db *engine.DB
2627
hashValueType string
28+
expire int64
2729
}
2830

2931
// NewHashStructure Returns a new NewHashStructure
@@ -56,7 +58,7 @@ func NewHashStructure(options config.Options) (*HashStructure, error) {
5658
// - The function creates a new HashField containing the field details and encodes it.
5759
// - The function uses a write batch to efficiently commit changes to the database.
5860
// - 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) {
6062
// Convert the parameters to bytes
6163
key := stringToBytesWithKey(k)
6264

@@ -114,6 +116,18 @@ func (hs *HashStructure) HSet(k string, f, v interface{}) (bool, error) {
114116
_ = batch.Put(key, hashMeta.encodeHashMeta())
115117
}
116118

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+
117131
// Put the field to the database
118132
_ = batch.Put(hfBuf, value)
119133

@@ -149,6 +163,12 @@ func (hs *HashStructure) HSet(k string, f, v interface{}) (bool, error) {
149163
// - The retrieved byte data is converted back to the corresponding data type.
150164
// - Returns the value corresponding to the field and any possible error.
151165
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+
152172
// Convert the parameters to bytes
153173
key := stringToBytesWithKey(k)
154174

@@ -223,6 +243,7 @@ func (hs *HashStructure) HMGet(k string, f ...interface{}) ([]interface{}, error
223243
// Convert the parameters to bytes
224244
field, err, _ := interfaceToBytes(fi)
225245
if err != nil {
246+
fmt.Println("err", err)
226247
return nil, err
227248
}
228249

@@ -928,7 +949,7 @@ func (hs *HashStructure) HMove(source, destination string, f interface{}) (bool,
928949
//
929950
// bool: True if the field was set, false otherwise.
930951
// 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) {
932953
// Convert the parameters to bytes
933954
key := stringToBytesWithKey(k)
934955

@@ -968,7 +989,7 @@ func (hs *HashStructure) HSetNX(k string, f, v interface{}) (bool, error) {
968989
// Get the field from the database
969990
_, err = hs.db.Get(hfBuf)
970991
if err != nil && err == _const.ErrKeyNotFound {
971-
_, err := hs.HSet(k, field, value)
992+
_, err := hs.HSet(k, field, value, ttl)
972993
if err != nil {
973994
return false, err
974995
}
@@ -1054,6 +1075,91 @@ func (hs *HashStructure) Keys() ([]string, error) {
10541075
return keys, nil
10551076
}
10561077

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+
10571163
func isFirstFiveBytesField(data []byte) bool {
10581164
if len(data) < 5 {
10591165
return false
@@ -1086,10 +1192,6 @@ func (hs *HashStructure) findHashMeta(k string, dataType DataStructure) (*HashMe
10861192
return nil, ErrInvalidType
10871193
}
10881194

1089-
// Check the expiration time
1090-
if hashMeta.expire > 0 && hashMeta.expire < time.Now().UnixNano() {
1091-
exist = false
1092-
}
10931195
}
10941196

10951197
// If the hash metadata is not found, create a new one

0 commit comments

Comments
 (0)