Page MenuHomePhabricator

D13484.id38990.diff
No OneTemporary

D13484.id38990.diff

diff --git a/web/alias-server/config.js b/web/alias-server/config.js
--- a/web/alias-server/config.js
+++ b/web/alias-server/config.js
@@ -7,6 +7,9 @@
pendingAliases: 'pendingAliasTxs',
serverState: 'serverState',
},
+ startup: {
+ serverState: { processedBlockheight: 0, processedConfirmedTxs: 0 },
+ },
connectionUrl: 'mongodb://localhost:27017',
},
telegram: {
diff --git a/web/alias-server/dbMethods.js b/web/alias-server/dbMethods.js
new file mode 100644
--- /dev/null
+++ b/web/alias-server/dbMethods.js
@@ -0,0 +1,112 @@
+'use strict';
+const config = require('./config');
+const log = require('./log');
+const { sendAdminAlert } = require('./telegram');
+module.exports = {
+ getValidAliasesFromDb: async function (db) {
+ let validAliasesInDb;
+ try {
+ validAliasesInDb = await db
+ .collection(config.database.collections.validAliases)
+ .find()
+ .sort({ blockheight: 1 })
+ .project({ _id: 0 })
+ .toArray();
+ log(
+ `${validAliasesInDb.length} valid aliases fetched from the database.`,
+ );
+ return validAliasesInDb;
+ } catch (err) {
+ const errorDesc = `Error in determining validAliasesInDb in function getValidAliasesFromDb. Notifying admin.`;
+ await module.exports.handleFailedDatabaseRequest(errorDesc, err);
+ return false;
+ }
+ },
+ getServerStateFromDb: async function (db) {
+ let serverStateArray;
+ try {
+ serverStateArray = await db
+ .collection(config.database.collections.serverState)
+ .find()
+ .toArray();
+ if (serverStateArray.length === 0) {
+ // Special case where you are just starting the app
+ log(
+ `Server startup: App has no serverState; processedConfirmedTxs and processedBlockheight will default to 0`,
+ );
+ return config.database.startup.serverState;
+ } else {
+ return serverStateArray[0];
+ }
+ } catch (err) {
+ const errorDesc = `Error in determining serverState. Notifying admin.`;
+ await module.exports.handleFailedDatabaseRequest(errorDesc, err);
+ return false;
+ }
+ },
+ addAliasesToDb: async function (db, newValidAliases) {
+ let validAliasesAddedToDbSuccess;
+ try {
+ validAliasesAddedToDbSuccess = await db
+ .collection(config.database.collections.validAliases)
+ .insertMany(newValidAliases);
+ log(
+ `Inserted ${validAliasesAddedToDbSuccess.insertedCount} reserved aliases into ${config.database.collections.validAliases}`,
+ );
+ return true;
+ } catch (err) {
+ const errorDesc = `Error in function addAliasesToDb. Notifying admin.`;
+ await module.exports.handleFailedDatabaseRequest(errorDesc, err);
+ return false;
+ }
+ },
+ updateServerStateInDb: async function (db, newServerState) {
+ const { processedConfirmedTxs, processedBlockheight } = newServerState;
+ // An empty document will update the first document returned in the collection
+ // serverState only has one document
+ const serverStateQuery = {};
+ const serverStateUpdate = {
+ $set: {
+ processedConfirmedTxs,
+ processedBlockheight,
+ },
+ };
+ // If you are running the server for the first time and there is no
+ // serverState in the db, create it
+ const serverStateOptions = { upsert: true };
+ let serverStateUpdateResult;
+ try {
+ serverStateUpdateResult = await db
+ .collection(config.database.collections.serverState)
+ .updateOne(
+ serverStateQuery,
+ serverStateUpdate,
+ serverStateOptions,
+ );
+ return true;
+ } catch (err) {
+ // If this isn't updated, the server will process too many txs next time
+ // Let the admin know. This won't impact parsing but will cause processing too many txs
+ const errorDesc = `Error in function updateServerState. Notifying admin.`;
+ await module.exports.handleFailedDatabaseRequest(errorDesc, err);
+ return false;
+ }
+ },
+ handleFailedDatabaseRequest: async function (
+ adminErrorDescription,
+ thrownError,
+ ) {
+ log(adminErrorDescription, thrownError);
+ // Send prelim admin notification with text only.
+ // Notification with error msg may fail to send due to telegram markdown requirements.
+ const prelimAdminNotified = await sendAdminAlert(adminErrorDescription);
+ if (!prelimAdminNotified) {
+ log(`Prelim admin notification failed to send.`);
+ }
+ const adminNotifyMsg = `${adminErrorDescription}\n\n${thrownError}`;
+ const adminNotified = await sendAdminAlert(adminNotifyMsg);
+ if (!adminNotified) {
+ log(`Admin notification failed to send.`);
+ }
+ },
+};
diff --git a/web/alias-server/websocket.js b/web/alias-server/websocket.js
--- a/web/alias-server/websocket.js
+++ b/web/alias-server/websocket.js
@@ -1,3 +1,4 @@
+'use strict';
const config = require('./config');
const log = require('./log');
const {
@@ -13,9 +14,14 @@
} = require('./utils');
const {
returnTelegramBotSendMessagePromise,
- sendAdminAlert,
buildAliasAnnouncementMsg,
} = require('./telegram');
+const {
+ getValidAliasesFromDb,
+ getServerStateFromDb,
+ addAliasesToDb,
+ updateServerStateInDb,
+} = require('./dbMethods');
const { chronik } = require('./chronik');
const axios = require('axios');
@@ -53,92 +59,27 @@
: log(`Checking for new aliases on startup`);
// Get the valid aliases already in the db
- let validAliasesInDb;
- try {
- validAliasesInDb = await db
- .collection(config.database.collections.validAliases)
- .find()
- .sort({ blockheight: 1 })
- .project({ _id: 0 })
- .toArray();
- log(`${validAliasesInDb.length} valid aliases in database`);
- } catch (err) {
- /*
- - Notify Admin
- - Do not finish this function. Leave alias info unchanged
- (will be in error state or not displaying the most recent aliases)
- - Not displaying the most recent aliases is acceptable if the API does display
- the blockheight at which info is valid
- */
- const errMsg = `Error in determining validAliasesInDb, notifying admin and exiting parseWebsocketMessage()`;
- log(errMsg, err);
- const adminNotifyMsg = `${errMsg}\n\n${err}`;
- const adminNotified = await sendAdminAlert(adminNotifyMsg);
- if (!adminNotified) {
- log(`Admin notification failed to send`);
- }
+ let validAliasesInDb = await getValidAliasesFromDb(db);
+ if (!validAliasesInDb) {
+ log(
+ `Failed to fetch valid aliases from database at block ${wsMsg.blockHash}. Exiting loop.`,
+ );
+ // Break out of this loop
+ // API will continue to show the last alias set we know to be valid
return;
}
- // Get the valid aliases already in the db
- let serverState, serverStateArray;
- try {
- serverStateArray = await db
- .collection(config.database.collections.serverState)
- .find()
- .toArray();
- if (serverStateArray.length === 0) {
- // Special case where you are just starting the app
- log(
- `App has no serverState. processedConfirmedTxs will default to 0`,
- );
- }
- serverState = serverStateArray[0];
- } catch (err) {
- /*
- If this happens,
- - Notify Admin
- - Do not finish this function. Leave alias info unchanged
- (will be in error state or not displaying the most recent aliases)
- - Not displaying the most recent aliases is acceptable if the API does display
- the blockheight at which info is valid
- */
- const errMsg = `Error in determining serverState, notifying admin and exiting parseWebsocketMessage()`;
- log(errMsg, err);
- const adminNotifyMsg = `${errMsg}\n\n${err}`;
- const adminNotified = await sendAdminAlert(adminNotifyMsg);
- if (!adminNotified) {
- log(`Admin notification failed to send`);
- }
- return;
- }
+ let serverState;
+ let isServerStartup = false;
+ // If you have no valid aliases in the db, the server is starting up
+ if (validAliasesInDb.length === 0) {
+ log(`Server startup: No valid aliases in the database.`);
+ isServerStartup = true;
+ // No point in checking database for serverState. Assume startup.
+ // For edge case where user has serverState reflecting processed blocks,
+ // this assumption will overwrite it correctly at the end of this block found loop
+ serverState = config.database.startup.serverState;
- let processedBlockheight, processedConfirmedTxs;
- // If you have aliases in the db and processedConfirmedTxs in serverState,
- // determine the most recently processed block and processedConfirmedTxs
- if (
- validAliasesInDb &&
- validAliasesInDb.length > 0 &&
- serverStateArray.length > 0 &&
- serverStateArray[0] &&
- typeof serverStateArray[0].processedConfirmedTxs !==
- 'undefined'
- ) {
- /*
- Note processedBlockheight is not the same as the blockheight of
- the most recent alias tx
- */
- processedBlockheight = serverState.processedBlockheight;
- processedConfirmedTxs = serverState.processedConfirmedTxs;
- log(`processedBlockheight`, processedBlockheight);
- log(`processedConfirmedTxs`, processedConfirmedTxs);
- } else {
- log(
- `Server startup. There are no valid aliases in the database.`,
- );
- // If nothing is in cache, get the full tx history
- processedBlockheight = 0;
- processedConfirmedTxs = 0;
// If validAliasesInDb is empty, set it to ABC whitelist
// This will be built on with getUnprocessedValidAliasRegistrations()
validAliasesInDb = generateReservedAliasTxArray();
@@ -147,22 +88,25 @@
log(
`Initializing ${config.database.collections.validAliases} with reserved aliases`,
);
- try {
- const reservedAliasTxsCollectionInsertResult = await db
- .collection(
- config.database.collections.validAliases,
- )
- .insertMany(validAliasesInDb);
- log(
- `Inserted ${reservedAliasTxsCollectionInsertResult.insertedCount} reserved aliases into ${config.database.collections.validAliases}`,
- );
- } catch (err) {
+ let validAliasesDbInitializedWithReservedAliases =
+ await addAliasesToDb(db, validAliasesInDb);
+ if (!validAliasesDbInitializedWithReservedAliases) {
log(
- `Error in db.collection(${config.database.collections.validAliases}.insertMany on server startup)`,
- err,
+ `Failed to initialize validAliases collection with reserved aliases`,
);
+ // Don't stop processing in this case. This does not create a risk of corrupting alias info.
+ }
+ } else {
+ // Get server state
+ serverState = await getServerStateFromDb(db);
+ if (!serverState) {
+ // Break out of this loop
+ // API will continue to show the last alias set we know to be valid
+ return;
}
}
+ const { processedBlockheight, processedConfirmedTxs } =
+ serverState;
// Look for unprocessed transactions at alias registration address
const unprocessedTxs = await getUnprocessedTxHistory(
@@ -176,8 +120,12 @@
//TODO
// if unprocessedTxs is zero, should be able to just exit
// probably irrelevant for IFP address which will always have coinbase txs
- for (let i = 0; i < unprocessedTxs.length; i += 1) {
- log(`Unprocessed tx: ${unprocessedTxs[i].txid}`);
+
+ // Only log unprocessed txs if this is not server startup
+ if (!isServerStartup) {
+ for (let i = 0; i < unprocessedTxs.length; i += 1) {
+ log(`Unprocessed tx: ${unprocessedTxs[i].txid}`);
+ }
}
// Process unprocessed alias txs
@@ -204,7 +152,7 @@
);
// If you are starting up the server, and have 100s of aliases to log, don't
- if (newlyValidAliasTxs.length < 25) {
+ if (!isServerStartup) {
for (let i = 0; i < newlyValidAliasTxs.length; i += 1) {
log(
`New valid alias processed: ${newlyValidAliasTxs[i].alias}`,
@@ -245,43 +193,23 @@
`Updating ${processedConfirmedTxs} from ${processedConfirmedTxs} to ${updatedProcessedConfirmedTxs}`,
);
- // Update server state
- // An empty document will update the first document returned in the collection
- // serverState only has one document
- const serverStateQuery = {};
- const serverStateUpdate = {
- $set: {
- processedConfirmedTxs: updatedProcessedConfirmedTxs,
- processedBlockheight: updatedProcessedBlockheight,
- },
+ // Update server state in database
+ const newServerState = {
+ processedConfirmedTxs: updatedProcessedConfirmedTxs,
+ processedBlockheight: updatedProcessedBlockheight,
};
- // If you are running the server for the first time and there is no
- // serverState in the db, create it
- const serverStateOptions = { upsert: true };
- let serverStateUpdateResult;
- try {
- serverStateUpdateResult = await db
- .collection(config.database.collections.serverState)
- .updateOne(
- serverStateQuery,
- serverStateUpdate,
- serverStateOptions,
- );
+ const serverStateUpdatedInDb = await updateServerStateInDb(
+ db,
+ newServerState,
+ );
+ if (!serverStateUpdatedInDb) {
+ // If serverState isn't updated, this API endpoint could become inconsistent with
+ // valid aliases in the db
+ // Stop this loop before you update valid aliases
log(
- `Updated serverState.processedConfirmedTxs from ${processedConfirmedTxs} to ${updatedProcessedConfirmedTxs}.`,
+ `Server state failed to update for block ${wsMsg.blockHash}, exiting loop.`,
);
- } catch (err) {
- // If this isn't updated, the server will process too many txs next time
- // Let the admin know. This won't impact parsing but will cause processing too many txs
- const errMsg = `Error in db.collection(${config.database.collections.serverState}).update(${serverStateQuery}, ${serverStateUpdate})`;
- log(errMsg, err);
- const adminNotifyMsg = `${errMsg}\n\n${err}`;
- const adminNotified = await sendAdminAlert(
- adminNotifyMsg,
- );
- if (!adminNotified) {
- log(`Admin notification failed to send`);
- }
+ return;
}
} else {
log(`No new confirmed alias txs since last block`);
@@ -289,34 +217,19 @@
if (newlyValidAliasTxs.length > 0) {
// Update validAliasTxs
- try {
- const validAliasTxsCollectionInsertResult = await db
- .collection(
- config.database.collections.validAliases,
- )
- .insertMany(newlyValidAliasTxs);
- log(
- `Inserted ${validAliasTxsCollectionInsertResult.insertedCount} aliases into ${config.database.collections.validAliases}`,
- );
- } catch (err) {
- log(
- `Error in db.collection(${config.database.collections.validAliases}.insertMany)`,
- err,
- );
- /*
- let ids = err.result.result.insertedIds;
- for (let id of Object.values(ids)) {
- log(`Processed a document with id ${id._id}`);
- }
-
+ let newlyValidAliasesAddedToDb = await addAliasesToDb(
+ db,
+ newlyValidAliasTxs,
+ );
+ if (!newlyValidAliasesAddedToDb) {
log(
- `Number of documents inserted: ${err.result.result.nInserted}`,
+ `alias-server failed to add newly valid aliases to the database for block ${wsMsg.blockHash}`,
);
- */
+ // Admin is notified. No point in breaking out of the loop here.
}
// To prevent rate limiting, do not send Telegram msg on server startup
- if (processedBlockheight !== 0) {
+ if (!isServerStartup) {
// Get the XEC price to use in the Telegram msgs
let coingeckoPriceResponse;
let xecPrice;

File Metadata

Mime Type
text/plain
Expires
Thu, Feb 6, 16:23 (17 h, 12 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5082685
Default Alt Text
D13484.id38990.diff (20 KB)

Event Timeline