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