Atomix isn't just a simple key-value store. The runtime API supports common data structures that can be used by applications to store and share state. Lists, maps, sets, and purpose-built data structures are provided to enable a variety of use cases.
Primitives for solving common distributed systems problems like leader election, distributed locking, scheduling, and more. Atomix distributed coordination primitives enable developers to implement distributed systems solutions without distributed systems expertise.
The Atomix runtime is built on gRPC, enabling SDKs in a variety of different languages. Applications can be written in your language of choice, and containers or services can share state across languages.
The Atomix runtime provides a unified API across numerous databases and protocols, decoupling application code from data stores to enable applications to be developed independent of the underlying architecture.
Atomix provides both custom distributed systems protocols and database proxies. Data storage is driven by configuration, so Atomix-enabled applications can choose their own data stores and even swap between data stores without changing a single line of code.
Atomix is built from the ground up for Kubernetes. Data stores and applications are managed via custom resources, providing seamless integration with Kubernetes tools like kubectl and Helm.
Primitives are the building blocks of distributed application in Atomix. Choose from a variety of different data structures to store application state or share state across pod or services. Distributed coordination primitives enable safe interaction with other nodes and services within the Kubernetes cluster. Use the primitives that are right for your use case.
// Build the counter
counter, err := atomix.Counter("my-counter").
Get(context.Background())
if err != nil {
...
}
// Increment the counter
count, err = counter.Increment(context.Background())
if err != nil {
...
}
// Get the counter value
value, err = counter.Get(context.Background())
if err != nil {
...
}
// Build the leader election
election, err := atomix.LeaderElection("my-counter").
Get(context.Background())
if err != nil {
...
}
// Get the current leadership term
term, err := election.GetTerm(context.Background())
if err != nil {
...
}
// Enter the election
term, err = election.Enter(context.Background())
if err != nil {
...
}
// Get a string list
l, err := atomix.List[string]("my-list").
Codec(generic.Scalar[string]()).
Get(context.Background())
if err != nil {
...
}
// Append a value to the list
err = l.Append(context.Background(), "Hello world!")
if err != nil {
...
}
// Iterate through the items in the list
items, err := l.Items(context.Background())
if err != nil {
...
}
for {
item, err := items.Next()
if err == io.EOF {
break
}
...
}
// Build the lock
l, err := atomix.Lock("my-lock").
Get(context.Background())
if err != nil {
...
}
// Acquire the lock
if err := l.Lock(context.Background()); err != nil {
...
}
// Do stuff...
// Release the lock
if err := l.Unlock(context.Background()); err != nil {
...
}
// Get a string:string map
m, err := atomix.Map[string, string]("my-map").
Codec(generic.Scalar[string]()).
Get(context.Background())
if err != nil {
...
}
// Write to the map
_, err = m.Put(context.Background(), "foo", "bar")
if err != nil {
...
}
// Read from the map
entry, err := m.Get(context.Background(), "foo")
if err != nil {
...
}
// Get a string:string multimap
m, err := atomix.MultiMap[string, string]("my-multimap").
Codec(generic.Scalar[string]()).
Get(context.Background())
if err != nil {
...
}
// Write to the multimap
_, err = m.Put(context.Background(), "foo", "bar")
if err != nil {
...
}
// Write to the multimap
_, err = m.Put(context.Background(), "foo", "baz")
if err != nil {
...
}
// Read from the multimap
values, err := m.Get(context.Background(), "foo")
if err != nil {
...
}
// Get a string set
l, err := atomix.Set[string]("my-set").
Codec(generic.Scalar[string]()).
Get(context.Background())
if err != nil {
...
}
// Add a value to the set
err = l.Add(context.Background(), "Hello world!")
if err != nil {
...
}
// Check if the set contains the added value
if ok, err := l.Contains(context.Background()); err != nil {
...
} else if ok {
...
}
// Get a string value
v, err := atomix.Value[string]("my-value").
Codec(generic.Scalar[string]()).
Get(context.Background())
if err != nil {
...
}
// Set the value
err = v.Set(context.Background(), "Hello world!")
if err != nil {
...
}
// Get the value
value, err := v.Get(context.Background())
if err != nil {
...
}
// Get the "foo" counter
AtomicCounter counter = AtomicCounter.builder()
.withName("foo")
.build();
// Increment the counter
long value = counter.incrementAndGet();
// Get the counter value
value = counter.get();
// Get the "foo" map
Map<String, String> map = AtomicMap.builder()
.withName("foo")
.withSerializer(mySerializer)
.build();
// Write to the map
map.put("foo", "bar");
// Read from the map
Entry<string, string> entry = map.get("foo");
The Atomix runtime API acts as an abstraction layer for data stores, decoupling your application’s code from specific databases and protocols, and enabling developers to choose the tools they like without concern for vendor lock-in.
apiVersion: consensus.atomix.io/v1beta1
kind: ConsensusStore
metadata:
name: my-consensus-store
spec:
replicas: 3
groups: 30
Coming soon!
Coming soon!
apiVersion: podmemory.atomix.io/v1beta1
kind: PodMemoryStore
Coming soon!
apiVersion: sharedmemory.atomix.io/v1beta1
kind: SharedMemoryStore
Atomix is designed around the same principles of cloud-native architecture that are familiar to Kubernetes developers. Storage is defined for each application via the StorageProfile
custom resource. A tag-based routing system enables applications to use multiple data stores while allowing application developers and their users to optimize applications and the atoms within them without having to change a single line of code.
apiVersion: atomix.io/v3beta3
kind: StorageProfile
metadata:
name: my-application-profile
spec:
bindings:
- store:
name: consensus-store
tags:
- my-app
- persistent
- store:
name: cache-store
tags:
- my-app
- volatile
We're seeking contributors for new primitives, SDKs, and data stores. Do you know a language Atomix doesn't support yet? Is there a database you want to integrate with Atomix? Do you have a use case for a new type of primitive? Visit the contributor guide to get started!
Together, we can make distributed systems accessible for everyone!