Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
K
key_value-server
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
amir.yosef
key_value-server
Commits
c6c0e76b
Commit
c6c0e76b
authored
Aug 23, 2024
by
amir.yosef
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Optimizing LRU policy
parent
35baeb5b
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
237 additions
and
40 deletions
+237
-40
misc.xml
.idea/misc.xml
+1
-1
Main.java
src/Main.java
+45
-15
LRUCachePolicy.java
src/storage/LRUCachePolicy.java
+191
-24
No files found.
.idea/misc.xml
View file @
c6c0e76b
<?xml version="1.0" encoding="UTF-8"?>
<project
version=
"4"
>
<component
name=
"JsBuildToolPackageJson"
sorting=
"DEFINITION_ORDER"
/>
<component
name=
"ProjectRootManager"
version=
"2"
languageLevel=
"JDK_22"
default=
"true"
project-jdk-name=
"
ib
m-22"
project-jdk-type=
"JavaSDK"
>
<component
name=
"ProjectRootManager"
version=
"2"
languageLevel=
"JDK_22"
default=
"true"
project-jdk-name=
"
graalv
m-22"
project-jdk-type=
"JavaSDK"
>
<output
url=
"file://$PROJECT_DIR$/out"
/>
</component>
</project>
\ No newline at end of file
src/Main.java
View file @
c6c0e76b
...
...
@@ -4,35 +4,65 @@ import server.Server;
import
server.ServerBuilder
;
import
java.io.IOException
;
import
java.util.Arrays
;
import
java.util.List
;
import
java.util.concurrent.Callable
;
import
java.util.concurrent.ExecutorService
;
import
java.util.concurrent.Executors
;
import
java.util.concurrent.TimeUnit
;
public
class
Main
{
public
static
ExecutorService
executor
;
public
static
void
main
(
String
[]
args
)
{
executor
=
Executors
.
newFixedThreadPool
(
2
);
int
availableProcessors
=
Runtime
.
getRuntime
().
availableProcessors
();
executor
=
Executors
.
newFixedThreadPool
(
availableProcessors
);
Director
director
=
new
Director
();
ServerBuilder
builder
=
new
ServerBuilder
(
args
);
// try (Server server = builder.build()) {
// server.start();
// }
// builder.setPort(16379);
// executor.submit(() -> {
// ReplicaConnectionService service = null;
// try {
// director.buildReplica(builder, service);
// } catch (IOException e) {
// throw new RuntimeException(e);
// }
// });
executor
.
submit
(()
->
{
List
<
Callable
<
Void
>>
tasks
=
Arrays
.
asList
(
()
->
{
try
{
ReplicaConnectionService
service
=
null
;
director
.
buildReplica
(
builder
,
service
);
}
catch
(
IOException
e
)
{
throw
new
RuntimeException
(
e
);
}
return
null
;
},
()
->
{
try
(
Server
server
=
builder
.
build
())
{
server
.
start
();
}
catch
(
IOException
e
)
{
throw
new
RuntimeException
(
e
);
}
return
null
;
}
);
try
{
executor
.
invokeAll
(
tasks
);
}
catch
(
InterruptedException
e
)
{
Thread
.
currentThread
().
interrupt
();
throw
new
RuntimeException
(
"Task execution was interrupted"
,
e
);
}
finally
{
shutdownExecutor
(
executor
);
}
}
private
static
void
shutdownExecutor
(
ExecutorService
executor
)
{
executor
.
shutdown
();
try
{
if
(!
executor
.
awaitTermination
(
60
,
TimeUnit
.
SECONDS
))
{
executor
.
shutdownNow
();
if
(!
executor
.
awaitTermination
(
60
,
TimeUnit
.
SECONDS
))
{
System
.
err
.
println
(
"Executor did not terminate"
);
}
}
}
catch
(
InterruptedException
e
)
{
executor
.
shutdownNow
();
Thread
.
currentThread
().
interrupt
();
}
}
}
\ No newline at end of file
}
src/storage/LRUCachePolicy.java
View file @
c6c0e76b
package
storage
;
import
java.util.Map
;
import
java.util.Queue
;
import
java.util.concurrent.ConcurrentHashMap
;
import
java.util.concurrent.ConcurrentLinkedQueue
;
import
java.util.concurrent.atomic.AtomicInteger
;
import
java.util.concurrent.atomic.AtomicReference
;
import
java.util.concurrent.locks.StampedLock
;
public
class
LRUCachePolicy
<
K
,
V
>
implements
CachePolicy
<
K
,
V
>
{
private
final
int
maxCapacity
;
private
final
int
threshold
;
private
final
Map
<
K
,
V
>
cacheMap
;
private
final
Queue
<
K
>
accessOrder
;
private
final
Map
<
K
,
Node
<
K
,
V
>>
cacheMap
;
private
final
StampedLock
lock
;
private
final
AtomicInteger
size
;
private
final
AtomicReference
<
Node
<
K
,
V
>>
head
;
private
final
AtomicReference
<
Node
<
K
,
V
>>
tail
;
public
LRUCachePolicy
(
int
maxCapacity
,
int
threshold
)
{
this
.
maxCapacity
=
maxCapacity
;
this
.
threshold
=
threshold
;
this
.
cacheMap
=
new
ConcurrentHashMap
<>(
maxCapacity
);
this
.
lock
=
new
StampedLock
();
this
.
size
=
new
AtomicInteger
(
0
);
this
.
head
=
new
AtomicReference
<>(
null
);
this
.
tail
=
new
AtomicReference
<>(
null
);
RdbFileReader
<
String
,
String
>
reader
=
new
RdbFileReader
<>();
cacheMap
.
putAll
((
Map
<?
extends
K
,
?
extends
V
>)
reader
.
readFile
());
this
.
threshold
=
threshold
;
this
.
accessOrder
=
new
ConcurrentLinkedQueue
<>();
Map
<?
extends
K
,
?
extends
V
>
initialData
=
(
Map
<?
extends
K
,
?
extends
V
>)
reader
.
readFile
();
for
(
Map
.
Entry
<?
extends
K
,
?
extends
V
>
entry
:
initialData
.
entrySet
())
{
add
(
entry
.
getKey
(),
entry
.
getValue
());
}
}
@Override
public
void
add
(
K
key
,
V
value
)
{
cacheMap
.
put
(
key
,
value
);
updateAccessOrder
(
key
);
Node
<
K
,
V
>
newNode
=
new
Node
<>(
key
,
value
);
Node
<
K
,
V
>
oldNode
=
cacheMap
.
put
(
key
,
newNode
);
if
(
oldNode
==
null
)
{
if
(
size
.
incrementAndGet
()
>
maxCapacity
)
{
evictLeastRecentlyUsed
();
}
addToFrontOptimistic
(
newNode
);
}
else
{
oldNode
.
setValue
(
value
);
moveToFrontOptimistic
(
oldNode
);
}
}
@Override
public
V
retrieve
(
K
key
)
{
V
value
=
cacheMap
.
get
(
key
);
if
(
value
!=
null
)
{
updateAccessOrder
(
key
);
Node
<
K
,
V
>
node
=
cacheMap
.
get
(
key
);
if
(
node
!=
null
)
{
moveToFrontOptimistic
(
node
);
return
node
.
value
;
}
return
value
;
return
null
;
}
@Override
public
void
delete
(
K
key
)
{
cacheMap
.
remove
(
key
);
accessOrder
.
remove
(
key
);
Node
<
K
,
V
>
node
=
cacheMap
.
remove
(
key
);
if
(
node
!=
null
)
{
removeNodeOptimistic
(
node
);
size
.
decrementAndGet
();
}
}
@Override
public
void
runMaintenance
()
{
while
(
cacheMap
.
entrySet
().
size
()
>
threshold
)
{
while
(
size
.
get
()
>
threshold
)
{
evictLeastRecentlyUsed
();
}
}
private
void
updateAccessOrder
(
K
key
)
{
accessOrder
.
remove
(
key
);
accessOrder
.
offer
(
key
);
private
void
addToFrontOptimistic
(
Node
<
K
,
V
>
node
)
{
long
stamp
=
lock
.
tryOptimisticRead
();
Node
<
K
,
V
>
oldHead
=
head
.
get
();
node
.
next
=
oldHead
;
if
(!
lock
.
validate
(
stamp
))
{
stamp
=
lock
.
writeLock
();
try
{
oldHead
=
head
.
get
();
node
.
next
=
oldHead
;
if
(
oldHead
!=
null
)
{
oldHead
.
prev
=
node
;
}
else
{
tail
.
set
(
node
);
}
head
.
set
(
node
);
}
finally
{
lock
.
unlockWrite
(
stamp
);
}
}
else
{
if
(
oldHead
!=
null
)
{
oldHead
.
prev
=
node
;
}
else
{
tail
.
set
(
node
);
}
head
.
set
(
node
);
}
}
private
void
moveToFrontOptimistic
(
Node
<
K
,
V
>
node
)
{
long
stamp
=
lock
.
tryOptimisticRead
();
if
(
node
!=
head
.
get
())
{
Node
<
K
,
V
>
oldHead
=
head
.
get
();
Node
<
K
,
V
>
oldTail
=
tail
.
get
();
if
(!
lock
.
validate
(
stamp
))
{
stamp
=
lock
.
writeLock
();
try
{
if
(
node
!=
head
.
get
())
{
if
(
node
.
prev
!=
null
)
{
node
.
prev
.
next
=
node
.
next
;
}
if
(
node
.
next
!=
null
)
{
node
.
next
.
prev
=
node
.
prev
;
}
else
{
tail
.
set
(
node
.
prev
);
}
oldHead
=
head
.
get
();
node
.
next
=
oldHead
;
node
.
prev
=
null
;
if
(
oldHead
!=
null
)
{
oldHead
.
prev
=
node
;
}
head
.
set
(
node
);
if
(
tail
.
get
()
==
null
)
{
tail
.
set
(
node
);
}
}
}
finally
{
lock
.
unlockWrite
(
stamp
);
}
}
else
{
if
(
node
.
prev
!=
null
)
{
node
.
prev
.
next
=
node
.
next
;
}
if
(
node
.
next
!=
null
)
{
node
.
next
.
prev
=
node
.
prev
;
}
else
if
(
node
==
oldTail
)
{
tail
.
compareAndSet
(
node
,
node
.
prev
);
}
node
.
next
=
oldHead
;
node
.
prev
=
null
;
if
(
oldHead
!=
null
)
{
oldHead
.
prev
=
node
;
}
head
.
compareAndSet
(
oldHead
,
node
);
}
}
}
private
void
removeNodeOptimistic
(
Node
<
K
,
V
>
node
)
{
long
stamp
=
lock
.
tryOptimisticRead
();
Node
<
K
,
V
>
prevNode
=
node
.
prev
;
Node
<
K
,
V
>
nextNode
=
node
.
next
;
if
(!
lock
.
validate
(
stamp
))
{
stamp
=
lock
.
writeLock
();
try
{
if
(
node
.
prev
!=
null
)
{
node
.
prev
.
next
=
node
.
next
;
}
else
{
head
.
set
(
node
.
next
);
}
if
(
node
.
next
!=
null
)
{
node
.
next
.
prev
=
node
.
prev
;
}
else
{
tail
.
set
(
node
.
prev
);
}
}
finally
{
lock
.
unlockWrite
(
stamp
);
}
}
else
{
if
(
prevNode
!=
null
)
{
prevNode
.
next
=
nextNode
;
}
else
{
head
.
compareAndSet
(
node
,
nextNode
);
}
if
(
nextNode
!=
null
)
{
nextNode
.
prev
=
prevNode
;
}
else
{
tail
.
compareAndSet
(
node
,
prevNode
);
}
}
}
private
void
evictLeastRecentlyUsed
()
{
while
(!
accessOrder
.
isEmpty
())
{
K
leastRecentKey
=
accessOrder
.
poll
();
if
(
cacheMap
.
containsKey
(
leastRecentKey
))
{
delete
(
leastRecentKey
);
break
;
Node
<
K
,
V
>
nodeToEvict
;
long
stamp
=
lock
.
writeLock
();
try
{
nodeToEvict
=
tail
.
get
();
if
(
nodeToEvict
!=
null
)
{
Node
<
K
,
V
>
newTail
=
nodeToEvict
.
prev
;
tail
.
set
(
newTail
);
if
(
newTail
!=
null
)
{
newTail
.
next
=
null
;
}
else
{
head
.
set
(
null
);
}
}
}
finally
{
lock
.
unlockWrite
(
stamp
);
}
if
(
nodeToEvict
!=
null
)
{
cacheMap
.
remove
(
nodeToEvict
.
key
);
size
.
decrementAndGet
();
}
}
private
static
class
Node
<
K
,
V
>
{
final
K
key
;
volatile
V
value
;
volatile
Node
<
K
,
V
>
prev
;
volatile
Node
<
K
,
V
>
next
;
Node
(
K
key
,
V
value
)
{
this
.
key
=
key
;
this
.
value
=
value
;
}
void
setValue
(
V
value
)
{
this
.
value
=
value
;
}
}
}
\ No newline at end of file
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment