Skip to content

Commit

Permalink
JCES-1757: Add docs and diagrams
Browse files Browse the repository at this point in the history
`read-replica` had poor documentation. It was hard to understand it on a high level.
I hope it's enough to lower the barrier of entry for the libraries consumers and
for the libraries contributors.

I skipped details about `ReplicaConsistency` as it's currently under development and
will probably change soon.
  • Loading branch information
wyrzyk committed Feb 8, 2021
1 parent 995b253 commit c8b9de8
Show file tree
Hide file tree
Showing 12 changed files with 154 additions and 3 deletions.
2 changes: 2 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ See the existing issues for things to start contributing.

For bigger changes, please make sure you start a discussion first by creating an issue and explaining the intended change.

Check [the class diagram](docs/classes-overview.md) to understand how things are connected.

Atlassian requires contributors to sign a Contributor License Agreement, known as a CLA. This serves as a record stating that the contributor is entitled to contribute the code/documentation/translation to the project and is willing to have it used in distributions and derivative works (or is willing to transfer ownership).

Prior to accepting your contributions we ask that you please follow the appropriate link below to digitally sign the CLA. The Corporate CLA is for those who are contributing as a member of an organization and the individual CLA is for those contributing as an individual.
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ It integrates at the `java.sql.Connection` level, so you don't have to hunt down

## Features

- [Automatic switching between main and replica databases](src/main/java/com/atlassian/db/replica/api/DualConnection.java).
- [Configurable consistency model](src/main/java/com/atlassian/db/replica/spi/ReplicaConsistency.java).
- [Automatic switching between main and replica databases](docs/switching-between-main-and-replica.md).
- [Configurable consistency model](docs/consistency.md).
- [Configurable circuit breaker](src/main/java/com/atlassian/db/replica/spi/circuitbreaker/CircuitBreaker.java).
- [Configurable main/replica split instrumentation](src/main/java/com/atlassian/db/replica/spi/DualCall.java).
- [Configurable main/replica split instrumentation](docs/split-instrumentation.md).
- [Connection state change listener](src/main/java/com/atlassian/db/replica/spi/state/StateListener.java).

## Usage
Expand Down
19 changes: 19 additions & 0 deletions docs/classes-overview.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
## Classes diagram

This diagram is intended to help understand how the library works internally. It may leak some implementation details and should not be used as API.

![Classes diagram](classes-overview.png)

`DualConnection#Builder` creates [DualConnection](../src/main/java/com/atlassian/db/replica/api/DualConnection.java).
[CircuitBreaker](../src/main/java/com/atlassian/db/replica/spi/circuitbreaker/CircuitBreaker.java)
if [DualConnection](../src/main/java/com/atlassian/db/replica/api/DualConnection.java) can be created.
Every call that goes to the database directly through the connection or through
one of the `java.sql.Statement` implementations can be intercepted with
[DualCall](../src/main/java/com/atlassian/db/replica/spi/DualCall.java).

[ConnectionState](../src/main/java/com/atlassian/db/replica/internal/state/ConnectionState.java) is an internal class
that is the source of truth to the current [State](../src/main/java/com/atlassian/db/replica/api/state/State.java).
It can use [ConnectionProvider](../src/main/java/com/atlassian/db/replica/spi/ConnectionProvider.java)
to obtain a connection to a database. It utilises
[ReplicaConsistency](../src/main/java/com/atlassian/db/replica/spi/ReplicaConsistency.java) while transitioning
between states. [StateListener](../src/main/java/com/atlassian/db/replica/spi/state/StateListener.java) is called on each transition.
Binary file added docs/classes-overview.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
42 changes: 42 additions & 0 deletions docs/classes-overview.puml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
@startuml
class "DualConnection#Builder" as builder
builder : Connection build()

class DualConnection
interface ReplicaConsistency
interface ConnectionProvider
interface DualCall
interface StateListener
interface CircuitBreaker
class ConnectionState
class State

interface Statement
interface PreparedStatement
interface CallableStatement
interface Connection


builder::build --> DualConnection : create


DualConnection --|> Connection
PreparedStatement --|> Statement
CallableStatement --|> Statement


ConnectionState *-- ReplicaConsistency
ConnectionState *-- ConnectionProvider
ConnectionState *-- StateListener
ConnectionState *-- Connection
ConnectionState *-- State

DualConnection *-- DualCall
DualConnection *-- Statement
builder *-- CircuitBreaker
DualConnection *-- ConnectionState




@enduml
18 changes: 18 additions & 0 deletions docs/consistency.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
## Consistency

[DualConnection](../src/main/java/com/atlassian/db/replica/api/DualConnection.java) consistency is controlled by
[com.atlassian.db.replica.spi.ReplicaConsistency](../src/main/java/com/atlassian/db/replica/spi/ReplicaConsistency.java).

Currently, there's no default consistency implementation. Consistency model highly depends on the database and
application's needs. There's one example of the implementation for Postgres available in
[the integration test](../src/test/java/com/atlassian/db/replica/it/DualConnectionIT.java).

The easiest way to start is to use
[PessimisticPropagationConsistency](../src/main/java/com/atlassian/db/replica/api/PessimisticPropagationConsistency.java).

![ReplicaConsistency](consistency.png "ReplicaConsistency")

Every write operation is registered by `ReplicaConsistency#write` method.
[DualConnection](../src/main/java/com/atlassian/db/replica/api/DualConnection.java) verifies
the state of replica's consistency by calling `ReplicaConsistency#isConsistent` method.

Binary file added docs/consistency.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
21 changes: 21 additions & 0 deletions docs/consistency.puml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
@startuml

@startuml

class DualConnection

interface ReplicaConsistency{
void write(Connection main)
boolean isConsistent(Supplier<Connection> replica)
}

DualConnection *-- ReplicaConsistency
DualConnection --> ReplicaConsistency::write : db write operation

DualConnection --> ReplicaConsistency::isConsistent : validate consistency for db read operation


@enduml


@enduml
10 changes: 10 additions & 0 deletions docs/split-instrumentation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
## Configurable main/replica split instrumentation

All calls to the database goes through [DualCall](../src/main/java/com/atlassian/db/replica/spi/DualCall.java).
By default [DualConnection](../src/main/java/com/atlassian/db/replica/api/DualConnection.java) forwards all the calls.
It can be used to log queries, gather metrics or to handle exceptions.

![Split](split-instrumentation.png "SplitInstrumentation")

Every database operation on replica will go through `DualCall#callReplica`.
Every call to the main database will go through `DualCall#callMain` method.
Binary file added docs/split-instrumentation.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
20 changes: 20 additions & 0 deletions docs/split-instrumentation.puml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
@startuml

@startuml

class DualConnection

interface DualCall{
<T> T callReplica(final SqlCall<T> call)
<T> T callMain(final SqlCall<T> call)
}

DualConnection *-- DualCall
DualConnection --> DualCall::callReplica : db operation in ReplicaConnection state
DualConnection --> DualCall::callMain : db operatin in MainConnection state


@enduml


@enduml
19 changes: 19 additions & 0 deletions docs/switching-between-main-and-replica.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
## How DualConnection chooses between main and replica?

[DualConnection](../src/main/java/com/atlassian/db/replica/api/DualConnection.java) takes multiple aspects while deciding
which connection to use:

1. A [connection's state](dual-connection-states.md).
2. A replica's [consistency](../src/main/java/com/atlassian/db/replica/spi/ReplicaConsistency.java).
3. Context of `java.sql.Connection`/`java.sql.Statement` API usage.

Some of the methods are intended to write into the database. For example every call to `java.sq.PreparedStatement#executeUpdate`
will switch the connection's state to the main database.

4. Availability of replica
5. The query will use the main database in case it's:
- `SELECT FOR UPDATE` statement.
- `UPDATE` statement.
- `DELETE` statement.
- an SQL function call.
- All calls with transaction isolation level higher than `TRANSACTION_READ_COMMITTED`.

0 comments on commit c8b9de8

Please sign in to comment.