Last active
May 24, 2018 18:56
-
-
Save Mardiniii/9886621bbd6f31c16b64d7424ec844a3 to your computer and use it in GitHub Desktop.
1st approach to solve data replication problem using Ruby.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'minitest/autorun' | |
# Problem | |
# https://softwareengineering.stackexchange.com/questions/153806/conflict-resolution-for-two-way-sync | |
# interface DB { | |
# String Get(String) | |
# void Set(String, String) | |
# []String Keys() | |
# | |
# // we can add extra methods as we sit fit | |
# } | |
# | |
# We want to implement the replicate method | |
# | |
# db1.set("a", "a") | |
# db2.set("b", "b") | |
# replicate(db1, db2) | |
# assert db2.get("a") == "a" | |
# assert db1.get("b") == "b" | |
class Database | |
attr_accessor :records | |
attr_reader :logs | |
def initialize | |
@records = {} | |
@logs = {} # key, last_set | |
end | |
def get(key) | |
@records[key] | |
end | |
def set(key, value) | |
@records[key] = value | |
@logs[key] = Time.now | |
end | |
def size | |
@records.count | |
end | |
def keys | |
@records.keys | |
end | |
end | |
def replicate(db1, db2) | |
db = Database.new | |
db1_records = db1.records | |
db2_records = db2.records | |
db1_keys = db1.records.keys | |
db2_keys = db2.records.keys | |
db1_records.each do |k, v| | |
if db2_records.keys.include?(k) | |
value = db1.logs[k] > db2.logs[k] ? v : db2.get(k) | |
db.set(k, value) | |
else | |
db.set(k, v) | |
end | |
end | |
db2_records.each do |k, v| | |
unless db1_records.keys.include?(k) | |
db.set(k, v) | |
end | |
end | |
db1.records = db.records | |
db2.records = db.records | |
end | |
# Test Database class | |
class TestDatabase < Minitest::Test | |
def setup | |
@db = Database.new | |
end | |
def test_that_database_start_with_empty_records | |
assert_equal 0, @db.size, "Database size is not 0" | |
end | |
def test_that_database_can_set_and_get_key | |
@db.set("foo", "bar") | |
assert_equal "bar", @db.records["foo"], "Value wasn't stored" | |
assert_equal "bar", @db.get("foo"), "Value wasn't fetched" | |
assert_equal 1, @db.size, "Database size is incorrect" | |
end | |
def test_that_database_returns_nil_for_non_existing_key | |
assert_nil @db.get("foo"), "Not nil value returned for non existing key" | |
end | |
def test_that_database_keys_return_keys | |
@db.set("hi", "bye") | |
@db.set("salt", "sugar") | |
@db.set("black", "white") | |
assert_equal ["hi", "salt", "black"], @db.keys, "Wrong existing keys returned" | |
end | |
end | |
# Test replicate method | |
class TestReplicate < Minitest::Test | |
def setup | |
@db1 = Database.new | |
@db2 = Database.new | |
@db1.set("a", "a") | |
@db2.set("b", "b") | |
end | |
def test_that_database_are_replicated_without_conflicts | |
replicate(@db1, @db2) | |
assert_equal "b", @db1.get("b"), "Database one is not replicated correctly" | |
assert_equal "a", @db2.get("a"), "Database two is not replicated correctly" | |
end | |
def test_that_database_are_replicated_with_conflicts | |
@db1.set("c", "d") | |
@db2.set("c", "f") | |
replicate(@db1, @db2) | |
assert_equal "f", @db1.get("c"), "Database one is not consistent" | |
assert_equal "f", @db2.get("c"), "Database two is not consistent" | |
end | |
def test_that_database_are_replicated_with_conflicts_and_time_stamp_checking | |
@db1.set("c", "d") | |
@db2.set("c", "f") | |
@db1.set("c", "x") | |
replicate(@db1, @db2) | |
assert_equal "x", @db1.get("c"), "Database one is not consistent" | |
assert_equal "x", @db2.get("c"), "Database two is not consistent" | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment