Introdução ao escalonamento automático de slots

As reservas que você configura para usar o escalonamento automático de slots escalonam automaticamente a capacidade alocada para acomodar as demandas de carga de trabalho. À medida que a carga de trabalho aumenta ou diminui, o BigQuery ajusta dinamicamente os slots para um nível apropriado. As reservas com escalonamento automático de slots estão disponíveis apenas com as edições do BigQuery.

Usar reservas de escalonamento automático

Você não precisa adquirir compromissos de slot antes de criar reservas de escalonamento automático. Os compromissos de slot fornecem um preço com desconto para slots usados de forma consistente, mas são opcionais com reservas de escalonamento automático. Para criar uma reserva de escalonamento automático, atribua uma reserva a um número máximo de slots (o tamanho máximo da reserva). Para identificar o número máximo de slots de escalonamento automático, subtraia o tamanho máximo da reserva de todos os slots de valor de referência opcionais atribuídos à reserva.

Ao criar reservas de escalonamento automático, considere o seguinte:

  • O BigQuery escalona reservas por múltiplos de 100 até atingir o número de slots necessários para executar os jobs ou atingir o número máximo de slots disponíveis para a reserva.
  • O aumento é baseado em uma estimativa e pode ser provisionado em excesso ou de forma insuficiente. Por exemplo, o escalonador automático pode ser escalonado para 500 slots quando apenas 400 forem necessários ou tentar escalonar uma pequena quantidade quando a carga de trabalho puder ser processada sem escalonar.
  • Os slots com escalonamento automático são cobrados de acordo com os preços de capacidade de computação da edição associada durante o escalonamento vertical. Você é cobrado pelo número de slots escalonados, não pelo número de slots usados. Essa cobrança será aplicada mesmo se o job que faz o BigQuery escalonar verticalmente falhar.
  • O número de slots sempre é escalonado por múltiplos de 100, mas é possível escalonar mais de 100 slots em uma etapa. Por exemplo, se sua carga de trabalho exigir mais 450 slots, o BigQuery pode tentar escalonar por 500 slots de uma vez para atender ao requisito de capacidade.
  • O BigQuery reduz escala vertical quando os slots alocados excedem o número de slots necessários e a capacidade de escalonamento automático permanece estável por algum tempo.

Para saber como trabalhar com o escalonamento automático, consulte Trabalhar com escalonamento automático de slots.

Como usar reservas com slots de valor de referência e escalonamento automático

Além de especificar o tamanho máximo da reserva, é possível opcionalmente determinar um número de referência de slots por reserva. O valor de referência é o número mínimo de slots que sempre serão alocados na reserva e que sempre serão cobrados. Os slots de escalonamento automático são adicionados somente depois que todos os slots de linha de base (e os slots inativos, se aplicável) são consumidos. É possível compartilhar slots de valor de referência inativos em uma reserva com outras que precisam de capacidade.

É possível aumentar o número de slots de valor de referência em uma reserva a cada poucos minutos. Se você quiser diminuir os slots de valor de referência, terá o limite de uma hora por hora se tiver alterado recentemente a capacidade de slots do valor de referência e os slots de referência excederem os slots confirmados. Caso contrário, é possível diminuir os slots de valor de referência em intervalos de alguns minutos.

Os slots de valor de referência e de escalonamento automático foram projetados para fornecer capacidade com base na sua carga de trabalho recente. Se você antecipar uma grande carga de trabalho que seja muito diferente das suas cargas de trabalho recentes, recomendamos aumentar a capacidade de valor de referência antes do evento, em vez de depender de slots de escalonamento automático para cobrir a capacidade.

Se a reserva não tiver slots de valor de referência ou não estiver configurada para emprestar slots inativos de outras reservas, o BigQuery tentará escalonar. Caso contrário, os slots de valor de referência precisam ser totalmente utilizados antes do escalonamento.

As reservas usam e adicionam slots na seguinte prioridade:

  1. Slots de referência:
  2. Compartilhamento de slots inativos (se ativado). As reservas só podem compartilhar valor de referência ocioso ou slots confirmados de outras reservas que foram criadas com a mesma edição e a mesma região.
  3. Slots de escalonamento automático.

No exemplo a seguir, os slots são dimensionados de acordo com um valor de referência especificado. As reservas etl e dashboard têm um tamanho de referência de 700 e 300 slots, respectivamente.

Exemplo de escalonamento automático sem compromissos.

Neste exemplo, a reserva etl pode ser dimensionada para 1.300 slots (700 slots de valor de referência mais 600 slots de escalonamento automático). Se a reserva dashboard não estiver em uso, a reserva etl poderá usar os 300 slots da reserva dashboard se nenhum job estiver em execução lá, levando a um máximo de 1.600 slots possíveis slots.

A reserva dashboard pode ser escalonada para 1.100 slots (300 slots de valor de referência mais 800 slots de escalonamento automático). Se a reserva etl estiver totalmente inativa, a reserva dashboard poderá ser escalonada para, no máximo, 1.800 slots (300 slots de valor de referência, 800 slots de escalonamento automático, mais 700 slots inativos na reserva etl).

Se a reserva etl exigir mais de 700 slots de valor de referência, que estão sempre disponíveis, ela tentará adicionar slots usando os seguintes métodos na ordem:

  1. 700 slots de referência.
  2. Compartilhamento de slot inativo com os 300 slots de valor de referência na reserva dashboard. Sua reserva compartilha apenas slots de valor de referência ociosos com outras reservas criadas com a mesma edição.
  3. Escalonar verticalmente mais 600 slots para o tamanho máximo de reserva.

Como usar compromissos de slot

No exemplo a seguir, mostramos o escalonamento automático de slots usando compromissos de capacidade.

exemplo de escalonamento automático

Assim como os valores de referência de reserva, os compromissos de slot permitem alocar um número fixo de slots disponíveis para todas as reservas. Ao contrário dos slots de valor de referência, não é possível reduzir um compromisso durante o prazo. Os compromissos de slot são opcionais, mas podem economizar custos se forem necessários slots de valor de referência por longos períodos.

Neste exemplo, é cobrada uma taxa predefinida pelos slots de compromisso de capacidade. Você será cobrado pela taxa de escalonamento automático pelo número de slots de escalonamento automático após a ativação do escalonamento automático e as reservas em um estado otimizado. Para a taxa de escalonamento automático, você é cobrado pelo número de slots escalonados, não pelo número de slots usados.

Máximo de slots disponíveis

É possível calcular o número máximo de slots que uma reserva pode usar adicionando os slots de valor de referência, o número máximo de slots de escalonamento automático e todos os slots em confirmações que foram criadas com escalonamento automático de slots e não são cobertas pelo slots de referência. O exemplo na imagem anterior é configurado da seguinte maneira:

  • Um compromisso de capacidade de 1.000 slots anuais. Esses slots são atribuídos como slots de referência na reserva etl e na reserva dashboard.
  • 700 slots de valor de referência atribuídos à reserva etl.
  • 300 slots de valor de referência atribuídos à reserva dashboard.
  • Escalonamento automático de slots de 600 para a reserva etl.
  • Escalonamento automático de slots de 800 para a reserva dashboard.

Para a reserva etl, o número máximo de slots possível é igual aos slots de referência etl (700) mais os slots de valor de referência dashboard (300, se todos os slots estiverem inativos). mais o número máximo de slots de escalonamento automático (600). Portanto, o número máximo de slots que a reserva etl pode usar neste exemplo é 1.600. Esse número excede o do compromisso de capacidade.

No exemplo a seguir, o compromisso anual excede os slots de valor de referência atribuídos.

Calculando horários disponíveis

Neste exemplo, temos:

  • Um compromisso de capacidade de 1.600 slots anuais.
  • Um tamanho máximo de reserva de 1.500 (incluindo 500 slots de escalonamento automático).
  • 1.000 slots de valor de referência atribuídos à reserva etl.

O número máximo de slots disponíveis para a reserva é igual aos slots de valor de referência (1.000), mais os slots inativos com compromisso não dedicados aos slots de valor de referência (1.600 slots anuais - 1.000 slots de valor de referência = 600) mais o número de slots de escalonamento automático (500). Portanto, o número máximo de slots possíveis nessa reserva é de 2.100. Os slots com escalonamento automático são slots adicionais acima do compromisso de capacidade.

Práticas recomendadas para escalonamento automático

  1. Ao usar o escalonador automático pela primeira vez, defina o número de slots de escalonamento automático como um número significativo com base no desempenho anterior e esperado. Depois que a reserva for criada, monitore ativamente a taxa de falhas, o desempenho e o faturamento e ajuste o número de slots de escalonamento automático conforme necessário.

  2. A cobrança é feita pelo número de slots escalonados, e não pelo número de slots usados. Se o número máximo de slots de escalonamento automático for muito grande, o BigQuery poderá escalonar mais do que o necessário, gerando custos adicionais.

    Por exemplo, um job pode usar um grande número de slots em um ponto, mas só precisa de um número significativamente menor. Como o BigQuery não libera slots não utilizados imediatamente, isso poderá resultar em menor utilização se nenhum outro job na reserva puder usá-los. Ao reduzir o valor máximo de uma reserva, o número máximo de slots alocados é reduzido, o que diminui o uso de slots de job e melhora a utilização de slots alocados.

  3. De vez em quando, o uso de slots pode exceder a soma do valor de referência mais os slots dimensionados. Você não vai receber cobranças pelo uso de slots maior que o valor de referência mais os slots escalonados.

  4. O autoescalador é mais eficiente para cargas de trabalho pesadas e de longa duração, como cargas de trabalho com várias consultas simultâneas. Evite enviar uma consulta de cada vez ao back-end, mesmo que elas não usem o cache. Sem slots de valor de referência, isso pode causar um escalonamento inesperado e gerar altos custos.

  5. O escalonamento automático do BigQuery está sujeito à disponibilidade de capacidade. O BigQuery tenta atender à demanda de capacidade do cliente com base no uso histórico. Para alcançar as garantias de capacidade, defina um valor de referência de slot opcional, que é o número de slots garantidos em uma reserva. Com os valores de referência, os slots ficam disponíveis imediatamente e você paga por eles, independentemente de usá-los ou não. Para garantir que a capacidade esteja disponível para grandes demandas inorgânicas, como feriados de alto tráfego, entre em contato com a equipe do BigQuery com várias semanas de antecedência.

  6. Os slots de valor de referência sempre são carregados. Se um compromisso de capacidade expirar, talvez seja necessário ajustar manualmente a quantidade de slots de valor de referência nas reservas para evitar cobranças indesejadas. Por exemplo, considere que você tem um compromisso de um ano com 100 slots e uma reserva com 100 slots de valor de referência. O compromisso expira e não tem um plano de renovação. Quando o compromisso expirar, você pagará por 100 slots de valor de referência com a taxa de pagamento por utilização.

Monitorar o escalonamento automático

Ao monitorar o uso de slots com gráficos de recursos administrativos, é possível conferir mais slots significativamente dimensionados do que o uso de slots, porque os gráficos suavizam o número de slots usados durante o período de alinhamento. Para visualizar o uso de slots de escalonamento automático com detalhes mais precisos, reduza a opção de período. Isso atualiza automaticamente o período de alinhamento para um incremento menor.

No exemplo a seguir, o gráfico mostra muito mais slots dimensionados do que as demandas da carga de trabalho.

O período de alinhamento é definido como um intervalo de um minuto, e os slots dimensionados
são exibidos mais do que as demandas de uso
de slots.

No entanto, se você reduzir a opção de período para que o período de alinhamento seja dois segundos, o escalonador automático é escalonado de acordo com a demanda da carga de trabalho e mostra dados mais precisos. Para ajustar a opção de período, arraste os intervalos de início e término dela. Para exibir os dados mais precisos de demanda de carga de trabalho, selecione p90 ou p99 na lista Métrica.

O período de alinhamento é definido como um intervalo de dois segundos, e os slots escalonados
são adequados para a demanda da
carga de trabalho.

Para ter a visualização mais precisa do uso do escalonamento automático, use um período de alinhamento entre 1 e 15 segundos. Para mais informações sobre o período de alinhamento dos gráficos de recursos administrativos, consulte Opção de período.

Para informações sobre como visualizar o uso de slots, consulte Ver gráficos de recursos administrativos

Monitorar o escalonamento automático com esquema de informações

É possível usar os seguintes scripts SQL para verificar os segundos de slot faturados de uma edição específica. Execute esses scripts no mesmo projeto em que as reservas foram criadas. O primeiro script mostra os segundos de slot faturados cobertos por commitment_plan, enquanto o segundo mostra os segundos de slot faturados que não são cobertos por um compromisso.

Você só precisa definir o valor de três variáveis para executar esses scripts:

  • start_time
  • end_time
  • edition_to_check

Esses scripts estão sujeitos às seguintes ressalvas:

  • As reservas e os compromissos de capacidade excluídos são removidos das visualizações do esquema de informações no final do período de armazenamento de dados. Especifique uma janela de tempo recente que não contenha reservas e compromissos excluídos para resultados corretos.

  • O resultado dos scripts pode não corresponder exatamente à fatura devido a pequenos erros de arredondamento.

O script a seguir verifica o uso de slot coberto pelos compromissos de uma edição específica.

Clique para abrir o script das situações abordadas

DECLARE start_time,end_time TIMESTAMP;

DECLARE
  edition_to_check STRING;

/* Google uses Pacific Time to calculate the billing period for all customers,
regardless of their time zone. Use the following format if you want to match the
billing report. Change the start_time and end_time values to match the desired
window. */

/* The following three variables (start_time, end_time, and edition_to_check)
are the only variables that you need to set in the script.

During daylight savings time, the start_time and end_time variables should
follow this format: 2024-02-20 00:00:00-08. */

SET start_time = "2023-07-20 00:00:00-07";
SET end_time = "2023-07-28 00:00:00-07";
SET edition_to_check = 'ENTERPRISE';

/* The following function returns the slot seconds for the time window between
two capacity changes. For example, if there are 100 slots between (2023-06-01
10:00:00, 2023-06-01 11:00:00), then during that window the total slot seconds
will be 100 * 3600.

This script calculates a specific window (based on the variables defined above),
which is why the following script includes script_start_timestamp_unix_millis
and script_end_timestamp_unix_millis. */

CREATE TEMP FUNCTION
GetSlotSecondsBetweenChanges(
  slots FLOAT64,
  range_begin_timestamp_unix_millis FLOAT64,
  range_end_timestamp_unix_millis FLOAT64,
  script_start_timestamp_unix_millis FLOAT64,
  script_end_timestamp_unix_millis FLOAT64)
RETURNS INT64
LANGUAGE js
AS r"""
    if (script_end_timestamp_unix_millis < range_begin_timestamp_unix_millis || script_start_timestamp_unix_millis > range_end_timestamp_unix_millis) {
      return 0;
    }
    var begin = Math.max(script_start_timestamp_unix_millis, range_begin_timestamp_unix_millis)
    var end = Math.min(script_end_timestamp_unix_millis, range_end_timestamp_unix_millis)
    return slots * Math.ceil((end - begin) / 1000.0)
""";

/*
Sample CAPACITY_COMMITMENT_CHANGES data (unrelated columns ignored):
+---------------------+------------------------+-----------------+--------+------------+--------+
|  change_timestamp   | capacity_commitment_id | commitment_plan | state  | slot_count | action |
+---------------------+------------------------+-----------------+--------+------------+--------+
| 2023-07-20 19:30:27 | 12954109101902401697   | ANNUAL          | ACTIVE |        100 | CREATE |
| 2023-07-27 22:29:21 | 11445583810276646822   | FLEX            | ACTIVE |        100 | CREATE |
| 2023-07-27 23:10:06 | 7341455530498381779    | MONTHLY         | ACTIVE |        100 | CREATE |
| 2023-07-27 23:11:06 | 7341455530498381779    | FLEX            | ACTIVE |        100 | UPDATE |

The last row indicates a special change from MONTHLY to FLEX, which happens
because of commercial migration.

*/

WITH
  /*
  Information containing which commitment might have plan
  updated (e.g. renewal or commercial migration). For example:
  +------------------------+------------------+--------------------+--------+------------+--------+-----------+----------------------------+
  |  change_timestamp   | capacity_commitment_id | commitment_plan | state  | slot_count | action | next_plan | next_plan_change_timestamp |
  +---------------------+------------------------+-----------------+--------+------------+--------+-----------+----------------------------+
  | 2023-07-20 19:30:27 | 12954109101902401697   | ANNUAL          | ACTIVE |        100 | CREATE | ANNUAL    |        2023-07-20 19:30:27 |
  | 2023-07-27 22:29:21 | 11445583810276646822   | FLEX            | ACTIVE |        100 | CREATE | FLEX      |        2023-07-27 22:29:21 |
  | 2023-07-27 23:10:06 | 7341455530498381779    | MONTHLY         | ACTIVE |        100 | CREATE | FLEX      |        2023-07-27 23:11:06 |
  | 2023-07-27 23:11:06 | 7341455530498381779    | FLEX            | ACTIVE |        100 | UPDATE | FLEX      |        2023-07-27 23:11:06 |
  */
  commitments_with_next_plan AS (
    SELECT
      *,
      IFNULL(
        LEAD(commitment_plan)
          OVER (
            PARTITION BY capacity_commitment_id ORDER BY change_timestamp ASC
          ),
        commitment_plan)
        next_plan,
      IFNULL(
        LEAD(change_timestamp)
          OVER (
            PARTITION BY capacity_commitment_id ORDER BY change_timestamp ASC
          ),
        change_timestamp)
        next_plan_change_timestamp
    FROM
      `region-us.INFORMATION_SCHEMA.CAPACITY_COMMITMENT_CHANGES_BY_PROJECT`
  ),

  /*
  Insert a 'DELETE' action for those with updated plans. The FLEX commitment
  '7341455530498381779' is has no 'CREATE' action, and is instead labeled as an
  'UPDATE' action.

  For example:
  +---------------------+------------------------+-----------------+--------+------------+--------+
  |  change_timestamp   | capacity_commitment_id | commitment_plan | state  | slot_count | action |
  +---------------------+------------------------+-----------------+--------+------------+--------+
  | 2023-07-20 19:30:27 | 12954109101902401697   | ANNUAL          | ACTIVE |        100 | CREATE |
  | 2023-07-27 22:29:21 | 11445583810276646822   | FLEX            | ACTIVE |        100 | CREATE |
  | 2023-07-27 23:10:06 | 7341455530498381779    | MONTHLY         | ACTIVE |        100 | CREATE |
  | 2023-07-27 23:11:06 | 7341455530498381779    | FLEX            | ACTIVE |        100 | UPDATE |
  | 2023-07-27 23:11:06 | 7341455530498381779    | MONTHLY         | ACTIVE |        100 | DELETE |
  */

  capacity_changes_with_additional_deleted_event_for_changed_plan AS (
    SELECT
      next_plan_change_timestamp AS change_timestamp,
      project_id,
      project_number,
      capacity_commitment_id,
      commitment_plan,
      state,
      slot_count,
      'DELETE' AS action,
      commitment_start_time,
      commitment_end_time,
      failure_status,
      renewal_plan,
      user_email,
      edition,
      is_flat_rate,
    FROM commitments_with_next_plan
    WHERE commitment_plan <> next_plan
    UNION ALL
    SELECT * FROM `region-us.INFORMATION_SCHEMA.CAPACITY_COMMITMENT_CHANGES_BY_PROJECT`
  ),

  /*
  The committed_slots change the history. For example:
  +---------------------+------------------------+------------------+-----------------+
  |  change_timestamp   | capacity_commitment_id | slot_count_delta | commitment_plan |
  +---------------------+------------------------+------------------+-----------------+
  | 2023-07-20 19:30:27 | 12954109101902401697   |              100 | ANNUAL          |
  | 2023-07-27 22:29:21 | 11445583810276646822   |              100 | FLEX            |
  | 2023-07-27 23:10:06 | 7341455530498381779    |              100 | MONTHLY         |
  | 2023-07-27 23:11:06 | 7341455530498381779    |             -100 | MONTHLY         |
  | 2023-07-27 23:11:06 | 7341455530498381779    |              100 | FLEX            |
  */

  capacity_commitment_slot_data AS (
    SELECT
      change_timestamp,
      capacity_commitment_id,
      CASE
        WHEN action = "CREATE" OR action = "UPDATE"
          THEN
            IFNULL(
              IF(
                LAG(action)
                  OVER (
                    PARTITION BY capacity_commitment_id ORDER BY change_timestamp ASC, action ASC
                  )
                  IN UNNEST(['CREATE', 'UPDATE']),
                slot_count - LAG(slot_count)
                  OVER (
                    PARTITION BY capacity_commitment_id ORDER BY change_timestamp ASC, action ASC
                  ),
                slot_count),
              slot_count)
        ELSE
          IF(
            LAG(action)
              OVER (PARTITION BY capacity_commitment_id ORDER BY change_timestamp ASC, action ASC)
              IN UNNEST(['CREATE', 'UPDATE']),
            -1 * slot_count,
            0)
        END
        AS slot_count_delta,
      commitment_plan
    FROM
      capacity_changes_with_additional_deleted_event_for_changed_plan
    WHERE
      state = "ACTIVE"
      AND edition = edition_to_check
      AND change_timestamp <= end_time
  ),

  /*
  The total_committed_slots history for each plan. For example:
  +---------------------+---------------+-----------------+
  |  change_timestamp   | capacity_slot | commitment_plan |
  +---------------------+---------------+-----------------+
  | 2023-07-20 19:30:27 |           100 | ANNUAL          |
  | 2023-07-27 22:29:21 |           100 | FLEX            |
  | 2023-07-27 23:10:06 |           100 | MONTHLY         |
  | 2023-07-27 23:11:06 |             0 | MONTHLY         |
  | 2023-07-27 23:11:06 |           200 | FLEX            |
  */

  running_capacity_commitment_slot_data AS (
    SELECT
      change_timestamp,
      SUM(slot_count_delta)
        OVER (
          PARTITION BY commitment_plan
          ORDER BY change_timestamp
          RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
        )
        AS capacity_slot,
      commitment_plan,
    FROM
      capacity_commitment_slot_data
  ),

  /*

  The slot_seconds between each changes, partitioned by each plan. For example:
  +---------------------+--------------+-----------------+
  |  change_timestamp   | slot_seconds | commitment_plan |
  +---------------------+--------------+-----------------+
  | 2023-07-20 19:30:27 |     64617300 | ANNUAL          |
  | 2023-07-27 22:29:21 |       250500 | FLEX            |
  | 2023-07-27 23:10:06 |         6000 | MONTHLY         |
  | 2023-07-27 23:11:06 |            0 | MONTHLY         |
  | 2023-07-27 23:11:06 |      5626800 | FLEX            |
  */

  slot_seconds_data AS (
    SELECT
      change_timestamp,
      GetSlotSecondsBetweenChanges(
        capacity_slot,
        UNIX_MILLIS(change_timestamp),
        UNIX_MILLIS(
          IFNULL(
            LEAD(change_timestamp)
              OVER (PARTITION BY commitment_plan ORDER BY change_timestamp ASC),
            CURRENT_TIMESTAMP())),
        UNIX_MILLIS(start_time),
        UNIX_MILLIS(end_time)) AS slot_seconds,
      commitment_plan,
    FROM
      running_capacity_commitment_slot_data
    WHERE
      change_timestamp <= end_time
  )

/*

The final result is similar to the following:
+-----------------+--------------------+
| commitment_plan | total_slot_seconds |
+-----------------+--------------------+
| ANNUAL          |           64617300 |
| MONTHLY         |               6000 |
| FLEX            |            5877300 |
*/

SELECT
  commitment_plan,
  SUM(slot_seconds) AS total_slot_seconds
FROM
  slot_seconds_data
GROUP BY
  commitment_plan

O script a seguir verifica o uso de slots não coberto pelos compromissos de uma edição específica. Esse uso contém dois tipos de slots: escalonados e de referência não cobertos por compromissos.

Clique para abrir o script em situações não abrangidas

/*
This script has several parts:
1. Calculate the baseline and scaled slots for reservations
2. Calculate the committed slots
3. Join the two results above to calculate the baseline not covered by committed
   slots
4. Aggregate the number
*/

-- variables
DECLARE start_time, end_time TIMESTAMP;

DECLARE
  edition_to_check STRING;

/* Google uses Pacific Time to calculate the billing period for all customers,
regardless of their time zone. Use the following format if you want to match the
billing report. Change the start_time and end_time values to match the desired
window. */

/* The following three variables (start_time, end_time, and edition_to_check)
are the only variables that you need to set in the script.

During daylight savings time, the start_time and end_time variables should
follow this format: 2024-02-20 00:00:00-08. */

SET start_time = "2023-07-20 00:00:00-07";
SET end_time = "2023-07-28 00:00:00-07";
SET edition_to_check = 'ENTERPRISE';

/*
The following function returns the slot seconds for the time window between
two capacity changes. For example, if there are 100 slots between (2023-06-01
10:00:00, 2023-06-01 11:00:00), then during that window the total slot seconds
will be 100 * 3600.

This script calculates a specific window (based on the variables defined above),
which is why the following script includes script_start_timestamp_unix_millis
and script_end_timestamp_unix_millis. */

CREATE TEMP FUNCTION GetSlotSecondsBetweenChanges(
  slots FLOAT64,
  range_begin_timestamp_unix_millis FLOAT64,
  range_end_timestamp_unix_millis FLOAT64,
  script_start_timestamp_unix_millis FLOAT64,
  script_end_timestamp_unix_millis FLOAT64)
RETURNS INT64
LANGUAGE js
AS r"""
    if (script_end_timestamp_unix_millis < range_begin_timestamp_unix_millis || script_start_timestamp_unix_millis > range_end_timestamp_unix_millis) {
      return 0;
    }
    var begin = Math.max(script_start_timestamp_unix_millis, range_begin_timestamp_unix_millis)
    var end = Math.min(script_end_timestamp_unix_millis, range_end_timestamp_unix_millis)
    return slots * Math.ceil((end - begin) / 1000.0)
""";
/*
Sample RESERVATION_CHANGES data (unrelated columns ignored):
+---------------------+------------------+--------+---------------+---------------+
|  change_timestamp   | reservation_name | action | slot_capacity | current_slots |
+---------------------+------------------+--------+---------------+---------------+
| 2023-07-27 22:24:15 | res1             | CREATE |           300 |             0 |
| 2023-07-27 22:25:21 | res1             | UPDATE |           300 |           180 |
| 2023-07-27 22:39:14 | res1             | UPDATE |           300 |           100 |
| 2023-07-27 22:40:20 | res2             | CREATE |           300 |             0 |
| 2023-07-27 22:54:18 | res2             | UPDATE |           300 |           120 |
| 2023-07-27 22:55:23 | res1             | UPDATE |           300 |             0 |

Sample CAPACITY_COMMITMENT_CHANGES data (unrelated columns ignored):
+---------------------+------------------------+-----------------+--------+------------+--------+
|  change_timestamp   | capacity_commitment_id | commitment_plan | state  | slot_count | action |
+---------------------+------------------------+-----------------+--------+------------+--------+
| 2023-07-20 19:30:27 | 12954109101902401697   | ANNUAL          | ACTIVE |        100 | CREATE |
| 2023-07-27 22:29:21 | 11445583810276646822   | FLEX            | ACTIVE |        100 | CREATE |
| 2023-07-27 23:10:06 | 7341455530498381779    | MONTHLY         | ACTIVE |        100 | CREATE |
*/

WITH
  /*
  The scaled_slots & baseline change history:
  +---------------------+------------------+------------------------------+---------------------+
  |  change_timestamp   | reservation_name | autoscale_current_slot_delta | baseline_slot_delta |
  +---------------------+------------------+------------------------------+---------------------+
  | 2023-07-27 22:24:15 | res1             |                            0 |                 300 |
  | 2023-07-27 22:25:21 | res1             |                          180 |                   0 |
  | 2023-07-27 22:39:14 | res1             |                          -80 |                   0 |
  | 2023-07-27 22:40:20 | res2             |                            0 |                 300 |
  | 2023-07-27 22:54:18 | res2             |                          120 |                   0 |
  | 2023-07-27 22:55:23 | res1             |                         -100 |                   0 |
  */
  reservation_slot_data AS (
    SELECT
      change_timestamp,
      reservation_name,
      CASE action
        WHEN "CREATE" THEN autoscale.current_slots
        WHEN "UPDATE"
          THEN
            IFNULL(
              autoscale.current_slots - LAG(autoscale.current_slots)
                OVER (
                  PARTITION BY project_id, reservation_name
                  ORDER BY change_timestamp ASC, action ASC
                ),
              IFNULL(
                autoscale.current_slots,
                IFNULL(
                  -1 * LAG(autoscale.current_slots)
                    OVER (
                      PARTITION BY project_id, reservation_name
                      ORDER BY change_timestamp ASC, action ASC
                    ),
                  0)))
        WHEN "DELETE"
          THEN
            IF(
              LAG(action)
                OVER (
                  PARTITION BY project_id, reservation_name
                  ORDER BY change_timestamp ASC, action ASC
                )
                IN UNNEST(['CREATE', 'UPDATE']),
              -1 * autoscale.current_slots,
              0)
        END
        AS autoscale_current_slot_delta,
      CASE action
        WHEN "CREATE" THEN slot_capacity
        WHEN "UPDATE"
          THEN
            IFNULL(
              slot_capacity - LAG(slot_capacity)
                OVER (
                  PARTITION BY project_id, reservation_name
                  ORDER BY change_timestamp ASC, action ASC
                ),
              IFNULL(
                slot_capacity,
                IFNULL(
                  -1 * LAG(slot_capacity)
                    OVER (
                      PARTITION BY project_id, reservation_name
                      ORDER BY change_timestamp ASC, action ASC
                    ),
                  0)))
        WHEN "DELETE"
          THEN
            IF(
              LAG(action)
                OVER (
                  PARTITION BY project_id, reservation_name
                  ORDER BY change_timestamp ASC, action ASC
                )
                IN UNNEST(['CREATE', 'UPDATE']),
              -1 * slot_capacity,
              0)
        END
        AS baseline_slot_delta,
    FROM
      `region-us.INFORMATION_SCHEMA.RESERVATION_CHANGES`
    WHERE
      edition = edition_to_check
      AND change_timestamp <= end_time
  ),

  -- Convert the above to running total
  /*
  +---------------------+-------------------------+----------------+
  |  change_timestamp   | autoscale_current_slots | baseline_slots |
  +---------------------+-------------------------+----------------+
  | 2023-07-27 22:24:15 |                       0 |            300 |
  | 2023-07-27 22:25:21 |                     180 |            300 |
  | 2023-07-27 22:39:14 |                     100 |            300 |
  | 2023-07-27 22:40:20 |                     100 |            600 |
  | 2023-07-27 22:54:18 |                     220 |            600 |
  | 2023-07-27 22:55:23 |                     120 |            600 |
  */
  running_reservation_slot_data AS (
    SELECT
      change_timestamp,
      SUM(autoscale_current_slot_delta)
        OVER (ORDER BY change_timestamp RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
        AS autoscale_current_slots,
      SUM(baseline_slot_delta)
        OVER (ORDER BY change_timestamp RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
        AS baseline_slots,
    FROM
      reservation_slot_data
  ),

  /*
  The committed_slots change history. For example:
  +---------------------+------------------------+------------------+
  |  change_timestamp   | capacity_commitment_id | slot_count_delta |
  +---------------------+------------------------+------------------+
  | 2023-07-20 19:30:27 | 12954109101902401697   |              100 |
  | 2023-07-27 22:29:21 | 11445583810276646822   |              100 |
  | 2023-07-27 23:10:06 | 7341455530498381779    |              100 |
  */
  capacity_commitment_slot_data AS (
    SELECT
      change_timestamp,
      capacity_commitment_id,
      CASE
        WHEN action = "CREATE" OR action = "UPDATE"
          THEN
            IFNULL(
              IF(
                LAG(action)
                  OVER (
                    PARTITION BY capacity_commitment_id ORDER BY change_timestamp ASC, action ASC
                  )
                  IN UNNEST(['CREATE', 'UPDATE']),
                slot_count - LAG(slot_count)
                  OVER (
                    PARTITION BY capacity_commitment_id ORDER BY change_timestamp ASC, action ASC
                  ),
                slot_count),
              slot_count)
        ELSE
          IF(
            LAG(action)
              OVER (PARTITION BY capacity_commitment_id ORDER BY change_timestamp ASC, action ASC)
              IN UNNEST(['CREATE', 'UPDATE']),
            -1 * slot_count,
            0)
        END
        AS slot_count_delta
    FROM
      `region-us.INFORMATION_SCHEMA.CAPACITY_COMMITMENT_CHANGES_BY_PROJECT`
    WHERE
      state = "ACTIVE"
      AND edition = edition_to_check
      AND change_timestamp <= end_time
  ),

  /*
  The total_committed_slots history. For example:
  +---------------------+---------------+
  |  change_timestamp   | capacity_slot |
  +---------------------+---------------+
  | 2023-07-20 19:30:27 |           100 |
  | 2023-07-27 22:29:21 |           200 |
  | 2023-07-27 23:10:06 |           300 |
  */
  running_capacity_commitment_slot_data AS (
    SELECT
      change_timestamp,
      SUM(slot_count_delta)
        OVER (ORDER BY change_timestamp RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
        AS capacity_slot
    FROM
      capacity_commitment_slot_data
  ),

  /* Add next_change_timestamp to the above data,
   which will be used when joining with reservation data. For example:
  +---------------------+-----------------------+---------------+
  |  change_timestamp   | next_change_timestamp | capacity_slot |
  +---------------------+-----------------------+---------------+
  | 2023-07-20 19:30:27 |   2023-07-27 22:29:21 |           100 |
  | 2023-07-27 22:29:21 |   2023-07-27 23:10:06 |           200 |
  | 2023-07-27 23:10:06 |   2023-07-31 00:14:37 |           300 |
  */
  running_capacity_commitment_slot_data_with_next_change AS (
    SELECT
      change_timestamp,
      IFNULL(LEAD(change_timestamp) OVER (ORDER BY change_timestamp ASC), CURRENT_TIMESTAMP())
        AS next_change_timestamp,
      capacity_slot
    FROM
      running_capacity_commitment_slot_data
  ),

  /*
  Whenever we have a change in reservations or commitments,
  the scaled_slots_and_baseline_not_covered_by_commitments will be changed.
  Hence we get a collection of all the change_timestamp from both tables.
  +---------------------+
  |  change_timestamp   |
  +---------------------+
  | 2023-07-20 19:30:27 |
  | 2023-07-27 22:24:15 |
  | 2023-07-27 22:25:21 |
  | 2023-07-27 22:29:21 |
  | 2023-07-27 22:39:14 |
  | 2023-07-27 22:40:20 |
  | 2023-07-27 22:54:18 |
  | 2023-07-27 22:55:23 |
  | 2023-07-27 23:10:06 |
  */
  merged_timestamp AS (
    SELECT
      change_timestamp
    FROM
      running_reservation_slot_data
    UNION DISTINCT
    SELECT
      change_timestamp
    FROM
      running_capacity_commitment_slot_data
  ),

  /*
  Change running reservation-slots and make sure we have one row when commitment changes.
  +---------------------+-------------------------+----------------+
  |  change_timestamp   | autoscale_current_slots | baseline_slots |
  +---------------------+-------------------------+----------------+
  | 2023-07-20 19:30:27 |                       0 |              0 |
  | 2023-07-27 22:24:15 |                       0 |            300 |
  | 2023-07-27 22:25:21 |                     180 |            300 |
  | 2023-07-27 22:29:21 |                     180 |            300 |
  | 2023-07-27 22:39:14 |                     100 |            300 |
  | 2023-07-27 22:40:20 |                     100 |            600 |
  | 2023-07-27 22:54:18 |                     220 |            600 |
  | 2023-07-27 22:55:23 |                     120 |            600 |
  | 2023-07-27 23:10:06 |                     120 |            600 |
  */
  running_reservation_slot_data_with_merged_timestamp AS (
    SELECT
      change_timestamp,
      IFNULL(
        autoscale_current_slots,
        IFNULL(
          LAST_VALUE(autoscale_current_slots IGNORE NULLS) OVER (ORDER BY change_timestamp ASC), 0))
        AS autoscale_current_slots,
      IFNULL(
        baseline_slots,
        IFNULL(LAST_VALUE(baseline_slots IGNORE NULLS) OVER (ORDER BY change_timestamp ASC), 0))
        AS baseline_slots
    FROM
      running_reservation_slot_data
    RIGHT JOIN
      merged_timestamp
      USING (change_timestamp)
  ),

  /*
  Join the above, so that we will know the number for baseline not covered by commitments.
  +---------------------+-----------------------+-------------------------+------------------------------------+
  |  change_timestamp   | next_change_timestamp | autoscale_current_slots | baseline_not_covered_by_commitment |
  +---------------------+-----------------------+-------------------------+------------------------------------+
  | 2023-07-20 19:30:27 |   2023-07-27 22:24:15 |                       0 |                                  0 |
  | 2023-07-27 22:24:15 |   2023-07-27 22:25:21 |                       0 |                                200 |
  | 2023-07-27 22:25:21 |   2023-07-27 22:29:21 |                     180 |                                200 |
  | 2023-07-27 22:29:21 |   2023-07-27 22:39:14 |                     180 |                                100 |
  | 2023-07-27 22:39:14 |   2023-07-27 22:40:20 |                     100 |                                100 |
  | 2023-07-27 22:40:20 |   2023-07-27 22:54:18 |                     100 |                                400 |
  | 2023-07-27 22:54:18 |   2023-07-27 22:55:23 |                     220 |                                400 |
  | 2023-07-27 22:55:23 |   2023-07-27 23:10:06 |                     120 |                                400 |
  | 2023-07-27 23:10:06 |   2023-07-31 00:16:07 |                     120 |                                300 |
  */
  scaled_slots_and_baseline_not_covered_by_commitments AS (
    SELECT
      r.change_timestamp,
      IFNULL(LEAD(r.change_timestamp) OVER (ORDER BY r.change_timestamp ASC), CURRENT_TIMESTAMP())
        AS next_change_timestamp,
      r.autoscale_current_slots,
      IF(
        r.baseline_slots - IFNULL(c.capacity_slot, 0) > 0,
        r.baseline_slots - IFNULL(c.capacity_slot, 0),
        0) AS baseline_not_covered_by_commitment
    FROM
      running_reservation_slot_data_with_merged_timestamp r
    LEFT JOIN
      running_capacity_commitment_slot_data_with_next_change c
      ON
        r.change_timestamp >= c.change_timestamp
        AND r.change_timestamp < c.next_change_timestamp
  ),

  /*
  The slot_seconds between each changes. For example:
  +---------------------+--------------------+
  |  change_timestamp   | slot_seconds |
  +---------------------+--------------+
  | 2023-07-20 19:30:27 |            0 |
  | 2023-07-27 22:24:15 |        13400 |
  | 2023-07-27 22:25:21 |        91580 |
  | 2023-07-27 22:29:21 |       166320 |
  | 2023-07-27 22:39:14 |        13200 |
  | 2023-07-27 22:40:20 |       419500 |
  | 2023-07-27 22:54:18 |        40920 |
  | 2023-07-27 22:55:23 |       459160 |
  | 2023-07-27 23:10:06 |     11841480 |
  */
  slot_seconds_data AS (
    SELECT
      change_timestamp,
      GetSlotSecondsBetweenChanges(
        autoscale_current_slots + baseline_not_covered_by_commitment,
        UNIX_MILLIS(change_timestamp),
        UNIX_MILLIS(next_change_timestamp),
        UNIX_MILLIS(start_time),
        UNIX_MILLIS(end_time)) AS slot_seconds
    FROM
      scaled_slots_and_baseline_not_covered_by_commitments
    WHERE
      change_timestamp <= end_time AND next_change_timestamp > start_time
  )

/*
Final result for this example:
+--------------------+
| total_slot_seconds |
+--------------------+
|           13045560 |
*/
SELECT
  SUM(slot_seconds) AS total_slot_seconds
FROM
  slot_seconds_data

Cotas

A soma do tamanho máximo da reserva não pode exceder sua cota de slots.

Para mais informações sobre cotas, consulte Cotas e limites.

A seguir