<commit_before>[[reference.ha]]
= High Availability (HA) Environments

[[reference_configuration_ha]]
== Configuring Spring Data Neo4j 4.2 in an HA Environment

[[reference_configuration_transactions]]
=== Transaction Binding in HA Mode
A typical Neo4j HA cluster will consist of a master node and a couple of slave nodes for providing failover capability and optionally for handling reads. (Although it is possible to write to slaves, this is uncommon because it requires additional effort to synchronise a slave with the master node)

image:neo4j-cluster.png[Typical HA Cluster]

When operating in HA mode, Neo4j does not make open transactions available across all nodes in the cluster. This means we must bind every request within a specific transaction to the same node in the cluster, or the commit will fail with `404 Not Found`.

[[reference_configuration_readwrite]]
=== Read-only Transactions

As of version 4, Spring Data Neo4j does not distinguish between WRITE transactions and READ-ONLY transactions. We cannot therefore bind read-only transactions to slaves and write transactions to master. A future version will address this deficiency, but in the meantime the only way to ensure that everything works as expected is to direct every transaction to master. There are a couple of ways to to achieve this.

[[reference_configuration_staticbinding]]
=== Static Binding to a Designated Master

==== Example cluster:

. master:    192.168.0.55
. slave1:    192.168.0.56
. slave2:    192.168.0.67

.SDN4 Binding to master IP address
[source,java]
----

    Components.driver().setURI("http://192.168.0.55:7474");

----

[NOTE]
====
We don't really recommend this approach, except for testing purposes and non-critical deployments. Firstly, it will only work if you always bring up the designated master first, and secondly, if the master goes down all subsequent transactions will fail until it is restarted. In HA mode, the cluster is able to elect a new master when this happens, but as of version 4 of Spring Data Neo4j, there is no mechanism for querying the cluster to identify the current master. The solution in this case is to use a load balancer such as HAProxy that can do this for us. This is described in the next section.

====

[[reference_configuration_haproxy]]
=== Dynamic Binding via a Load Balancer
In the Neo4j HA architecture, a cluster is typically fronted by a load balancer. The following example shows how to configure your application and set up HAProxy as a load balancer to route all requests to whichever machine in the cluster is currently identified as the master. Since only one machine can ever be the elected master, this should work exactly as we would like. Furthermore, should the elected master fail, a new server will be elected from the cluster as master and HAProxy will automatically route transactions to this server.


==== Example cluster fronted by HAProxy

. haproxy:          10.0.2.200
. neo4j-server1:    10.0.1.10
. neo4j-server2:    10.0.1.11
. neo4j-server3:    10.0.1.12

.Spring Data Neo4j 4 Binding via HAProxy
[source,java]
----

    Components.driver().setURI("http://10.0.2.200");

----

.Sample haproxy.cfg

```
global
    daemon
    maxconn 256

defaults
    mode http
    timeout connect 5000ms
    timeout client 50000ms
    timeout server 50000ms

frontend http-in
    bind *:80
    default_backend neo4j

backend neo4j
    option httpchk GET /db/manage/server/ha/master
    server s1 10.0.1.10:7474 maxconn 32
    server s2 10.0.1.11:7474 maxconn 32
    server s3 10.0.1.12:7474 maxconn 32

listen admin
    bind *:8080
    stats enable
```
<commit_msg>DATAGRAPH-907 - Update user documentation for READ_ONLY Transactions.<commit_after>[[reference.ha]]
= High Availability (HA) Environments

[[reference_configuration_ha]]
== Configuring Spring Data Neo4j 4.2 in an HA Environment

[[reference_configuration_transactions]]
=== Transaction Binding in HA Mode
A typical Neo4j HA cluster will consist of a master node and a couple of slave nodes for providing failover capability and optionally for handling reads. (Although it is possible to write to slaves, this is uncommon because it requires additional effort to synchronise a slave with the master node)

image:neo4j-cluster.png[Typical HA Cluster]

When operating in HA mode, Neo4j does not make open transactions available across all nodes in the cluster. This means we must bind every request within a specific transaction to the same node in the cluster, or the commit will fail with `404 Not Found`.

[[reference_configuration_readwrite]]
=== Read-only Transactions

As of Version 4.12.0 read-only transactions are fully supported by Spring Data Neo4j

To declare a read-only transaction specify the `readOnly` attribute in your annotation:

```
@Transactional(readOnly=true)
```

=== Drivers
The Neo4j OGM Drivers have been updated to transmit additional information about the transaction type of the current transaction to the server.

- The HttpDriver implementation sets a Http Header "X-WRITE" to "1" for READ_WRITE transactions (the default) or to "0" for READ_ONLY ones.

- The Embedded Driver can support READ_ONLY, but this only makes sense in an HA environment. As there is no support for HA Embedded in the OGM at the moment, the implementation is currently on hold.

- The native Bolt Driver currently has no support for READ_ONLY transactions or READ_ONLY sessions, so the implementation of this in the OGM is also on hold until this capability becomes available.

[[reference-configuration-haproxy]]
== Dynamic binding via a load balancer

In the Neo4j HA architecture, a cluster is typically fronted by a load balancer.

The following example shows how to configure your application and set up HAProxy as a load balancer to route write requests to whichever machine in the cluster is currently identified as the master, with read requests being distributed to any available machine in the cluster on a round-robin basis.

This configuration will also ensure that requests against a specific transaction are directed to the server where the transaction was created.

=== Example cluster fronted by HAProxy

. haproxy:          10.0.2.200
. neo4j-server1:    10.0.1.10
. neo4j-server2:    10.0.1.11
. neo4j-server3:    10.0.1.12

.OGM Binding via HAProxy
[source, java]
----
Components.driver().setURI("http://10.0.2.200");
----

.Sample haproxy.cfg

[source, config]
----
global
    daemon
    maxconn 256

defaults
    mode http
    timeout connect 5000ms
    timeout client 50000ms
    timeout server 50000ms

frontend http-in
    bind *:80
    acl write_hdr hdr_val(X-WRITE) eq 1
    use_backend neo4j-master if write_hdr
    default_backend neo4j-cluster

backend neo4j-cluster
    balance roundrobin
    # create a sticky table so that requests with a transaction id are always sent to the correct server
    stick-table type integer size 1k expire 70s
    stick match path,word(4,/)
    stick store-response hdr(Location),word(6,/)
    option httpchk GET /db/manage/server/ha/available
    server s1 10.0.1.10:7474 maxconn 32
    server s2 10.0.1.11:7474 maxconn 32
    server s3 10.0.1.12:7474 maxconn 32

backend neo4j-master
    option httpchk GET /db/manage/server/ha/master
    server s1 10.0.1.10:7474 maxconn 32
    server s2 10.0.1.11:7474 maxconn 32
    server s3 10.0.1.12:7474 maxconn 32

listen admin
    bind *:8080
    stats enable
----

{'fraction_non_alphanumeric': 0.06293895191788222, 'fraction_numerical': 0.04470556455969746, 'mean_word_length': 3.913735899137359, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 23, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4023285', 'n_tokens_mistral': 2233, 'n_tokens_neox': 1990, 'n_words': 983}