-
Notifications
You must be signed in to change notification settings - Fork 411
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Script para reconstruir histórico de TabCoins #652
Comments
Rodei hoje de manhã em Homologação, deu tudo certo demorou 60 segundos: https://tabnews-8b589clch-tabnews.vercel.app/filipedeschamps/rodei-o-script-de-preencher-o-historico-de-tabcoins-em-homologacao Mas fui rodar agora em produção, e dado que a minha máquina aqui está longe da instância do banco em São Paulo, demorou muito mais (18 minutos) e nesse meio tempo, as TabCoins se mexeram e o script fez rollback 😂 {
name: 'UnprocessableEntityError',
message: 'Number of added Content TabCoins does not match',
action: 'Os dados enviados estão corretos, porém não foi possível realizar esta operação.',
status_code: 422,
error_id: 'b56c7726-907e-42b9-8be5-09f9bcbb8de3',
request_id: '54cc3af3-7a91-4099-a82d-a09f05406f19',
context: {
total_contents_before_tabcoins: 2815,
total_contents_tabcoins_before: '5271',
total_contents_tabcoins_after: '8088',
added_tab_coins: 2817
}
} O legal é que o rollback funcionou certinho pelo que pude avaliar aqui 👍 |
Script executado em produção, durou 18 minutos:
Isso injetou muita TabCoin no sistema, muita mesmo, mas tudo bem, pois era o que tinhamos combinado :) |
E este foi o exato script que rodei: import nextConnect from 'next-connect';
import controller from 'models/controller.js';
import database from 'infra/database.js';
import event from 'models/event.js';
import balance from 'models/balance.js';
import { UnprocessableEntityError } from 'errors/index.js';
export default nextConnect({
attachParams: true,
onNoMatch: controller.onNoMatchHandler,
onError: controller.onErrorHandler,
})
.use(controller.injectRequestMetadata)
.get(runTabCoinsScript);
async function runTabCoinsScript(request, response) {
// Para quem não conhece, o conceito de "dry run" é tentar rodar um código,
// mas que ao final não faz nada de verdade. No caso desse script, é
// iniciada uma transação no banco de dados, que de fato executa tudo,
// mas ao final é feito o rollback, o que cancela tudo que foi feito.
const DRY_RUN = false;
const transaction = await database.transaction();
try {
await transaction.query('BEGIN');
// 0) CRIAR EVENTO
// Nós primeiro criamos um evento, pois este evento será usado
// como lastro em todas as entradas de balanço que iremos criar
// mais para frente. A única coisa chata de "marretar" um evento
// assim fora do fluxo normal é que eu precisei hardcode o meu
// usuário e ip (localhost).
const currentEvent = await event.create(
{
type: 'system:update:tabcoins',
originatorUserId: '07ea33ea-78bd-4578-bad2-1cf5323cab07', // filipedeschamps
originatorIp: '127.0.0.1',
metadata: {
description: `Backfill all TabCoins from contents created before '2022-07-03 16:48:12.457+00'.`,
},
},
{
transaction: transaction,
}
);
// 1) PEGAR NÚMERO TOTAL DE CONTEÚDOS PUBLICADOS
// Para referência, eu pego o número total de Conteúdos publicados
// e isso vai dar um comparativo bom com a próxima query.
const totalContents = await transaction.query(`
SELECT
count(*)
FROM
contents
WHERE
status = 'published'
;`);
console.log(`Contents: ${totalContents.rows[0].count}`);
// 2) PEGAR CONTEÚDOS CRIADOS ANTES DO DEPLOY SOBRE AS TABCOINS
// Estes Conteúdos hoje podem ter saldo de TabCoins, mas não
// importa, pois precisamos mais para frente creditar as TabCoins
// originais do Conteúdo e do Usuário criador do Conteúdo. Então
// dessa query, a gente extrai o ID do Conteúdo e o ID do Autor
// e não importa se o Conteúdo é "root" ou "child", precisa apenas
// estar com status "published" e ter sido criado antes do deploy.
const totalContentsBeforeTabcoins = await transaction.query(`
SELECT
id as content_id,
owner_id as user_id
FROM
contents
WHERE
contents.status = 'published'
AND contents.created_at < '2022-07-03 16:48:12+00'
;`);
console.log(`Contents before TabCoins existence: ${totalContentsBeforeTabcoins.rows.length}`);
console.log(`---`);
// 3) PEGAR TOTAL DE TABCOINS POSITIVAS DOS CONTEÚDOS (ANTES)
// Apenas como referência, eu somo o total de TabCoins positivas
// de todos os Conteúdos do sistema só para entender o que vai
// acontecer com esse valor depois de rodarmos o script.
const totalContentsTabcoinsBefore = await transaction.query(`
SELECT
sum(amount)
FROM
balance_operations
WHERE
balance_type = 'content:tabcoin'
and amount > 0
;`);
console.log(`Content TabCoins (before): ${totalContentsTabcoinsBefore.rows[0].sum}`);
// 4) CRIAR TABCOINS PARA OS CONTEÚDOS
// Basicamente é criado uma entrada na tabela de balanço para cada
// Conteúdo que foi criado antes do deploy e está na variável "totalContentsBeforeTabcoins"
// acima. Fora isso, usamos o evento também criado acima para registrar o lastro
// dessa movimentação.
for await (const contentWithoutTabcoins of totalContentsBeforeTabcoins.rows) {
const contentId = contentWithoutTabcoins.content_id;
await balance.create(
{
balanceType: 'content:tabcoin',
recipientId: contentId,
amount: 1, // Valor padrão que o Conteúdo ganha ao ser criado
originatorType: 'event',
originatorId: currentEvent.id,
},
{
transaction: transaction,
}
);
}
// 4) PEGAR TOTAL DE TABCOINS POSITIVAS DOS CONTEÚDOS (DEPOIS)
// Então agora nós conseguiremos comparar o que aconteceu com
// o número total de TabCoins no sistema, que deveria ter a mais
// o mesmo número de Conteúdo sem TabCoins, dado que é creditado
// 1 TabCoin por criação de conteúdo.
const totalContentsTabcoinsAfter = await transaction.query(`
SELECT
sum(amount)
FROM
balance_operations
WHERE
balance_type = 'content:tabcoin'
and amount > 0
;`);
console.log(
`Content TabCoins (after): ${totalContentsTabcoinsAfter.rows[0].sum} (+${
totalContentsTabcoinsAfter.rows[0].sum - totalContentsTabcoinsBefore.rows[0].sum
})`
);
console.log(`---`);
// 5) PEGAR TOTAL DE TABCOINS POSITIVAS DOS USUÁRIOS (ANTES)
// Mesma lógica usada no caso das TabCoins dos Conteúdos, mas agora
// para os Usuários.
const totalUsersTabcoinsBefore = await transaction.query(`
SELECT
sum(amount)
FROM
balance_operations
WHERE
balance_type = 'user:tabcoin'
and amount > 0
;`);
console.log(`User TabCoins (before): ${totalUsersTabcoinsBefore.rows[0].sum}`);
// 6) CRIAR TABCOINS PARA OS USUÁRIOS
// Praticamente a mesma lógica que antes, mas agora bonificando os Usuários.
for await (const contentWithoutTabcoins of totalContentsBeforeTabcoins.rows) {
const userId = contentWithoutTabcoins.user_id;
await balance.create(
{
balanceType: 'user:tabcoin',
recipientId: userId,
amount: 10, // O valor bônus para quem se arriscou a criar um Conteúdo no passado
originatorType: 'event',
originatorId: currentEvent.id,
},
{
transaction: transaction,
}
);
}
// 7) PEGAR TOTAL DE TABCOINS POSITIVAS DOS USUÁRIOS (DEPOIS)
// Isso deveria gerar para os usuários 10x mais TabCoins que foram
// geradas para os Conteúdos.
const totalUsersTabcoinsAfter = await transaction.query(`
SELECT
sum(amount)
FROM
balance_operations
WHERE
balance_type = 'user:tabcoin'
and amount > 0
;`);
console.log(
`User TabCoins (after): ${totalUsersTabcoinsAfter.rows[0].sum} (+${
totalUsersTabcoinsAfter.rows[0].sum - totalUsersTabcoinsBefore.rows[0].sum
})`
);
if (
totalContentsBeforeTabcoins.rows.length !==
totalContentsTabcoinsAfter.rows[0].sum - totalContentsTabcoinsBefore.rows[0].sum
) {
throw new UnprocessableEntityError({
message: 'Number of added Content TabCoins does not match',
context: {
totalContentsBeforeTabcoins: totalContentsBeforeTabcoins.rows.length,
totalContentsTabcoinsBefore: totalContentsTabcoinsBefore.rows[0].sum,
totalContentsTabcoinsAfter: totalContentsTabcoinsAfter.rows[0].sum,
addedTabCoins: totalContentsTabcoinsAfter.rows[0].sum - totalContentsTabcoinsBefore.rows[0].sum,
},
});
} else if (
totalContentsBeforeTabcoins.rows.length !==
(totalUsersTabcoinsAfter.rows[0].sum - totalUsersTabcoinsBefore.rows[0].sum) / 10
) {
throw new UnprocessableEntityError({
message: 'Number of added User TabCoins does not match',
context: {
totalContentsBeforeTabcoins: totalContentsBeforeTabcoins.rows.length,
totalUsersTabcoinsBefore: totalUsersTabcoinsBefore.rows[0].sum,
totalUsersTabcoinsAfter: totalUsersTabcoinsAfter.rows[0].sum,
},
});
} else if (DRY_RUN) {
throw new UnprocessableEntityError({
message: 'Dry run',
});
} else {
await transaction.query('COMMIT');
await transaction.release();
return response.status(200).json({});
}
} catch (error) {
await transaction.query('ROLLBACK');
await transaction.release();
throw error;
}
} |
Contexto e Execução
Nesse caso, sugiro fortemente centralizar a discussão na publicação abaixo
The text was updated successfully, but these errors were encountered: