Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}
Refcard #287

RavenDB 4.0

RavenDB is a mature, multi-platform, NoSQL document-oriented database, safe by default and optimized for efficiency. It is easy to learn and use with languages like C#, Java, Python, NodeJS, and C++. Learn the basics of creating your first databases and clusters in RavenDB with this Refcard.

454

Brought to you by

RavenDB
Free .PDF for easy Reference

Written by

Elemar Junior CTO, Guiando
Refcard #287

RavenDB 4.0

RavenDB is a mature, multi-platform, NoSQL document-oriented database, safe by default and optimized for efficiency. It is easy to learn and use with languages like C#, Java, Python, NodeJS, and C++. Learn the basics of creating your first databases and clusters in RavenDB with this Refcard.

454
Free .PDF for easy Reference

Written by

Elemar Junior CTO, Guiando

Brought to you by

RavenDB
Table of Contents

Starting With the Basics

Querying Basics

Accessing RavenDB Using C#

A Few Words About Indexing

Revisions

Commands and Operations

How to Get Notified Whenever a Document Changes

Data Subscriptions

Clustering

Section 1

Starting With the Basics

Creating Your First Database

After installing RavenDB, you can use the Management Studio to easily create your first database.

Image title

To create your first database:

  1. Select the Database option in the left panel
  2. Click on the New Database button
  3. Type a name for the new database
  4. Click on the Create button

Image title

Also, RavenDB provides you with sample data for learning purposes. To load it:

  1. Select Databases on the left panel
  2. In the right panel, click on the name of the database you just created
  3. In the left panel, click on Settings, and then Create Sample Data
  4. Click on the big Create button

The sample data comes from the Northwind database. Originally the sample database that came with SQL Server, it has been used for decades as the sample database in the Microsoft community. If you are new to the NoSQL world, this is a valuable resource to learn about NoSQL database modeling.

Going to the Documents section (left panel), you will see the sample data that was loaded into your database.

Image title

Understanding the Document Concept

A document is a self-describing, hierarchical tree data structure that can consist of maps, collections, and scalar values. RavenDB documents are created with simple JSON.

For example, assuming that you loaded the sample data into a database, you could use the Go to document feature (the text box in the Studio toolbar), to go to the document orders/101-A.

{
    "Company": "companies/86-A", 
    "Employee": "employees/4-A", 
    "Freight": 0.78, 
    "Lines": [ 
        { 
            "Discount": 0.15, 
            "PricePerUnit": 14.4, 
            "Product": "products/1-A", 
            "ProductName": "Chai", 
            "Quantity": 15 
        }, 
        { 
            "Discount": 0, 
            "PricePerUnit": 7.2, 
            "Product": "products/23-A", 
            "ProductName": "Tunnbröd", 
            "Quantity": 25 
        } 
    ], 
    "OrderedAt": "1996-11-07T00:00:00.0000000", 
    "RequireAt": "1996-12-05T00:00:00.0000000", 
    "ShipTo": { 
        "City": "Stuttgart", 
        "Country": "Germany", 
        "Line1": "Adenauerallee 900", 
        "Line2": null, 
        "Location": { 
            "Latitude": 48.7794494, 
            "Longitude": 9.1852878 
        }, 
        "PostalCode": "70563", 
        "Region": null 
    }, 
    "ShipVia": "shippers/2-A", 
    "ShippedAt": "1996-11-15T00:00:00.0000000", 
    "@metadata": { 
        "@collection": "Orders", 
        "@flags": "HasRevisions" 
    } 
}


As you can see, you can aggregate related information into a common object, as in the case of the ShipTo property, which has all the shipping information. Also, you can reference other documents (as in the ShipVia property).

In a document-oriented database, documents are organized in collections.

Understanding the Collection Concept

Collections provide a good way to establish a level of organization. For example, documents holding customer data are very different from documents holding product information, and you want to talk about groups of them. RavenDB allows for a document to be stamped with a string value that will be evidence of its type (like “Customers” and “Products”).

Documents that are in the same collection can have completely different structures. Because RavenDB is schemaless, this is fine.

Section 2

Querying Basics

The basic characteristics of a database allow it to store and query data. RavenDB makes querying easy through RQL.

RQL, the Raven Query Language, is a SQL-like language used to retrieve the data from the server when queries are being executed. It is designed to expose the RavenDB query pipeline in a way that is easy to understand, easy to use, and not overwhelming to the user.

Querying for the Very First Time

Writing queries is easy using the RavenDB Management Studio. Let’s go step-by-step:

  1. Open the RavenDB Management Studio
  2. In the left panel, click on Databases
  3. Open the database we created in the previous lesson (Northwind, if you followed our recommendation)
  4. In the left panel, select the Documents section
  5. Click on Query
  6. Enter your query (using RQL) from Employees
  7. Click on Run

This query returns all the documents inside the Employees collection (from the sample data).

Image title

Other Interesting Basic Queries

Getting all the documents from a collection is nice, but quite useless. Let’s make something more exciting.

from Employees
where FirstName == "Nancy"


FirstName is the name of one of the properties present in the documents from the Employees collection.

The next query shows how to shape the data returned from the server. This query returns the name of all products, ordering date, and shipping city from all orders with more than four lines.

from Orders
where Lines.Count > 4
select Lines[].ProductName as ProductNames, OrderedAt, ShipTo.City as
City

We can also use JavaScript to define the shape of the query results. Also, this query shows how to combine two or more documents in a single result. As you noticed, the Company field of an Order document contains the ID of another document stored in the database. The load instruction is smart enough to get that document for you.

from Orders as o
load o.Company as c
select {
    Name: c.Name.toLowerCase(),
    Country: c.Address.Country,
    LinesCount: o.Lines.length
}

Another important basic querying feature is to aggregate data. The following query groups the Orders using the Company field as a grouping key. Also, there is a filter to get only groups with six documents at least, and ordering criteria by the number of elements per group in descending order. Finally, we are projecting the number of documents per group and the group key.

from Orders
group by Company
where count() > 5
order by count() desc
select count() as Count, key() as Company
Section 3

Accessing RavenDB Using C#

Hibernating Rhinos provides official client APIs for the most popular programming languages. When using .NET, you need to install the RavenDB.Client NuGet package.

Install-Package RavenDB.Client

Connecting to the Server

To connect to the RavenDB server, you will need to create a DocumentStore object.

var documentStore = new DocumentStore
{
    Urls = new [] {"http://localhost:8080"},
    Database = "Northwind"
};

documentStore.Initialize();

A document store is the main client API object that establishes and manages the connection channel between an application and a database instance. It acts as the connection manager and also exposes methods to perform all the operations you can run against an associated server instance. The document store object has an array of URL addresses to the cluster nodes. However, it can work against multiple databases that exist there.

You should only need to create a single instance of the DocumentStore object for a program’s lifetime. To do that, I would recommend that you provide it through a Singleton interface.

public static class DocumentStoreHolder
{
    private static readonly Lazy<IDocumentStore> LazyStore =
        new Lazy<IDocumentStore>(() =>
        {
            var store = new DocumentStore
            {
                Urls = new[] { "http://localhost:8080" },
                Database = "Northwind"
            };

            return store.Initialize();
        });
    public static IDocumentStore Store =>
        LazyStore.Value;
}

The use of Lazy ensures that the document store is only created once, without having to worry about locking or other thread safety issues.

Your First Session

The session is the primary way your code interacts with RavenDB. You need to create a session via the document store, and then use the session methods to perform operations.

A session object is very easy to create and use. To create a session, you simply call the DocumentStore.OpenSession() method.

using (var session = DocumentStoreHolder.Store.OpenSession())
{
    // ready to interact with the database
}

Loading Documents

To load documents, you will use the session’s load method.

As the name implies, the Load method gives you the option of loading a document or a set of documents, passing the document(s) ID(s) as parameters. The result will be an object representing the document, or null if the document does not exist.

A document is loaded only once in a session. Even though we call the Load method twice passing the same document ID, only a single remote call to the server will be made. Whenever a document is loaded, it is added to an internal dictionary managed by the session.

using (var session = DocumentStoreHolder.Store.OpenSession())
{
    var p1 = session.Load<Product>("products/1-A");
    var p2 = session.Load<Product>("products/1-A");
    Debug.Assert(ReferenceEquals(p1, p2));
}

The easiest way to kill your application performance is to make a lot of remote calls. RavenDB provides a lot of features to help you significantly reduce calls and boost performance.

Consider the Northwind products/1-A document:

{
    "Name": "Chai",
    "Supplier": "suppliers/1-A",
    "Category": "categories/1-A",
    "QuantityPerUnit": "10 boxes x 20 bags",
    "PricePerUnit": 18,
    "UnitsInStock": 1,
    "UnitsOnOrder": 39,
    "Discontinued": false,
    "ReorderLevel": 10,
    "@metadata": {
        "@collection": "Products"
    }
}

As you can see, the Supplier and Category properties are references to other documents.

Considering you need to load the product and the related category, how would you write the code? Your first attempt might look something like this:

var p = session.Load<Product>("products/1-A");
var c = session.Load<Category>(p.Category);

This approach will make two remote calls — not good.

var p = session
    .Include<Product>(x => x.Category)
    .Load("products/1-A");
var c = session.Load<Category>(p.Category);

The Include session method changes the way RavenDB will process the request.

It will:

  1. Find a document with the ID: products/1-A
  2. Read its Category property value
  3. Find a document with that ID
  4. Send both documents back to the client

When the session.Load(p.Category); is executed, the document is in the session cache, and no additional remote call is made.

Here is a powerful example of the application of this technique in a complex scenario.

var order = session
    .Include<Order>(x => x.Company)
    .Include(x => x.Employee)
    .Include(x => x.Lines.Select(l => l.Product))
    .Load("orders/1-A");

This code will, in a single remote call, load the order, including the company and employee documents, and also load all the products in all the lines in the order.

Querying From C#

While coding C#, you can make queries using LINQ or RQL.

For LINQ, you need to open a session and write your query using the session’s Query method.

var orders = (
    from order in session.Query<Order>()
                            .Include(o => o.Company)
    where order.Company == companyReference
    select order
    ).ToList();

For RQL, you need to open a session and write your query using the session’s RawQuery method.

var orders = session.Advanced.RawQuery<Order>(
        @"from Orders 
        where Company== $companyId 
        include Company"
    ).AddParameter("companyId", companyReference);

It’s important to know that all queries are translated to RQL before sending to the server.

Storing, Modifying, and Deleting Documents

The easiest way to learn how to store, modify, and delete documents from the database is with an example.

// storing a new document 
string categoryId; 
using (var session = DocumentStoreHolder.Store.OpenSession()) 
{ 
    var newCategory = new Category 
    { 
        Name = "My New Category", 
        Description = "Description of the new category" 
    }; 
    session.Store(newCategory); 
    categoryId = newCategory.Id; 
    session.SaveChanges(); 
} 
// loading and modifying 
using (var session = DocumentStoreHolder.Store.OpenSession()) 
{ 
    var storedCategory = session 
        .Load<Category>(categoryId); 
    storedCategory.Name = "abcd"; 
    session.SaveChanges(); 
} 
// deleting 
using (var session = DocumentStoreHolder.Store.OpenSession()) 
{ 
    session.Delete(categoryId); 
    session.SaveChanges(); 
}

Any .NET object can be stored by RavenDB. It only needs to be serializable to JSON.

The Store method is responsible for registering the “storing” intention in the session. You can access the document right after the Store call was made, even though the document was not saved to the database yet. The SaveChanges method applies the registered actions in the session to the database.

When you change the state of an entity, the session is smart enough to detect it and update the matching document on the server side. The session keeps track of all the entities you have loaded or stored (with Load or Query methods), and when you call SaveChanges, all changes to those entities are sent to the database in a single remote call.

The Delete method, which we have used in the last part of the code, will delete the matching document on the server side. You can provide the document ID or an entity instance.

All the changes are applied on the server side only after you call the SaveChanges method. This is how RavenDB supports transactions.

Section 4

A Few Words About Indexing

An index is a data structure that the RavenDB engine uses to perform all queries. Thanks to this data structure, RavenDB can quickly locate data without having to search every document in the database.

RavenDB is safe by default, and whenever you make a query, the query optimizer will try to select an appropriate index to use. If there is no such appropriate index, then the query optimizer will create an index for you.

Creating Your First Index

This is the basic process for creating an index:

  1. Access your database using the Management Studio
  2. Go to the Indexes section, then List of Indexes, click on New Index
  3. Specify the index name
  4. Specify the Map function (LINQ syntax)

RavenDB does not have fixed naming rules, but I strongly recommend that you follow a naming convention. For example, Employees/ByFirstAndLastName (collection name/By selected fields Of filtering criteria)

Here is an example of a map function for indexing the Employees documents by FirstName and LastName.

from doc in docs.Employees
select new
{
    FirstName = doc.FirstName,
    LastName = doc.LastName
}

In the expression, the docs object represents the entire collection of documents stored in the database. The docs.Employees object represents all documents from the Employees collection.

Image title

RQL provides a special syntax when you want to specify what index you want to use. Here is an example:

from index 'Employees/ByFirstNameAndLastName' as e
where e.LastName = "Davolio"

Defining a Multi-map Index Using C#

Multi-Map indexes allow you to index data from multiple collections e.g. polymorphic data or any common data between types.

Everything that you can do using the Management studio, you can do using the RavenDB client API.

The following code shows how to create a complex index that maps three different types of documents.

using System.Linq; 
using Raven.Client.Indexes; 

namespace MultimapIndexes 
{ 
    public class People_Search : 
        AbstractMultiMapIndexCreationTask<People_Search.Result> 
    { 
        public class Result 
        { 
            public string SourceId { get; set; } 
            public string Name { get; set; } 
            public string Type { get; set; } 
        } 

        public People_Search() 
        { 
            AddMap<Company>(companies => 
                from company in companies 
                select new Result 
                { 
                    SourceId = company.Id, 
                    Name = company.Contact.Name, 
                    Type = "Company's contact" 
                } 
                ); 

            AddMap<Supplier>(suppliers => 
                from supplier in suppliers 
                select new Result 
                { 
                    SourceId = supplier.Id, 
                    Name = supplier.Contact.Name, 
                    Type = "Supplier's contact" 
                } 
                ); 

            AddMap<Employee>(employees => 
                from employee in employees 
                select new Result 
                { 
                    SourceId = employee.Id, 
                    Name = $"{employee.FirstName} {employee.LastName}", 
                    Type = "Employee" 
                } 
                ); 

            Index(entry => entry.Name, FieldIndexing.Search); 

            Store(entry => entry.SourceId, FieldStorage.Yes); 
            Store(entry => entry.Name, FieldStorage.Yes); 
            Store(entry => entry.Type, FieldStorage.Yes); 
        } 
    } 
}

You can define as many map functions as you need. Each map function is defined using the AddMap method, and has to produce the same output type. The “source” collection is specified by the generic parameter type you specify in the AddMap function (the type is the same you use to retrieve documents as objects in the client-side).

The Index method here was used to mark the Name property as Search which enables full-text search with this field.

The Store method was used to enable projections and to store those defined properties along with the Index. Thereby, the return of searched data comes from the Index, instead of having to load the document and get the fields from it. In most cases, this isn’t an interesting optimization. RavenDB is already heavily optimized toward loading documents. Use this when you are creating new values in the indexing function and want to project them, not merely skip the (pretty cheap) loading of the document.

When creating indexes in the client-side, you need to remember registering it in the server. You do that during the DocumentStore object initialization.

var store = new DocumentStore 
                { 
                    Urls = new[] { "http://localhost:8080" }, 
                    Database = "Northwind" 
                }; 

store.Initialize(); 

var asm = Assembly.GetExecutingAssembly(); 
IndexCreation.CreateIndexes(asm, store);

Performing Advanced Queries

For querying using a multi-map index, you need to use a special interface from the RavenDB client.

public static IEnumerable<People_Search.Result> Search( 
    IDocumentSession session, 
    string searchTerms 
) 
{ 
    var results = session.Query<People_Search.Result, People_Search>() 
        .Search( 
            r => r.Name, 
            searchTerms 
        ) 
        .ProjectInto<People_Search.Result>() 
        .ToList(); 

    return results; 
}

As you can see, you can specify the index to use and the shape of the result.

The query’s Search method allows you to perform advanced querying using all the power of the Lucene engine (underlying used by RavenDB), including wildcards.

Section 5

Revisions

Every time you update or delete a document, RavenDB 4 can create a snapshot (revision) of the previous state. More than that, it’s useful when you need to track the history of the documents or when you need a full audit trail. 

You can choose to keep track of the last N revisions. If necessary, you could “track everything.”

How to Enable Revisions

You can configure the revisions feature using the Studio:

Image title

When activated, by default, RavenDB will track the history for all documents. Also by default, RavenDB will never purge old revisions. 

As the administrator, you can configure this for all collections. You can specify a different setup for a specific collection. By default, RavenDB will track history for all documents.

The options you have are:

  • Purge on Delete: delete the revisions upon the document delete
  • Limit # of revisions: How many revisions to keep
  • Limit # of revisions by age: Configure a minimum retention time before the revisions can be expired

You need to change the settings accordingly your needs.

Retrieving a Document’s Revision

When using the Management Studio, it is easy to get access to historic information about a document by accessing the Revisions tab.

Image title

Programmatically, you need to use the session’s advanced interface:

static void Main(string[] args) 
{ 
    using (var session = DocumentStoreHolder.Store.OpenSession()) 
    { 
        var revisions = session.Advanced.Revisions 
            .GetFor<Employee>("employees/7-A"); 

        foreach (var revision in revisions) 
        { 
            // process revision 
        } 
    } 
}
Section 6

Commands and Operations

The session is a high-level interface to RavenDB that provides the identity map and LINQ queries. But if you want to do something low-level or advanced, then you should start using Commands and Operations.

Adding an Order’s Line into an Order Document Without Loading the Entire Document

Until now, the only way to change a document would be loading it, modifying it, and then storing it again. But there is a cheaper way.

using (var session = DocumentStoreHolder.Store.OpenSession()) 
{ 
    session.Advanced.Defer(new PatchCommandData( 
        id: "orders/816-A", 
        changeVector: null, 
        patch: new PatchRequest 
        { 
            Script = "this.Lines.push(args.NewLine)", 
            Values = 
            { 
                { 
                    "NewLine", new  
                    { 
                        Product = "products/1-a", 
                        ProductName = "Chai", 
                        PricePerUnit=18M, 
                        Quantity=1, 
                        Discount=0 
                    } 
                } 
            } 

        }, 
        patchIfMissing: null)); 

    session.SaveChanges(); 
}

In this example, you use the Patch command which performs partial document updates without having to load, modify, and save a full document. The Script needs to be in JavaScript.

If you have types, and you probably do, you can also use a typed version:

static void Main() 

{ 
    using (var session = DocumentStoreHolder.Store.OpenSession()) 
    { 
        session.Advanced.Patch<Order, OrderLine>("orders/816-A", 
            x => x.Lines, 
            lines => lines.Add(new OrderLine 
            { 
                Product = "products/1-a", 
                ProductName = "Chai", 
                PricePerUnit = 18M, 
                Quantity = 1, 
                Discount = 0 
            })); 

        session.SaveChanges(); 
    } 
}

Performing a Batch Operation

Batch operations provide a good alternative to performing operations that affect a lot of documents.

The following example increments the price of all products that were not discontinued.

static void Main() 
{ 
    var operation = DocumentStoreHolder.Store 
        .Operations 
        .Send(new PatchByQueryOperation(@"from Products as p 
                                where p.Discontinued = false 
                                update 
                                { 
                                    p.PricePerUnit = p.PricePerUnit * 1.1 
                                }")); 
    operation.WaitForCompletion(); 
}
Section 7

How to Get Notified Whenever a Document Changes

RavenDB provides a very useful, reactive interface to share information whenever a document is changed.

using System; 
using Raven.Client; 
using Raven.Client.Documents; 

namespace BasicsOfChangesAPI 
{ 
    using static Console; 

    class Program 
    { 
        static void Main(string[] args) 
        { 
            using (var subscription = DocumentStoreHolder.Store 
                .Changes() 
                .ForAllDocuments() 
                .Subscribe(change => 
                    WriteLine($"{change.Type} on document {change.Id}"))) 
            {
                WriteLine("Press any key to exit..."); 
                ReadKey(); 
            }        
        } 
    } 
}

If you are not familiar with reactive programming, the lambda function will be invoked whenever a change occurs in the server side.

You could use the Where function to introduce a filter.

IMPORTANT: To use the reactive extension, please add references to System.Reactive.Core and System.Reactive.Linq NuGet packages.

Section 8

Data Subscriptions

Data Subscription is a powerful feature that is simpler to explain with an example. Consider the following query:

from Orders
where Lines.length > 5

This query would retrieve all the big orders from your database. But what if a new big order is added after you run this query? What if you want to be notified whenever a big order occurs? This is what the Data Subscription feature allows you to do.

There are some important facts that you need to know to use this feature correctly.

  • Documents that match pre-defined criteria are sent in batches from the server to the client.
  • The client sends an acknowledgment to the server once it is done with processing the batch.
  • The server keeps track of the latest document that the cliente acknowledged so that processing can continue from the latest acknowledged position if it was paused or interrupted.

Creating a Data Subscription

To create a Data Subscription, you need to open the Settings section of your database. Click on the Manage Ongoing Tasks option, then click on the Add Task button.

Image title

Then, click on Subscription.

RavenDB will ask you for a Task Name (for example, Big Orders) and then you can provide the very same query we wrote before.

Image title

Test and save it!

Consuming a Data Subscription

Data Subscriptions are consumed by clients, called subscription workers. In any given moment, only one worker can be connected to a data subscription. A worker connected to a data subscription receives a batch of documents and gets to process it. When it’s done (depending on the code that the client gave the worker, this can take seconds or hours), it informs the server about the progress, and the server is ready to send the next batch.

Here is an example of how to consume a Data Subscription.

static void Main(string[] args) 
{ 
    var subscriptionWorker = DocumentStoreHolder.Store 
        .Subscriptions 
        .GetSubscriptionWorker<Order>("Big Orders"); 

    var subscriptionRuntimeTask = subscriptionWorker.Run(batch => 
    { 
        foreach (var order in batch.Items) 
        { 
            // business logic here. 
            Console.WriteLine(order.Id); 
        } 
    }); 

    WriteLine("Press any key to exit..."); 
    ReadKey(); 
}
Section 9

Clustering

A RavenDB cluster is a set of machines that have been joined together. Each machine in the cluster is a node.

When you create a database, it can live on a single node (one machine in the cluster), some number of the nodes, or even all the nodes. Each node will hold a complete copy of the database and will be able to serve all queries, operations, and writes. RavenDB clusters implement multi-master replication.

The primary reason for duplicating data is to allow high availability. If a node goes down, the cluster will still have copies of the data, and then can shuffle things around — so clients can talk to another node without realizing that anything happened.

The cluster can distribute the work, handle the failures, and adopt recovery procedures automatically.

Your First Cluster

Assuming that you have started the RavenDB server on your computer, you are already using a cluster. It’s a cluster with a single node, but it is still a cluster.

Image title

Add Other Servers as Nodes in the Cluster

Adding nodes in an existing cluster is easy. Open the Management Studio from the first server, then, in the left panel, select the option Manage Server and then Cluster. Click in Add Node To Cluster. Inform the address of the second server and hit Test Connection.

Image title

RavenDB will show you an updated topology:

Image title

Some Concepts That You Need to Know

Now that you know what a cluster is and know how to add nodes to it, it’s time to learn some new concepts.

  • Database – the named database we’re talking about, regardless of whether we’re speaking about a specific instance or the whole group
  • Database Instance – exists on a single node
  • Database Group – the grouping of all the different instances, typically used to explicitly refer to its distributed nature
  • Database Topology – the specific nodes that all the database instances in a database group reside on at a particular point in time

Consider you have a cluster with five nodes. It is possible to create a database setting with a replication factor of just three. In this case, only three nodes will have instances of the database. The selected nodes will form the Database Topology, and all the three instances will constitute the Database Group.

Distributed, Yes! So, Consistent or Available?

Operations in RavenDB can be classified into two categories:

  1. Cluster-Wide Operations: They impact the entire cluster, like creating a new database
  2. Internal Database Operations: They impact a single database, like creating a new document

This distinction is vital because cluster-wide operations demand consensus between the nodes of the cluster. This is only possible when the majority of the nodes are working. RavenDB uses a consensus protocol called RAFT. To create a database in a cluster with three nodes, it would be necessary that at least two nodes are working (the majority). This is the reason why you should not have a cluster with only two nodes. When one is down, there is no majority.

There are a lot of details about how the RAFT consensus protocol works in RavenDB that are beyond the scope of this bootcamp.

Database operations, on the other hand, are treated quite differently. Each node in the cluster has the full copy of the topology that specifies which node hosts which database. The connection between the database instances does not use the consensus protocol. Instead, they’re direct connections among the various nodes, and they form a multi-master mesh. A write to any of those nodes will be automatically replicated to all the other nodes.

Cluster Consensus provides firm consistency. Unfortunately, we also need to ensure the availability of the databases, and it is a tradeoff (look for the CAP theorem to understand the reasons). We decided to provide consistency where it is indispensable and chose explicitly for availability elsewhere.

Creating a Database With Replication

Whenever you create a new database, you can specify a replication factor. That is the number of the cluster nodes that will contain an instance of the database.

Image title

Managing the Database Group

As you already know, the database group is the set of cluster nodes containing the instances of a database.

You can manage the database group of a specified database accessing the Databases option in the left panel.

Image title

This image displays a database that has instances on two nodes, Node C and Node A.

You can see a status of Online, indicating that my database is active (meaning that I have an instance of this database running on this cluster node). Accessing the same screen on the server that has no instance of my new database (in my example, Node B, which is running on http://127.0.0.2:8080), the status changes to Remote.

Image title

Clicking on the Manage Group button of the database that you want to manage allows you to change the nodes where there are instances of the selected database.

It is important to remember that managing a database group is a cluster-wide operation that requires a majority to work.

Connecting to a Cluster

Whenever you create an instance of the DocumentStore, you should specify the list of nodes in the cluster that you are connecting on.

var store = new DocumentStore 
{
    Urls = 
    { 
        "http://127.0.0.1:8080" ,"http://127.0.0.2:8080" , 
        "http://128.0.0.3:8080"  
    }, 
    Database = "Northwind" 
}; 
store.Initialize();

As we mentioned earlier, all cluster nodes contain the full topology for all the databases hosted in the cluster. The very first thing that a client will do upon initialization is to query the defined URLs and figure out what the actual nodes are that it needs to get to the database you are trying to connect to.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}