OVO Key/Value Storage
Yet another Key/Value Storage ?
I started developing OVO, my own implementation of a Key/Value Storage, six month ago because I wanted to test developing a project using the Go language. In the past, I had already worked on a similar project, GetCache, but it is developed with .Net framework.I don't think that the world lacks the Key/Value Storage, but for me it is treated to a great gym to train my Go. I have realized that this project with the Go language is extraordinarily simple and so I would like to share this experience.
What is OVO
Initially, the project aimed to create a distributed cache, like Memcached, that would allow to store objects and JSON structures quickly by spreading the load across multiple servers, and could run on any kind of Linux, Windows, iOS machine. Each cached object can have a time-to-live property (TTL) set so that the removal is done automatically when it expires.Then I realized that I needed to get a high reliability service introducing a mechanism of data replication: I decided that I would use asynchronous replication of data. Cluster nodes would communicate using RPC and data replication commands are queued on Go channels, that allows me to manage the communications between processes easily. I did not want to introduce differences between nodes, there are no Masters and Slaves nodes, they are all Peers.
I finally added some typical features of Key/Value Storage systems expanding the possibilities of OVO:
- Update a value if it is not changed
- Delete a value if it is not changed
- Get and remove
- Manage atomic counters
For example the operation "Delete a value if it is not changed" was initially developed in this way:
// Delete an item if the value is not changed.
func (coll *InMemoryCollection) DeleteValueIfEqual(obj *storage.MetaDataObj) bool {
retChan := make(chan bool)
defer close(retChan)
coll.commands <- func() {
if ret, ok := coll.storage[obj.Key]; ok {
if bytes.Equal(ret.Data, obj.Data) {
delete(coll.storage, obj.Key)
retChan <- true
} else {
retChan <- false // values are not equal
}
} else {
retChan <- true // already deleted
}
}
return <-retChan //wait for result
}
I wanted to see what's the performance difference between an implementation that adopts only gorutines and channels and another that is using the Mutex.
// Delete an item if the value is not changed.
The use of a Mutex appears on average two and a half times faster than the use of gorutines and channels. This large difference has led me to choose the latter.func (coll *InMemoryMutexCollection) DeleteValueIfEqual(obj *storage.MetaDataObj) bool {
coll.Lock()
defer coll.Unlock()
if ret, ok := coll.storage[obj.Key]; ok {
if bytes.Equal(ret.Data, obj.Data) {
delete(coll.storage, obj.Key)
return true
} else {
return false // values are not equal
}
} else {
return true
}
}
Even if the first method is slower, an interesting result that has adopting routines and channels is the linear response of the system. Performing tests with 10000, 100000, 1000000 gorutines we get a linear growth in response times and this honor goes to the creators of Go.
Client libraries
The OVO server is accessible through RESTful API so that anyone can develop client libraries for all the languages and platforms.Until now I have been developed clients in the languages Go and C#, while the Java client is in development.
The client libraries must implement the sharding mechanism by which the data are distributed on the nodes of a OVO cluster. The distribution of data on the nodes takes place by means of a deterministic calculation of the hashcode of the key.
The cluster of OVO nodes partitions the range of hashcode using a simple distribution policy.
What I appreciated all this
Many of the aspects of the programming language Go make it easy and pleasant the realization of distributed applications:- manage concurrent accesses easy
- quick implementation of RESTful APIs midleware
- RPC communications
- make simple what appears complex
References
OVO server and clients are all open source software and they are under MIT license.
https://github.com/maxzerbini/ovo
OVO Go client library repository
https://github.com/maxzerbini/ovoclient
OVO .Net client library repository
https://github.com/maxzerbini/ovodotnet
Commenti
Posta un commento