<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: antsmartian</title>
    <description>The latest articles on DEV Community by antsmartian (@antsmartian).</description>
    <link>https://dev.to/antsmartian</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F426851%2F0e3b4873-5cc2-42d1-95a6-f7f556bc8450.png</url>
      <title>DEV Community: antsmartian</title>
      <link>https://dev.to/antsmartian</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/antsmartian"/>
    <language>en</language>
    <item>
      <title>Guide to create a simple concurrent database in Go.</title>
      <dc:creator>antsmartian</dc:creator>
      <pubDate>Wed, 08 Jul 2020 09:47:39 +0000</pubDate>
      <link>https://dev.to/antsmartian/creating-a-simple-concurrent-database-in-go-2l7j</link>
      <guid>https://dev.to/antsmartian/creating-a-simple-concurrent-database-in-go-2l7j</guid>
      <description>&lt;h4&gt;
  
  
  A minimal in-memory database
&lt;/h4&gt;

&lt;p&gt;Learning Golang can be quite tricky when it comes to concurrency. I'm in the process of learning Golang&lt;br&gt;
and found it bit hard to understand concurrency and locks. I created a simple in-memory database, that allow&lt;br&gt;
you to store key-value pair of type &lt;code&gt;string&lt;/code&gt;. Well all this is done for understanding mutex's and go's&lt;br&gt;
concurrency. The code of the entire database would be around &lt;code&gt;100&lt;/code&gt; lines! Simple &amp;amp; Elegant. Inspired from BoltDB source.&lt;/p&gt;

&lt;p&gt;Note: The key-value pair is stored in &lt;code&gt;map&lt;/code&gt;. I expect you all have a beginner level understanding of&lt;br&gt;
golang syntax and primitive types to understand the source-code. Full code can found here: &lt;a href="https://github.com/antsmartian/go_concurrency_tutorial"&gt;https://github.com/antsmartian/go_concurrency_tutorial&lt;/a&gt;&lt;/p&gt;
&lt;h6&gt;
  
  
  Defining the interfaces:
&lt;/h6&gt;

&lt;p&gt;We are going to store a simple key-value pair in our memory database. The idea is that our database&lt;br&gt;
is fully safe to use in concurrent environment, hence we would be using mutex to lock our data.&lt;br&gt;
We will use &lt;code&gt;map&lt;/code&gt; as our datastore, so lets go and define a simple struct that puts our requirement&lt;br&gt;
on target:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;type DB struct {
    mu sync.RWMutex
    data map[string]string
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Note: In fact we could have used files to save our data, but I'm keeping it simple.&lt;/p&gt;

&lt;p&gt;We will discuss why &lt;code&gt;mu&lt;/code&gt; is declared as &lt;code&gt;sync.RWMutex&lt;/code&gt; and understand its use case later on.&lt;/p&gt;

&lt;p&gt;Now to declare and create our &lt;code&gt;DB&lt;/code&gt;, lets go and create a simple function that does for us:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func Create() (*DB) {
    db := &amp;amp;DB{
        data : make(map[string]string),
    }
    return db
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;the above function does initialize our &lt;code&gt;data&lt;/code&gt; to be of type &lt;code&gt;[string]string&lt;/code&gt;.&lt;/p&gt;

&lt;h6&gt;
  
  
  Creating APIs.
&lt;/h6&gt;

&lt;p&gt;We will be exposing our API's which will allow the end user to put their data into the memory. Since we are&lt;br&gt;
calling this a in-memory database, we will create a struct which holds the current transaction data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;type Tx struct {
    db *DB
    writable bool
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;here, &lt;code&gt;db&lt;/code&gt; is just a reference to our low lying database and &lt;code&gt;writable&lt;/code&gt; is a bool type, which says whether&lt;br&gt;
the transaction happening is for Reading or Writing.&lt;/p&gt;

&lt;p&gt;Since all our operation of setting and getting the value is via a transaction, we will add functions to our&lt;br&gt;
&lt;code&gt;Tx&lt;/code&gt; struct:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func (tx *Tx) Set(key, value string) {
    tx.db.data[key] = value
}

func (tx *Tx) Get(key string) string {
    return tx.db.data[key]
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Nothing interesting here, a simple &lt;code&gt;Set&lt;/code&gt; and &lt;code&gt;Get&lt;/code&gt; for our underlying datastore. Now here comes the interesting&lt;br&gt;
part, since we are going to be concurrently safe, we need someway to &lt;strong&gt;protect our data&lt;/strong&gt;. Computer science&lt;br&gt;
has very old concept of &lt;strong&gt;locks&lt;/strong&gt;, which will allow us to protect our data in concurrent env.&lt;br&gt;
Well, Golang has a support for locks at the language level.&lt;/p&gt;
&lt;h6&gt;
  
  
  What are locks?
&lt;/h6&gt;

&lt;p&gt;Simply put &lt;strong&gt;locks protect a shared memory (once acquired), until the lock is released&lt;/strong&gt;. Here our shared memory is our&lt;br&gt;
&lt;code&gt;map&lt;/code&gt;. So how we are going to protect the following scenario:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;T1 -&amp;gt; Set key,value1 =&amp;gt; map
T1 -&amp;gt; Set key,value2 =&amp;gt; map
T2 -&amp;gt; Get key &amp;lt;= map
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Imagine at time T1, we are getting two request for &lt;code&gt;Set&lt;/code&gt; to our &lt;code&gt;map&lt;/code&gt;. Well, what will be the value of &lt;code&gt;key&lt;/code&gt;&lt;br&gt;
at T2, when you do a &lt;code&gt;Get&lt;/code&gt;? Yes, its confusing and hard to say and there is no correct answer. If we run&lt;br&gt;
the above pseudo code for 1000 times, we get different answer at T2 (either &lt;code&gt;value1&lt;/code&gt; or &lt;code&gt;value2&lt;/code&gt;). The reason&lt;br&gt;
for this is the underlying datastore our &lt;code&gt;map&lt;/code&gt; is &lt;strong&gt;shared&lt;/strong&gt; between concurrent or parallel process.&lt;/p&gt;

&lt;p&gt;In order to solve the issue, we need a lock.&lt;/p&gt;
&lt;h6&gt;
  
  
  Mutex In Golang
&lt;/h6&gt;

&lt;p&gt;Remember our &lt;code&gt;DB&lt;/code&gt; struct:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;type DB struct {
    mu sync.RWMutex
    data map[string]string
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;the &lt;code&gt;mu&lt;/code&gt; is of type &lt;code&gt;sync.RWMutex&lt;/code&gt;. Actually speaking we could have used &lt;code&gt;Mutex&lt;/code&gt; package, but we will use&lt;br&gt;
&lt;code&gt;RWMutex&lt;/code&gt; instead, which stands for &lt;code&gt;ReadWrite Mutex&lt;/code&gt;. A mutex does provide a &lt;code&gt;lock&lt;/code&gt; method which will&lt;br&gt;
prevent accessing the shared memory until the corresponding &lt;code&gt;unlock&lt;/code&gt; method is called. So the psudeocode,&lt;br&gt;
looks like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;lock()
    //access map
unlock()
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;with our &lt;code&gt;lock&lt;/code&gt; and &lt;code&gt;unlock&lt;/code&gt; functions in place, our previous concurrent code would be safe:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;T1 -&amp;gt; Set key,value1 =&amp;gt; map //acquires the lock
T1 -&amp;gt; Set key,value2 =&amp;gt; map //wait till the lock is released by first acquired lock
T2 -&amp;gt; Get key &amp;lt;= map  //wait till the lock is released by first acquired lock
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;As we could able to see, with &lt;code&gt;lock&lt;/code&gt; we are protecting our data! Now that's about lock in simple terms,&lt;br&gt;
what is so special about RWMutex? Well, in our simple in memory database, we want our caller of &lt;code&gt;Get&lt;/code&gt;&lt;br&gt;
not to wait i.e there could be concurrent reads without any blocks. Something like the below is totally&lt;br&gt;
acceptable and scalable solution:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;T1 -&amp;gt; Get key &amp;lt;= map
T1 -&amp;gt; Get key2 &amp;lt;= map
T1 -&amp;gt; Get key3 &amp;lt;= map
T1 -&amp;gt; Get key4 &amp;lt;= map
T1 -&amp;gt; Get key5 &amp;lt;= map
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;but there is a catch here. What happens when our callstack looks like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;T1 -&amp;gt; Get key &amp;lt;= map
T1 -&amp;gt; Get key2 &amp;lt;= map
T1 -&amp;gt; Get key3 &amp;lt;= map
T1 -&amp;gt; Get key4 &amp;lt;= map
T1 -&amp;gt; Set key4,value4 =&amp;gt; map
T1 -&amp;gt; Get key5 &amp;lt;= map
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;oh oh, &lt;code&gt;Get&lt;/code&gt;/&lt;code&gt;Set&lt;/code&gt; call for &lt;code&gt;key4&lt;/code&gt; is in &lt;strong&gt;race condition&lt;/strong&gt; and our database won't be consistent.&lt;br&gt;
What we could do? That's where RWMutex comes into picture. RWMutex has a special type of lock called&lt;br&gt;
as RLock, which eventually means Read Lock. The way Read Lock works is as follows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;There could be n number of Read Locks (without blocking other Read Lock)&lt;/li&gt;
&lt;li&gt;However, Write Lock can't be acquired until all Read Locks are released.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Example, a valid scenario:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RLock() &amp;lt;- Thread 1
RLock() &amp;lt;- Thread 2
RLock() &amp;lt;- Thread 3
RLock() &amp;lt;- Thread 4
//... read operations..
RUnlock() &amp;lt;- Thread 1
RUnlock() &amp;lt;- Thread 2
RUnlock() &amp;lt;- Thread 3
RUnlock() &amp;lt;- Thread 4
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Completely fine and note that all are running concurrently. However the below scenario:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RLock() &amp;lt;- Thread 1
RLock() &amp;lt;- Thread 2
RLock() &amp;lt;- Thread 3
RLock() &amp;lt;- Thread 4
//... some operations..
WLock() &amp;lt;- Thread 5 //blocked until all Threads call RUnlock();  can't acquire the lock, waiting..
RUnlock() &amp;lt;- Thread 1
RUnlock() &amp;lt;- Thread 2
RUnlock() &amp;lt;- Thread 3
RUnlock() &amp;lt;- Thread 4
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This is exactly, what is required for our solution. Note that, RWMutex does also has a &lt;code&gt;lock&lt;/code&gt; which is&lt;br&gt;
simply mean write lock. Now with our lock understanding, we have solved most of our problem.&lt;/p&gt;
&lt;h6&gt;
  
  
  Transaction API
&lt;/h6&gt;

&lt;p&gt;Now lets create a simple &lt;code&gt;lock&lt;/code&gt; and &lt;code&gt;unlock&lt;/code&gt; API, which internally handles RWMutex locks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func (tx *Tx) lock() {
    if tx.writable {
        tx.db.mu.Lock()
    } else {
        tx.db.mu.RLock()
    }
}

func (tx *Tx) unlock() {
    if tx.writable {
        tx.db.mu.Unlock()
    } else {
        tx.db.mu.RUnlock()
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We are checking the &lt;code&gt;tx.writable&lt;/code&gt;, if true, we are acquiring the &lt;code&gt;Lock()&lt;/code&gt; (for write)&lt;br&gt;
else &lt;code&gt;RLock()&lt;/code&gt; (for read). The same for &lt;code&gt;unlock&lt;/code&gt;&lt;/p&gt;
&lt;h6&gt;
  
  
  Database API
&lt;/h6&gt;

&lt;p&gt;Create a simple API called &lt;code&gt;managed&lt;/code&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func (db *DB) managed(writable bool, fn func(tx *Tx) error) (err error) {
    var tx *Tx
    tx, err = db.Begin(writable)
    if err != nil {
        return
    }

    defer func() {
        if writable {
            fmt.Println("Write Unlocking...")
            tx.unlock()
        } else {
            fmt.Println("Read Unlocking...")
            tx.unlock()
        }
    }()

    err = fn(tx)
    return
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;it begins the transaction and has a &lt;code&gt;defer&lt;/code&gt; func which make sure we call our transaction API's&lt;br&gt;
&lt;code&gt;unlock&lt;/code&gt; function when our function returns. &lt;code&gt;Begin&lt;/code&gt; function does acquire the lock for us:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func (db *DB) Begin(writable bool) (*Tx,error) {
    tx := &amp;amp;Tx {
        db : db,
        writable: writable,
    }
    tx.lock()

    return tx,nil
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;(either in read or write mode) based on &lt;code&gt;writable&lt;/code&gt; and calls transaction's &lt;code&gt;lock&lt;/code&gt;.&lt;/p&gt;

&lt;h6&gt;
  
  
  Helper API
&lt;/h6&gt;

&lt;p&gt;Now finally, we will provide a helper API for our database calls:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func (db * DB) View(fn func (tx *Tx) error) error {
    return db.managed(false, fn)
}

func (db * DB) Update(fn func (tx *Tx) error) error {
    return db.managed(true, fn)
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;which in turn calls &lt;code&gt;managed&lt;/code&gt; with appropriate parameter for &lt;code&gt;View&lt;/code&gt; &amp;amp; &lt;code&gt;Update&lt;/code&gt;.&lt;/p&gt;

&lt;h5&gt;
  
  
  Using our simple database:
&lt;/h5&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func main() {

    db := Create()

    go db.Update(func(tx *Tx) error {
        tx.Set("mykey", "go")
        tx.Set("mykey2", "is")
        tx.Set("mykey3", "awesome")
        return nil
    })

    go db.View(func(tx *Tx) error {
        fmt.Println(tx.Get("mykey3"))
        return nil
    })

    time.Sleep(200000) //to see the output
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Here we are creating two goroutines which will concurrently/parallel based on your machine core configs.&lt;br&gt;
Try spawning many goroutines and see it in action our simple concurrent safe in memory database.&lt;/p&gt;

&lt;p&gt;Remember that, all operation inside either &lt;code&gt;Update&lt;/code&gt; or &lt;code&gt;View&lt;/code&gt; is concurrently safe.&lt;/p&gt;

&lt;p&gt;The entire source code is at the file &lt;code&gt;simple_db.go&lt;/code&gt;.&lt;/p&gt;

&lt;h6&gt;
  
  
  Disclaimer
&lt;/h6&gt;

&lt;p&gt;This database is only for fun, not fit for production use cases or replacing the concurrent sync.Map!&lt;br&gt;
Have a golang day!&lt;/p&gt;

&lt;h6&gt;
  
  
  Help
&lt;/h6&gt;

&lt;p&gt;If you find any grammatical mistake, please raise a PR, would be happy to accept.&lt;/p&gt;

</description>
      <category>go</category>
      <category>tutorial</category>
      <category>database</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
