И така, вие измерихте и настроихте вашата система, както е описано в Част I и Част II на тази поредица публикации в блога. Сега искате да получите някои цифри колко крайни потребители вашата система ще може да обслужва. Следователно вие определяте „сценарии“, които ще бъдат типични за това, което правят вашите потребители.
Един такъв потребителски сценарий може да бъде, например:

  • Влизам
  • направи нещо
  • излез от профила си

Тъй като вашите потребители няма да се наредят на опашка и да чакат други потребители да приключат работата си, темпото, от което се нуждаете, за да тествате вашата дефинирана система, е „стартиране на n сценария всяка секунда“. Много сценарии, симулиращи различни потребители, може да се изпълняват паралелно. Ако вашият сценарий ще изисква 10 секунди за завършване и ще започнете 1 на секунда, това означава, че вашата система трябва да може да обработва 10 потребители паралелно. Ако не може да се справи с това, ще видите, че повече от 10 сесии се изпълняват паралелно и времето, необходимо за справяне с такъв сценарий, ще се удължи. Ще видите как използването на сървърните ресурси се увеличава и накрая ще избухне в пламъци.

За да намерите тесните места във вашия сценарий, вие използвате statsd сонди, както направихме в последните статии. Тъй като искаме да оценим възможната честотна лента на нашата система, ще я поставим в червената зона и накрая ще я накараме да избухне в пламъци — за да стане ясно — трябва да направим това, за да видим възможните ограничения.

Как да създадете сценарии за използване

Трябва да симулирате клиенти паралелно. Едно решение би било да има една нишка на клиент. Въпреки това, хвърлянето на хайвер на нишки не е толкова евтино и в крайна сметка ще претоварите системата си, извършвайки инжектиране на натоварване. Тъй като вашият клиент така или иначе ще чака отговорите на ArangoDB, можем да използваме цикъл на събитията, за да обработваме „паралелно“ изпълняващите тестове: gevent.

Спазвайте правилата на eventloop!

Ние използваме pyArango, който използва python requests като свой бекенд. Тъй като заявките на python използват блокиращ вход/изход (по този начин — изчаква отговор на сървъра), трябва да го заменим с неблокираща версия. Можем да използваме „маймунски корекции“, за да го заменим под капака:

Както можете да видите, ние съобщаваме за прекъсване/свързване към statsd gauge, така че също така можем да наблюдаваме дали това не се случва често (тъй като също е от значение за производителността).

Обработка на работници

Gevent има концепцията за "greenlets" като един лек процесор. Ще начертаем една зеленичка към един сценарий.

Основният процес в Python ще породи зеленичките (ние ги наричаме работници засега). Един greenlet ще се превърне в жизнения цикъл на една такава симулирана потребителска сесия.

Има два начина за унищожаване на зелените петна, единият е основният процес - събирането им. След това ще се изчака освобождаването на ресурсите на greenlets, докато процесът излезе. Така че можехме да направим това само след нашия тестов сценарий. Следователно тези зеленички ще се задържат наоколо, използвайки памет, чакайки окончателния край на теста. Тъй като също възнамеряваме да проведем тестове за издръжливост, това ще се натрупа, докато основната ни памет не се изразходва - не е толкова умно. Затова поемаме по друг начин, правим повдигане на GreenletExit, за да кажем на gevent, че трябва да ги изчисти:

Мащабиране на loadtest

Нашият процес на симулация на зареждане на Python трябва:

  • симулирайте потока на нашето приложение
  • генериране на документи за публикуване на json
  • генериране на http заявки
  • анализирайте http отговорите
  • анализирайте json отговорите
  • проверете отговорите за грешки

Следователно имаме нужда и от определено количество CPU. Тъй като избрахме да преминем към единичната нишка с зелените, ние сме ограничени до максималната честотна лента, с която един процес на python може да се справи. Тъй като искаме да имаме стабилен модел, излъчван от симулатора, трябва да следим най-отгоре за нашия процес на Python и неговото използване на процесора. Въпреки че на многоядрена система повече от 100% CPU може да е възможно за многонишкови процеси, това не е вярно за нашия Python процес. За да имаме стабилен модел, не трябва да използваме повече от 80% CPU.

Ако искаме да генерираме повече натоварване, имаме нужда от повече работещи процеси на python и по този начин да можем да разделим потребителската база, върху която изпълняваме теста, между тези процеси.

Как най-добре да оцените възможната честотна лента?

Сега, след като имаме достатъчно познания за това, че всичко работи - как да оценим възможната честотна лента на дадена система?

Въпреки че ArangoDB може да преживее кратък „взрив“, всъщност това не е това, което искаме да имаме. Интересуваме се да разберем какво натоварване може да издържи за по-дълъг период от време, включително работата на вътрешните фонови задачи на базата данни и т.н. Ние определяме време за нарастване, което системата трябва да поддържа в крак с определен модел на натоварване. т.е. 4 минути могат да бъдат добро време за набиране на скорост за първа оценка. След като оцеля през времето за нарастване, ние натискаме по-силно, като увеличаваме модела на натоварване. По този начин би трябвало да можем бързо да стесним възможната честотна лента.

След като имаме грубо очакване какво трябва да оцелее системата, можем да проведем по-дълги тестове само с този модел за 10 минути или дори часове. В зависимост от сложността на вашия сценарий, най-добрият начин да създадете тази рампа е да създадете повече Python процеси - или на една, или повече машини за инжектиране на натоварване. Трябва също така да изпратите този брой процеси към вашата система за наблюдение, така че да можете да видите стъпките си на нарастване заедно с другите показатели за наблюдение.

Как да разпознаем дали SUT е достигнал максимум?

Има няколко индикатора, че вашият ArangoDB, известен още като „тествана система“ (SUT), е достигнал максимума:

  • Брой файлови дескриптори — (затова добавихме възможността да го наблюдаваме към collectd предварително) Всяка връзка на клиент към базата данни се равнява на файлов дескриптор, плюс виртуалната памет на базата данни и отворените регистрационни файлове, ако е клъстерна връзка в рамките на възлите, и т.н. това трябва да е доста постоянно число. След като видите, че този брой нараства, броят на паралелно работещите клиенти се увеличава, тъй като отделните клиенти не са готови навреме поради заявките за бази данни, които отнемат повече време
  • Процент на грешки на клиента — Определено трябва да улавяте грешки в потока на клиента си и след като се появят, кажете на statsd, че се случват, и напишете някакъв резултат от журнала. Въпреки това, трябва да се уверите, че те не са поради т.е. повторно използване на ID и по този начин припокриващи се заявки, което не би се случило обикновено
  • Натоварване на процесора — След като всички процесори бъдат максимизирани до определено количество, повече натоварване няма да е възможно. След като това се случи, вие сте обвързани с процесора
  • I/O Amount - След като дискът не може да чете/записва повече данни наведнъж, вие сте обвързани с I/O
  • Памет — това е най-трудното за откриване, тъй като системата може да прави много с паметта, като да я използва за дисков кеш, симулиране на налична памет от суап и т.н. Така че обвързаността с памет обикновено показва само това, че дискът и процесорът са доста под червените нива
  • Карти на паметта — Те се използват за архивиране на по-големи парчета памет на диск или за зареждане на динамични библиотеки в процеси. Ядрото на linux дава общ преглед на него в /proc//maps. Ще можем да ги наблюдаваме с collectd 5.8
  • Налични V8 контексти — въпреки че е възможно да се изпълнят широк набор от AQL заявки без използване на Javascript / V8, Transactions, User Defined Functions или Foxx Microservices ги изискват; и по този начин те могат да се превърнат в оскъден ресурс

Стартиране на пример

Инициализиране на данните

За да получим груб преглед на възможностите на ArangoDB, ние създаваме „малка графика“, която можем да използваме по-късно в заявки за документи или „преминавания“:

Действителният тестов код

Ние създаваме пример, извършвайки транзакция със заявка за графика, актуализираща някои от върховете на дълбочина от ниво 1, които намираме. Изпълняваме някои други заявки, за да модифицираме директно основния връх. Смятаме, че този случай на употреба е процедура за влизане, която отчита влизанията на потребителя и групите, в които е назначен да бъде. Ние контролираме набор от потребители и модела на зареждане, който искаме да инжектираме чрез параметри на командния ред:

Допълнителните зависимости на горния код могат да бъдат инсталирани по следния начин на система debian:

Анализиране на резултатите от теста

Стартираме първи инжектор за натоварване в 17:51 : ./test.py 100 2000 1 и втори, увеличаващ натоварването в 17:55: ./test.py 2000 4000 1

Това е, което виждаме в kcollectd от теста (графика 1 в долната част):

Ние анализираме тези графики, като започваме от най-ниската и вървим нагоре графика по графика. Тъй като KCollectd може да групира измервателни уреди само по една ос и не може да прави никакво преобразуване на стойности, графиките са групирани по диапазони на стойности, така че да можем да видим значими отклонения. В Graphite можем да добавим втора Y-ос, да добавим скали и т.н., за да групираме по-добре стойностите по тяхното значение, вместо по тяхната стойност.

Графика 1

Както бе споменато по-горе, ние също наблюдаваме броя на грешките чрез уред за измерване на брояча на statsd, така че всъщност можем да видим кога желаният тестов модел не може да бъде постигнат поради например припокриване на акаунти или грешки от страна на сървъра.

В текущото тестово изпълнение виждаме, че броят на грешките започва да нараства в 17:57 — по този начин сървърът не успява да издържи модела на натоварване. В изхода за грешка от теста виждаме действителната причина:

Така че ресурсът, който ни изчерпва, са контекстите V8 за изпълнение на нашата транзакция. Процентилът на заявките обаче показва, че времето за изпълнение е постоянно.

Графика 2

Проверявайки следващата графика, виждаме, че броят на едновременно активните сценарии нараства рязко, когато стартираме втория тестов агент. След като грешките се повишат, тези активни клиенти ще отпаднат отново, тъй като неуспешните сесии завършват по-бързо.

Графика 3

В третата графика виждаме, че броят на картираните файлове в паметта се удвоява от под 4k до над 8k — което би било около очакваните цифри при удвояване на тестовия сценарий.

Графика 4

В 4-та графика виждаме, че използването на процесора на потребителското пространство се удвоява, докато използването на системния процесор рязко нараства. Част от работата с повече нишки е заключването на ресурси, споделени между нишките. По този начин използването на процесора на пространството на ядрото се повишава с претоварването на сървъра.

Графика 5

Петата графика показва броя на нишките и броя на отворените файлови дескриптори и сървърни нишки. Докато вдигаме товара, виждаме и двамата да се катерят. По този начин ефектът, който виждаме, е, че клиентът за тестване на натоварване отваря повече tcp връзки (които също са манипулатори на файлове). Сървърът създава повече нишки, за да следва сценария на натоварване - но наличната пропускателна способност на тази система не е по-голяма.

По този начин се стартират все повече нишки, които чакат процесорното време да свърши работата си. В даден момент тези нишки започват да изтичат в изчакване за придобиване на V8 контексти за транзакциите. Въпреки че клиентът все още отваря повече връзки, за да удовлетвори модела на натоварване, който искахме да създадем, сървърът вече не може да го следва.

Заключение

Успешно демонстрирахме тестов клиент, симулиращ клиентски сценарии. Тъй като извършваме голяма част от работата си в транзакция и не прехвърляме големи отговори на клиента, комбинацията python/gevent може лесно да създаде модела на натоварване, който желаем. SUT може да поддържа пропускателна способност от 1 сценария/секунда. При последващи тестове за нарастване може да се опита да се оцени на по-подробни стъпки дали т.е. 1,5 сценария/секунда са възможни. С такъв резултат може да се направи тест за издръжливост за по-дълъг период от време.

Както е публикувано по-рано в ArangoDB