We have built ADB (Kotlin) on Excelsior JET

We have compiled ADB (AcapellaDB) to a native binary with Excelsior JET.
Everything just works. Startup and first query time decreased by 10 times, but runtime performance has not changed.

Excelsior JET is not a JIT, it is an AOT compiler.

I previously wanted to obfuscate the code for the box version — this is it.

Time of first SELECT request:
Oracle JVM (general java) — 500 ms
Excelsior JET (compiled to bin) — 50 ms

Lets look at SELECT timings and compare compiled and interpreted version:

Compiled version doesn’t use dynamic runtime information and application profile, but source not extractable and performance is predictably high. The chart shows, that at first the AOT version is faster, but over time the JIT outperforms the AOT.

Начало работы с Acapella DB

Введение

Что такое Acapella DB?

Это NewSQL база данных. От новых NoSQL баз данных она взяла масштабируемость и отказоустойчивость, а от классических – мощный язык запросов и ACID транзакции.

Acapella DB поддерживает распределённые транзакции, непривязанные к соединению: можно запустить транзакцию в одном месте, а продолжить из другого, указав её идентификатор. Уровень изоляции транзакций всегда SERIALIZABLE.

В этой статье описано начало работы с базой: подключение через SqlLine и JDBC, создание схемы и заполнение данных.

Когда нужна Acapella DB?

При работе с большими объёмами реляционных данных. Для реляционных данных небольшого объёма (до 1 ТБ) существуют классические SQL базы. Для плохо структурированных или слабо связанных данных используются NoSQL базы. 

NewSQL – единственный способ работы с большим количеством сложно взаимосвязанных консистентных данных.

При необходимости распределённых транзакций. У вас есть веб-сервис, который должен поддерживать расширения через web-hooks, но при этом нужно запускать их в одной транзакции с изначальным запросом. Распределённые транзакции Acapella DB позволяют это сделать.

Acapella DB уже можно пользоваться?

Да, на февраль 2019 года Acapella DB на раннем этапе развития: поддерживается не весь SQL, не полностью реализовано распределение данных по узлам, возможны баги. Но мы уже реализовали распределённые транзакции и реплицирование. Доступ к текущей версии можно получить по адресу db.acapella.ru:4567.

Начало работы

У Acapella DB ещё нет своего консольного клиента, но можно воспользоваться утилитой sqlline, которая позволяет подключиться к любой SQL базе через JDBC драйвер. SqlLine написан на Java, поэтому поддерживаются Windows, Linux и MacOS. Здесь приведены команды для Linux.

Скачаем sqlline, специально собранный для работы с Acapella DB: со всеми зависимостями и JDBC драйвером:

1
wget https://github.com/AcapellaSoft/adb-tools/releases/download/0.2.0/sqlline-0.2.0.jar -O sqlline.jar

Для запуска требуется Java Runtime Environment (JRE) версии 1.8 или выше.

Создадим пользователя. Его имя должно быть уникальным:

1
2
$ java -jar sqlline.jar -u jdbc:acapelladb://db.acapella.ru:4567
> CREATE USER <username> WITH PASSWORD '<password>';

Сейчас имя базы данных привязано к имени пользователя. Для входа в свою базу, её имя должно совпадать с именем пользователя.

Подключимся к базе данных и создадим простую таблицу:

1
2
3
$ java -jar sqlline.jar -u jdbc:acapelladb://db.acapella.ru:4567/<username> -n <username> -p <password>
> CREATE TABLE test("key" VARCHAR, "value" VARCHAR, PRIMARY KEY("key"));
No rows affected (2.337 seconds)

Первые запросы после изменения схемы могут быть долгими, не пугайтесь :). Созданная таблица с именем test содержит две колонки: key и value, обе строкового типа. Колонка key является основным ключом, по нему данные таблицы будут распределяться по узлам базы. Создать таблицу без основного ключа пока нельзя.

Создадим первую запись в таблице:

1
2
> INSERT INTO test VALUES ('hello', 'world');
1 ROW affected (0.045 seconds)

и получим её обратно:

1
2
3
4
5
6
7
> SELECT * FROM test;
+--------+--------+
| KEY    | VALUE  |
+--------+--------+
| hello  | world  |
+--------+--------+
1 ROW selected (0.343 seconds)

Работа с базой через sqlline подходит для проведения экспериментов, создания и изменения схемы данных, но не для полноценной работы. Дальше рассмотрим другие варианты использования базы.

JDBC-клиент

Чтобы начать работу с JDBC, подключим adb-jdbc-driver к приложению:

Maven:

1
2
3
4
5
6
7
8
9
10
11
12
13
<repositories>
    <repository>
        <id>jcenter</id>
        <url>https://jcenter.bintray.com/</url>
    </repository>
</repositories>
...
<dependency>
    <groupId>ru.acapella.db</groupId>
    <artifactId>adb-jdbc-driver</artifactId>
    <version>0.2.0</version>
    <type>pom</type>
</dependency>

Gradle:

1
2
3
4
5
6
7
repositories {
  jcenter()
}
...
dependencies {
    compile 'ru.acapella.db:adb-jdbc-driver:0.2.0'
}

Создадим соединение:

1
2
3
4
5
6
7
8
// Kotlin
Class.forName("ru.acapella.db.jdbc.Driver")
val connection = DriverManager.getConnection(
  "jdbc:acapelladb://db.acapella.ru:4567/<username>",
  "<username>",
  "<password>"
)
connection.autoCommit = true
1
2
3
4
5
6
7
Class.forName("ru.acapella.db.jdbc.Driver");
Connection connection = DriverManager.getConnection(
  "jdbc:acapelladb://db.acapella.ru:4567/<username>",
  "<username>",
  "<password>"
);
connection.setAutoCommit(true);

Создадим и заполним таблицу:

1
2
3
4
// Kotlin
val statement = connection.createStatement()
statement.execute("""CREATE OR REPLACE TABLE test("key" VARCHAR, "value" VARCHAR, PRIMARY KEY("key"))""")
statement.execute("""INSERT INTO test VALUES ('hello', 'world')""")
1
2
3
4
// Java
Statement statement = connection.createStatement();
statement.execute("CREATE OR REPLACE TABLE test("key" VARCHAR, "value" VARCHAR, PRIMARY KEY("key"))");
statement.execute("INSERT INTO test VALUES ('hello', 'world')");

Выполним простую выборку:

1
2
3
4
5
6
7
// Kotlin
val resultSet = statement.executeQuery("""SELECT * FROM test""")
while (resultSet.next()) {
    val key = resultSet.getString(1)
    val value = resultSet.getString(2)
    println("$key, $value")
}
1
2
3
4
5
6
7
// Java
ResultSet resultSet = statement.executeQuery("SELECT * FROM test");
while (resultSet.next()) {
    String key = resultSet.getString(1);
    String value = resultSet.getString(2);
    System.out.printf("%s, %s", key, value);
}

JDBC клиент позволяет менять схему, обновлять и выбирать данные. Поддерживаются prepared statements, получение мета-информации о БД и основные типы данных. Уже сейчас можно запустить Hibernate поверх драйвера, чтобы работать с базой было удобнее.

«Real-world example»

Чтобы показать больше возможностей Acapella DB, создадим БД для работы с отелями, которую часто используют как пример работы с SQL:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
CREATE TABLE city (
  zip VARCHAR,
  name VARCHAR,
  state VARCHAR,
  PRIMARY KEY(zip)
);

CREATE TABLE customer (
  id INTEGER,
  title VARCHAR,
  first_name VARCHAR,
  name VARCHAR,
  zip VARCHAR,
  address VARCHAR,
  PRIMARY KEY(id)
);

CREATE TABLE hotel (
  id INTEGER,
  name VARCHAR,
  zip VARCHAR,
  address VARCHAR,
  PRIMARY KEY(id)
);

CREATE INDEX hotel_by_name ON hotel (name);

CREATE TABLE room (
  hotel_id INTEGER,
  TYPE VARCHAR,
  "free" DECIMAL,
  price DECIMAL,
  PRIMARY KEY (hotel_id, TYPE)
);

CREATE TABLE reservation (
  id INTEGER,
  customer_id INTEGER,
  hotel_id INTEGER,
  TYPE VARCHAR,
  arrival DATE,
  departure DATE,
  PRIMARY KEY(id)
);

Acapella DB поддерживает основные типы данных: INTEGER, VARCHAR, DATE и др. Сейчас на создание таблиц действует ограничение: они должны иметь основной ключ. Поддерживаются PRIIMARY KEY с одной колонкой или с несколькими. Уже поддерживаются индексы, но пока нельзя создать уникальный индекс. Нет ограничений на вставку значения с уже существующим PRIMARY KEY, при этом произойдёт обновление записи.

Заполним таблицы данными:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
INSERT INTO city VALUES ('60615','Chicago','IL');
INSERT INTO city VALUES ('45211','Cincinnati','OH');
INSERT INTO city VALUES ('33575','Clearwater','FL');
INSERT INTO city VALUES ('75243','Dallas','TX');
INSERT INTO city VALUES ('32018','Daytona Beach','FL');
INSERT INTO city VALUES ('33441','Deerfield Beach','FL');
INSERT INTO city VALUES ('48226','Detroit','MI');
INSERT INTO city VALUES ('90029','Hollywood','CA');
INSERT INTO city VALUES ('92714','Irvine','CA');
INSERT INTO city VALUES ('90804','Long Beach','CA');
INSERT INTO city VALUES ('11788','Long Island','NY');
INSERT INTO city VALUES ('90018','Los Angeles','CA');
INSERT INTO city VALUES ('70112','New Orleans','LA');
INSERT INTO city VALUES ('10580','New York','NY');
INSERT INTO city VALUES ('10019','New York','NY');
INSERT INTO city VALUES ('92262','Palm Springs','CA');
INSERT INTO city VALUES ('97213','Portland','OR');
INSERT INTO city VALUES ('60018','Rosemont','IL');
INSERT INTO city VALUES ('95054','Santa Clara','CA');
INSERT INTO city VALUES ('20903','Silver Spring','MD');
INSERT INTO city VALUES ('20037','Seattle','WA');
INSERT INTO city VALUES ('20005','Seattle','WA');
INSERT INTO city VALUES ('20019','Seattle','WA');

INSERT INTO customer VALUES (3000,'Mrs','Jenny','Porter','10580','1340 N. Ash Street, #3');
INSERT INTO customer VALUES (3100,'Mr','Peter','Brown','48226','1001 34th St., APT.3');
INSERT INTO customer VALUES (3200,'Company',NULL,'Datasoft','90018','486 Maple St.');
INSERT INTO customer VALUES (3300,'Mrs','Rose','Brian','75243','500 Yellowstone Drive, #2');
INSERT INTO customer VALUES (3400,'Mrs','Mary','Griffith','20005','3401 Elder Lane');
INSERT INTO customer VALUES (3500,'Mr','Martin','Randolph','60615','340 MAIN STREET, #7');
INSERT INTO customer VALUES (3600,'Mrs','Sally','Smith','75243','250 Curtis Street');
INSERT INTO customer VALUES (3700,'Mr','Mike','Jackson','45211','133 BROADWAY APT. 1');
INSERT INTO customer VALUES (3800,'Mrs','Rita','Doe','97213','2000 Humboldt St., #6');
INSERT INTO customer VALUES (3900,'Mr','George','Howe','75243','111 B Parkway, #23');
INSERT INTO customer VALUES (4000,'Mr','Frank','Miller','95054','27 5th St., 76');
INSERT INTO customer VALUES (4100,'Mrs','Susan','Baker','90018','200 MAIN STREET, #94');
INSERT INTO customer VALUES (4200,'Mr','Joseph','Peters','92714','700 S. Ash St., APT.12');
INSERT INTO customer VALUES (4300,'Company',NULL,'TOOLware','20019','410 Mariposa St., #10');
INSERT INTO customer VALUES (4400,'Mr','Antony','Jenkins','20903','55 A Parkway, #15');

INSERT INTO hotel VALUES (10,'Congress','20005','155 Beechwood St.');
INSERT INTO hotel VALUES (30,'Regency','20037','477 17th Avenue');
INSERT INTO hotel VALUES (20,'Long Island','11788','1499 Grove Street');
INSERT INTO hotel VALUES (70,'Empire State','12203','65 Yellowstone Dr.');
INSERT INTO hotel VALUES (80,'Midtown','10019','12 Barnard St.');
INSERT INTO hotel VALUES (40,'Eighth Avenue','10019','112 8th Avenue');
INSERT INTO hotel VALUES (50,'Lake Michigan','60601','354 OAK Terrace');
INSERT INTO hotel VALUES (60,'Airport','60018','650 C Parkway');
INSERT INTO hotel VALUES (90,'Sunshine','33575','200 Yellowstone Dr.');
INSERT INTO hotel VALUES (100,'Beach','32018','1980 34th St.');
INSERT INTO hotel VALUES (110,'Atlantic','33441','111 78th St.');
INSERT INTO hotel VALUES (120,'Long Beach','90804','35 Broadway');
INSERT INTO hotel VALUES (150,'Indian Horse','92262','16 MAIN STREET');
INSERT INTO hotel VALUES (130,'Star','90029','13 Beechwood Place');
INSERT INTO hotel VALUES (140,'River Boat','70112','788 MAIN STREET');

INSERT INTO room VALUES (10,'single',20,135.00);
INSERT INTO room VALUES (10,'double',45,200.00);
INSERT INTO room VALUES (30,'single',12,45.00);
INSERT INTO room VALUES (30,'double',15,80.00);
INSERT INTO room VALUES (20,'single',10,70.00);
INSERT INTO room VALUES (20,'double',13,100.00);
INSERT INTO room VALUES (70,'single',4,115.00);
INSERT INTO room VALUES (70,'double',11,180.00);
INSERT INTO room VALUES (80,'single',15,90.00);
INSERT INTO room VALUES (80,'double',19,150.00);
INSERT INTO room VALUES (80,'suite',5,400.00);
INSERT INTO room VALUES (40,'single',20,85.00);
INSERT INTO room VALUES (40,'double',35,140.00);
INSERT INTO room VALUES (50,'single',50,105.00);
INSERT INTO room VALUES (50,'double',230,180.00);
INSERT INTO room VALUES (50,'suite',12,500.00);
INSERT INTO room VALUES (60,'single',10,120.00);
INSERT INTO room VALUES (60,'double',39,200.00);
INSERT INTO room VALUES (60,'suite',20,500.00);
INSERT INTO room VALUES (90,'single',45,90.00);
INSERT INTO room VALUES (90,'double',145,150.00);
INSERT INTO room VALUES (90,'suite',60,300.00);
INSERT INTO room VALUES (100,'single',11,60.00);
INSERT INTO room VALUES (100,'double',24,100.00);
INSERT INTO room VALUES (110,'single',2,70.00);
INSERT INTO room VALUES (110,'double',10,130.00);
INSERT INTO room VALUES (120,'single',34,80.00);
INSERT INTO room VALUES (120,'double',78,140.00);
INSERT INTO room VALUES (120,'suite',55,350.00);
INSERT INTO room VALUES (150,'single',44,100.00);
INSERT INTO room VALUES (150,'double',115,190.00);
INSERT INTO room VALUES (150,'suite',6,450.00);
INSERT INTO room VALUES (130,'single',89,160.00);
INSERT INTO room VALUES (130,'double',300,270.00);
INSERT INTO room VALUES (130,'suite',100,700.00);
INSERT INTO room VALUES (140,'single',10,125.00);
INSERT INTO room VALUES (140,'double',9,200.00);
INSERT INTO room VALUES (140,'suite',78,600.00);

INSERT INTO reservation VALUES (100,3000,80,'single',DATE'2004-11-13',DATE'2004-11-15');
INSERT INTO reservation VALUES (110,3000,100,'double',DATE'2004-12-24',DATE'2005-01-06');
INSERT INTO reservation VALUES (120,3200,50,'suite',DATE'2004-11-14',DATE'2004-11-18');
INSERT INTO reservation VALUES (130,3900,110,'single',DATE'2005-02-01',DATE'2005-02-03');
INSERT INTO reservation VALUES (150,3600,70,'double',DATE'2005-03-14',DATE'2005-03-24');
INSERT INTO reservation VALUES (140,4300,80,'double',DATE'2004-04-12',DATE'2004-04-30');
INSERT INTO reservation VALUES (160,4100,70,'single',DATE'2004-04-12',DATE'2004-04-15');
INSERT INTO reservation VALUES (170,4400,150,'suite',DATE'2004-09-01',DATE'2004-09-03');
INSERT INTO reservation VALUES (180,3100,120,'double',DATE'2004-12-23',DATE'2005-01-08');
INSERT INTO reservation VALUES (190,4300,140,'double',DATE'2004-11-14',DATE'2004-11-17');

Acapella DB поддерживает сложные условия выборок: JOIN, ORDER BY, агрегации, алиасы. Сделаем несколько выборок.

Выберем все отели в Нью-Йорке, отсортированные по названию:

1
2
3
4
5
SELECT h.name, h.id, h.address
FROM hotel h
JOIN city c ON h.zip = c.zip
WHERE c.name = 'New York'
ORDER BY h.name;

результат:

+---------------+------------+----------------+
|     name      | id         | address        |
+---------------+------------+----------------+
| Eighth Avenue | 40         | 112 8th Avenue |
| Midtown       | 80         | 12 Barnard St. |
+---------------+------------+----------------+

Выберем среднюю цену за комнату:

1
SELECT AVG(price) AS "avg" FROM room;

результат:

+---------------------+
|        avg          |
+---------------------+
| 201.5789473684211   |
+---------------------+

Выберем количество свободных комнат по отелям, отсортируем отели по названию:

1
2
3
4
SELECT h.name, SUM(r."free") AS "sum"
FROM hotel h
JOIN room r ON h.id = r.hotel_id
GROUP BY h.name;

результат:

+---------------+---------------------+
|     name      | sum                 |
+---------------+---------------------+
| Congress      | 65                  |
| Regency       | 27                  |
| Indian Horse  | 165                 |
| Long Island   | 23                  |
| Lake Michigan | 292                 |
| Sunshine      | 250                 |
| Eighth Avenue | 55                  |
| Beach         | 35                  |
| Star          | 489                 |
| Long Beach    | 167                 |
| River Boat    | 97                  |
| Airport       | 69                  |
| Empire State  | 15                  |
| Atlantic      | 12                  |
| Midtown       | 39                  |
+---------------+---------------------+

Заключение

Мы рассмотрели Acapella DB со стороны классических SQL баз данных: подключились через JDBC, создали реляционную схему данных, выполнили запросы с использованием джоинов и агрегаций. Но мы ещё далеки от уровня MySQL или PostgreSQL. Поддержка SQL позволяет попробовать Acapella DB без серьёзных изменений в стеке технологий. 

Мы рассказали только про SQL, но в Acapella DB есть ещё много интересного: распределение данных по узлам, поддержка консистентности в распределённых транзакциях.

Подробнее об этом мы расскажем в следующих статьях, которые будут в ближайшее время.

FAQ — frequently asked questions

How is the launch of new machines scaled?

When we launch new tasks, how is the load distributed across the cluster?
the search for nodes for execution? . Is there a task transfer? statistics are taken into account .. what? is it collected and used by you? to select the machine it is used, for example?

To select a machine, the following indicators are used:

* number of tasks in the outskirts,
* Number of active tasks,
* number of sleeping tasks,
* free ram (rss), free jvm heap

The statistics you see in the «CLI» for balancing are not used

Do you have better logging than competitors?

There are no restrictions (now) on the size, but also to block the cluster by hammering aggressively with logs. backpressure all the way data movement.

It is much easier (aggregating or dividing logs through a couple of lines in the launch configuration)

Inside the fragments, you can just do nothing

Partial order guarantee with parallel execution.

The language of the fragment does not matter, logging from the user’s point of view works the same.

how is the fragment restarted?

>  only on the basis of conflicts and only in «transactional launches»? how do I configure it to happen?

You do not need to work on your own to have a restart.

AcapellaVM (CPVM) in the «acapella preset» can detect an access conflict (control flow error caused by excessive concurrency of execution)
and sometimes with the help of restart, fix it unnoticed by the user.

Restarting is a way to fix a detected error. You can not fix it with restarting under all conditions.

Restart of the fragment will occur only if (conflict or (if the failover and the applicationerver are not working)) And the allowRestart flag is used in the parameters of the transaction start)

What if snapshots are covered (overal fragments)?

> The user upload a snapshot with two fragments.
> One of the fragments completely match with some existing fragment in the CodeBase (for simplicity, let’s say it’s the same for both the code and the metadata).

The current implementation of snapshots will not write a new fragment to CodeBase, but in the new snapshot it will refer to the existing fragment.
The fragment is the same, but it participates in different snapshots.

Interpret the same fragments is also important because cpvm collects statistics for analyzing LPF of projects, therefore it is required, if possible.
Do not refill what’s already in CodeBase

in the file ~ / .netrc what kind of hash is this?

This is a token, suitable for curl queries in the API.

From curl it is possible to work, substituting the parameter -u «USER: <TOKEN>» — <TOKEN> can be taken from ~ / .netrc after authorization in the launcher

Also in curl there is a parameter -netrc

Запись 5

Lorem ipsum — dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam,

Запись 4

Lorem ipsum — dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam,

Запись 3

Lorem ipsum — dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam,

запись2

Lorem ipsum — dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam,