Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions config/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package config

var Host string="0.0.0.0"
var Port int=7379

var KeysLimit int=100 //like ye hum threshold set krdiye hai like our database still support atmax this many keys

//will evict EvictionRation of keys whenever evictionruns
//it would dictate whenever my eviction is triggered, how many keys i will be evicting.
var EvictionRatio flaot64=0.40 //Generally aise real scenario me kabhi itna to nahi hi krte hai but still

var EvictionStrategy string="allkeys-random"
var AOFFile string="./velox.aof"

Comment on lines +2 to +14
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file duplicates configuration variables already declared in config/config.go (Host, Port, KeysLimit, etc.), which will cause redeclaration compile errors within the config package. Also flaot64 is a misspelling of float64. Prefer updating the existing config file (or removing one) and fix the type name.

Suggested change
var Host string="0.0.0.0"
var Port int=7379
var KeysLimit int=100 //like ye hum threshold set krdiye hai like our database still support atmax this many keys
//will evict EvictionRation of keys whenever evictionruns
//it would dictate whenever my eviction is triggered, how many keys i will be evicting.
var EvictionRatio flaot64=0.40 //Generally aise real scenario me kabhi itna to nahi hi krte hai but still
var EvictionStrategy string="allkeys-random"
var AOFFile string="./velox.aof"

Copilot uses AI. Check for mistakes.
37 changes: 35 additions & 2 deletions core/eval.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,10 @@ func evalGET(args []string)[]byte{
return RESP_NIL
}


//if key already expired then return nil
if hasExpired(obj){
return RESP_NIL
}
//return te RESP encoded value
// c.Write(Encode(obj.Value,false))
// return nil
Expand Down Expand Up @@ -184,6 +187,20 @@ func evalTTL(args []string) []byte{
return RESP_MINUS_2
}

exp.isExpirySet:=getExpiry(obj)
if !isExpirySet{
return RESP_MINUS_1
}

//if key expired i.e key does not exist hence return -2
if uint64(time.Now().UnixMilli())>exp{
return RESP_MINUS_2
}

//compute the time remaining for the key to expire and
//return the RESP encoded form of it
durationMS:=exp-uint64(time.Now().UnixMilli())

// c.Write(Encode(int64(durationMS/1000),false))
// return nil
return Encode(int64(durationMs/1000), false)
Comment on lines +190 to 206
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new TTL logic has invalid Go syntax and name mismatches: exp.isExpirySet:=getExpiry(obj) should destructure into two variables, and the function mixes durationMS/durationMs. As written, this block will not compile and will return the wrong value; fix tuple assignment and use one duration variable consistently.

Copilot uses AI. Check for mistakes.
Expand Down Expand Up @@ -220,7 +237,9 @@ func evalEXPIRE(args []string) []byte{
return RESP_ZERO
}

obj.ExpiresAt = time.Now().UnixMilli() + exDurationSec*1000
// obj.ExpiresAt = time.Now().UnixMilli() + exDurationSec*1000

setExpiry(obj,exDurationSec*1000)

//1 print krenge if the timeout was set
// c.Write([]byte(":1\r\n"))
Expand All @@ -234,6 +253,14 @@ func evalBGREWRITEAOF(args []string) []byte{
return RESP_OK
}

func evalINFO(args []string)[]byte{
var info []byte
buf:=bytes.NewBuffer(info)
for i := range KeyspaceStat{
buf.WriteString(fmt.Sprintf("db%d:keys=%d,expire=0,avg_ttl=0\r\n",i,KeyspaceStat[i]["keys"]))
}
Comment on lines +258 to +261
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

evalINFO assumes KeyspaceStat[i] is non-nil for every DB and indexes KeyspaceStat[i]["keys"]. If any entry is nil (default for an array of maps), this will panic at runtime; initialize all DB stat maps up-front or nil-check each entry before reading.

Copilot uses AI. Check for mistakes.
}
Comment on lines +256 to +262
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

evalINFO uses fmt.Sprintf but fmt is not imported, and the function doesn't return the built buffer (it currently has no return statement). Return buf.Bytes() (RESP-encoded if needed) and add the required imports.

Copilot uses AI. Check for mistakes.

// func EvalAndRespond(cmd *Rediscmd,c net.Conn)error{
func EvalAndRespond(cmds []*RedisCmd, c io.ReadWriter) error{
//It's job is like depending on what job is sent to us
Expand All @@ -260,6 +287,12 @@ func EvalAndRespond(cmds []*RedisCmd, c io.ReadWriter) error{
buf.Write(evalBGREWRITEAOF(cmd.Args))
case "INCR":
buf.Write(evalPING(cmd.Args))
case "INFO":
buf.Write(evalINFO(cmd.Args))
case "CLIENT":
buf.Write(evalCLIENT(cmd.Args))
case "LATENCY":
buf.Write(evalLATENCY(cmd.Args))
Comment on lines +292 to +295
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EvalAndRespond adds cases for CLIENT and LATENCY, but evalCLIENT/evalLATENCY are not defined anywhere in core. This will not compile; either implement these handlers or remove the switch cases.

Suggested change
case "CLIENT":
buf.Write(evalCLIENT(cmd.Args))
case "LATENCY":
buf.Write(evalLATENCY(cmd.Args))

Copilot uses AI. Check for mistakes.
default:
buf.Write(evalPING(cmd.Args))
}
Expand Down
76 changes: 75 additions & 1 deletion core/eviction.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ whenevr a cache is full, we will be evicting the first key which we do find

package core

import "github.com/sharpsalt/Velox-In-Memory-Database/config"

//Evcits the first key it found while iterating the map
//TODP: Make it efficient by doing thrugh somehting
func evictFirst(){
Expand All @@ -16,9 +18,81 @@ func evictFirst(){
}
}

/*
The approximated LRU algorithm
*/
func getCurrentClock() uint32{
return uint32(time.Now().Unix())&0x00FFFFF // it would give us 24 bit clock resolution of the time
}
Comment on lines +24 to +26
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getCurrentClock uses time.Now() but time is not imported in this file, so it will not compile. Add the missing import (and keep imports grouped with existing ones).

Copilot uses AI. Check for mistakes.
Comment on lines +24 to +26
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getCurrentClock claims to return a 24-bit clock but masks with 0x00FFFFF (20 bits). This will skew idle-time calculations and LRU behavior; use the correct 24-bit mask consistently across both getCurrentClock and wrap-around logic.

Copilot uses AI. Check for mistakes.

func getIdleTime(LastAccessdAt uint32) uint32{
/*
it gives me current clock and if it is greater than lastaccessedat then for anytimestamp then diff, other (max-last)*c
*/
c:=getCurrentClock()
if c>=LastAccessdAt{
return c-LastAccessdAt
}
return (0x00FFFFF-LastAccessdAt)*c
Comment on lines +30 to +36
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getIdleTime wrap-around path returns (mask-last)*c, which multiplies two time values and produces meaningless/overflow-prone results. For a wrapping clock, the idle time should be computed as the remaining ticks until wrap plus the current tick value (i.e., an addition-based wrap calculation).

Suggested change
it gives me current clock and if it is greater than lastaccessedat then for anytimestamp then diff, other (max-last)*c
*/
c:=getCurrentClock()
if c>=LastAccessdAt{
return c-LastAccessdAt
}
return (0x00FFFFF-LastAccessdAt)*c
it gives me current clock and if it is greater than lastaccessedat then for anytimestamp then diff,
otherwise the elapsed time is the remaining ticks until wrap plus the current tick value
*/
c:=getCurrentClock()
if c>=LastAccessdAt{
return c-LastAccessdAt
}
return (0x00FFFFF-LastAccessdAt) + c + 1

Copilot uses AI. Check for mistakes.
}

func populateEvictionPool(){
sampleSize:=5
for k:=range store{
ePool.Push(k,store[k].lastaccessedat)
sampleSize--
if sampleSize==0{
break
}
}
Comment on lines +39 to +47
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

populateEvictionPool references store[k].lastaccessedat, but the field on Obj is LastAccessedAt (exported) and PoolItem uses a different name as well. This mismatch will not compile; align field names and casing across Obj, PoolItem, and call sites.

Copilot uses AI. Check for mistakes.
}

//TODO: no need to populate everytime, should populate
//only when the number of keys to evict is less than what we have in the pool
funct evictAllKeysLRU(){
populateEvictionPool()
evictCount:=int16(config.EvictionRatio=float64(config.KeysLimit))
for i:=;i<int(evictCount) && len(ePool.pool)>0 ;i++{
item:=ePool.Pop()
if item==nil{
return
}
Del(item.key)
}
}
Comment on lines +50 to +62
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

evictAllKeysLRU has several syntax/type errors (funct, invalid evictCount assignment expression, malformed for loop initializer). As written, the LRU eviction path will not compile; fix the function signature and compute evictCount as EvictionRatio * KeysLimit similar to evictAllKeysRandom.

Copilot uses AI. Check for mistakes.

//Randomly removes keys to make space for the new data added
//The number of keys removed will be sufficient to free up least 10% space
func evictAllKeysRandom(){
evictCount:=int64(config.EvictionRatio*float64(config.KeysLimit))
//Iteration of Golang dictionary can be considered as a random
//because it depends on the has of the inserted key
for k:= range store{
Del(k)
evictCount--
if evictCount==0{
break
}
}
}
/*
How do we know that our eviction is working correctly??

which means when we are doing large number of key sets , we are putting lots of keys in db , let's say we put kelimit to 100
, how do we sure that we are not breachng it , so every dbs supports statistics
*/


//TODO: Make the eveiction strategy configuration dirven
//TODO: Support multiple eviction strategies
func evict(){
evictFirst()
// evictFirst()
switch config.EvictionStrategy{
case "simple-first":
evictfirst()
case "allkets-random":
evictAllkeysRandom()
case: "allkeys-lru":
Comment on lines +92 to +95
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The evict switch has invalid Go syntax and mismatched function names/strategy strings (evictfirst vs evictFirst, allkets-random, case:). This will not compile and also won’t match the configured strategy values; fix case labels and call the correct functions.

Suggested change
evictfirst()
case "allkets-random":
evictAllkeysRandom()
case: "allkeys-lru":
evictFirst()
case "allkeys-random":
evictAllKeysRandom()
case "allkeys-lru":

Copilot uses AI. Check for mistakes.
evictAllKeysLRU()
}
}
80 changes: 80 additions & 0 deletions core/evictionpool.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package core

import{

Check failure on line 3 in core/evictionpool.go

View workflow job for this annotation

GitHub Actions / build

missing import path

Check failure on line 3 in core/evictionpool.go

View workflow job for this annotation

GitHub Actions / build

missing import path
"sort"
}
Comment on lines +3 to +5
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

import{ "sort" } is invalid Go syntax; the import block must be import ( ... ). As written, this file will not compile.

Suggested change
import{
"sort"
}
import (
"sort"
)

Copilot uses AI. Check for mistakes.


type PoolItem struct{
key string
lastaccessedat uint32
}

//TODO: when last accessed at of object changes
//updates the poolItem corresponding to that
type EvictionPool struct{
pool []*PoolItem //it is basically an array of pool items
keyset map[string]*PoolItem //na keyset , like the key which are present.
}

type ByIdleTime []*PoolItem

func (a ByIdleTime) len() int{
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ByIdleTime doesn't implement sort.Interface because the method must be named Len(), not len(). Without Len, sort.Sort(ByIdleTime(...)) will not compile.

Suggested change
func (a ByIdleTime) len() int{
func (a ByIdleTime) Len() int{

Copilot uses AI. Check for mistakes.
return len(a)
}

func (a ByIdleTime) Swap(i int,j int){
a[i],a[j]=a[j],a[i]
}

func (a ByIdleTime) Less(i int,j int) bool{//basically it is a comparator function which i am suing to sort the time
return getIdleTime(a[i].lastaccessedat)>getIdleTime(a[j].lastaccessedat)
}

//TODO: Make the Implementation efficient to not need repeated sorting
func (pq *EvictionPool) Push(key string,lastaccessedat uint32){
_,ok:=pq.keyset[key]
if ok{
//while pushing it into eviction pool if it already exists then we don;t have to push it again it in eveiction pool
return
}
ietm:=&PoolItem(key:key,lastaccessedat:lastaccessedat)
if len(pq.pool)<ePoolSizeMax{
//which means there is some space for adding element
//remeber eviction pool is needed to be sorted by idle time
pq.keyset[key]=items
pq.pool=append(pq.pool,item)


//Performance bottleneck
sort.Sort(ByIdleTime(pq.pool))
}else if lastaccessedat>pq.pool[len(pq.pool)-1].lastaccessedat{
//if i have no space in eviction pool but the element which i have smapled is worse than my current
//i will create space for that by removing the 1st one and adding new element by appending it
///so this way we are ensuring that our pool contains best possible candidates to be evcited
pq.pool=pq.pool[1:]
pq.keyset[key]=item
pq.pool=append(pq.pool,item)
}
Comment on lines +35 to +58
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Push contains multiple identifier/syntax issues that prevent compilation (&PoolItem(key:key,...), ietm vs item, items undefined, etc.). Clean up the composite literal and use a single consistently-named *PoolItem variable when updating keyset and appending to pool.

Copilot uses AI. Check for mistakes.
}

func (pq *EvictionPool) Pop() *PoolItem{
if len(pq.pool)==0{
return nil
}
item:=pq.pool[0]
pq.pool=pq.pool[1:]
return item
}

func newEvictionPool(size int) *EvictionPool{
return &EvictionPool{
pool: make([]*PoolItem,size)
keyset: make(map[string]*PoolItem)
}
}

var ePoolSizeMax int=16
var ePool *EvictionPool=newEvictionPool(0)


13 changes: 13 additions & 0 deletions core/expire.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,19 @@ import (
"time"
)

func hasExpired(obj *Obj) bool{
exp,ok:=expires[obj]
if !ok{
return false
}
return exp<=uint64(time.Now().UnixMilli())
}

func getExpiry(obj *Obj)(uint64,bool){
exp,ok:=expires[obj]
return exp,ok
}
Comment on lines +8 to +19
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new expires map helpers (hasExpired/getExpiry) introduce a second expiry source of truth, but the rest of this file still uses obj.ExpiresAt (which no longer exists on Obj). To avoid compile errors and inconsistent behavior, migrate the active-expiration logic in this file to use the expires map (or reintroduce ExpiresAt on Obj).

Copilot uses AI. Check for mistakes.

//Delete all the expired keys- the active way
//Sampling approach: https://redis.io/commands/expire/

Expand Down
17 changes: 15 additions & 2 deletions core/object.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,21 @@ package core
//TODO: Change ExpiresAt it to LRU Bits as handled by Redis
type Obj struct{
TypeEncoding uint8
Value interface{}
ExpiresAt int64
Value interface{} //which means we acn put literally anything,anyvalue and which would work fine
// ExpiresAt int64
/*
Earlier we use to have expireAt here, we don't need as we need LastAccessdAt
since golang doesn't support bitfield so LastAccessedAt is storing everytime the key is getting accessed
*/

/*
Redis allot 24 bits to these bits, but we will use 32 bits because
golang does not support bitfields and we need not make this super-compplicated
by merging TypeEncoding + LastAccessedAt in one 32 bit integer
but nonthelss, we can benchmarks and see how that fares
For noe, we continue with 32 bit integer to stre th LastAccessedAt
*/
LastAccessedAt uint32
}
/*
C and c++ give us ways to apply bit fields on set, means in each set i can assign a fix number of bits
Expand Down
10 changes: 10 additions & 0 deletions core/stats.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package core

var KeyspaceStat [4]map[string]int //just all global object
//eg i support 4 databases within my redis
//in redis you can have 16 databases in redis itself, by deault it goes from db0,db1,db2,...db15

//for which db,which metric,which value
func UpdatDBStat(num int,metric string,value int){
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UpdatDBStat assigns into KeyspaceStat[num] without ensuring the map is initialized; if the DB stats map is nil this will panic. Initialize KeyspaceStat[num] when nil (or provide an init() that allocates all DB maps) before assignment.

Suggested change
func UpdatDBStat(num int,metric string,value int){
func UpdatDBStat(num int,metric string,value int){
if KeyspaceStat[num] == nil {
KeyspaceStat[num] = make(map[string]int)
}

Copilot uses AI. Check for mistakes.
KeyspaceStat[num][metric]=value
}
Loading
Loading