Персистентные структуры данных и архитектура

87
Функциональные (персистентные) структуры данных и их влияние на архитектуру приложений Vadim Shalts Vadim Shalts Функциональные структуры данных и архитектура 1 / 86

Upload: vadim-shalts

Post on 13-Jul-2015

1.767 views

Category:

Software


12 download

TRANSCRIPT

Page 1: Персистентные структуры данных и архитектура

Функциональные (персистентные) структуры данных иих влияние на архитектуру приложений

Vadim Shalts

Vadim Shalts Функциональные структуры данных и архитектура 1 / 86

Page 2: Персистентные структуры данных и архитектура

Персистентные структуры данных и их анализ

Оговорки

Vadim Shalts Функциональные структуры данных и архитектура 2 / 86

Page 3: Персистентные структуры данных и архитектура

Персистентные структуры данных и их анализ

Сase классы

Scala:

case class Foo(myval: String)

Java:final class Foo {

private final String myval;

public Foo(String initialValue) {this.myval = initialValue;

}

public String getValue() {return this.myval;

}

@Overridepublic boolean equals(Object o) {

// ....}

@Overridepublic int hashCode() {

// ...}

}

Возможные решения для Java

projectlombok.org

immutables.org

github.com/google/auto

www.jannocessor.org

Scala, Groovy, Clojure :)

Vadim Shalts Функциональные структуры данных и архитектура 3 / 86

Page 4: Персистентные структуры данных и архитектура

Персистентные структуры данных и их анализ

Сase классы

Scala:

case class Foo(myval: String)

Java:final class Foo {

private final String myval;

public Foo(String initialValue) {this.myval = initialValue;

}

public String getValue() {return this.myval;

}

@Overridepublic boolean equals(Object o) {

// ....}

@Overridepublic int hashCode() {

// ...}

}

Возможные решения для Java

projectlombok.org

immutables.org

github.com/google/auto

www.jannocessor.org

Scala, Groovy, Clojure :)

Vadim Shalts Функциональные структуры данных и архитектура 3 / 86

Page 5: Персистентные структуры данных и архитектура

Персистентные структуры данных и их анализ

Project valhalla. JEP 169: Value Objects

Vadim Shalts Функциональные структуры данных и архитектура 4 / 86

Page 6: Персистентные структуры данных и архитектура

Персистентные структуры данных и их анализ

Важные свойства неизменяемых (immutable) объектов

Легко создавать, тестировать и использоватьЯвляются потокобезопаснымиХорошие кандидаты для кэшированияПоддерживают инвариант состоянияИсключения не могут “разломать” состояние

Vadim Shalts Функциональные структуры данных и архитектура 5 / 86

Page 7: Персистентные структуры данных и архитектура

Персистентные структуры данных и их анализ

Класс будет неизменяемым, если верно следующее:

Класс объявляется как finalВсе его поля являются finalСсылка this не должна пропасть во время конструированияНе содержат методов, которые могли бы изменить наблюдаемоесостояние объектаЛюбые поля, содержащие ссылки на изменяемые объекты,например массивы, совокупности или изменяемые классы,например Date:

Являются приватнымиНикогда не возвращаются и никаким другим образом нестановятся доступными вызывающим операторамЯвляются единственными ссылками на те объекты, на которые ониссылаютсяНе изменяют после конструирования состояние объектов, накоторые они ссылаются

Vadim Shalts Функциональные структуры данных и архитектура 6 / 86

Page 8: Персистентные структуры данных и архитектура

Персистентные структуры данных и их анализ

JLS. Семантика final полей

«A thread-safe immutable object is seen as immutable by all threads,even if a data race is used to pass references to the immutable objectbetween threads»

JLS 17.5

Vadim Shalts Функциональные структуры данных и архитектура 7 / 86

Page 9: Персистентные структуры данных и архитектура

Персистентные структуры данных и их анализ Необходимость (или в чём проблемы)

Data Processing in Exascale-class Computing Systems | April 27, 2011 | CRM 3

Transistors (thousands) Single-thread Performance (SpecINT) Frequency (MHz) Typical Power (Watts) Number of Cores

Original data collected and plotted by M. Horowitz, F. Labonte, O. Shacham, K. Olukotun, L. Hammond and C. Batten Dotted line extrapolations by C. Moore

35 YEARS OF MICROPROCESSOR TREND DATA

Vadim Shalts Функциональные структуры данных и архитектура 8 / 86

Page 10: Персистентные структуры данных и архитектура

Персистентные структуры данных и их анализ Необходимость (или в чём проблемы)

Закон Амдала. Рост производительностивычислительной системы

Vadim Shalts Функциональные структуры данных и архитектура 9 / 86

Page 11: Персистентные структуры данных и архитектура

Персистентные структуры данных и их анализ Необходимость (или в чём проблемы)

Функциональное программирование

«In computer science, functional programming is aprogramming paradigm, a style of building the structure andelements of computer programs, that treats computation asthe evaluation of mathematical functions and avoids stateand mutable data.»

wikipedia

Vadim Shalts Функциональные структуры данных и архитектура 10 / 86

Page 12: Персистентные структуры данных и архитектура

Персистентные структуры данных и их анализ Необходимость (или в чём проблемы)

Простая нотация

f (x)Что скрывается за простой нотацией?

Vadim Shalts Функциональные структуры данных и архитектура 11 / 86

Page 13: Персистентные структуры данных и архитектура

Персистентные структуры данных и их анализ Необходимость (или в чём проблемы)

Сложность f(x)

int[] func(int[] param);

Какова сложность, если параметр:простой типпростой объектколлекция простых типов...

Vadim Shalts Функциональные структуры данных и архитектура 12 / 86

Page 14: Персистентные структуры данных и архитектура

Персистентные структуры данных и их анализ Необходимость (или в чём проблемы)

Традиционный подход

class Order {private ArrayList<Item> items = EMPTY_LIST;private double total = 0.0;

void addItem(Item item) {items.add(item);total = calcTotal();

}

String createCheck() {// use items and total ...// ....

}

// ....}

Vadim Shalts Функциональные структуры данных и архитектура 13 / 86

Page 15: Персистентные структуры данных и архитектура

Персистентные структуры данных и их анализ Необходимость (или в чём проблемы)

Традиционный подход

class Order {private ArrayList<Item> items = EMPTY_LIST;private double total = 0.0;

synchronized void addItem(Item item) {items.add(item);total = calcTotal();

}

synchronized String createCheck() {// use items and total ...// ....

}

// ....}

Vadim Shalts Функциональные структуры данных и архитектура 14 / 86

Page 16: Персистентные структуры данных и архитектура

Персистентные структуры данных и их анализ Необходимость (или в чём проблемы)

Java Language and Virtual Machine Specifications (14.19)

If execution of the Block completes normally, then themonitor is unlocked and the synchronized statementcompletes normally.

If execution of the Block completes abruptly for anyreason, then the monitor is unlocked and thesynchronized statement completes abruptly for thesame reason.

Vadim Shalts Функциональные структуры данных и архитектура 15 / 86

Page 17: Персистентные структуры данных и архитектура

Персистентные структуры данных и их анализ Необходимость (или в чём проблемы)

Потеряна предсказуемость

Потеряна предсказуемость.Что можно сделать?

Vadim Shalts Функциональные структуры данных и архитектура 16 / 86

Page 18: Персистентные структуры данных и архитектура

Персистентные структуры данных и их анализ Необходимость (или в чём проблемы)

Традиционный подход. Сохраняем состояние ошибки

class Order {private ArrayList<Item> items = EMPTY_LIST;private double total = 0.0;private boolean incorrectState = false;

synchronized void addItem(Item item) {

if (incorrectState)throw new IllegalStateException();

incorrectState = true;

items.add(item);total = calcTotal();

incorrectState = false;}

// ....}

Vadim Shalts Функциональные структуры данных и архитектура 17 / 86

Page 19: Персистентные структуры данных и архитектура

Персистентные структуры данных и их анализ Необходимость (или в чём проблемы)

Традиционный подход. Предотвращаем потерюсостояния

class Order {private List<Item> items = EMPTY_LIST;private double total = 0.0;

synchronized void addItem(Item item) {List<Item> newItems =

fastCopyAndAppend(items, item); // Как?

double newTotal = calcTotal(newItems);

items = newItems;total = newTotal;

}

// ....}

Vadim Shalts Функциональные структуры данных и архитектура 18 / 86

Page 20: Персистентные структуры данных и архитектура

Персистентные структуры данных и их анализ Необходимость (или в чём проблемы)

Еще лучше сделать класс Order неизменяемым(immutable)!

«Classes should be immutable unless there’s a very goodreason to make them mutable.»

Joshua Bloch

«If an object cannot change state, then it can neverencounter conflicts or inconsistencies when multiple activitiesattempt to change its state in incompatible ways. Programsare much simpler to understand if existing objects are neverchanged, but instead new ones are continually created duringthe course of any computation.»

Doug Lea

Vadim Shalts Функциональные структуры данных и архитектура 19 / 86

Page 21: Персистентные структуры данных и архитектура

Персистентные структуры данных и их анализ Определимся с терминологией

Термины

Неизменяемые (Immutable) структуры данных послесоздания не могут быть изменены

Immutable структуры данных (обычно) могут быть скопированы сизменениями для создания новой версииСоздание новой версии требует столько же памяти сколькопотребляет оригинал.

Персистентные (Persistent) структуры данных послеизменения содержат старую и новую версию данных

Persistent структуры данных являются Immutable в том смысле, чтосоздание новой версии не приводит к изменению старых версийСоздание новой версии может потребовать копирования данных изоригинала. При этом старая и новые версии будут содержатьобщие данные

Vadim Shalts Функциональные структуры данных и архитектура 20 / 86

Page 22: Персистентные структуры данных и архитектура

Персистентные структуры данных и их анализ Примеры структур данных и их анализ

Обозначения

val x = X Y Z null

val x = X Y Z

Vadim Shalts Функциональные структуры данных и архитектура 21 / 86

Page 23: Персистентные структуры данных и архитектура

Персистентные структуры данных и их анализ Примеры структур данных и их анализ

Список. Начальный

val x1 = X Y Z

Vadim Shalts Функциональные структуры данных и архитектура 22 / 86

Page 24: Персистентные структуры данных и архитектура

Персистентные структуры данных и их анализ Примеры структур данных и их анализ

Список. Добавление

val x1 = X Y Z

val x2 = W

Vadim Shalts Функциональные структуры данных и архитектура 23 / 86

Page 25: Персистентные структуры данных и архитектура

Персистентные структуры данных и их анализ Примеры структур данных и их анализ

Список. Удаление

val x1 = X Y Z

val x2 = W

val x3 =

Vadim Shalts Функциональные структуры данных и архитектура 24 / 86

Page 26: Персистентные структуры данных и архитектура

Персистентные структуры данных и их анализ Примеры структур данных и их анализ

Как получить такие коллекции в Java?

<dependency><groupId>org.clojure</groupId><artifactId>clojure</artifactId><version>1.6.0</version>

</dependency>

Илиgithub.com/krukow/clj-ds

pcollections.orgwww.functionaljava.org

Vadim Shalts Функциональные структуры данных и архитектура 25 / 86

Page 27: Персистентные структуры данных и архитектура

Персистентные структуры данных и их анализ Примеры структур данных и их анализ

Использование списка из Java

import com.github.krukow.clj_ds.*;import java.util.LinkedList;

public class ClojureListTest {

public static void main(String[] args) {PersistentList<String>

listCopy = Persistents.linkedList(new LinkedList());

PersistentList<String> list =Persistents.linkedList("my", "list");

PersistentList<String> extended = list.plus("!!!");PersistentList<String> cutted = extended.minus().minus();

System.out.println(list); // ("my" "list")System.out.println(extended); // ("!!!" "my" "list")System.out.println(cutted); // ("list")

}}

Vadim Shalts Функциональные структуры данных и архитектура 26 / 86

Page 28: Персистентные структуры данных и архитектура

Персистентные структуры данных и их анализ Примеры структур данных и их анализ

FIFO очередь. Начальное состояние

val x1 =

Queue

enqueue list

dequeue list

5 4

1 2 3

Vadim Shalts Функциональные структуры данных и архитектура 27 / 86

Page 29: Персистентные структуры данных и архитектура

Персистентные структуры данных и их анализ Примеры структур данных и их анализ

FIFO очередь. Вставка

val x1 =

Queue

enqueue list

dequeue list

5 4

1 2 3

Queue

enqueue list

dequeue list

val x2 =6

Vadim Shalts Функциональные структуры данных и архитектура 28 / 86

Page 30: Персистентные структуры данных и архитектура

Персистентные структуры данных и их анализ Примеры структур данных и их анализ

FIFO очередь. Удаление

val x1 =

Queue

enqueue list

dequeue list

5 4

1 2 3

Queue

enqueue list

dequeue list

val x2 =6

Queue

enqueue list

dequeue list

val x3 =

Vadim Shalts Функциональные структуры данных и архитектура 29 / 86

Page 31: Персистентные структуры данных и архитектура

Персистентные структуры данных и их анализ Примеры структур данных и их анализ

FIFO очередь. Удаление

val x5 =

Queue

enqueue list

dequeue list

Queue

enqueue list

dequeue list

Queue

enqueue list

dequeue list

val x6 =

6 5 4

4 5 6

Vadim Shalts Функциональные структуры данных и архитектура 30 / 86

Page 32: Персистентные структуры данных и архитектура

Персистентные структуры данных и их анализ Примеры структур данных и их анализ

Использование очереди из Java

import com.github.krukow.clj_lang.PersistentQueue;

public class ClojureQueueTest {

public static void main(String[] args) {PersistentQueue empty = PersistentQueue.EMPTY;

PersistentQueue queue = empty.cons("my").cons("queue");PersistentQueue extended = queue.cons("!!!");PersistentQueue cutted = extended.pop();

System.out.println(queue.seq()); // ("my" "queue")System.out.println(extended.seq()); // ("my" "queue" "!!!")System.out.println(cutted.seq()); // ("queue" "!!!")

}}

Vadim Shalts Функциональные структуры данных и архитектура 31 / 86

Page 33: Персистентные структуры данных и архитектура

Персистентные структуры данных и их анализ Примеры структур данных и их анализ

Вектор (Vector)Bit-partitioned hash tries

... 00000 00000 00000 = 0

... 00000 00000 11111 = 31

... 00000 00001 00000 = 32

... 00000 00001 11111 = 63

Values

bits 0-4

bits 5-9

Root

0 1 31 32 33 63

0 1 ... ... 31 0 1 ... ... 31

0 1

v1

Vadim Shalts Функциональные структуры данных и архитектура 32 / 86

Page 34: Персистентные структуры данных и архитектура

Персистентные структуры данных и их анализ Примеры структур данных и их анализ

Вектор (Vector). Замена элемента

Values

bits 0-4

bits 5-9

Root

0 1 31 32 33 63 Z

0 1 ... ... 31 0 1 ... ... 31 0 1 ... ... 31

0 1 0 1

v1 v2

Vadim Shalts Функциональные структуры данных и архитектура 33 / 86

Page 35: Персистентные структуры данных и архитектура

Персистентные структуры данных и их анализ Примеры структур данных и их анализ

Вектор (Vector). Полная структура

Values

bits 0-4

bits 5-9

Root

0 1 31 32 33 63 64 65

0 1 ... ... 31 0 1 ... ... 31

0 1

0 1

v1

Vadim Shalts Функциональные структуры данных и архитектура 34 / 86

Page 36: Персистентные структуры данных и архитектура

Персистентные структуры данных и их анализ Примеры структур данных и их анализ

Вектор (Vector). Добавление

Values

bits 0-4

bits 5-9

Root

0 1 31 32 33 63 64 65 66

0 1 ... ... 31 0 1 ... ... 31

0 1 0 1 2

0 1

v1 v2

Vadim Shalts Функциональные структуры данных и архитектура 35 / 86

Page 37: Персистентные структуры данных и архитектура

Персистентные структуры данных и их анализ Примеры структур данных и их анализ

Vector. Пример

import com.github.krukow.clj_ds.*;

public class PersistentVectorTest {

public static void main(String[] args) {PersistentVector<Integer> p = Persistents.vector(1, 2, 3);

PersistentVector<Integer> extended = p.plus(4).plus(5);PersistentVector<Integer> updated = p.plusN(1, 22);PersistentVector<Integer> cutted = p.minus();

System.out.println(p); // [1 2 3]System.out.println(extended); // [1 2 3 4 5]System.out.println(updated); // [1 22 3]System.out.println(cutted); // [1 2]

}}

Vadim Shalts Функциональные структуры данных и архитектура 36 / 86

Page 38: Персистентные структуры данных и архитектура

Персистентные структуры данных и их анализ Примеры структур данных и их анализ

Улучшаем скорость. Переходные (Transient) структурыданных

Инсоляция в рамках одного потокаO(1)

создание из персистентных структур данныхОбщие данных с персистентным источникомO(1)

создание персистентных структур данных, когдаредактирование завершеноНельзя использовать после создания конечной персистентнойверсииБыстрые

Vadim Shalts Функциональные структуры данных и архитектура 37 / 86

Page 39: Персистентные структуры данных и архитектура

Персистентные структуры данных и их анализ Примеры структур данных и их анализ

Transient структуры данных. Пример

import com.github.krukow.clj_ds.*;

public class TransienVectorTest {

public static void main(String[] args) {PersistentVector p = Persistents.vector(1, 2, 3);

TransientVector t = p.asTransient();

TransientVector tf = t.plus(4).plus(5); // efficient bulk modification

PersistentVector p2 = tf.persist();

System.out.println(p); // [1 2 3]System.out.println(p2); // [1 2 3 4 5]

}}

Vadim Shalts Функциональные структуры данных и архитектура 38 / 86

Page 40: Персистентные структуры данных и архитектура

Персистентные структуры данных и их анализ Примеры структур данных и их анализ

До создания Transient

Values

bits 0-4

bits 5-9

Root

0 1 31 32 33 63 64 65

0 1 ... ... 31 0 1 ... ... 31

0 1

0 1

v1

Vadim Shalts Функциональные структуры данных и архитектура 39 / 86

Page 41: Персистентные структуры данных и архитектура

Персистентные структуры данных и их анализ Примеры структур данных и их анализ

Transient Vector

Values

bits 0-4

bits 5-9

Root

0 1 31 32 33 63 64 65

0 1 ... ... 31 0 1 ... ... 31

0 1

0 1

v1 Transient

Vadim Shalts Функциональные структуры данных и архитектура 40 / 86

Page 42: Персистентные структуры данных и архитектура

Персистентные структуры данных и их анализ Примеры структур данных и их анализ

Transient Vector. Дешёвое добавление

Values

bits 0-4

bits 5-9

Root

0 1 31 32 33 63 64 65 66

0 1 ... ... 31 0 1 ... ... 31

0 1 0 1 ... ... 31

0 1

v1 Transient

Vadim Shalts Функциональные структуры данных и архитектура 41 / 86

Page 43: Персистентные структуры данных и архитектура

Персистентные структуры данных и их анализ Примеры структур данных и их анализ

Transient Vector. Дешёвое добавление

Values

bits 0-4

bits 5-9

Root

0 1 31 32 33 63 64 65 66 67

0 1 ... ... 31 0 1 ... ... 31

0 1 0 1 ... ... 31

0 1

v1 Transient

Vadim Shalts Функциональные структуры данных и архитектура 42 / 86

Page 44: Персистентные структуры данных и архитектура

Персистентные структуры данных и их анализ Примеры структур данных и их анализ

Возврат к Persistent Vector

Values

bits 0-4

bits 5-9

Root

0 1 31 32 33 63 64 65 66 67

0 1 ... ... 31 0 1 ... ... 31

0 1 0 1 2 3

0 1

v1 v2

Vadim Shalts Функциональные структуры данных и архитектура 43 / 86

Page 45: Персистентные структуры данных и архитектура

Персистентные структуры данных и их анализ Примеры структур данных и их анализ

RRB trees - Relaxed Radix Balanced Trees

“RRB-Trees: Efficient Immutable Vectors”,EPFL-REPORT-169879, September, 2011:http://infoscience.epfl.ch/record/169879/files/RMTrees.pdf

Дополнительные операции c O(logN

):

объединение коллекций (concatenation)вставка в позицию (insert-at)разделение на части (splits-at)

Реализации:github.com/clojure/core.rrb-vectorgithub.com/TiarkRompf/rrbtrees

Vadim Shalts Функциональные структуры данных и архитектура 44 / 86

Page 46: Персистентные структуры данных и архитектура

Персистентные структуры данных и их анализ Примеры структур данных и их анализ

HashMap. Hash Array Mapped Trie (HAMT)

Bit-partitioned hash tries

Vadim Shalts Функциональные структуры данных и архитектура 45 / 86

Page 47: Персистентные структуры данных и архитектура

Персистентные структуры данных и их анализ Примеры структур данных и их анализ

HashMap. ДобавлениеPath Copyingint count 15

INode root

HashMapint count 16

INode root

HashMap

Vadim Shalts Функциональные структуры данных и архитектура 46 / 86

Page 48: Персистентные структуры данных и архитектура

Персистентные структуры данных и их анализ Примеры структур данных и их анализ

Общие свойства

ВерсионностьПотоковая безопасностьБезопасность для итерированияУменьшение потребление памяти (для несколькихкопий)Возможно увеличение потребления памяти (дляодной копии)Простота использованияПроизводительность?

Vadim Shalts Функциональные структуры данных и архитектура 47 / 86

Page 49: Персистентные структуры данных и архитектура

Персистентные структуры данных и их анализ Примеры структур данных и их анализ

Теория за бортом

Детальный анализ производительностиЛинзы (Functional lenses) - упрощают работу свложенными неизменяемыми структурами данныхZipper - техника представления объединённыхструктур данных. Ускоряет обработку персистентныхколлекций.Ctrie - concurrent hash-trie

Vadim Shalts Функциональные структуры данных и архитектура 48 / 86

Page 50: Персистентные структуры данных и архитектура

Влияние на архитектуру приложений Применение и примеры архитектуры

Архитектура

Vadim Shalts Функциональные структуры данных и архитектура 49 / 86

Page 51: Персистентные структуры данных и архитектура

Влияние на архитектуру приложений Применение и примеры архитектуры

Undo/Redo. Команды

trait UndoableCommand {def redo()def undo()

}

var currentState = new StringBuilder()

class InsertCommand(pos: Int, value: String)extends UndoableCommand {

def redo() = currentState.insert(pos, value)def undo() = currentState.delete(pos, value.length)

}

Vadim Shalts Функциональные структуры данных и архитектура 50 / 86

Page 52: Персистентные структуры данных и архитектура

Влияние на архитектуру приложений Применение и примеры архитектуры

Undo/Redo + шаблон Хранитель (Memento)

case class Memento(storedState: String)

var currentState = new StringBuilder()val stateHistory = collection.mutable.Stack[Memento]()

class InsertCommand(pos: Int, value: String)extends UndoableCommand {

def redo() = {stateHistory.push( new Memento(currentState.mkString) )currentState.insert(pos, value)

}def undo() = {

currentState.clear()currentState.append( stateHistory.pop.storedState )

}}

Vadim Shalts Функциональные структуры данных и архитектура 51 / 86

Page 53: Персистентные структуры данных и архитектура

Влияние на архитектуру приложений Применение и примеры архитектуры

Undo/Redo + Персистентные структуры данных

case class Article(name: String, price: Double)case class Order(goods: Vector[Article], total: Double)

type State = Ordertype UndoableCommand = State => State

var currentState = new State(Vector.empty, 0)val stateHistory = collection.mutable.Stack[State]()

def executeCommand(command: UndoableCommand) = {stateHistory.push( currentState )currentState = command(currentState)

}

def undoCommand() =currentState = stateHistory.pop()

Vadim Shalts Функциональные структуры данных и архитектура 52 / 86

Page 54: Персистентные структуры данных и архитектура

Влияние на архитектуру приложений Применение и примеры архитектуры

Undo/Redo + Персистентные структуры данных

def buyArticle(article: Article): UndoableCommand =(state: State) =>

new State(state.goods :+ article,state.total + article.price)

// someCommand is function: State => Stateval someCommand = buyArticle(new Article("iPad", 499))

executeCommand(someCommand)println(state.total) // 499.0

undoCommand()println(state.total) // 0.0

Vadim Shalts Функциональные структуры данных и архитектура 53 / 86

Page 55: Персистентные структуры данных и архитектура

Влияние на архитектуру приложений Применение и примеры архитектуры

Блокировки. synchronized и ReentrantLock

Now t

Thread 1

Thread 2

Thread 3

Thread 4

Vadim Shalts Функциональные структуры данных и архитектура 54 / 86

Page 56: Персистентные структуры данных и архитектура

Влияние на архитектуру приложений Применение и примеры архитектуры

Блокировки. ReentrantReadWriteLock

Now t

Thread 1

Thread 2

Thread 3

Thread 4

Vadim Shalts Функциональные структуры данных и архитектура 55 / 86

Page 57: Персистентные структуры данных и архитектура

Влияние на архитектуру приложений Применение и примеры архитектуры

Блокировки. ReentrantReadWriteLock другой пример

Now t

Thread 1

Thread 2

Thread 3

Thread 4

Vadim Shalts Функциональные структуры данных и архитектура 56 / 86

Page 58: Персистентные структуры данных и архитектура

Влияние на архитектуру приложений Применение и примеры архитектуры

Блокировки. StampedLock

Now t

Thread 1

Thread 2

Thread 3Thread 3

Thread 4

Vadim Shalts Функциональные структуры данных и архитектура 57 / 86

Page 59: Персистентные структуры данных и архитектура

Влияние на архитектуру приложений Применение и примеры архитектуры

StampedLock. Лучше масштабируется

class Point {private double x, y;private final StampedLock sl = new StampedLock();

void move(double deltaX, double deltaY) { // an exclusively locked methodlong stamp = sl.writeLock();try {

x += deltaX;y += deltaY;

} finally {sl.unlockWrite(stamp);

}}

// ...}

Vadim Shalts Функциональные структуры данных и архитектура 58 / 86

Page 60: Персистентные структуры данных и архитектура

Влияние на архитектуру приложений Применение и примеры архитектуры

StampedLock. Оптимистичные блокировки

class Point {private double x, y;private final StampedLock sl = new StampedLock();

double distanceFromOrigin() { // A read-only methodlong stamp = sl.tryOptimisticRead();double currentX = x, currentY = y;if (!sl.validate(stamp)) {

stamp = sl.readLock();try {

currentX = x;currentY = y;

} finally {sl.unlockRead(stamp);

}}return Math.sqrt(currentX * currentX + currentY * currentY);

}

// ...}

Vadim Shalts Функциональные структуры данных и архитектура 59 / 86

Page 61: Персистентные структуры данных и архитектура

Влияние на архитектуру приложений Применение и примеры архитектуры

Оптимистичные блокировки через StampedLock.Проблемы и ограничения

«The use of optimistic mode for short read-only codesegments often reduces contention and improves throughput.However, its use is inherently fragile. Optimistic read sectionsshould only read fields and hold them in local variables forlater use after validation. Fields read while in optimisticmode may be wildly inconsistent, so usage applies only whenyou are familiar enough with data representations to checkconsistency and/or repeatedly invoke method validate()»

StampedLock. Java Documentation

Vadim Shalts Функциональные структуры данных и архитектура 60 / 86

Page 62: Персистентные структуры данных и архитектура

Влияние на архитектуру приложений Применение и примеры архитектуры

Блокировки. StampedLock

Now t

Thread 1

Thread 2

Thread 3

Thread 4

Vadim Shalts Функциональные структуры данных и архитектура 61 / 86

Page 63: Персистентные структуры данных и архитектура

Влияние на архитектуру приложений Применение и примеры архитектуры

Блокировки. Неизменяемые данные

t

Thread 1Thread 1

Thread 2Thread 2Thread 2

Thread 3Thread 3

Thread 4Thread 4

Vadim Shalts Функциональные структуры данных и архитектура 62 / 86

Page 64: Персистентные структуры данных и архитектура

Влияние на архитектуру приложений Применение и примеры архитектуры

Блокировки. Много писателей

@volatile var currentState: State = initialStateval writersLock = new StampedLock

def readState() = {val latestState = currentState// ...

}

def updateState(command: Command) = {val stamp = writersLock.writeLockInterruptibly()val latestState = currentStatetry {

currentState = command(currentState)} catch {

case AbortException => // ...case ex: Throwable => // ...

} finally {writersLock.unlockWrite(stamp)

}}

Vadim Shalts Функциональные структуры данных и архитектура 63 / 86

Page 65: Персистентные структуры данных и архитектура

Влияние на архитектуру приложений Применение и примеры архитектуры

Реализация с неизменяемым Point классом.Общий код

trait ReferenceManager[T] {def setState(state: T) = updateState(_ => state)def getState(): Tdef updateState(stateReplacer: T => T)

}

Vadim Shalts Функциональные структуры данных и архитектура 64 / 86

Page 66: Персистентные структуры данных и архитектура

Влияние на архитектуру приложений Применение и примеры архитектуры

Реализация с неизменяемым Point классом.Общий код

class LockBasedReferenceManager[T] extends ReferenceManager[T] {

@volatile private var currentState: T = _private val writersLock = new StampedLock

def getState() = currentState

def updateState(stateReplacer: T => T) = {val stamp = writersLock.writeLockInterruptibly()val latestState = currentStatetry {

currentState = stateReplacer(latestState)} catch {

case ex: Throwable => // ...} finally {

writersLock.unlockWrite(stamp)}

}}

Vadim Shalts Функциональные структуры данных и архитектура 65 / 86

Page 67: Персистентные структуры данных и архитектура

Влияние на архитектуру приложений Применение и примеры архитектуры

Реализация с неизменяемым Point классом.Основная логика

case class Point(x: Double, y: Double) {def distanceFromOrigin() = Math.sqrt(x * x + y * y)def move(deltaX: Double, deltaY: Double) =

new Point(x + deltaX, y + deltaY)}

class PointHolder(reference: ReferenceManager[Point]) {reference.setState(Point(0, 0)) // Initial state

def distanceFromOrigin() =reference.getState().distanceFromOrigin()

def move(deltaX: Double, deltaY: Double) =reference.updateState { s => s.move(deltaX, deltaY) }

}

Vadim Shalts Функциональные структуры данных и архитектура 66 / 86

Page 68: Персистентные структуры данных и архитектура

Влияние на архитектуру приложений Применение и примеры архитектуры

Блокировки. Оптимистичное обновление состояния

t

Thread 1Thread 1Thread 1Thread 1

Thread 2

Thread 3Thread 3

Thread 4Thread 4

Vadim Shalts Функциональные структуры данных и архитектура 67 / 86

Page 69: Персистентные структуры данных и архитектура

Влияние на архитектуру приложений Применение и примеры архитектуры

Реализация через CAS. Сходство с MVCC

class OptimisticReferenceManager[T] extends ReferenceManager[T] {

val ref = new AtomicReference[T]

override def setState(state: T) = ref.set(state)

def getState() = ref.get

def updateState(stateReplacer: T => T) = {var continue = true

while(continue) {val latestState = getState()val newState = stateReplacer(latestState)

if (ref.compareAndSet(latestState, newState))continue = false

}}

}

Vadim Shalts Функциональные структуры данных и архитектура 68 / 86

Page 70: Персистентные структуры данных и архитектура

Влияние на архитектуру приложений Применение и примеры архитектуры

STM - Software transactional memory

Механизм управления параллелизмом аналогичныймеханизму транзакций баз данных.

Используется для управления доступом к совместноиспользуемой памяти в параллельных вычислениях.

Альтернатива для синхронизации на основеблокировок.

Vadim Shalts Функциональные структуры данных и архитектура 69 / 86

Page 71: Персистентные структуры данных и архитектура

Влияние на архитектуру приложений Применение и примеры архитектуры

Clojure STM

Реализация основана на MVCCТранзакции обеспечиваются изоляцией снимков(Snapshot isolation)Оптимистичный подход, рассчитываем на малуювероятность коллизий на записьНевозможны deadlocks, livelocks или гонкиКомпонуемые операции

Vadim Shalts Функциональные структуры данных и архитектура 70 / 86

Page 72: Персистентные структуры данных и архитектура

Влияние на архитектуру приложений Применение и примеры архитектуры

STM - полезен

Если паттерны доступа укладываются в:частые чтениядостаточно редкие конфликты на запись

Почему?Если транзакция не удалась, то нужно выполнитьоткатНужно минимизировать пенальти за откат имаксимизировать параллелизм.

Vadim Shalts Функциональные структуры данных и архитектура 71 / 86

Page 73: Персистентные структуры данных и архитектура

Влияние на архитектуру приложений Применение и примеры архитектуры

STM - Механика

Значения вычитанные внутри транзакции:гарантированно целостные и отражают всеизмененияотносятся к транзакциям, которые были завершеныдо начала этой транзакции.

Изменения внутри транзакция:локальны и видимы только внутри транзакциистановятся целиком видимыми после коммита

Vadim Shalts Функциональные структуры данных и архитектура 72 / 86

Page 74: Персистентные структуры данных и архитектура

Влияние на архитектуру приложений Применение и примеры архитектуры

STM. Работа с ссылками

Слайд Neale Swinnerton (Clojure STM - What? Why? How?)Vadim Shalts Функциональные структуры данных и архитектура 73 / 86

Page 75: Персистентные структуры данных и архитектура

Влияние на архитектуру приложений Применение и примеры архитектуры

И снова менеджер

def as[T](value: AnyRef) = value.asInstanceOf[T]

class StmReferenceManager[T <: AnyRef]extends ReferenceManager[T] {

private val ref = new Ref(null)

override def setState(state: T) = as[T]( ref.set(state) )

def getState() = as[T]( ref.deref() )

def updateState(stateReplacer: T => T) = ref.alter(new AFn() {override def invoke(state: AnyRef): AnyRef = as[T]{

as[AnyRef]( stateReplacer( as[T](state) ) )}

}, null)}

Vadim Shalts Функциональные структуры данных и архитектура 74 / 86

Page 76: Персистентные структуры данных и архитектура

Влияние на архитектуру приложений Применение и примеры архитектуры

Скрываем работу с транзакциями

def atomic[T](func: => T) = as[T] {LockingTransaction.runInTransaction(new Callable[T] {

def call = func})

}

Vadim Shalts Функциональные структуры данных и архитектура 75 / 86

Page 77: Персистентные структуры данных и архитектура

Влияние на архитектуру приложений Применение и примеры архитектуры

Надёжный, дерзкий, губернский банк

case class History(change: Double, balanceBeforeChange: Double)

case class Account(balance: Double, history: Vector[History]) {

if (balance < 0 || history.length > 100)throw new IllegalArgumentException("Not today")

if (history.length > 0 && history.last.change < 0.10)throw new IllegalArgumentException("Suspicious behavior")

def changeBalanceBy(amount: Double) =new Account(balance + amount, history :+ History(amount, balance))

}

class AccountHolder(reference: ReferenceManager[Account]) {reference.setState(Account(0, Vector())) // Initial state

def balance = reference.getState().balance

def changeBalanceBy(amount: Double) =reference.updateState ( s => s.changeBalanceBy(amount) )

}

Vadim Shalts Функциональные структуры данных и архитектура 76 / 86

Page 78: Персистентные структуры данных и архитектура

Влияние на архитектуру приложений Применение и примеры архитектуры

Соединяем всё вместе

val (source, target) = atomic {(new AccountHolder(new StmReferenceManager()),new AccountHolder(new StmReferenceManager()))

}

try {atomic {

target.changeBalanceBy(200)println(target.balance) // 200.0source.changeBalanceBy(-200) // IllegalStateException: Balance negative

}} catch {

case ex: Throwable => ex.printStackTrace();}

println(source.balance) // 0.0println(target.balance) // 0.0

source.changeBalanceBy(200) // IllegalStateException: No transaction running

Vadim Shalts Функциональные структуры данных и архитектура 77 / 86

Page 79: Персистентные структуры данных и архитектура

Влияние на архитектуру приложений Применение и примеры архитектуры

Транзакции. ACI(D) для персистентных структур

✓ Atomicity (Атомарность)✓ Consistency (Согласованность)✓ Isolation (Изолированность)✗ Durability (Долговечность)

Vadim Shalts Функциональные структуры данных и архитектура 78 / 86

Page 80: Персистентные структуры данных и архитектура

Влияние на архитектуру приложений Персистентные базы данных (Datomic)

Базы данных. Классика

Vadim Shalts Функциональные структуры данных и архитектура 79 / 86

Page 81: Персистентные структуры данных и архитектура

Влияние на архитектуру приложений Персистентные базы данных (Datomic)

Datomic. Общая идея

Vadim Shalts Функциональные структуры данных и архитектура 80 / 86

Page 82: Персистентные структуры данных и архитектура

Влияние на архитектуру приложений Персистентные базы данных (Datomic)

Datomic

Иммутабильная база данных с поддержкой ACID

Явным образом оперирует временем - время всегда частьюмодели данных

Основная единица хранения данных - факт привязанный ковремени (datom)

Datom - запись со списком атрибутов.

Атрибуты могут ссылаться на другие факты - отношения

Изменения модели происходит путём добавления новыхфактов

Vadim Shalts Функциональные структуры данных и архитектура 81 / 86

Page 83: Персистентные структуры данных и архитектура

Влияние на архитектуру приложений Персистентные базы данных (Datomic)

Datomic 2

Полагается на внешнюю реализацию хранилища данных(RAM, DynamoDB, RDBMS, ...)

Изменения/транзакции отделены от чтения и проходят черезспециальный компонент (transactor).

Возможно симулирование состояния базы без коммитов

Практически безграничное масштабирование запросов начтение и встроенная поддержка кеширования

Запросы могут использовать функции на Java/Clojure.

Vadim Shalts Функциональные структуры данных и архитектура 82 / 86

Page 84: Персистентные структуры данных и архитектура

Влияние на архитектуру приложений Персистентные базы данных (Datomic)

Datomic - возможные проблемы.

Платная дорогая лицензия (есть бесплатная урезаннаяверсия и версия для разработчиков)

Плохо подходит для хранения больших (BLOB) или частоменяющихся данных.

Значительно увеличивается потребность в дисковомпространстве (особенно индексы)

Для узлов сети желательно использовать большой кеш

Законы могут требовать возможности удалять данныепользователя.

Vadim Shalts Функциональные структуры данных и архитектура 83 / 86

Page 85: Персистентные структуры данных и архитектура

Влияние на архитектуру приложений Персистентные базы данных (Datomic)

Персистентные структуры данных - итоги

Выгодный компромисс - платим памятью замасштабируемость

Изменения общих данных не блокируют обработку текущихзапросов (настройки, текущее состояние и т.д)

Долгие чтения не блокируют записи (обращение по сети,построение отчета)

Упрощается обработка долгих запросов (не связаныограничениями по времени)

Vadim Shalts Функциональные структуры данных и архитектура 84 / 86

Page 86: Персистентные структуры данных и архитектура

Влияние на архитектуру приложений Персистентные базы данных (Datomic)

Персистентные структуры данных - итоги 2

Стратегия работы с блокировками стала гибкой и не связанажестко с реализацией логики

Упрощается тестирование и разработка бизнес логики(наиболее часто меняющийся код)

Повышается надёжность - случайные ошибки (исключения)имеют меньше шансов навредить

Доступность старых фактов/данных упрощает разработку иоткрывает новые возможности (datomic + blueprints)

Vadim Shalts Функциональные структуры данных и архитектура 85 / 86

Page 87: Персистентные структуры данных и архитектура

Вопросы

Вопросы?

Vadim Shalts Функциональные структуры данных и архитектура 86 / 86