Skip to content

Commit

Permalink
MySQL integration
Browse files Browse the repository at this point in the history
  • Loading branch information
MaxBartkov committed Feb 8, 2022
1 parent de2eceb commit b7f9788
Show file tree
Hide file tree
Showing 11 changed files with 556 additions and 25 deletions.
Expand Up @@ -18,4 +18,4 @@ include::distributed-checklist.adoc[]

include::implement-custom-database/concept.adoc[]

include::implement-custom-database/postgresql.adoc[]
include::implement-custom-database/jdbc-integraions.adoc[]
Expand Up @@ -65,5 +65,5 @@ In order to do that, we need to create a custom class to implement from LockBase
void update(byte[] data);
----

As an example, you can see to the PostgreSQL realization.
As an example, you can see to the PostgreSQL or MySQL realization.

@@ -1,5 +1,38 @@
=== PostgreSQL integration
==== Dependencies
=== JDBC integrations
General compatibility matrix principles:

* Bucket4j authors do not provide create a table for store buckets, you must make the table personally
* You should create a trigger or a scheduler that will clear your bucket storage table since DBMS is not IMDB, and DBMS don't give TTL the opportunity
* You should create a table, which includes the next required columns: BIGINT as a PRIMARY KEY, BYTEA as a state. By default, Bucket4j works with the next structure:
.PostgreSQL
----
CREATE TABLE IF NOT EXISTS buckets(id BIGINT PRIMARY KEY, state BYTEA);
----

.MySQL
----
CREATE TABLE IF NOT EXISTS buckets(id BIGINT PRIMARY KEY, state BLOB);
----

[[listener]]
===== Overriding table configuration
You can override the names of the columns to set your custom name of columns, in order to do that, you should use `BucketTableSettings` to set into `ProxyConfigurationBuilder` of your JDBC implementation.

Each `ProxyConfigurationBuilder` Takes `BucketTableSettings` - is the class to define a configuration of the table to use as a buckets store. By default, under the hood uses `BucketTableSettings.getDefault()`

Parameters:

`tableName` - name of table to use as a Buckets store

`idName` - name of id (PRIMARY KEY - BIGINT)

`stateName` - name of state (BYTEA)

By default, uses: "buckets" as a `tableName`; "id" as a `idName`; "state" as a `stateName`

==== PostgreSQL integration
===== Dependencies
To use Bucket4j extension for PostgreSQL you need to add following dependency:
[source, xml, subs=attributes+]
----
Expand All @@ -10,16 +43,7 @@ To use Bucket4j extension for PostgreSQL you need to add following dependency:
</dependency>
----

==== General compatibility matrix principles:
* Bucket4j authors do not provide create a table for store buckets, you must make the table personally
* You should create a trigger or a scheduler that will clear your bucket storage table since PostgreSQL is not IMDB, and DBMS don't give TTL the opportunity
* You should create a table, which includes the next required columns: BIGINT as a PRIMARY KEY, BYTEA as a state. By default, Bucket4j works with the next structure:
----
CREATE TABLE IF NOT EXISTS buckets(id BIGINT PRIMARY KEY, state BYTEA);
----
But you can override the names of the columns to set your custom name of columns

==== Example of Bucket instantiation
===== Example of Bucket instantiation
----
Long key = 1L;
PostgreSQLProxyManager proxyManager = new PostgreSQLProxyManager(new PostgreSQLProxyConfiguration(dataSource));
Expand All @@ -29,7 +53,7 @@ But you can override the names of the columns to set your custom name of columns
BucketProxy bucket = proxyManager.builder().build(key, bucketConfiguration);
----

==== Configuring custom settings of PostgreSQLProxyManager
===== Configuring custom settings of PostgreSQLProxyManager
* `PostgreSQLProxyManager` takes a few configurations that you can set to make it customizable
* In order to do that, you should use `PostgreSQLProxyConfigurationBuilder`, which includes the next parameters:
----
Expand Down Expand Up @@ -74,9 +98,9 @@ But you can override the names of the columns to set your custom name of columns
PostgreSQLProxyManager proxyManager = new PostgreSQLProxyManager(configuration);
----

==== Parameters to customize table buckets storage settings via PostgreSQLProxyConfigurationBuilder
===== Parameters to customize table buckets storage settings via PostgreSQLProxyConfigurationBuilder

===== addLockBasedTransactionType
====== addLockBasedTransactionType
Takes `LockBasedTransactionType` - is a strategy of transaction for the table. By default, under the hood uses `ADVISORY`

Lock based transaction types:
Expand All @@ -93,18 +117,78 @@ That is, other transactions that attempt UPDATE, DELETE, or SELECT FOR UPDATE of
Also, if an UPDATE, DELETE, or SELECT FOR UPDATE from another transaction has already locked a selected row or rows, SELECT FOR UPDATE will wait for the other transaction to complete, and will then lock and return the updated row (or no row, if the row was deleted).
Within a SERIALIZABLE transaction, however, an error will be thrown if a row to be locked has changed since the transaction started.

===== addTableSettings
Takes `BucketTableSettings` - is the class to define a configuration of the table to use as a buckets store. By default, under the hood uses `BucketTableSettings.getDefault()`
====== addTableSettings
Takes `BucketTableSettings` - See <<listener, Overriding table configuration>>.

Parameters:
====== addClientSideConfig
Takes `ClientSideConfig` - is a client-side configuration for proxy-manager. By default, under the hood uses `ClientSideConfig.getDefault()`

`tableName` - name of table to use as a Buckets store

`idName` - name of id (PRIMARY KEY - BIGINT)
==== MySQL integration
===== Dependencies
To use Bucket4j extension for MySQL you need to add following dependency:
[source, xml, subs=attributes+]
----
<dependency>
<groupId>com.github.vladimir-bukhtoyarov</groupId>
<artifactId>bucket4j-mysql</artifactId>
<version>{revnumber}</version>
</dependency>
----

`stateName` - name of state (BYTEA)
===== Example of Bucket instantiation
----
Long key = 1L;
MySQLProxyManager proxyManager = new MySQLProxyManager(new MySQLProxyConfiguration(dataSource));
BucketConfiguration bucketConfiguration = BucketConfiguration.builder()
.addLimit(Bandwidth.simple(10, Duration.ofSeconds(1)))
.build();
BucketProxy bucket = proxyManager.builder().build(key, bucketConfiguration);
----

By default, uses: "buckets" as a `tableName`; "id" as a `idName`; "state" as a `stateName`
===== Configuring custom settings of MySQLProxyManager
* `MySQLProxyManager` takes a few configurations that you can set to make it customizable
* In order to do that, you should use `MySQLProxyConfigurationBuilder`, which includes the next parameters:
----
/**
* @param clientSideConfig {@link ClientSideConfig} client-side configuration for proxy-manager.
* By default, under the hood uses {@link ClientSideConfig#getDefault}
* @return {@link MySQLProxyConfigurationBuilder}
*/
public MySQLProxyConfigurationBuilder addClientSideConfig(ClientSideConfig clientSideConfig) {
this.clientSideConfig = clientSideConfig;
return this;
}
===== addClientSideConfig
/**
* @param tableSettings {@link BucketTableSettings} define a configuration of the table to use as a Buckets store.
* By default, under the hood uses {@link BucketTableSettings#getDefault}
* @return {@link MySQLProxyConfigurationBuilder}
*/
public MySQLProxyConfigurationBuilder addTableSettings(BucketTableSettings tableSettings) {
this.tableSettings = tableSettings;
return this;
}
----

* The example of usage `MySQLProxyConfigurationBuilder` to customize your setting for the `MySQLProxyManager`:
----
MySQLProxyConfiguration configuration = MySQLProxyConfigurationBuilder.builder()
.addClientSideConfig(ClientSideConfig.getDefault().withClientClock(TimeMeter.SYSTEM_MILLISECONDS))
.addTableSettings(BucketTableSettings.customSettings("tableName", "idName", "stateName"))
.build(dataSource);
MySQLProxyManager proxyManager = new MySQLProxyManager(configuration);
----

===== Parameters to customize table buckets storage settings via MySQLProxyConfigurationBuilder

====== addTableSettings
Takes `BucketTableSettings` - See <<listener, Overriding table configuration>>.

====== addClientSideConfig
Takes `ClientSideConfig` - is a client-side configuration for proxy-manager. By default, under the hood uses `ClientSideConfig.getDefault()`





13 changes: 13 additions & 0 deletions bucket4j-examples/pom.xml
Expand Up @@ -64,6 +64,19 @@
<version>2.3.8</version>
</dependency>

<!-- For MySQL examples -->
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>mysql</artifactId>
<version>1.16.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
</dependency>

<!-- For PostgreSQL examples -->
<dependency>
<groupId>org.testcontainers</groupId>
Expand Down
54 changes: 54 additions & 0 deletions bucket4j-mysql/pom.xml
@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>com.github.vladimir-bukhtoyarov</groupId>
<artifactId>bucket4j-parent</artifactId>
<version>7.2.0</version>
<relativePath>../bucket4j-parent</relativePath>
</parent>

<artifactId>bucket4j-mysql</artifactId>
<name>bucket4j-mysql</name>

<repositories>
<repository>
<id>central</id>
<name>Maven Central</name>
<url>https://repo1.maven.org/maven2/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>com.github.vladimir-bukhtoyarov</groupId>
<artifactId>bucket4j-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.github.vladimir-bukhtoyarov</groupId>
<artifactId>bucket4j-core</artifactId>
<version>${project.version}</version>
<type>test-jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>mysql</artifactId>
<version>1.16.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
</dependency>
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP-java6</artifactId>
<version>2.3.8</version>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,43 @@
package io.github.bucket4j.mysql;

import io.github.bucket4j.distributed.jdbc.BucketTableSettings;
import io.github.bucket4j.distributed.proxy.ClientSideConfig;

import javax.sql.DataSource;

public class MySQLProxyConfiguration {

private final DataSource dataSource;
private final ClientSideConfig clientSideConfig;
private final BucketTableSettings tableSettings;

public MySQLProxyConfiguration(DataSource dataSource) {
this(dataSource, ClientSideConfig.getDefault(), BucketTableSettings.getDefault());
}

public MySQLProxyConfiguration(DataSource dataSource, ClientSideConfig clientSideConfig, BucketTableSettings tableSettings) {
this.dataSource = dataSource;
this.clientSideConfig = clientSideConfig;
this.tableSettings = tableSettings;
}

public String getIdName() {
return tableSettings.getIdName();
}

public String getStateName() {
return tableSettings.getStateName();
}

public String getTableName() {
return tableSettings.getTableName();
}

public DataSource getDataSource() {
return dataSource;
}

public ClientSideConfig getClientSideConfig() {
return clientSideConfig;
}
}
@@ -0,0 +1,61 @@
package io.github.bucket4j.mysql;

import io.github.bucket4j.BucketExceptions;
import io.github.bucket4j.distributed.jdbc.BucketTableSettings;
import io.github.bucket4j.distributed.proxy.ClientSideConfig;

import javax.sql.DataSource;

/**
* @author Maxim Bartkov
* The class to build {@link MySQLProxyConfiguration}
*/
public final class MySQLProxyConfigurationBuilder {
private ClientSideConfig clientSideConfig;
private BucketTableSettings tableSettings;

private MySQLProxyConfigurationBuilder() {
}

public static MySQLProxyConfigurationBuilder builder() {
return new MySQLProxyConfigurationBuilder();
}

/**
* @param clientSideConfig {@link ClientSideConfig} client-side configuration for proxy-manager.
* By default, under the hood uses {@link ClientSideConfig#getDefault}
* @return {@link MySQLProxyConfigurationBuilder}
*/
public MySQLProxyConfigurationBuilder addClientSideConfig(ClientSideConfig clientSideConfig) {
this.clientSideConfig = clientSideConfig;
return this;
}

/**
* @param tableSettings {@link BucketTableSettings} define a configuration of the table to use as a Buckets store.
* By default, under the hood uses {@link BucketTableSettings#getDefault}
* @return {@link MySQLProxyConfigurationBuilder}
*/
public MySQLProxyConfigurationBuilder addTableSettings(BucketTableSettings tableSettings) {
this.tableSettings = tableSettings;
return this;
}

/**
* The method takes a {@link DataSource} as a required parameter
* @param dataSource - a database configuration
* @return {@link MySQLProxyConfiguration}
*/
public MySQLProxyConfiguration build(DataSource dataSource) {
if(dataSource == null) {
throw new BucketExceptions.BucketExecutionException("DataSource cannot be null");
}
if(tableSettings == null) {
this.tableSettings = BucketTableSettings.getDefault();
}
if(clientSideConfig == null) {
this.clientSideConfig = ClientSideConfig.getDefault();
}
return new MySQLProxyConfiguration(dataSource, clientSideConfig, tableSettings);
}
}

0 comments on commit b7f9788

Please sign in to comment.