├── .gitignore ├── README.md ├── hakuku-master ├── .cache ├── conf │ ├── users.conf │ └── servants.conf ├── src │ └── hakuku │ │ └── master │ │ ├── Constants.scala │ │ ├── Master.scala │ │ ├── Memory.scala │ │ └── MasterRequest.scala ├── .classpath ├── .project └── .settings │ └── org.eclipse.jdt.core.prefs ├── hakuku-servant ├── .cache ├── src │ └── hakuku │ │ └── servant │ │ ├── Constants.scala │ │ ├── Servant.scala │ │ ├── ServantRequest.scala │ │ └── Memory.scala ├── .classpath ├── .project └── .settings │ ├── org.eclipse.jdt.core.prefs │ └── org.scala-ide.sdt.core.prefs └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.class 3 | 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | hakuku 2 | ====== 3 | 4 | in-memory high performance distributed caching 5 | -------------------------------------------------------------------------------- /hakuku-master/.cache: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngsankha/hakuku/master/hakuku-master/.cache -------------------------------------------------------------------------------- /hakuku-servant/.cache: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngsankha/hakuku/master/hakuku-servant/.cache -------------------------------------------------------------------------------- /hakuku-master/conf/users.conf: -------------------------------------------------------------------------------- 1 | # Users authentication file 2 | # Add the details of authentication in the form username=password 3 | 4 | test=abc123 -------------------------------------------------------------------------------- /hakuku-master/conf/servants.conf: -------------------------------------------------------------------------------- 1 | # This file is the list of IP Addresses of the servants 2 | # Put the IP Address of each servant in a newline 3 | 127.0.0.1 -------------------------------------------------------------------------------- /hakuku-servant/src/hakuku/servant/Constants.scala: -------------------------------------------------------------------------------- 1 | package hakuku.servant 2 | 3 | object Constants { 4 | 5 | val PORT = 2001 6 | val MIN_MEM: Long = 1024*1024*1024*1024 7 | 8 | } -------------------------------------------------------------------------------- /hakuku-master/src/hakuku/master/Constants.scala: -------------------------------------------------------------------------------- 1 | package hakuku.master 2 | 3 | import java.util.Properties 4 | 5 | object Constants { 6 | 7 | val PORT = 2002 8 | val SERVANT_PORT = 2001 9 | var users = new Properties 10 | } -------------------------------------------------------------------------------- /hakuku-master/.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /hakuku-servant/.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /hakuku-servant/src/hakuku/servant/Servant.scala: -------------------------------------------------------------------------------- 1 | package hakuku.servant 2 | 3 | import java.net.ServerSocket 4 | import scala.concurrent.ops._ 5 | 6 | object Servant { 7 | var r = Runtime getRuntime 8 | 9 | def main(args: Array[String]) { 10 | val server = new ServerSocket(Constants PORT) 11 | println("Hakuku Servant up and running...") 12 | while(true) { 13 | val socket = server accept 14 | var request = new ServantRequest(socket) 15 | spawn { request loop} 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /hakuku-master/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | hakuku-master 4 | 5 | 6 | 7 | 8 | 9 | org.scala-ide.sdt.core.scalabuilder 10 | 11 | 12 | 13 | 14 | 15 | org.scala-ide.sdt.core.scalanature 16 | org.eclipse.jdt.core.javanature 17 | 18 | 19 | -------------------------------------------------------------------------------- /hakuku-servant/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | hakuku-servant 4 | 5 | 6 | 7 | 8 | 9 | org.scala-ide.sdt.core.scalabuilder 10 | 11 | 12 | 13 | 14 | 15 | org.scala-ide.sdt.core.scalanature 16 | org.eclipse.jdt.core.javanature 17 | 18 | 19 | -------------------------------------------------------------------------------- /hakuku-master/src/hakuku/master/Master.scala: -------------------------------------------------------------------------------- 1 | package hakuku.master 2 | 3 | import java.net.ServerSocket 4 | import java.io.FileInputStream 5 | import scala.concurrent.ops._ 6 | 7 | object Master { 8 | 9 | def main(args: Array[String]) { 10 | val server = new ServerSocket(Constants PORT) 11 | Constants.users.load(new FileInputStream("conf/users.conf")) 12 | println("Connecting to the servants...") 13 | Memory.loadIpMem 14 | println("Hakuku Master Server up and running...") 15 | while(true) { 16 | val socket = server accept 17 | var request = new MasterRequest(socket) 18 | spawn { request loop} 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /hakuku-master/.settings/org.eclipse.jdt.core.prefs: -------------------------------------------------------------------------------- 1 | #Fri May 25 13:10:36 IST 2012 2 | eclipse.preferences.version=1 3 | org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled 4 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 5 | org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve 6 | org.eclipse.jdt.core.compiler.compliance=1.6 7 | org.eclipse.jdt.core.compiler.debug.lineNumber=generate 8 | org.eclipse.jdt.core.compiler.debug.localVariable=generate 9 | org.eclipse.jdt.core.compiler.debug.sourceFile=generate 10 | org.eclipse.jdt.core.compiler.problem.assertIdentifier=error 11 | org.eclipse.jdt.core.compiler.problem.enumIdentifier=error 12 | org.eclipse.jdt.core.compiler.source=1.6 13 | -------------------------------------------------------------------------------- /hakuku-servant/.settings/org.eclipse.jdt.core.prefs: -------------------------------------------------------------------------------- 1 | #Tue May 22 20:37:59 IST 2012 2 | eclipse.preferences.version=1 3 | org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled 4 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 5 | org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve 6 | org.eclipse.jdt.core.compiler.compliance=1.6 7 | org.eclipse.jdt.core.compiler.debug.lineNumber=generate 8 | org.eclipse.jdt.core.compiler.debug.localVariable=generate 9 | org.eclipse.jdt.core.compiler.debug.sourceFile=generate 10 | org.eclipse.jdt.core.compiler.problem.assertIdentifier=error 11 | org.eclipse.jdt.core.compiler.problem.enumIdentifier=error 12 | org.eclipse.jdt.core.compiler.source=1.6 13 | -------------------------------------------------------------------------------- /hakuku-servant/src/hakuku/servant/ServantRequest.scala: -------------------------------------------------------------------------------- 1 | package hakuku.servant 2 | 3 | import java.net.Socket 4 | import java.io.InputStreamReader 5 | import java.io.BufferedReader 6 | import java.io.PrintWriter 7 | import java.util.StringTokenizer 8 | 9 | class ServantRequest(socket: Socket) { 10 | 11 | var in = new BufferedReader(new InputStreamReader(socket getInputStream)) 12 | var out = new PrintWriter(socket getOutputStream,true) 13 | 14 | def loop { 15 | out println("HELLO") 16 | while(socket isConnected) { 17 | var st = new StringTokenizer(in readLine) 18 | var res: String = null 19 | val cmd = st nextToken() 20 | if(cmd == "ADD") 21 | out println(Memory.add(st nextToken, st nextToken)) 22 | else if(cmd == "GET") 23 | out println(Memory.get(st nextToken)) 24 | else if(cmd == "MEMQUERY") 25 | out println(Servant.r.freeMemory) 26 | else 27 | out println("ERROR") 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Sankha Narayan Guria 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so, 8 | subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /hakuku-master/src/hakuku/master/Memory.scala: -------------------------------------------------------------------------------- 1 | package hakuku.master 2 | 3 | import scala.collection.mutable.HashMap 4 | import java.util.TreeMap 5 | import java.util.NoSuchElementException 6 | import java.io.BufferedReader 7 | import java.io.InputStreamReader 8 | import java.io.FileInputStream 9 | import java.net.Socket 10 | import java.io.PrintWriter 11 | 12 | object Memory { 13 | 14 | var iptable = new HashMap[String, Socket] 15 | var ipMem = new HashMap[Socket, Long] 16 | var memIp = new TreeMap[Long, Socket] 17 | 18 | def loadIpMem { 19 | var reader = new BufferedReader(new InputStreamReader(new FileInputStream("conf/servants.conf"))) 20 | var line = reader.readLine 21 | while(line != null) { 22 | if(!line.startsWith("#")) { 23 | val s = new Socket(line, Constants.SERVANT_PORT) 24 | val in = new BufferedReader(new InputStreamReader(s getInputStream)) 25 | val out = new PrintWriter(s getOutputStream, true) 26 | if(in.readLine == "HELLO") { 27 | out println("MEMQUERY") 28 | val mem: Long = java.lang.Long parseLong(in readLine) 29 | ipMem += (s -> mem) 30 | memIp.put(mem, s) 31 | } 32 | } 33 | line = reader.readLine 34 | } 35 | println(ipMem) 36 | } 37 | } -------------------------------------------------------------------------------- /hakuku-servant/src/hakuku/servant/Memory.scala: -------------------------------------------------------------------------------- 1 | package hakuku.servant 2 | 3 | import scala.collection.mutable.HashMap 4 | import java.util.TreeMap 5 | import java.util.NoSuchElementException 6 | 7 | object Memory { 8 | 9 | var mem = new HashMap[String, String] 10 | var keyTime = new HashMap[String, Long] 11 | var timeKey = new TreeMap[Long, String] 12 | 13 | def add(key: String, value: String): String = synchronized { 14 | if(Servant.r.freeMemory < Constants.MIN_MEM) { 15 | val key = timeKey.get(timeKey.lastKey) 16 | mem -= key 17 | keyTime -= key 18 | timeKey.remove(timeKey.lastKey()) 19 | System gc 20 | } 21 | val time = System.currentTimeMillis 22 | mem += (key -> value) 23 | try { 24 | val oldTime = keyTime(key) 25 | timeKey.remove(oldTime) 26 | } catch { 27 | case _: NoSuchElementException => 28 | } 29 | keyTime += (key -> time) 30 | timeKey.put(time, key) 31 | println("Added key " + key) 32 | "OK " + Servant.r.freeMemory() 33 | } 34 | 35 | def get(key: String): String = synchronized { 36 | var res: String = null 37 | try{ 38 | res = "OK " + mem(key) 39 | val oldTime = keyTime(key) 40 | timeKey.remove(oldTime) 41 | val time = System.currentTimeMillis 42 | keyTime += (key -> time) 43 | timeKey.put(time, key) 44 | } catch { 45 | case _: NoSuchElementException => res = "ERROR" 46 | } 47 | res 48 | } 49 | } -------------------------------------------------------------------------------- /hakuku-master/src/hakuku/master/MasterRequest.scala: -------------------------------------------------------------------------------- 1 | package hakuku.master 2 | 3 | import java.net.Socket 4 | import java.io.BufferedReader 5 | import java.io.InputStreamReader 6 | import java.io.PrintWriter 7 | import java.util.StringTokenizer 8 | 9 | class MasterRequest(socket: Socket) { 10 | 11 | var in = new BufferedReader(new InputStreamReader(socket getInputStream)) 12 | var out = new PrintWriter(socket getOutputStream,true) 13 | var servantIn: BufferedReader = null 14 | var servantOut: PrintWriter = null 15 | var authed = false 16 | var reply = "" 17 | 18 | def loop { 19 | out println("HELLO") 20 | while(socket isConnected) { 21 | var st = new StringTokenizer(in readLine) 22 | var res: String = null 23 | val cmd = st nextToken() 24 | if(cmd == "LOGIN") 25 | out println(auth(st nextToken, st nextToken)) 26 | else if(cmd == "ADD" && authed) 27 | out println(add(st nextToken, st nextToken)) 28 | else if(cmd == "GET" && authed) 29 | out println(get(st nextToken)) 30 | else 31 | out println("ERROR") 32 | } 33 | } 34 | 35 | def auth(username: String, password: String): String = { 36 | if(Constants.users.getProperty(username) == password) authed = true 37 | println("Login from "+username) 38 | if(authed) "OK" else "ERROR" 39 | } 40 | 41 | def add(key: String, value: String): String = { 42 | if(Memory.iptable.contains(key)) Memory.iptable -= key 43 | val s = Memory.memIp.get(Memory.memIp.lastKey) 44 | servantIn = new BufferedReader(new InputStreamReader(s getInputStream)) 45 | servantOut = new PrintWriter(s getOutputStream,true) 46 | servantOut println("ADD " + key + " " + value) 47 | reply = servantIn readLine 48 | val mem = java.lang.Long.parseLong(reply.substring(reply.lastIndexOf(' ') + 1)) 49 | Memory.memIp.remove(Memory.memIp.lastKey) 50 | Memory.ipMem += (s -> mem) 51 | Memory.memIp.put(mem, s) 52 | Memory.iptable += (key -> s) 53 | "OK" 54 | } 55 | 56 | def get(key: String): String = { 57 | if(!Memory.iptable.contains(key)) "ERROR" 58 | else { 59 | val s = Memory.iptable(key) 60 | servantIn = new BufferedReader(new InputStreamReader(s getInputStream)) 61 | servantOut = new PrintWriter(s getOutputStream,true) 62 | servantOut println("GET " + key) 63 | servantIn readLine 64 | } 65 | } 66 | } -------------------------------------------------------------------------------- /hakuku-servant/.settings/org.scala-ide.sdt.core.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | formatter.alignParameters=false 3 | formatter.alignSingleLineCaseStatements=false 4 | formatter.alignSingleLineCaseStatements.maxArrowIndent=40 5 | formatter.compactControlReadability=false 6 | formatter.compactStringConcatenation=false 7 | formatter.doubleIndentClassDeclaration=false 8 | formatter.formatXml=true 9 | formatter.indentLocalDefs=false 10 | formatter.indentPackageBlocks=true 11 | formatter.indentSpaces=2 12 | formatter.indentWithTabs=false 13 | formatter.multilineScaladocCommentsStartOnFirstLine=false 14 | formatter.placeScaladocAsterisksBeneathSecondAsterisk=false 15 | formatter.preserveDanglingCloseParenthesis=false 16 | formatter.preserveSpaceBeforeArguments=false 17 | formatter.rewriteArrowSymbols=false 18 | formatter.spaceBeforeColon=false 19 | formatter.spaceInsideBrackets=false 20 | formatter.spaceInsideParentheses=false 21 | formatter.spacesWithinPatternBinders=true 22 | organizeimports.expandcollapse=expand 23 | organizeimports.groups=java$scala$org$com 24 | organizeimports.wildcards=scalaz$scalaz.Scalaz 25 | syntaxColouring.bracket.bold=false 26 | syntaxColouring.bracket.colour=0,0,0 27 | syntaxColouring.bracket.italic=false 28 | syntaxColouring.bracket.strikethrough=false 29 | syntaxColouring.bracket.underline=false 30 | syntaxColouring.default.bold=false 31 | syntaxColouring.default.colour=0,0,0 32 | syntaxColouring.default.italic=false 33 | syntaxColouring.default.strikethrough=false 34 | syntaxColouring.default.underline=false 35 | syntaxColouring.keyword.bold=true 36 | syntaxColouring.keyword.colour=127,0,85 37 | syntaxColouring.keyword.italic=false 38 | syntaxColouring.keyword.strikethrough=false 39 | syntaxColouring.keyword.underline=false 40 | syntaxColouring.operator.bold=false 41 | syntaxColouring.operator.colour=0,0,0 42 | syntaxColouring.operator.italic=false 43 | syntaxColouring.operator.strikethrough=false 44 | syntaxColouring.operator.underline=false 45 | syntaxColouring.scaladoc.bold=false 46 | syntaxColouring.scaladoc.colour=63,95,191 47 | syntaxColouring.scaladoc.italic=false 48 | syntaxColouring.scaladoc.strikethrough=false 49 | syntaxColouring.scaladoc.underline=false 50 | syntaxColouring.singleLineComment.bold=false 51 | syntaxColouring.singleLineComment.colour=63,127,95 52 | syntaxColouring.singleLineComment.italic=false 53 | syntaxColouring.singleLineComment.strikethrough=false 54 | syntaxColouring.singleLineComment.underline=false 55 | syntaxColouring.string.bold=false 56 | syntaxColouring.string.colour=42,0,255 57 | syntaxColouring.string.italic=false 58 | syntaxColouring.string.strikethrough=false 59 | syntaxColouring.string.underline=false 60 | syntaxColouring.xml.attributeName.bold=false 61 | syntaxColouring.xml.attributeName.colour=127,0,127 62 | syntaxColouring.xml.attributeName.italic=false 63 | syntaxColouring.xml.attributeName.strikethrough=false 64 | syntaxColouring.xml.attributeName.underline=false 65 | syntaxColouring.xml.attributeValue.bold=false 66 | syntaxColouring.xml.attributeValue.colour=42,0,255 67 | syntaxColouring.xml.attributeValue.italic=true 68 | syntaxColouring.xml.attributeValue.strikethrough=false 69 | syntaxColouring.xml.attributeValue.underline=false 70 | syntaxColouring.xml.comment.bold=false 71 | syntaxColouring.xml.comment.colour=63,85,191 72 | syntaxColouring.xml.comment.italic=false 73 | syntaxColouring.xml.comment.strikethrough=false 74 | syntaxColouring.xml.comment.underline=false 75 | syntaxColouring.xml.equals.bold=false 76 | syntaxColouring.xml.equals.colour=0,0,0 77 | syntaxColouring.xml.equals.italic=false 78 | syntaxColouring.xml.equals.strikethrough=false 79 | syntaxColouring.xml.equals.underline=false 80 | syntaxColouring.xml.processingInstruction.bold=false 81 | syntaxColouring.xml.processingInstruction.colour=0,128,128 82 | syntaxColouring.xml.processingInstruction.italic=false 83 | syntaxColouring.xml.processingInstruction.strikethrough=false 84 | syntaxColouring.xml.processingInstruction.underline=false 85 | syntaxColouring.xml.tagDelimiter.bold=false 86 | syntaxColouring.xml.tagDelimiter.colour=0,128,128 87 | syntaxColouring.xml.tagDelimiter.italic=false 88 | syntaxColouring.xml.tagDelimiter.strikethrough=false 89 | syntaxColouring.xml.tagDelimiter.underline=false 90 | syntaxColouring.xml.tagName.bold=false 91 | syntaxColouring.xml.tagName.colour=63,127,127 92 | syntaxColouring.xml.tagName.italic=false 93 | syntaxColouring.xml.tagName.strikethrough=false 94 | syntaxColouring.xml.tagName.underline=false 95 | --------------------------------------------------------------------------------