© Netcracker 2016 1
Производительность
open source решений
Владимир Ситников
JPoint 2016
© Netcracker 2016 2
•Владимир Ситников
•Инженер по производительности в Netcracker,
10 лет
•sitnikov@netcracker.com
•@VladimirSitnikv
Кто я
© Netcracker 2016 3
•Spring
•Cglib
•Pgjdbc
•HornetQ
•Wildfly
•Jenkins
План
© Netcracker 2016 4
План
Наш любимый
enterprise
продукт
© Netcracker 2016 5
Внедряем Spring в сжатые сроки
Монолит
spring
© Netcracker 2016 6
Внедряем Spring в сжатые сроки
Монолит
spring
© Netcracker 2016 7
Внедряем Spring в сжатые сроки
Core
Security
UI
© Netcracker 2016 8
Внедряем Spring в сжатые сроки
Core
Security
UI
© Netcracker 2016 9
Внедряем Spring в сжатые сроки
Core
Security
UI
© Netcracker 2016 10
•Когда нужен бин, то выполняем
beanFactory.getBean(Security.class)
В UI spring пока не наступил
© Netcracker 2016 11
•Когда нужен бин, то выполняем
beanFactory.getBean(Security.class)
•Но где взять beanFactory?
В UI spring пока не наступил
© Netcracker 2016 12
•Когда нужен бин, то выполняем
beanFactory.getBean(Security.class)
•Но где взять beanFactory?
•Правильно, Паблик Морозов поможет нам
В UI spring пока не наступил
© Netcracker 2016 13
public class AppContext implements BeanFactoryAware
{
static public BeanFactory beanFactory;
...
Получаем бин
© Netcracker 2016 14
public class AppContext implements BeanFactoryAware
{
static public BeanFactory beanFactory;
...
AppContext.beanFactory.getBean(Security.class)
Получаем бин
© Netcracker 2016 15
•Работает
Spring.getBean
© Netcracker 2016 16
•Работает
•Но медленно-медленно
Spring.getBean
© Netcracker 2016 17
•Работает
•Но медленно-медленно
•На getBean и 50% времени может уходить
Spring.getBean
© Netcracker 2016 18
•Java Flight Recorder
Подходящие для анализа инструменты
© Netcracker 2016 19
•Java Flight Recorder
•kill -3 profiler (poormansprofiler.org)
Подходящие для анализа инструменты
© Netcracker 2016 20
•Java Flight Recorder
•kill -3 profiler (poormansprofiler.org)
•Инструментирующий профилировщик.
Например самописный, настроенный на
getBean
Подходящие для анализа инструменты
© Netcracker 2016 21
160 секунд на getBean. Здорово?
© Netcracker 2016 22
160 секунд на getBean. Здорово?
•Java 8 / Spring 4.1.7
© Netcracker 2016 23
•SPR-6870 Cache by-type lookups in
DefaultListableBeanFactory
Проверяем известные проблемы
© Netcracker 2016 24
•SPR-6870 Cache by-type lookups in
DefaultListableBeanFactory
Проверяем известные проблемы
ЯЗЬ!!!
© Netcracker 2016 25
•SPR-6870 Cache by-type lookups in
DefaultListableBeanFactory
•Fix version: 3.2 M1, а у нас 4.1+ 
Проверяем известные проблемы
© Netcracker 2016 26
Flight Recorder, allocations in new TLAB
© Netcracker 2016 27
protected Object getCacheKey(
Class<?> beanClass, String beanName) {
return beanClass.getName() + "_" + beanName;
}
AbstractAutoProxyCreator
© Netcracker 2016 28
protected Object getCacheKey(
Class<?> beanClass, String beanName) {
return beanClass.getName() + "_" + beanName;
}
AbstractAutoProxyCreator
© Netcracker 2016 29
protected Object getCacheKey(
Class<?> beanClass, String beanName) {
return beanName == null ? beanClass : ...;
}
github.com/spring-projects/spring-
framework/pull/913
AbstractAutoProxyCreator
© Netcracker 2016 30
public class SharedSecrets {
@Inject
public Security security;
@PostConstruct
public void init() { INSTANCE = this; }
public static volatile SharedSecrets INSTANCE;
Как выжить без обновления Spring?
© Netcracker 2016 31
•Не стоит злоупотреблять prototype
bean’ами. Если singleton, то singleton
Prototype vs singleton
© Netcracker 2016 32
•Не стоит злоупотреблять prototype
bean’ами. Если singleton, то singleton
•Замена getBean на
javax.inject.Provider<...> лишь
ухудшает время работы
•
Prototype vs singleton
© Netcracker 2016 33
@Pointcut(
"execution(
public* my.service.*ServiceImpl.*(..))”
)
Spring AOP
© Netcracker 2016 34
•OutOfMemory: perm gen
Spring AOP
© Netcracker 2016 35
•OutOfMemory: perm gen
•В хипдампе куча java.lang.reflect.Method
Spring AOP
© Netcracker 2016 36
•OutOfMemory: perm gen
•В хипдампе куча java.lang.reflect.Method
•Method’ы занимают 100-500MiB
Spring AOP
© Netcracker 2016 37
•OutOfMemory: perm gen
•В хипдампе куча java.lang.reflect.Method
•Method’ы занимают 100-500MiB
•Все они лежат в AspectJExpressionPointcut
Spring AOP
© Netcracker 2016 38
@Pointcut(
"execution(
public* my.service.*ServiceImpl.*(..))”
)
Spring AOP
© Netcracker 2016 39
•Решение: добавлять
within(my.package.service.business..*)
Spring AOP vs AspectJ
© Netcracker 2016 40
•Решение: добавлять
within(my.package.service.business..*)
•Разумеется, AspectJ рекомендуют
использовать within всегда:
http://dev.eclipse.org/mhonarc/lists/aspectj-
users/msg10969.html
•
Spring AOP vs AspectJ
© Netcracker 2016 41
•Cglib используется много где: рукотворный
код, тот же Spring
Cglib
© Netcracker 2016 42
•Cglib используется много где: рукотворный
код, тот же Spring
•Наверняка уже всё исправлено давным-
давно
Cglib
© Netcracker 2016 43
•Cglib используется много где: рукотворный
код, тот же Spring
•Наверняка уже всё исправлено давным-
давно
•Наверняка туда давно никто не заглядывал
Cglib
© Netcracker 2016 44
@Benchmark
public Object newProxy() {
return Beans.newProxy(Beans.class);
}
Cglib: замеряем
© Netcracker 2016 45
0
2
4
6
8
10
12
14
16
18
Cglib 3.1
1 поток 2 потока 4 потока 8 потоков
Замеры Cglib, μs/opБыстрее
© Netcracker 2016 46
0
2
4
6
8
10
12
14
16
18
Cglib 3.1 Cglib 3.2.2
1 поток 2 потока 4 потока 8 потоков
Замеры Cglib, μs/opБыстрее
© Netcracker 2016 47
Benchmark Score
newProxy 2.1 ± 0.3 μs/op
newProxy.alloc.rate 1240 B/op
Стало
newProxy 0.14 ± 0.02 μs/op
newProxy.alloc.rate 256 B/op
Cglib 3.2.2: ждём в очередном Spring
© Netcracker 2016 48
•Skip finalize while building proxy
https://github.com/cglib/cglib/pull/51
•Concurrent cache of generated classes
https://github.com/cglib/cglib/pull/53
План захвата Cglib
© Netcracker 2016 49
•А, может, ну его этот cglib, есть же быстрый
ByteBuddy?
Но есть же ByteBuddy
© Netcracker 2016 50
•А, может, ну его этот cglib, есть же быстрый
ByteBuddy?
@Benchmark
public ExampleClass benchmarkCglib() {
Enhancer enhancer = new Enhancer();
enhancer.setUseCache(false);
ByteBuddy, jmh тест
© Netcracker 2016 51
•А, может, ну его этот cglib, есть же быстрый
ByteBuddy?
@Benchmark
public ExampleClass benchmarkCglib() {
Enhancer enhancer = new Enhancer();
enhancer.setUseCache(false);
ByteBuddy, jmh тест
© Netcracker 2016 52
•JMH тесты в ByteBuddy показывают
скорость создания классов
•Постоянно создавать классы нехорошо
•Значит, нужно измерять скорость
кэшированного обращения
ByteBuddy, jmh тест
© Netcracker 2016 53
•КО: «Бери pgjdbc»
Подключаемся к PostgreSQL
© Netcracker 2016 54
•КО: «Бери pgjdbc»
•КО: «Используй batch statements»
Подключаемся к PostgreSQL
© Netcracker 2016 55
•КО: «Бери pgjdbc»
•КО: «Используй batch statements»
•Что может пойти не так?
Подключаемся к PostgreSQL
© Netcracker 2016 56
Connection con = ...;
PreparedStatement ps =
con.prepareStatement("SELECT...");
...
ps.close();
PreparedStatement
© Netcracker 2016 57
Connection con = ...;
PreparedStatement ps =
con.prepareStatement("SELECT...");
...
ps.close();
PreparedStatement
© Netcracker 2016 58
PARSE S_1 as ...; // con.prepareStmt
BIND/EXEC
DEALLOCATE // ps.close()
PARSE S_2 as ...;
BIND/EXEC
DEALLOCATE // ps.close()
Работа с PostgreSQL курильщика
© Netcracker 2016 59
PARSE S_1 as ...; 
BIND/EXEC
BIND/EXEC
BIND/EXEC 
BIND/EXEC
BIND/EXEC
...
DEALLOCATE
Работа с PostgreSQL здорового человека
© Netcracker 2016 60
PARSE S_1 as ...;  1 раз в жизни
BIND/EXEC  обработка REST
BIND/EXEC
BIND/EXEC  ещё REST
BIND/EXEC
BIND/EXEC
...
DEALLOCATE  желательно «никогда»
DEALLOCATE
Работа с PostgreSQL здорового человека
© Netcracker 2016 61
Вывод: чтобы работало быстрее,
закрывать statement’ы не нужно
ps = con.prepareStatement(...)
ps.execueQuery();
ps = con.prepareStatement(...)
ps.execueQuery();
...
Счастливые statement’ов не закрывают
© Netcracker 2016 62
Вывод: чтобы работало быстрее,
закрывать statement’ы не нужно
ps = con.prepare...
ps.execueQuery();
ps = con.prepare...
ps.execueQuery();
...
Счастливые statement’ов не закрывают
© Netcracker 2016 63
@Benchmark
public Statement leakStatement() {
return con.createStatement();
}
pgjdbc < 9.4.1202, -Xmx128m, OracleJDK 1.8u40
# Warmup Iteration 1: 1147,070 ns/op
# Warmup Iteration 2: 12101,537 ns/op
# Warmup Iteration 3: 90825,971 ns/op
# Warmup Iteration 4: <failure>
java.lang.OutOfMemoryError: GC overhead limit exceeded
OpenJDK: не все JRE одинаково полезны
© Netcracker 2016 64
@Benchmark
public Statement leakStatement() {
return con.createStatement();
}
pgjdbc >= 9.4.1202, -Xmx128m, OracleJDK 1.8u40
# Warmup Iteration 1: 30 ns/op
# Warmup Iteration 2: 27 ns/op
...
github.com/pgjdbc/pgjdbc/pull/299
Убираем finalize из класса PgConnection
© Netcracker 2016 65
Вывод: чтобы работало быстро, нужно
как-то кэшировать statement’ы
ps = con.prepareStatement("select id,
name ...");
ps.execueQuery();
ps.close();
ps2 = con.prepareStatement("select id,
name ...");
Счастливые statement’ов не закрывают
© Netcracker 2016 66
•Кэш запросов появился в версии 9.4.1202
(2015-08-27)
см. https://github.com/pgjdbc/pgjdbc/pull/319
Кэш запросов в PgJDBC
© Netcracker 2016 67
•Кэш запросов появился в версии 9.4.1202
(2015-08-27)
см. https://github.com/pgjdbc/pgjdbc/pull/319
•Работает прозрачно для приложения
Кэш запросов в PgJDBC
© Netcracker 2016 68
•Кэш запросов появился в версии 9.4.1202
(2015-08-27)
см. https://github.com/pgjdbc/pgjdbc/pull/319
•Работает прозрачно для приложения
•Скорость такая, что PL/PgSQL не нужен
Кэш запросов в PgJDBC
© Netcracker 2016 69
•Кэш запросов появился в версии 9.4.1202
(2015-08-27)
см. https://github.com/pgjdbc/pgjdbc/pull/319
•Работает прозрачно для приложения
•Скорость такая, что PL/PgSQL не нужен
•Server-prepare активируется после 5-го
выполнения (prepareThreshold)
Кэш запросов в PgJDBC
© Netcracker 2016 70
•Конечно, затраты planning time напрямую
зависят от сложности запросов
Цифры где?
© Netcracker 2016 71
•Конечно, затраты planning time напрямую
зависят от сложности запросов
•У нас доходило до 20мс+ planning time на
OLTP запросах: 10КиБ запрос, 170 строк
explain
•
Цифры где?
© Netcracker 2016 72
•Конечно, затраты planning time напрямую
зависят от сложности запросов
•У нас доходило до 20мс+ planning time на
OLTP запросах: 10КиБ запрос, 170 строк
explain
•Стало ~0мс
Цифры где?
© Netcracker 2016 73
Если типы параметров меняются, то server-
prepared statement приходится менять
ps.setInt(1, 42);
...
ps.setNull(1, Types.VARCHAR);
JDBC: Типы параметров
© Netcracker 2016 74
Если типы параметров меняются, то server-
prepared statement приходится менять
ps.setInt(1, 42);
...
ps.setNull(1, Types.VARCHAR);
JDBC: Типы параметров
© Netcracker 2016 75
•Вывод: даже у NULL’ов должен быть
верный тип
JDBC: Тип параметров изменять нельзя
© Netcracker 2016 76
•Вывод: даже у NULL’ов должен быть
верный тип
•Ещё раз: setObject(1, null)
использовать нельзя
JDBC: Тип параметров изменять нельзя
© Netcracker 2016 77
•Перешли на prepared, и запрос замедлился
в 5'000 раз. Как так?
Нежданчик
© Netcracker 2016 78
•Перешли на prepared, и запрос замедлился
в 5'000 раз. Как так?
Нежданчик
A. Бага C. Фича
B. Фича D. Бага
© Netcracker 2016 79
https://gist.github.com/vlsi -> 01_plan_flipper.sql
select *
from plan_flipper -- <- таблица
where skewed = 0 -- 1 млн строк
and non_skewed = 42 -- 20 строк
Нежданчик
© Netcracker 2016 80
https://gist.github.com/vlsi -> 01_plan_flipper.sql
0.1мс  1-е выполнение
0.05мс  2-е выполнение
0.05мс  3-е выполнение
0.05мс  4-е выполнение
0.05мс  5-е выполнение
250 мс  6-е выполнение
Нежданчик
© Netcracker 2016 81
https://gist.github.com/vlsi -> 01_plan_flipper.sql
0.1мс  1-е выполнение
0.05мс  2-е выполнение
0.05мс  3-е выполнение
0.05мс  4-е выполнение
0.05мс  5-е выполнение
250 мс  6-е выполнение
Нежданчик
© Netcracker 2016 82
Запрещаем использование индекса через +0:
select *
from plan_flipper
where skewed+0 = 0  ~ /*+no_index*/
and non_skewed = 42
Чиним
© Netcracker 2016 83
Как сделать опциональные фичи?
•NO_RESULTS
•BOTH_ROWS_AND_STATUS
•DESCRIBE_ONLY
Фичи
© Netcracker 2016 84
Конечно, enum!
enum ... {
NO_RESULTS,
BOTH_ROWS_AND_STATUS,
DESCRIBE_ONLY;
}
Фичи
© Netcracker 2016 85
Конечно, enum!
enum ... {
NO_RESULTS,
BOTH_ROWS_AND_STATUS,
DESCRIBE_ONLY;
}
А на java 1.4?
Фичи
© Netcracker 2016 86
В java 1.4, конечно, interface!
interface ... {
int NO_RESULTS = 1;
int BOTH_ROWS_AND_STATUS = 2;
int DESCRIBE_ONLY = 4;
}
Фичи: java 1.4 наносит ответный удар
© Netcracker 2016 87
Меняем 1 строку, и скорость работы batch insert
возрастает в 10 раз:
https://github.com/pgjdbc/pgjdbc/pull/380
- static int QUERY_FORCE_DESCRIBE_PORTAL = 128;
+ static int QUERY_FORCE_DESCRIBE_PORTAL = 512;
Ужасы нашего городка
© Netcracker 2016 88
Меняем 1 строку, и скорость работы batch insert
возрастает в 10 раз:
https://github.com/pgjdbc/pgjdbc/pull/380
- static int QUERY_FORCE_DESCRIBE_PORTAL = 128;
+ static int QUERY_FORCE_DESCRIBE_PORTAL = 512;
// оказалось, значение 128 уже было занято
static int QUERY_DISALLOW_BATCHING = 128;
Ужасы нашего городка
© Netcracker 2016 89
•WildFly 8.2, JMS
HornetQ
© Netcracker 2016 90
•WildFly 8.2, JMS
•Всего ~100 JMS/сек
HornetQ
© Netcracker 2016 91
•WildFly 8.2, JMS
•Всего ~100 JMS/сек
•1 вызов sendMessage – 5-30 секунд
HornetQ
© Netcracker 2016 92
at java.util.concurrent.Semaphore.tryAcquire
at org.hornetq ... ClientProducerCreditsImpl.acquireCredits
at com ... JMSSender.sendMessage
kill -3 профайлер
© Netcracker 2016 93
at java.util.concurrent.Semaphore.tryAcquire
at org.hornetq ... ClientProducerCreditsImpl.acquireCredits
at com ... JMSSender.sendMessage
kill -3 профайлер
© Netcracker 2016 94
at java.util.concurrent.Semaphore.tryAcquire
at org.hornetq ... ClientProducerCreditsImpl.acquireCredits
at com ... JMSSender.sendMessage
kill -3 профайлер
© Netcracker 2016 95
at java.util.concurrent.Semaphore.tryAcquire
at org.hornetq ... ClientProducerCreditsImpl.acquireCredits
at org.hornetq ... ClientProducerImpl.sendRegularMessage
at com ... JMSSender.sendMessage
kill -3 профайлер
© Netcracker 2016 96
at java.util.concurrent.Semaphore.tryAcquire
at org.hornetq ... ClientProducerCreditsImpl.acquireCredits
at org.hornetq ... ClientProducerImpl.sendRegularMessage
at org.hornetq ... ClientProducerImpl.doSend
at com ... JMSSender.sendMessage
kill -3 профайлер
© Netcracker 2016 97
at java.util.concurrent.Semaphore.tryAcquire
at org.hornetq ... ClientProducerCreditsImpl.acquireCredits
at org.hornetq ... ClientProducerImpl.sendRegularMessage
at org.hornetq ... ClientProducerImpl.doSend
at org.hornetq ... ClientProducerImpl.send
at com ... JMSSender.sendMessage
kill -3 профайлер
© Netcracker 2016 98
at java.util.concurrent.Semaphore.tryAcquire
at org.hornetq ... ClientProducerCreditsImpl.acquireCredits
at org.hornetq ... ClientProducerImpl.sendRegularMessage
at org.hornetq ... ClientProducerImpl.doSend
at org.hornetq ... ClientProducerImpl.send
at org.hornetq ... HornetQMessageProducer.doSendx
at com ... JMSSender.sendMessage
kill -3 профайлер
© Netcracker 2016 99
• 'Producer Window Size’ на connection factory
Backpressure
© Netcracker 2016 100
• 'Producer Window Size’ на connection factory
• address-settings -> address-setting -> max-size-bytes
Backpressure
© Netcracker 2016 101
• 'Producer Window Size’ на connection factory
• address-settings -> address-setting -> max-size-bytes
• Если в JMS очереди накапливается 10МиБ, то отправка
JMS начинает притормаживать
Backpressure
© Netcracker 2016 102
• 'Producer Window Size’ на connection factory
• address-settings -> address-setting -> max-size-bytes
• Если в JMS очереди накапливается 10МиБ, то отправка
JMS начинает притормаживать
<address-settings>
<address-setting match="#">
<max-size-bytes>10 485 760</max-size-bytes>
…
Backpressure
© Netcracker 2016 103
•Настраиваем max-size-bytes для каждой
очереди
Backpressure: чиним
© Netcracker 2016 104
•Настраиваем max-size-bytes для каждой
очереди
•Либо смотрим в сторону RxJava
Backpressure: чиним
© Netcracker 2016 105
•Старт приложения на WF 8.2 занимает 2-5
минут
WildFly: пытаемся взлететь
© Netcracker 2016 106
•Старт приложения на WF 8.2 занимает 2-5
минут
•Concurrent deploy работает плохо
WildFly: пытаемся взлететь
© Netcracker 2016 107
•Старт приложения на WF 8.2 занимает 2-5
минут
•Concurrent deploy работает плохо
•Spring xml app config анализирует все
jar’ники
WildFly: пытаемся взлететь
© Netcracker 2016 108
•Уменьшать размер jar (исключать лишние)
• WildFly копирует jar в /tmp при запуске
WildFly: как чинить
© Netcracker 2016 109
•Уменьшать размер jar (исключать лишние)
• WildFly копирует jar в /tmp при запуске
•Делать патчи на WF, чтобы он не
складывал строки
• https://github.com/jbossas/jboss-vfs/pull/25
• https://github.com/wildfly/wildfly-core/pull/1219
WildFly: как чинить
© Netcracker 2016 110
•Уменьшать размер jar (исключать лишние)
• WildFly копирует jar в /tmp при запуске
•Делать патчи на WF, чтобы он не
складывал строки
• https://github.com/jbossas/jboss-vfs/pull/25
• https://github.com/wildfly/wildfly-core/pull/1219
•Профилировать запуск вашего WF (kill -3
profiler)
WildFly: как чинить
© Netcracker 2016 111
Самое главное в CI –
переменные окружения
Jenkins
© Netcracker 2016 112
https://wiki.jenkins-ci.org/display/JENKINS/EnvInject+Plugin
Jenkins: EnvInjectPlugin
© Netcracker 2016 113
Пишем по образу и подобию:
LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$ORAC
LE_HOME/…
PATH=$PATH:$ORACLE/bin:…
Jenkins: EnvInjectPlugin
© Netcracker 2016 114
EnvInjectEnvVars.resolveVars()
Jenkins: EnvInjectPlugin
© Netcracker 2016 115
EnvInjectEnvVars.resolveVars()
-> hudson.Util.replaceMacro()
Jenkins: EnvInjectPlugin
© Netcracker 2016 116
EnvInjectEnvVars.resolveVars()
-> hudson.Util.replaceMacro()
-> OutOfMemoryError.<init>()
Jenkins: EnvInjectPlugin
© Netcracker 2016 117
$LD_LIBRARY_PATH:$ORACLE_HOME/lib
:$ORACLE_HOME/lib:$ORACLE_HOME/lib
:$ORACLE_HOME/lib:$ORACLE_HOME/lib
:$ORACLE_HOME/lib:$ORACLE_HOME/lib
:$ORACLE_HOME/lib:$ORACLE_HOME/lib
:$ORACLE_HOME/lib:$ORACLE_HOME/lib
:$ORACLE_HOME/lib:$ORACLE_HOME/lib
:$ORACLE_HOME/lib:...
Jenkins: EnvInjectPlugin
© Netcracker 2016 118
https://issues.jenkins-ci.org/browse/JENKINS-
19856
^^^ с вами с 2013 года
Jenkins: EnvInjectPlugin
© Netcracker 2016 119
•Общение Jenkins master и slave тщательно
логируется
Jenkins: master vs slave
© Netcracker 2016 120
•Общение Jenkins master и slave тщательно
логируется
•«Разумеется», в памяти хранятся
последние 1000 записей
Jenkins: master vs slave
© Netcracker 2016 121
•Общение Jenkins master и slave тщательно
логируется
•«Разумеется», в памяти хранятся
последние 1000 записей
•Н
а эти логи может уходить 100-200МиБ
Jenkins: master vs slave
© Netcracker 2016 122
•Общение Jenkins master и slave тщательно
логируется
•«Разумеется», в памяти хранятся
последние 1000 записей
•Н
•Да, да. Логи хранятся в памяти и никогда не
попадают в файл
Jenkins: master vs slave
© Netcracker 2016 123
•Хорошо, что есть опция
• -Dhudson.remoting.ExportTable.unexportLog=0
Jenkins: master vs slave
© Netcracker 2016 124
•Хорошо, что есть опция
• -Dhudson.remoting.ExportTable.unexportLog=0
•Разумеется, в Google ровно один результат
Jenkins: master vs slave
© Netcracker 2016 125
•Хорошо, что есть опция
• -Dhudson.remoting.ExportTable.unexportLog=0
•Разумеется, в Google ровно один результат
•На исходный код, где она определена :)
Jenkins: master vs slave
© Netcracker 2016 126
•Оказалось, что запуск master занимает 50
минут
• 3000 jobs, 10-500 runs per job
• 24 CPU, 64 GiB RAM
• -Xmx40g
Jenkins: запускаемся
© Netcracker 2016 127
•При старте, Jenkins инициализирует job’ы
Jenkins: файлы разные нужны, файлы разные важны
© Netcracker 2016 128
•При старте, Jenkins инициализирует job’ы
•Maven job загружает данные по всем
запускам
Jenkins: файлы разные нужны, файлы разные важны
© Netcracker 2016 129
•При старте, Jenkins инициализирует job’ы
•Maven job загружает данные по всем
запускам
•В
каждом запуске есть fingerprint’ы, они тоже
грузятся
Jenkins: файлы разные нужны, файлы разные важны
© Netcracker 2016 130
•При старте, Jenkins инициализирует job’ы
•Maven job загружает данные по всем
запускам
•В
•В итоге много-много операций с диском
•и тоже грузятся
Jenkins: файлы разные нужны, файлы разные важны
© Netcracker 2016 131
•Решение в лоб: удалить fingerprint’ы перед
запуском
Jenkins: чиним
© Netcracker 2016 132
•Решение в лоб: удалить fingerprint’ы перед
запуском
•Более сложное: не плодить fingerprint’ы
Jenkins: чиним
© Netcracker 2016 133
•Решение в лоб: удалить fingerprint’ы перед
запуском
•Более сложное: не плодить fingerprint’ы
•Поможет и сокращение хранимой истории
Jenkins: чиним
© Netcracker 2016 134
•Решение в лоб: удалить fingerprint’ы перед
запуском
•Более сложное: не плодить fingerprint’ы
•Поможет и сокращение хранимой истории
•В идеальном мире нужно разбираться с
maven-jenkins-plugin
Jenkins: чиним
© Netcracker 2016 135
Кто виноват и что делать?
© Netcracker 2016 136
•Владимир Ситников
•Инженер по производительности в Netcracker
•sitnikov@netcracker.com
•@VladimirSitnikv
С вами был
© Netcracker 2016 137
Вопросы?

Проблемы производительности open source библиотек

  • 1.
    © Netcracker 20161 Производительность open source решений Владимир Ситников JPoint 2016
  • 2.
    © Netcracker 20162 •Владимир Ситников •Инженер по производительности в Netcracker, 10 лет •sitnikov@netcracker.com •@VladimirSitnikv Кто я
  • 3.
    © Netcracker 20163 •Spring •Cglib •Pgjdbc •HornetQ •Wildfly •Jenkins План
  • 4.
    © Netcracker 20164 План Наш любимый enterprise продукт
  • 5.
    © Netcracker 20165 Внедряем Spring в сжатые сроки Монолит spring
  • 6.
    © Netcracker 20166 Внедряем Spring в сжатые сроки Монолит spring
  • 7.
    © Netcracker 20167 Внедряем Spring в сжатые сроки Core Security UI
  • 8.
    © Netcracker 20168 Внедряем Spring в сжатые сроки Core Security UI
  • 9.
    © Netcracker 20169 Внедряем Spring в сжатые сроки Core Security UI
  • 10.
    © Netcracker 201610 •Когда нужен бин, то выполняем beanFactory.getBean(Security.class) В UI spring пока не наступил
  • 11.
    © Netcracker 201611 •Когда нужен бин, то выполняем beanFactory.getBean(Security.class) •Но где взять beanFactory? В UI spring пока не наступил
  • 12.
    © Netcracker 201612 •Когда нужен бин, то выполняем beanFactory.getBean(Security.class) •Но где взять beanFactory? •Правильно, Паблик Морозов поможет нам В UI spring пока не наступил
  • 13.
    © Netcracker 201613 public class AppContext implements BeanFactoryAware { static public BeanFactory beanFactory; ... Получаем бин
  • 14.
    © Netcracker 201614 public class AppContext implements BeanFactoryAware { static public BeanFactory beanFactory; ... AppContext.beanFactory.getBean(Security.class) Получаем бин
  • 15.
    © Netcracker 201615 •Работает Spring.getBean
  • 16.
    © Netcracker 201616 •Работает •Но медленно-медленно Spring.getBean
  • 17.
    © Netcracker 201617 •Работает •Но медленно-медленно •На getBean и 50% времени может уходить Spring.getBean
  • 18.
    © Netcracker 201618 •Java Flight Recorder Подходящие для анализа инструменты
  • 19.
    © Netcracker 201619 •Java Flight Recorder •kill -3 profiler (poormansprofiler.org) Подходящие для анализа инструменты
  • 20.
    © Netcracker 201620 •Java Flight Recorder •kill -3 profiler (poormansprofiler.org) •Инструментирующий профилировщик. Например самописный, настроенный на getBean Подходящие для анализа инструменты
  • 21.
    © Netcracker 201621 160 секунд на getBean. Здорово?
  • 22.
    © Netcracker 201622 160 секунд на getBean. Здорово? •Java 8 / Spring 4.1.7
  • 23.
    © Netcracker 201623 •SPR-6870 Cache by-type lookups in DefaultListableBeanFactory Проверяем известные проблемы
  • 24.
    © Netcracker 201624 •SPR-6870 Cache by-type lookups in DefaultListableBeanFactory Проверяем известные проблемы ЯЗЬ!!!
  • 25.
    © Netcracker 201625 •SPR-6870 Cache by-type lookups in DefaultListableBeanFactory •Fix version: 3.2 M1, а у нас 4.1+  Проверяем известные проблемы
  • 26.
    © Netcracker 201626 Flight Recorder, allocations in new TLAB
  • 27.
    © Netcracker 201627 protected Object getCacheKey( Class<?> beanClass, String beanName) { return beanClass.getName() + "_" + beanName; } AbstractAutoProxyCreator
  • 28.
    © Netcracker 201628 protected Object getCacheKey( Class<?> beanClass, String beanName) { return beanClass.getName() + "_" + beanName; } AbstractAutoProxyCreator
  • 29.
    © Netcracker 201629 protected Object getCacheKey( Class<?> beanClass, String beanName) { return beanName == null ? beanClass : ...; } github.com/spring-projects/spring- framework/pull/913 AbstractAutoProxyCreator
  • 30.
    © Netcracker 201630 public class SharedSecrets { @Inject public Security security; @PostConstruct public void init() { INSTANCE = this; } public static volatile SharedSecrets INSTANCE; Как выжить без обновления Spring?
  • 31.
    © Netcracker 201631 •Не стоит злоупотреблять prototype bean’ами. Если singleton, то singleton Prototype vs singleton
  • 32.
    © Netcracker 201632 •Не стоит злоупотреблять prototype bean’ами. Если singleton, то singleton •Замена getBean на javax.inject.Provider<...> лишь ухудшает время работы • Prototype vs singleton
  • 33.
    © Netcracker 201633 @Pointcut( "execution( public* my.service.*ServiceImpl.*(..))” ) Spring AOP
  • 34.
    © Netcracker 201634 •OutOfMemory: perm gen Spring AOP
  • 35.
    © Netcracker 201635 •OutOfMemory: perm gen •В хипдампе куча java.lang.reflect.Method Spring AOP
  • 36.
    © Netcracker 201636 •OutOfMemory: perm gen •В хипдампе куча java.lang.reflect.Method •Method’ы занимают 100-500MiB Spring AOP
  • 37.
    © Netcracker 201637 •OutOfMemory: perm gen •В хипдампе куча java.lang.reflect.Method •Method’ы занимают 100-500MiB •Все они лежат в AspectJExpressionPointcut Spring AOP
  • 38.
    © Netcracker 201638 @Pointcut( "execution( public* my.service.*ServiceImpl.*(..))” ) Spring AOP
  • 39.
    © Netcracker 201639 •Решение: добавлять within(my.package.service.business..*) Spring AOP vs AspectJ
  • 40.
    © Netcracker 201640 •Решение: добавлять within(my.package.service.business..*) •Разумеется, AspectJ рекомендуют использовать within всегда: http://dev.eclipse.org/mhonarc/lists/aspectj- users/msg10969.html • Spring AOP vs AspectJ
  • 41.
    © Netcracker 201641 •Cglib используется много где: рукотворный код, тот же Spring Cglib
  • 42.
    © Netcracker 201642 •Cglib используется много где: рукотворный код, тот же Spring •Наверняка уже всё исправлено давным- давно Cglib
  • 43.
    © Netcracker 201643 •Cglib используется много где: рукотворный код, тот же Spring •Наверняка уже всё исправлено давным- давно •Наверняка туда давно никто не заглядывал Cglib
  • 44.
    © Netcracker 201644 @Benchmark public Object newProxy() { return Beans.newProxy(Beans.class); } Cglib: замеряем
  • 45.
    © Netcracker 201645 0 2 4 6 8 10 12 14 16 18 Cglib 3.1 1 поток 2 потока 4 потока 8 потоков Замеры Cglib, μs/opБыстрее
  • 46.
    © Netcracker 201646 0 2 4 6 8 10 12 14 16 18 Cglib 3.1 Cglib 3.2.2 1 поток 2 потока 4 потока 8 потоков Замеры Cglib, μs/opБыстрее
  • 47.
    © Netcracker 201647 Benchmark Score newProxy 2.1 ± 0.3 μs/op newProxy.alloc.rate 1240 B/op Стало newProxy 0.14 ± 0.02 μs/op newProxy.alloc.rate 256 B/op Cglib 3.2.2: ждём в очередном Spring
  • 48.
    © Netcracker 201648 •Skip finalize while building proxy https://github.com/cglib/cglib/pull/51 •Concurrent cache of generated classes https://github.com/cglib/cglib/pull/53 План захвата Cglib
  • 49.
    © Netcracker 201649 •А, может, ну его этот cglib, есть же быстрый ByteBuddy? Но есть же ByteBuddy
  • 50.
    © Netcracker 201650 •А, может, ну его этот cglib, есть же быстрый ByteBuddy? @Benchmark public ExampleClass benchmarkCglib() { Enhancer enhancer = new Enhancer(); enhancer.setUseCache(false); ByteBuddy, jmh тест
  • 51.
    © Netcracker 201651 •А, может, ну его этот cglib, есть же быстрый ByteBuddy? @Benchmark public ExampleClass benchmarkCglib() { Enhancer enhancer = new Enhancer(); enhancer.setUseCache(false); ByteBuddy, jmh тест
  • 52.
    © Netcracker 201652 •JMH тесты в ByteBuddy показывают скорость создания классов •Постоянно создавать классы нехорошо •Значит, нужно измерять скорость кэшированного обращения ByteBuddy, jmh тест
  • 53.
    © Netcracker 201653 •КО: «Бери pgjdbc» Подключаемся к PostgreSQL
  • 54.
    © Netcracker 201654 •КО: «Бери pgjdbc» •КО: «Используй batch statements» Подключаемся к PostgreSQL
  • 55.
    © Netcracker 201655 •КО: «Бери pgjdbc» •КО: «Используй batch statements» •Что может пойти не так? Подключаемся к PostgreSQL
  • 56.
    © Netcracker 201656 Connection con = ...; PreparedStatement ps = con.prepareStatement("SELECT..."); ... ps.close(); PreparedStatement
  • 57.
    © Netcracker 201657 Connection con = ...; PreparedStatement ps = con.prepareStatement("SELECT..."); ... ps.close(); PreparedStatement
  • 58.
    © Netcracker 201658 PARSE S_1 as ...; // con.prepareStmt BIND/EXEC DEALLOCATE // ps.close() PARSE S_2 as ...; BIND/EXEC DEALLOCATE // ps.close() Работа с PostgreSQL курильщика
  • 59.
    © Netcracker 201659 PARSE S_1 as ...;  BIND/EXEC BIND/EXEC BIND/EXEC  BIND/EXEC BIND/EXEC ... DEALLOCATE Работа с PostgreSQL здорового человека
  • 60.
    © Netcracker 201660 PARSE S_1 as ...;  1 раз в жизни BIND/EXEC  обработка REST BIND/EXEC BIND/EXEC  ещё REST BIND/EXEC BIND/EXEC ... DEALLOCATE  желательно «никогда» DEALLOCATE Работа с PostgreSQL здорового человека
  • 61.
    © Netcracker 201661 Вывод: чтобы работало быстрее, закрывать statement’ы не нужно ps = con.prepareStatement(...) ps.execueQuery(); ps = con.prepareStatement(...) ps.execueQuery(); ... Счастливые statement’ов не закрывают
  • 62.
    © Netcracker 201662 Вывод: чтобы работало быстрее, закрывать statement’ы не нужно ps = con.prepare... ps.execueQuery(); ps = con.prepare... ps.execueQuery(); ... Счастливые statement’ов не закрывают
  • 63.
    © Netcracker 201663 @Benchmark public Statement leakStatement() { return con.createStatement(); } pgjdbc < 9.4.1202, -Xmx128m, OracleJDK 1.8u40 # Warmup Iteration 1: 1147,070 ns/op # Warmup Iteration 2: 12101,537 ns/op # Warmup Iteration 3: 90825,971 ns/op # Warmup Iteration 4: <failure> java.lang.OutOfMemoryError: GC overhead limit exceeded OpenJDK: не все JRE одинаково полезны
  • 64.
    © Netcracker 201664 @Benchmark public Statement leakStatement() { return con.createStatement(); } pgjdbc >= 9.4.1202, -Xmx128m, OracleJDK 1.8u40 # Warmup Iteration 1: 30 ns/op # Warmup Iteration 2: 27 ns/op ... github.com/pgjdbc/pgjdbc/pull/299 Убираем finalize из класса PgConnection
  • 65.
    © Netcracker 201665 Вывод: чтобы работало быстро, нужно как-то кэшировать statement’ы ps = con.prepareStatement("select id, name ..."); ps.execueQuery(); ps.close(); ps2 = con.prepareStatement("select id, name ..."); Счастливые statement’ов не закрывают
  • 66.
    © Netcracker 201666 •Кэш запросов появился в версии 9.4.1202 (2015-08-27) см. https://github.com/pgjdbc/pgjdbc/pull/319 Кэш запросов в PgJDBC
  • 67.
    © Netcracker 201667 •Кэш запросов появился в версии 9.4.1202 (2015-08-27) см. https://github.com/pgjdbc/pgjdbc/pull/319 •Работает прозрачно для приложения Кэш запросов в PgJDBC
  • 68.
    © Netcracker 201668 •Кэш запросов появился в версии 9.4.1202 (2015-08-27) см. https://github.com/pgjdbc/pgjdbc/pull/319 •Работает прозрачно для приложения •Скорость такая, что PL/PgSQL не нужен Кэш запросов в PgJDBC
  • 69.
    © Netcracker 201669 •Кэш запросов появился в версии 9.4.1202 (2015-08-27) см. https://github.com/pgjdbc/pgjdbc/pull/319 •Работает прозрачно для приложения •Скорость такая, что PL/PgSQL не нужен •Server-prepare активируется после 5-го выполнения (prepareThreshold) Кэш запросов в PgJDBC
  • 70.
    © Netcracker 201670 •Конечно, затраты planning time напрямую зависят от сложности запросов Цифры где?
  • 71.
    © Netcracker 201671 •Конечно, затраты planning time напрямую зависят от сложности запросов •У нас доходило до 20мс+ planning time на OLTP запросах: 10КиБ запрос, 170 строк explain • Цифры где?
  • 72.
    © Netcracker 201672 •Конечно, затраты planning time напрямую зависят от сложности запросов •У нас доходило до 20мс+ planning time на OLTP запросах: 10КиБ запрос, 170 строк explain •Стало ~0мс Цифры где?
  • 73.
    © Netcracker 201673 Если типы параметров меняются, то server- prepared statement приходится менять ps.setInt(1, 42); ... ps.setNull(1, Types.VARCHAR); JDBC: Типы параметров
  • 74.
    © Netcracker 201674 Если типы параметров меняются, то server- prepared statement приходится менять ps.setInt(1, 42); ... ps.setNull(1, Types.VARCHAR); JDBC: Типы параметров
  • 75.
    © Netcracker 201675 •Вывод: даже у NULL’ов должен быть верный тип JDBC: Тип параметров изменять нельзя
  • 76.
    © Netcracker 201676 •Вывод: даже у NULL’ов должен быть верный тип •Ещё раз: setObject(1, null) использовать нельзя JDBC: Тип параметров изменять нельзя
  • 77.
    © Netcracker 201677 •Перешли на prepared, и запрос замедлился в 5'000 раз. Как так? Нежданчик
  • 78.
    © Netcracker 201678 •Перешли на prepared, и запрос замедлился в 5'000 раз. Как так? Нежданчик A. Бага C. Фича B. Фича D. Бага
  • 79.
    © Netcracker 201679 https://gist.github.com/vlsi -> 01_plan_flipper.sql select * from plan_flipper -- <- таблица where skewed = 0 -- 1 млн строк and non_skewed = 42 -- 20 строк Нежданчик
  • 80.
    © Netcracker 201680 https://gist.github.com/vlsi -> 01_plan_flipper.sql 0.1мс  1-е выполнение 0.05мс  2-е выполнение 0.05мс  3-е выполнение 0.05мс  4-е выполнение 0.05мс  5-е выполнение 250 мс  6-е выполнение Нежданчик
  • 81.
    © Netcracker 201681 https://gist.github.com/vlsi -> 01_plan_flipper.sql 0.1мс  1-е выполнение 0.05мс  2-е выполнение 0.05мс  3-е выполнение 0.05мс  4-е выполнение 0.05мс  5-е выполнение 250 мс  6-е выполнение Нежданчик
  • 82.
    © Netcracker 201682 Запрещаем использование индекса через +0: select * from plan_flipper where skewed+0 = 0  ~ /*+no_index*/ and non_skewed = 42 Чиним
  • 83.
    © Netcracker 201683 Как сделать опциональные фичи? •NO_RESULTS •BOTH_ROWS_AND_STATUS •DESCRIBE_ONLY Фичи
  • 84.
    © Netcracker 201684 Конечно, enum! enum ... { NO_RESULTS, BOTH_ROWS_AND_STATUS, DESCRIBE_ONLY; } Фичи
  • 85.
    © Netcracker 201685 Конечно, enum! enum ... { NO_RESULTS, BOTH_ROWS_AND_STATUS, DESCRIBE_ONLY; } А на java 1.4? Фичи
  • 86.
    © Netcracker 201686 В java 1.4, конечно, interface! interface ... { int NO_RESULTS = 1; int BOTH_ROWS_AND_STATUS = 2; int DESCRIBE_ONLY = 4; } Фичи: java 1.4 наносит ответный удар
  • 87.
    © Netcracker 201687 Меняем 1 строку, и скорость работы batch insert возрастает в 10 раз: https://github.com/pgjdbc/pgjdbc/pull/380 - static int QUERY_FORCE_DESCRIBE_PORTAL = 128; + static int QUERY_FORCE_DESCRIBE_PORTAL = 512; Ужасы нашего городка
  • 88.
    © Netcracker 201688 Меняем 1 строку, и скорость работы batch insert возрастает в 10 раз: https://github.com/pgjdbc/pgjdbc/pull/380 - static int QUERY_FORCE_DESCRIBE_PORTAL = 128; + static int QUERY_FORCE_DESCRIBE_PORTAL = 512; // оказалось, значение 128 уже было занято static int QUERY_DISALLOW_BATCHING = 128; Ужасы нашего городка
  • 89.
    © Netcracker 201689 •WildFly 8.2, JMS HornetQ
  • 90.
    © Netcracker 201690 •WildFly 8.2, JMS •Всего ~100 JMS/сек HornetQ
  • 91.
    © Netcracker 201691 •WildFly 8.2, JMS •Всего ~100 JMS/сек •1 вызов sendMessage – 5-30 секунд HornetQ
  • 92.
    © Netcracker 201692 at java.util.concurrent.Semaphore.tryAcquire at org.hornetq ... ClientProducerCreditsImpl.acquireCredits at com ... JMSSender.sendMessage kill -3 профайлер
  • 93.
    © Netcracker 201693 at java.util.concurrent.Semaphore.tryAcquire at org.hornetq ... ClientProducerCreditsImpl.acquireCredits at com ... JMSSender.sendMessage kill -3 профайлер
  • 94.
    © Netcracker 201694 at java.util.concurrent.Semaphore.tryAcquire at org.hornetq ... ClientProducerCreditsImpl.acquireCredits at com ... JMSSender.sendMessage kill -3 профайлер
  • 95.
    © Netcracker 201695 at java.util.concurrent.Semaphore.tryAcquire at org.hornetq ... ClientProducerCreditsImpl.acquireCredits at org.hornetq ... ClientProducerImpl.sendRegularMessage at com ... JMSSender.sendMessage kill -3 профайлер
  • 96.
    © Netcracker 201696 at java.util.concurrent.Semaphore.tryAcquire at org.hornetq ... ClientProducerCreditsImpl.acquireCredits at org.hornetq ... ClientProducerImpl.sendRegularMessage at org.hornetq ... ClientProducerImpl.doSend at com ... JMSSender.sendMessage kill -3 профайлер
  • 97.
    © Netcracker 201697 at java.util.concurrent.Semaphore.tryAcquire at org.hornetq ... ClientProducerCreditsImpl.acquireCredits at org.hornetq ... ClientProducerImpl.sendRegularMessage at org.hornetq ... ClientProducerImpl.doSend at org.hornetq ... ClientProducerImpl.send at com ... JMSSender.sendMessage kill -3 профайлер
  • 98.
    © Netcracker 201698 at java.util.concurrent.Semaphore.tryAcquire at org.hornetq ... ClientProducerCreditsImpl.acquireCredits at org.hornetq ... ClientProducerImpl.sendRegularMessage at org.hornetq ... ClientProducerImpl.doSend at org.hornetq ... ClientProducerImpl.send at org.hornetq ... HornetQMessageProducer.doSendx at com ... JMSSender.sendMessage kill -3 профайлер
  • 99.
    © Netcracker 201699 • 'Producer Window Size’ на connection factory Backpressure
  • 100.
    © Netcracker 2016100 • 'Producer Window Size’ на connection factory • address-settings -> address-setting -> max-size-bytes Backpressure
  • 101.
    © Netcracker 2016101 • 'Producer Window Size’ на connection factory • address-settings -> address-setting -> max-size-bytes • Если в JMS очереди накапливается 10МиБ, то отправка JMS начинает притормаживать Backpressure
  • 102.
    © Netcracker 2016102 • 'Producer Window Size’ на connection factory • address-settings -> address-setting -> max-size-bytes • Если в JMS очереди накапливается 10МиБ, то отправка JMS начинает притормаживать <address-settings> <address-setting match="#"> <max-size-bytes>10 485 760</max-size-bytes> … Backpressure
  • 103.
    © Netcracker 2016103 •Настраиваем max-size-bytes для каждой очереди Backpressure: чиним
  • 104.
    © Netcracker 2016104 •Настраиваем max-size-bytes для каждой очереди •Либо смотрим в сторону RxJava Backpressure: чиним
  • 105.
    © Netcracker 2016105 •Старт приложения на WF 8.2 занимает 2-5 минут WildFly: пытаемся взлететь
  • 106.
    © Netcracker 2016106 •Старт приложения на WF 8.2 занимает 2-5 минут •Concurrent deploy работает плохо WildFly: пытаемся взлететь
  • 107.
    © Netcracker 2016107 •Старт приложения на WF 8.2 занимает 2-5 минут •Concurrent deploy работает плохо •Spring xml app config анализирует все jar’ники WildFly: пытаемся взлететь
  • 108.
    © Netcracker 2016108 •Уменьшать размер jar (исключать лишние) • WildFly копирует jar в /tmp при запуске WildFly: как чинить
  • 109.
    © Netcracker 2016109 •Уменьшать размер jar (исключать лишние) • WildFly копирует jar в /tmp при запуске •Делать патчи на WF, чтобы он не складывал строки • https://github.com/jbossas/jboss-vfs/pull/25 • https://github.com/wildfly/wildfly-core/pull/1219 WildFly: как чинить
  • 110.
    © Netcracker 2016110 •Уменьшать размер jar (исключать лишние) • WildFly копирует jar в /tmp при запуске •Делать патчи на WF, чтобы он не складывал строки • https://github.com/jbossas/jboss-vfs/pull/25 • https://github.com/wildfly/wildfly-core/pull/1219 •Профилировать запуск вашего WF (kill -3 profiler) WildFly: как чинить
  • 111.
    © Netcracker 2016111 Самое главное в CI – переменные окружения Jenkins
  • 112.
    © Netcracker 2016112 https://wiki.jenkins-ci.org/display/JENKINS/EnvInject+Plugin Jenkins: EnvInjectPlugin
  • 113.
    © Netcracker 2016113 Пишем по образу и подобию: LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$ORAC LE_HOME/… PATH=$PATH:$ORACLE/bin:… Jenkins: EnvInjectPlugin
  • 114.
    © Netcracker 2016114 EnvInjectEnvVars.resolveVars() Jenkins: EnvInjectPlugin
  • 115.
    © Netcracker 2016115 EnvInjectEnvVars.resolveVars() -> hudson.Util.replaceMacro() Jenkins: EnvInjectPlugin
  • 116.
    © Netcracker 2016116 EnvInjectEnvVars.resolveVars() -> hudson.Util.replaceMacro() -> OutOfMemoryError.<init>() Jenkins: EnvInjectPlugin
  • 117.
    © Netcracker 2016117 $LD_LIBRARY_PATH:$ORACLE_HOME/lib :$ORACLE_HOME/lib:$ORACLE_HOME/lib :$ORACLE_HOME/lib:$ORACLE_HOME/lib :$ORACLE_HOME/lib:$ORACLE_HOME/lib :$ORACLE_HOME/lib:$ORACLE_HOME/lib :$ORACLE_HOME/lib:$ORACLE_HOME/lib :$ORACLE_HOME/lib:$ORACLE_HOME/lib :$ORACLE_HOME/lib:... Jenkins: EnvInjectPlugin
  • 118.
    © Netcracker 2016118 https://issues.jenkins-ci.org/browse/JENKINS- 19856 ^^^ с вами с 2013 года Jenkins: EnvInjectPlugin
  • 119.
    © Netcracker 2016119 •Общение Jenkins master и slave тщательно логируется Jenkins: master vs slave
  • 120.
    © Netcracker 2016120 •Общение Jenkins master и slave тщательно логируется •«Разумеется», в памяти хранятся последние 1000 записей Jenkins: master vs slave
  • 121.
    © Netcracker 2016121 •Общение Jenkins master и slave тщательно логируется •«Разумеется», в памяти хранятся последние 1000 записей •Н а эти логи может уходить 100-200МиБ Jenkins: master vs slave
  • 122.
    © Netcracker 2016122 •Общение Jenkins master и slave тщательно логируется •«Разумеется», в памяти хранятся последние 1000 записей •Н •Да, да. Логи хранятся в памяти и никогда не попадают в файл Jenkins: master vs slave
  • 123.
    © Netcracker 2016123 •Хорошо, что есть опция • -Dhudson.remoting.ExportTable.unexportLog=0 Jenkins: master vs slave
  • 124.
    © Netcracker 2016124 •Хорошо, что есть опция • -Dhudson.remoting.ExportTable.unexportLog=0 •Разумеется, в Google ровно один результат Jenkins: master vs slave
  • 125.
    © Netcracker 2016125 •Хорошо, что есть опция • -Dhudson.remoting.ExportTable.unexportLog=0 •Разумеется, в Google ровно один результат •На исходный код, где она определена :) Jenkins: master vs slave
  • 126.
    © Netcracker 2016126 •Оказалось, что запуск master занимает 50 минут • 3000 jobs, 10-500 runs per job • 24 CPU, 64 GiB RAM • -Xmx40g Jenkins: запускаемся
  • 127.
    © Netcracker 2016127 •При старте, Jenkins инициализирует job’ы Jenkins: файлы разные нужны, файлы разные важны
  • 128.
    © Netcracker 2016128 •При старте, Jenkins инициализирует job’ы •Maven job загружает данные по всем запускам Jenkins: файлы разные нужны, файлы разные важны
  • 129.
    © Netcracker 2016129 •При старте, Jenkins инициализирует job’ы •Maven job загружает данные по всем запускам •В каждом запуске есть fingerprint’ы, они тоже грузятся Jenkins: файлы разные нужны, файлы разные важны
  • 130.
    © Netcracker 2016130 •При старте, Jenkins инициализирует job’ы •Maven job загружает данные по всем запускам •В •В итоге много-много операций с диском •и тоже грузятся Jenkins: файлы разные нужны, файлы разные важны
  • 131.
    © Netcracker 2016131 •Решение в лоб: удалить fingerprint’ы перед запуском Jenkins: чиним
  • 132.
    © Netcracker 2016132 •Решение в лоб: удалить fingerprint’ы перед запуском •Более сложное: не плодить fingerprint’ы Jenkins: чиним
  • 133.
    © Netcracker 2016133 •Решение в лоб: удалить fingerprint’ы перед запуском •Более сложное: не плодить fingerprint’ы •Поможет и сокращение хранимой истории Jenkins: чиним
  • 134.
    © Netcracker 2016134 •Решение в лоб: удалить fingerprint’ы перед запуском •Более сложное: не плодить fingerprint’ы •Поможет и сокращение хранимой истории •В идеальном мире нужно разбираться с maven-jenkins-plugin Jenkins: чиним
  • 135.
    © Netcracker 2016135 Кто виноват и что делать?
  • 136.
    © Netcracker 2016136 •Владимир Ситников •Инженер по производительности в Netcracker •sitnikov@netcracker.com •@VladimirSitnikv С вами был
  • 137.
    © Netcracker 2016137 Вопросы?