Skip to main content

Awarie na produkcji - dane potrzebne do analizy

Maxime avatar
Written by Maxime
Updated over 4 months ago

Rabbit

Narastająca liczba wiadomości na kolejkach

Kiedy jest zauważona sytuacja gdzie wiadomości kumulują się na kolejkach, i nie są one pobierane przez consumerów (po upewnieniu się, że są podpięci pod daną kolejkę), należy sprawdzić czy nie ma problemów z klastrem rabbita

Rozklastrowany rabbit

Jak sprawdzić czy rabbit jest rozklastrowany, jaki jest status klastra;

W kontenerze rabbita uruchomić polecenie rabbitmqctl cluster_status

Jeżeli wynik polecenia wygląda jak poniżej to znaczy, że wszystko jest ok;

Jeżeli są błędy z klastrem, wtedy należy podesłać wynik poprzedniego polecenia + logi z

node, który jakimś sposobem został wypięty (najczęściej jest to przez błąd związany z

network partition), wtedy należy:

Położyć node w ustalonej kolejności, np. 1 -> 2 -> 3 i później podnieść node w kolejności

odwrotnej, tj. 3 -> 2 -> 1

Jeżeli dalej któryś node nie może się podłączyć do klastra, należy:

na node_OK albo node_OK2:

w kontenerze rabbitmq:

rabbitmqctl forget_cluster_node rabbit@node_wadliwy

potem w kontenerze rabbitmq na node_wadliwy:

rabbitmqctl stop_app

rabbitmqctl reset

rabbitmqctl join_cluster rabbit@node_OK LUB node_OK2 (zależne od tego na którym

node wchodzimy do kontenera rabbitmq)

rabbitmqctl start_app

Jeżeli dalej będą problemy z wpięciem się nodem do klastra po prawidłowo

wykonanych poprzednich krokach, to wtedy rekomendujemy wyczyścić wadliwy

node:

1. wyłączyć rabbitmq na node_wadliwy, jeżeli nie jest wyłączony

2. na node_wadliwy - usunąć folder z danymi dla rabbitmq:

sprawdzić mapę volumes: w docker-compose, gdzie jest zmapowany folder

/var/lib/rabbitmq, np.

"/data/rabbitmq:/var/lib/rabbitmq:z"

taki wpis oznacza, że dane na hoście dane kontenera rabbitmq znajdują się w katalogu

/data/rabbitmq.

Usunąć zawartość katalogu /data/rabbitmq

3. na node_wadliwy - w docker-compose.yaml rabbitmq sprawdzić czy entrypoint to:

rabbit-cluster-policies.sh

Jeżeli tak to:

zmienić na rabbit-cluster-entrypoint.sh

Upewnić się, że plik znajduje się na hoście. Jeżeli nie ma, to można z innego node

“pożyczyć” ten skrypt.

4. zmienić linijkę w skrypcie

rabbit-cluster-entrypoint.sh:

rabbitmqctl join_cluster rabbit@node_wadliwy

na

rabbitmqctl join_cluster rabbit@node_OK LUB node_OK2 (zależne od tego z którego

node został “pożyczony” skrypt z kroku 3.)

5. uruchomić kontener rabbitmq.

6. Jak node_wadliwy dołączy do klastra to można przywrócić entrypoint do

poprzedniego stanu. Nie trzeba restartować po tym kontenera.

SQL

Sytuacja kiedy dostęp do bazy danych jest częściowo blokowany (błędy typu Execution Timeout Expired. The timeout period elapsed prior to completion of the operation or the server is not responding)

  1. Sprawdzić top 10 najcięższych operacji wykonujących się na bazie;


    SELECT TOP 10 st.text AS batch_text,SUBSTRING(st.TEXT, (qs.statement_start_offset / 2) + 1,((CASE qs.statement_end_offset WHEN - 1 THEN DATALENGTH(st.TEXT)ELSE qs.statement_end_offset END - qs.statement_start_offset) / 2) + 1) AS statement_text,(qs.total_worker_time / 1000) / qs.execution_count AS avg_cpu_time_ms,(qs.total_elapsed_time / 1000) / qs.execution_count AS avg_elapsed_time_ms, qs.total_logical_reads / qs.execution_count AS avg_logical_reads,  (qs.total_worker_time / 1000) AS cumulative_cpu_time_all_executions_ms,  (qs.total_elapsed_time / 1000) AS cumulative_elapsed_time_all_executions_ms   FROM sys.dm_exec_query_stats qs CROSS APPLY sys.dm_exec_sql_text(sql_handle) st   ORDER BY(qs.total_worker_time / qs.execution_count) DESC

  2. Uruchomić SQL profilera na jakiś czas i zapisać i podesłać wyniki

  3. Podesłanie wyników zapytania zwracającego listę sesji;

SELECT apis.requestdate, apis.externaluserid,
CASE
WHEN apis.status = 1 THEN 'Initiated'
WHEN apis.status = 2 THEN 'AuthenticationRequired'
WHEN apis.status = 3 THEN 'InProgress'
WHEN apis.status = 4 THEN 'Finished'
WHEN apis.status = 5 THEN 'AuthorizationFailed'
WHEN apis.status = 6 THEN 'Failed'
WHEN apis.status = 7 THEN 'DeletingConsent'
WHEN apis.status = 8 THEN 'FinishedManually'
WHEN apis.status = 9 THEN 'Expired'
ELSE 'other'
END AS SessionStatus,
CASE
WHEN [stage] = 1 THEN 'Authentication'
WHEN [stage] = 2 THEN 'Accounts'
WHEN [stage] = 3 THEN 'Transactions'
WHEN [stage] = 4 THEN 'Payment'
WHEN [stage] = 5 THEN 'TransactionDetails'
WHEN [stage] = 6 THEN 'DeleteConsent'
WHEN [stage] = 7 THEN 'Expired'
ELSE 'other'
END AS Stage,
CASE
WHEN [sessionsource] = 1 THEN 'Authorize'
WHEN [sessionsource] = 2 THEN 'RefreshActiveAccounts'
WHEN [sessionsource] = 3 THEN 'Batch'
WHEN [sessionsource] = 4 THEN 'DeleteConsent'
ELSE ' other'
END AS SessionSource,
[ExecutionId],
[failuremessageid],
[accountsfetchedcount],
[accountsprocessedcount],
(SELECT NAME FROM [dbo].bank b WHERE b.id = apis.bankid) AS BankName
FROM [dbo].apisession apis
WHERE requestdate > Dateadd(day, -2, (getdate()))
ORDER BY RequestDate DESC
  1. Podesłanie trace z sesji próbnych przechodzonych przez testerów z Banku - w tabeli ApiSession jest kolumna ExecutionId, która podaje wartość pola executionData.id w NLog w mongo

  2. Podesłanie consentu dla sesji z kroku 4.

Mongo

Dłuższe czasy odpowiedzi na np. authorize albo get bankaccount - należy podesłać wyniki zapytań

  1. Statystyki dla authorize:

var cursor=db.getCollection('KOLEKCJA').aggregate(
[{
$match: { $or: [
{
message: /api\/v2\/bank\/.*\/authorize/i,
exception: { $exists: false },
logger: 'Next',
'details.method': 'POST'
},
{
message: /api\/v2\/bank\/.*\/authorize/i,
exception: { $exists: false },
logger: 'LogResponse'}]}},
{
$group: {
_id: { executionId: '$executionData.id' },
name: { $min: "$message" },
containerId: { $min: "$machine.machineName" },
status: { $max: "$details.headers.Actual-Response-Status" },
min: { $min: "$date" },
max: { $max: "$date" },
elementCount: { $sum: 1 }
}},
{
$project: {
_id:0,
executionId: '$_id.executionId',
containerId: '$containerId',
status: '$status',
name: '$name',
numberOfDocs: '$elementCount',
when: '$min',
howLong: { $divide: [ {$subtract: [ "$max", "$min" ]}, 1000]}
}},
{
$match: {
numberOfDocs : { $gt: 1}
}}],
{ allowDiskUse: true }
);
while(cursor.hasNext())
{print(cursor.next());}
  1. Statystyki dla bankaccount:

var cursor=db.getCollection('KOLEKCJA').aggregate(
[
   {
       $match: { $or: [
                   {
                       message: /api\/v2\/bankaccount$/i,
                       exception: { $exists: false },
                       logger: 'Next'
                   },
                   {
                       message: /api\/v2\/bankaccount\/$/i,
                       exception: { $exists: false },
                       logger: 'Next'
                   },
                   {
                       message: /api\/v2\/bankaccount$/i,
                       exception: { $exists: false },
                       logger: 'LogResponse'
                   },
                   {
                       message: /api\/v2\/bankaccount\/$/i,
                       exception: { $exists: false },
                       logger: 'LogResponse'
                   }
               ]
           }
   },
   {
      $group: {
          _id: { executionId: '$executionData.id' },
          name: { $min: "$message" },
          containerId: { $min: "$machine.machineName" },
          refresh: { $max: "$details.arguments.refreshActiveAccounts" },
          status: { $max: "$details.headers.Actual-Response-Status" },
          min: { $min: "$date" },
          max: { $max: "$date" },
          elementCount: { $sum: 1 }
      }
   },
   {
      $project: {
          _id:0,
       executionId: '$_id.executionId',
       refresh: '$refresh',
       status: '$status',
       name: '$name',
       containerId: '$containerId',
       numberOfDocs: '$elementCount',
       when: '$min',
       howLong: { $divide: [ {$subtract: [ "$max", "$min" ]}, 1000]}
    }
 },
 {
       $match: {
           numberOfDocs : { $gt: 1}
           }
 }
],
{ allowDiskUse: true });
while(cursor.hasNext())
{print(cursor.next());}
  1. Wyniki poleceń z kontenera mongo na middleware (interwał ustawiony na odpytywanie co 10s, wyniki z 3-5 interwałów)


mongotop -u $MONGO_INITDB_ROOT_USERNAME -p $MONGO_INITDB_ROOT_PASSWORD mongodb://localhost/?authSource=admin 10

mongostat -u $MONGO_INITDB_ROOT_USERNAME -p $MONGO_INITDB_ROOT_PASSWORD mongodb://localhost/?authSource=admin 10

  1. Jeżeli zauważymy, że konektor na którymś node nie otrzymuje zadań, należy zobaczyć czy jest zarejestrowany w consulu;
    Wewnątrz kontenera consula na tym node uruchomić polecenie, podesłać jego wynik, zebrać logi z niedziałającego konektora i zrestartować go, powinien się zarejestrować do consula przy starcie

    curl http://localhost:8500/v1/catalog/service/PSD2Connector 

Kontener nie może podłączyć się do NLog

  1. Jeżeli zauważymy, że któryś kontener nie może podłączyć się do NLoga, należy wejść bezpośrednio do kontenera i ściągnąć bezpośrednio z głównej lokalizacji plik NLog.internal.txt, tam będzie informacja dlaczego pojawiają się błędy

Did this answer your question?