

As traduções são geradas por tradução automática. Em caso de conflito entre o conteúdo da tradução e da versão original em inglês, a versão em inglês prevalecerá.

# Apêndice B – Exemplo de cálculo do qui-quadrado
<a name="appendix-b-example-chi-squared-calculation"></a>

 Veja a seguir um exemplo de coleta de métricas de erro e realização de um teste qui-quadrado nos dados. O código não está pronto para produção e não executa o tratamento de erros necessário, mas fornece uma prova de conceito sobre como a lógica funciona. Você deve atualizar esse exemplo para atender às suas necessidades. 

 Primeiro, uma função do Lambda é do invocada a cada minuto por um evento programado do Amazon EventBridge. O conteúdo do evento é configurado com os seguintes dados: 

```
{ 
  "timestamp": "2023-03-15T15:26:37.527Z", 
  "namespace": "multi-az/frontend", 
  "metricName": "5xx", 
  "dimensions": [ 
    { "Name": "Region", "Value": "us-east-1" }, 
    { "Name": "Controller", "Value": "Home" }, 
    { "Name": "Action", "Value": "Index" } 
  ], 
  "period": 60, 
  "stat": "Sum", 
  "unit": "Count", 
  "chiSquareMetricName": "multi-az/chi-squared", 
  "azs": [ "use1-az2", "use1-az4", "use1-az6" ] 
}
```

 Os dados são usados para especificar os dados comuns necessários para recuperar as métricas apropriadas do CloudWatch (como namespace, nome da métrica e dimensões) e, em seguida, publicar os resultados do qui-quadrado para cada zona de disponibilidade. O código na função do Lambda é semelhante ao código a seguir, usando o Python 3.9. Em um alto nível, ele coleta as métricas especificadas do CloudWatch para o minuto anterior, executa o teste qui-quadrado nesses dados e, em seguida, publica as métricas do CloudWatch sobre o resultado do teste para cada zona de disponibilidade especificada. 

```
import os
import boto3
import datetime
import copy
import json
from datetime import timedelta
from scipy.stats import chisquare
from aws_embedded_metrics import metric_scope

cw_client = boto3.client("cloudwatch", os.environ.get("AWS_REGION", "us-east-1"))

@metric_scope
def handler(event, context, metrics):
    metrics.set_property("Event", json.loads(json.dumps(event, default = str)))
    time = datetime.datetime.strptime(event["timestamp"], "%Y-%m-%dT%H:%M:%S.%fZ")
    
    # Round down to the previous minute
    end: datetime = roundTime(time)

    # Subtract a minute for the start
    start: datetime = end - timedelta(minutes = 1)

    # Get all the metrics that match the query
    results = get_all_metrics(event, start, end, metrics)
    metrics.set_property("MetricCounts", results)

    # Calculate the chi squared result
    chi_sq_result = chisquare(list(results.values()))
    expected = sum(list(results.values())) / len(results.values())
    metrics.set_property("ChiSquaredResult", chi_sq_result)

    # Put the chi square metrics into CloudWatch
    put_all_metrics(event, results, chi_sq_result[1], expected, start, metrics)

def get_all_metrics(detail: dict, start: datetime, end: datetime, metrics):
    """
    Gets all of the error metrics for each AZ specified
    """
    metric_query = {
        "MetricDataQueries": [
        ],
        "StartTime": start,
        "EndTime": end
    }

    for az in detail["azs"]:

        dim = copy.deepcopy(detail["dimensions"])
        dim.append({"Name": "AZ-ID", "Value": az})

        query = {
            "Id": az.replace("-", "_"),
            "MetricStat": {
                "Metric": {
                    "Namespace": detail["namespace"],
                    "MetricName": detail["metricName"],
                    "Dimensions": dim
                },
                "Period": int(detail["period"]),
                "Stat": detail["stat"],
                "Unit": detail["unit"]
            },
            "Label": az,
            "ReturnData": True
        }

        metric_query["MetricDataQueries"].append(query)

    metrics.set_property("GetMetricRequest", json.loads(json.dumps(metric_query, default=str)))
    next_token: str = None
    results = {}

    while True:
        if next_token is not None:
            metric_query["NextToken"] = next_token

        data = cw_client.get_metric_data(**metric_query)

        if next_token is not None:
            metrics.set_property("GetMetricResult::" + next_token, json.loads(json.dumps(data, default = str)))
        else:
            metrics.set_property("GetMetricResult", json.loads(json.dumps(data, default = str)))

        for item in data["MetricDataResults"]:
            key = item["Id"].replace("_", "-")
            if key not in results:
              results[key] = 0

            results[key] += sum(item["Values"])

        if "NextToken" in data:
            next_token = data["NextToken"]

        if next_token is None:
            break

    return results

def put_all_metrics(detail: dict, results: dict, chi_sq_value: float, expected: float, timestamp: datetime, metrics):  
    """
    Adds the chi squared metric for all AZs to CloudWatch
    """
    farthest_from_expected = None
    if len(results) > 0:
        keys = list(results.keys())
        farthest_from_expected = keys[0]

        for key in keys:
            if abs(results[key] - expected) > abs(results[farthest_from_expected] - expected):
               farthest_from_expected = key

    metric_query = {
        "Namespace": detail["namespace"],
        "MetricData": []
    }

    for az in detail["azs"]:
        dim = copy.deepcopy(detail["dimensions"])
        dim.append({"Name": "AZ-ID", "Value": az})

        query = {
            "MetricName": detail["chiSquareMetricName"],
            "Dimensions": dim,
            "Timestamp": timestamp,
        }

        if chi_sq_value <= 0.05 and az == farthest_from_expected:
            query["Value"] = 1
        else:
            query["Value"] = 0

        metric_query["MetricData"].append(query)

    metrics.set_property("PutMetricRequest", json.loads(json.dumps(metric_query, default = str)))

    cw_client.put_metric_data(**metric_query)

def roundTime(dt=None, roundTo=60):
   """Round a datetime object to any time lapse in seconds
   dt : datetime.datetime object, default now.
   roundTo : Closest number of seconds to round to, default 1 minute.
   """
   if dt == None : dt = datetime.datetime.now()
   seconds = (dt.replace(tzinfo=None) - dt.min).seconds
   rounding = (seconds+roundTo/2) // roundTo * roundTo
   return dt + datetime.timedelta(0,rounding-seconds,-dt.microsecond)
```

 Em seguida, você já pode criar um alarme por AZ. O exemplo a seguir é para `use1-az2` e gera o alarme para três pontos de dados de um minuto em uma linha que tem um valor máximo igual a 1 (1 é a métrica publicada quando o teste qui-quadrado determina uma distorção estatisticamente significativa na taxa de erro). 

```
{
    "Type": "AWS::CloudWatch::Alarm",
    "Properties": {
        "AlarmName": "use1-az2-chi-squared",
        "ActionsEnabled": true,
        "OKActions": [],
        "AlarmActions": [],
        "InsufficientDataActions": [],
        "MetricName": "multi-az/chi-squared",
        "Namespace": "multi-az/frontend",
        "Statistic": "Maximum",
        "Dimensions": [
            {
                "Name": "AZ-ID",
                "Value": "use1-az2"
            },
            {
                "Name": "Action",
                "Value": "Index"
            },
            {
                "Name": "Region",
                "Value": "us-east-1"
            },
            {
                "Name": "Controller",
                "Value": "Home"
            }
        ],
        "Period": 60,
        "EvaluationPeriods": 3,
        "DatapointsToAlarm": 3,
        "Threshold": 1,
        "ComparisonOperator": "GreaterThanOrEqualToThreshold",
        "TreatMissingData": "missing"
    }
}
```

 Você também pode criar um alarme m-of-n e combinar esses dois alarmes em um alarme composto. Você também precisa criar os mesmos alarmes para cada combinação de controlador/ação ou microsserviço que tem em cada zona de disponibilidade. Finalmente, você pode adicionar o alarme composto de qui-quadrado ao alarme específico da zona de disponibilidade para cada combinação de controlador/ação, conforme mostrado em [Detecção de falhas usando detecção de discrepâncias](failure-detection-using-outlier-detection.md). 