Skip to content
This repository has been archived by the owner on Oct 2, 2019. It is now read-only.

Commit

Permalink
refactor: Use an abstraction layer instead of Hash
Browse files Browse the repository at this point in the history
Instead of using Hash by its own, use now an abstraction, which may
allow more flexibility in the future.

For now, as the benchmark below shows, Memory has almost the same
performance as Hash but it saves a memory (considering it is a new
abstraction this is awesome).

Benchmark:
    hash write  59.87M (±11.77%)        fastest
  memory write  59.63M (±12.64%)  1.00× slower
     hash read  81.25M (±10.24%)        fastest
   memory read  75.94M (±10.16%)  1.07x slower

Inspired by: https://github.com/kostya/memory_cache/blob/master/src/memory_cache.cr
  • Loading branch information
marceloboeira committed Jul 13, 2016
1 parent 98e4688 commit d6d472a
Show file tree
Hide file tree
Showing 5 changed files with 145 additions and 16 deletions.
29 changes: 29 additions & 0 deletions benchmarks/memory_vs_hash.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
require "../src/bojack/memory"
require "benchmark"

hash = {} of String => String
memory = BoJack::Memory(String, String).new

100_000.times do |i|
hash["key#{i}"] = "value#{i}"
memory.write("key#{i}", "value#{i}")
end

Benchmark.ips do |b|
b.report("hash write") do
hash["foo"] = "bar"
end
b.report("memory write") do
memory.write("foo", "bar")
end
end

Benchmark.ips do |b|
b.report("hash read") do
hash["foo"]?
end

b.report("memory read") do
memory.read("foo")
end
end
59 changes: 59 additions & 0 deletions spec/bojack/memory_spec.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
require "../spec_helper"
require "../src/bojack/memory"

memory = BoJack::Memory(String, String).new

module Spec
before_each do
memory.write("diane", "nguyen")
memory.write("todd", "chavez")
end
end

describe BoJack::Memory do
context "when reading" do
context "a valid key" do
it "reads the value from memory" do
memory.read("diane").should eq("nguyen")
end
end

context "an invalid key" do
it "raises invalid key error" do
expect_raises {
memory.read("invalid")
}
end
end
end

context "when writing" do
it "writes the key, value on the storage" do
value = memory.write("princess", "carolyn")

value.should eq("carolyn")
memory.read("princess").should eq("carolyn")
end
end

context "when deleting" do
context "a valid key" do
it "deletes the key from the memory" do
value = memory.delete("princess")

value.should eq("carolyn")
expect_raises {
memory.read("princess")
}
end
end

context "an invalid key" do
it "raises an error" do
expect_raises {
memory.delete("princess")
}
end
end
end
end
14 changes: 7 additions & 7 deletions spec/bojack/server_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ describe BoJack::Server do

TCPSocket.open("localhost", 5000) do |socket|
describe "ping" do
it "return pong" do
it "returns pong" do
socket.puts("ping")
buffer = socket.gets

Expand All @@ -16,7 +16,7 @@ describe BoJack::Server do
end

describe "set" do
it "set key with value" do
it "sets key with value" do
socket.puts("set bo jack")
buffer = socket.gets

Expand All @@ -26,7 +26,7 @@ describe BoJack::Server do

describe "get" do
context "with a valid key" do
it "return the key value" do
it "returns the key value" do
socket.puts("get bo")
buffer = socket.gets

Expand All @@ -35,7 +35,7 @@ describe BoJack::Server do
end

context "with an invalid key" do
it "return proper error message" do
it "returns proper error message" do
socket.puts("get bar")
buffer = socket.gets

Expand All @@ -46,7 +46,7 @@ describe BoJack::Server do

describe "delete" do
context "with a valid key" do
it "return the key value" do
it "returns the key value" do
socket.puts("delete bo")
buffer = socket.gets

Expand All @@ -55,7 +55,7 @@ describe BoJack::Server do
end

context "with an invalid key" do
it "return proper error message" do
it "returns proper error message" do
socket.puts("delete bar")
buffer = socket.gets

Expand All @@ -65,7 +65,7 @@ describe BoJack::Server do
end

describe "invalid command" do
it "return proper error message" do
it "returns proper error message" do
socket.puts("jack")
buffer = socket.gets

Expand Down
40 changes: 40 additions & 0 deletions src/bojack/memory.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
module BoJack
class Memory(K, V)
struct Entry(V)
getter value
def initialize(@value : V); end
end

def initialize
@cache = {} of K => Entry(V)
end

def write(key : K, value : V) : V
@cache[key] = Entry.new(value)

value
end

def read(key : K) : V?
if entry = @cache[key]?
entry.value
else
raise "#{key.to_s} is not a valid key"
end
end

def delete(key : K) : V?
if entry = @cache.delete(key)
entry.value
else
raise "#{key.to_s} is not a valid key"
end
end

private def read_entry(key : K) : Entry(V)?
if entry = @cache[key]?
entry
end
end
end
end
19 changes: 10 additions & 9 deletions src/bojack/server.cr
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
require "socket"
require "./memory"

module BoJack
module Server
def self.start(hostname = "127.0.0.1", port = 5000)
server = TCPServer.new(hostname, port)
server.recv_buffer_size = 4096
data = Hash(String, String).new
memory = BoJack::Memory(String, String).new

loop do
socket = server.accept
Expand All @@ -22,25 +23,25 @@ module BoJack
key = request[1]
value = request[2]

data[key] = value
memory.write(key, value)

socket.puts(data[key])
socket.puts(value)
elsif command == "get"
key = request[1]

if data[key]?
value = data[key]
begin
value = memory.read(key)
socket.puts(value)
else
rescue
socket.puts("error: '#{key}' is not a valid key")
end
elsif command == "delete"
key = request[1]

if data[key]?
value = data.delete(key)
begin
value = memory.delete(key)
socket.puts(value)
else
rescue
socket.puts("error: '#{key}' is not a valid key")
end
elsif command == "close"
Expand Down

0 comments on commit d6d472a

Please sign in to comment.