* [PATCH v1 0/2] Automatic recovery from a MySQL restart @ 2022-10-27 15:08 Ammar Faizi 2022-10-27 15:08 ` [PATCH v1 1/2] daemon: telegram: db: Allow the caller to reconnect Ammar Faizi 2022-10-27 15:08 ` [PATCH v1 2/2] daemon: telegram: Handle MySQL error Ammar Faizi 0 siblings, 2 replies; 42+ messages in thread From: Ammar Faizi @ 2022-10-27 15:08 UTC (permalink / raw) To: GNU/Weeb Mailing List Cc: Ammar Faizi, Muhammad Rizki, Alviro Iskandar Setiawan Hi, This series adds a mechanism for the daemon to recover from a MySQL restart event. Currently, the daemon is totally unusable when the MySQL server is restarted. It's spinning in a loop with the following error: mysql.connector.errors.OperationalError: 2013 (HY000): Lost connection to MySQL server during query When it happens, the only way to fix the situation is: restart the daemon manually, which is obviously not productive. Create a mechanism in the class DB to allow the caller to reconnect. This way, the caller can automatically reconnect without having the user restart the daemon. Handle MySQL error in the main daemon loop path. Do reconnect to the database if such an error happens. This way, the daemon can automatically recover from the MySQL server restart without having the user restart the daemon. This series is just a hot fix, there are two patches in this series: - Patch 1 is a preparation patch to create a recover mechanism. - Patch 2 is to implement the recover mechanism in the main daemon loop path. Signed-off-by: Ammar Faizi <[email protected]> --- Ammar Faizi (2): daemon: telegram: db: Allow the caller to reconnect daemon: telegram: Handle MySQL error daemon/telegram/database/core.py | 31 +++++++++++++++++++++++++----- daemon/telegram/mailer/listener.py | 29 ++++++++++++++++++++++++---- daemon/telegram/packages/client.py | 8 ++++---- daemon/tg.py | 4 ++-- 4 files changed, 57 insertions(+), 15 deletions(-) base-commit: 6e94cf607287e5bc209e9c14c8156c7ad49455e3 -- Ammar Faizi ^ permalink raw reply [flat|nested] 42+ messages in thread
* [PATCH v1 1/2] daemon: telegram: db: Allow the caller to reconnect 2022-10-27 15:08 [PATCH v1 0/2] Automatic recovery from a MySQL restart Ammar Faizi @ 2022-10-27 15:08 ` Ammar Faizi 2022-10-27 16:52 ` Muhammad Rizki 2022-10-27 15:08 ` [PATCH v1 2/2] daemon: telegram: Handle MySQL error Ammar Faizi 1 sibling, 1 reply; 42+ messages in thread From: Ammar Faizi @ 2022-10-27 15:08 UTC (permalink / raw) To: GNU/Weeb Mailing List Cc: Ammar Faizi, Muhammad Rizki, Alviro Iskandar Setiawan The daemon is totally unusable when the MySQL server is restarted. It's spinning in a loop with the following error: mysql.connector.errors.OperationalError: 2013 (HY000): Lost connection to MySQL server during query When it happens, the only way to fix the situation is: restart the daemon manually, which is obviously not productive. Create a mechanism in the class DB to allow the caller to reconnect. This way, the caller can automatically reconnect without having the user restart the daemon. Signed-off-by: Ammar Faizi <[email protected]> --- daemon/telegram/database/core.py | 31 +++++++++++++++++++++++++----- daemon/telegram/packages/client.py | 8 ++++---- daemon/tg.py | 4 ++-- 3 files changed, 32 insertions(+), 11 deletions(-) diff --git a/daemon/telegram/database/core.py b/daemon/telegram/database/core.py index c34d7a8..bc65b54 100644 --- a/daemon/telegram/database/core.py +++ b/daemon/telegram/database/core.py @@ -4,17 +4,38 @@ # Copyright (C) 2022 Ammar Faizi <[email protected]> # - +from mysql import connector from .methods import DBMethods class DB(DBMethods): - def __init__(self, conn): - self.conn = conn + def __init__(self, host, user, password, database): + self.host = host + self.user = user + self.password = password + self.database = database + self.conn = None + self.connect() + + # + # Allow the caller to reconnect. + # + def connect(self): + if self.conn: + self.close() + + self.conn = connector.connect( + host=self.host, + user=self.user, + password=self.password, + database=self.database + ) self.conn.autocommit = True self.cur = self.conn.cursor(buffered=True) - - def __del__(self): + def close(self): self.cur.close() self.conn.close() + + def __del__(self): + self.close() diff --git a/daemon/telegram/packages/client.py b/daemon/telegram/packages/client.py index c971ea1..2ee37f6 100644 --- a/daemon/telegram/packages/client.py +++ b/daemon/telegram/packages/client.py @@ -15,11 +15,11 @@ from .decorator import handle_flood class DaemonClient(Client): - def __init__(self, name: str, api_id: int, - api_hash: str, conn, **kwargs): + def __init__(self, name: str, api_id: int, api_hash: str, db: DB, + **kwargs): super().__init__(name, api_id, - api_hash, **kwargs) - self.db = DB(conn) + api_hash, **kwargs) + self.db = db @handle_flood diff --git a/daemon/tg.py b/daemon/tg.py index 5f8c21e..e6d5d05 100644 --- a/daemon/tg.py +++ b/daemon/tg.py @@ -6,11 +6,11 @@ from apscheduler.schedulers.asyncio import AsyncIOScheduler from dotenv import load_dotenv -from mysql import connector from atom import Scraper from telegram.packages import DaemonClient from telegram.mailer import BotMutexes from telegram.mailer import Bot +from telegram.database import DB import os @@ -22,7 +22,7 @@ def main(): api_id=int(os.getenv("API_ID")), api_hash=os.getenv("API_HASH"), bot_token=os.getenv("BOT_TOKEN"), - conn=connector.connect( + db=DB( host=os.getenv("DB_HOST"), user=os.getenv("DB_USER"), password=os.getenv("DB_PASS"), -- Ammar Faizi ^ permalink raw reply related [flat|nested] 42+ messages in thread
* Re: [PATCH v1 1/2] daemon: telegram: db: Allow the caller to reconnect 2022-10-27 15:08 ` [PATCH v1 1/2] daemon: telegram: db: Allow the caller to reconnect Ammar Faizi @ 2022-10-27 16:52 ` Muhammad Rizki 2022-10-27 16:54 ` Muhammad Rizki 0 siblings, 1 reply; 42+ messages in thread From: Muhammad Rizki @ 2022-10-27 16:52 UTC (permalink / raw) To: Ammar Faizi, GNU/Weeb Mailing List; +Cc: Alviro Iskandar Setiawan On 27/10/2022 22.08, Ammar Faizi wrote: > + def connect(self): > + if self.conn: > + self.close() > + > + self.conn = connector.connect( Have you tried the ping() method? Refer: http://www.neotitans.com/resources/python/mysql-python-connection-error-2006.html The ping()'s doc says: Checks whether or not the connection to the server is working. If it has gone down, an automatic reconnection is attempted. This function can be used by clients that remain idle for a long while, to check whether or not the server has closed the connection and reconnect if necessary. I think this is more practice. I haven't test the ping() method but, you got the idea? Thanks. ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v1 1/2] daemon: telegram: db: Allow the caller to reconnect 2022-10-27 16:52 ` Muhammad Rizki @ 2022-10-27 16:54 ` Muhammad Rizki 2022-10-28 5:48 ` Ammar Faizi 0 siblings, 1 reply; 42+ messages in thread From: Muhammad Rizki @ 2022-10-27 16:54 UTC (permalink / raw) To: Ammar Faizi, GNU/Weeb Mailing List; +Cc: Alviro Iskandar Setiawan On 27/10/2022 23.52, Muhammad Rizki wrote: > On 27/10/2022 22.08, Ammar Faizi wrote: >> + def connect(self): >> + if self.conn: >> + self.close() >> + >> + self.conn = connector.connect( > > Have you tried the ping() method? > > Refer: > http://www.neotitans.com/resources/python/mysql-python-connection-error-2006.html Sorry, I mean this: https://dev.mysql.com/doc/connector-python/en/connector-python-api-mysqlconnection-ping.html ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v1 1/2] daemon: telegram: db: Allow the caller to reconnect 2022-10-27 16:54 ` Muhammad Rizki @ 2022-10-28 5:48 ` Ammar Faizi 0 siblings, 0 replies; 42+ messages in thread From: Ammar Faizi @ 2022-10-28 5:48 UTC (permalink / raw) To: Muhammad Rizki, GNU/Weeb Mailing List; +Cc: Alviro Iskandar Setiawan On 10/27/22 11:54 PM, Muhammad Rizki wrote: > On 27/10/2022 23.52, Muhammad Rizki wrote: >> On 27/10/2022 22.08, Ammar Faizi wrote: >>> + def connect(self): >>> + if self.conn: >>> + self.close() >>> + >>> + self.conn = connector.connect( >> >> Have you tried the ping() method? >> >> Refer: http://www.neotitans.com/resources/python/mysql-python-connection-error-2006.html > > Sorry, I mean this: https://dev.mysql.com/doc/connector-python/en/connector-python-api-mysqlconnection-ping.html Thanks for the great feedback! Yes, using ping() can perform the reconnect. This way we don't have to save the connection credentials in DB class. I'll send a v2 later with the ping() method applied. Just a fast test on top of this series to see whether ping really works: diff --git a/daemon/telegram/database/core.py b/daemon/telegram/database/core.py index bc65b54..6260c5b 100644 --- a/daemon/telegram/database/core.py +++ b/daemon/telegram/database/core.py @@ -22,7 +22,8 @@ class DB(DBMethods): # def connect(self): if self.conn: - self.close() + self.conn.ping(reconnect=True, attempts=3, delay=3) + return self.conn = connector.connect( host=self.host, -- Ammar Faizi ^ permalink raw reply related [flat|nested] 42+ messages in thread
* [PATCH v1 2/2] daemon: telegram: Handle MySQL error 2022-10-27 15:08 [PATCH v1 0/2] Automatic recovery from a MySQL restart Ammar Faizi 2022-10-27 15:08 ` [PATCH v1 1/2] daemon: telegram: db: Allow the caller to reconnect Ammar Faizi @ 2022-10-27 15:08 ` Ammar Faizi 2022-10-28 9:21 ` Alviro Iskandar Setiawan 2022-10-28 9:24 ` Alviro Iskandar Setiawan 1 sibling, 2 replies; 42+ messages in thread From: Ammar Faizi @ 2022-10-27 15:08 UTC (permalink / raw) To: GNU/Weeb Mailing List Cc: Ammar Faizi, Muhammad Rizki, Alviro Iskandar Setiawan A previous patch creates a mechanism to allow the caller to reconnect to the database by calling db.connect(). Handle MySQL error in the main daemon loop path. Do reconnect to the database if such an error happens. This way, the daemon can automatically recover from the MySQL server restart without having the user restart the daemon. Signed-off-by: Ammar Faizi <[email protected]> --- daemon/telegram/mailer/listener.py | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/daemon/telegram/mailer/listener.py b/daemon/telegram/mailer/listener.py index 1c92f23..8a61367 100644 --- a/daemon/telegram/mailer/listener.py +++ b/daemon/telegram/mailer/listener.py @@ -7,6 +7,7 @@ from pyrogram.types import Message from apscheduler.schedulers.asyncio import AsyncIOScheduler from telegram.packages import DaemonClient +from mysql import connector from atom import Scraper from atom import utils from enums import Platform @@ -15,6 +16,7 @@ import re import traceback + class BotMutexes(): def __init__(self): self.send_to_tg = asyncio.Lock() @@ -43,13 +45,32 @@ class Bot(): ) + async def handle_db_error(self, e): + # + # TODO(ammarfaizi2): + # Ideally, we also want to log and report this situation. + # + print(f"Database error: {str(e)}") + print("Reconnecting to the database...") + self.db.connect() + + # + # Don't do this too often, avoid connect() burst. + # + await asyncio.sleep(3) + + async def __run(self): print("[__run]: Running...") - for url in self.db.get_atom_urls(): - try: + try: + for url in self.db.get_atom_urls(): await self.__handle_atom_url(url) - except: - print(traceback.format_exc()) + except connector.errors.OperationalError as e: + await self.handle_db_error(e) + except connector.errors.DatabaseError as e: + await self.handle_db_error(e) + except: + print(traceback.format_exc()) if not self.isRunnerFixed: self.isRunnerFixed = True -- Ammar Faizi ^ permalink raw reply related [flat|nested] 42+ messages in thread
* Re: [PATCH v1 2/2] daemon: telegram: Handle MySQL error 2022-10-27 15:08 ` [PATCH v1 2/2] daemon: telegram: Handle MySQL error Ammar Faizi @ 2022-10-28 9:21 ` Alviro Iskandar Setiawan 2022-10-28 9:28 ` Ammar Faizi 2022-10-28 9:24 ` Alviro Iskandar Setiawan 1 sibling, 1 reply; 42+ messages in thread From: Alviro Iskandar Setiawan @ 2022-10-28 9:21 UTC (permalink / raw) To: Ammar Faizi; +Cc: GNU/Weeb Mailing List, Muhammad Rizki On Thu, Oct 27, 2022 at 10:08 PM Ammar Faizi wrote: > + async def handle_db_error(self, e): > + # > + # TODO(ammarfaizi2): > + # Ideally, we also want to log and report this situation. > + # > + print(f"Database error: {str(e)}") > + print("Reconnecting to the database...") > + self.db.connect() > + > + # > + # Don't do this too often, avoid connect() burst. > + # > + await asyncio.sleep(3) What about sleeping first before connecting? As it'll give a chance for the server to start properly, MySQL server may take several seconds to restart. -- Viro ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v1 2/2] daemon: telegram: Handle MySQL error 2022-10-28 9:21 ` Alviro Iskandar Setiawan @ 2022-10-28 9:28 ` Ammar Faizi 0 siblings, 0 replies; 42+ messages in thread From: Ammar Faizi @ 2022-10-28 9:28 UTC (permalink / raw) To: Alviro Iskandar Setiawan; +Cc: GNU/Weeb Mailing List, Muhammad Rizki On 10/28/22 4:21 PM, Alviro Iskandar Setiawan wrote: > On Thu, Oct 27, 2022 at 10:08 PM Ammar Faizi wrote: >> + async def handle_db_error(self, e): >> + # >> + # TODO(ammarfaizi2): >> + # Ideally, we also want to log and report this situation. >> + # >> + print(f"Database error: {str(e)}") >> + print("Reconnecting to the database...") >> + self.db.connect() >> + >> + # >> + # Don't do this too often, avoid connect() burst. >> + # >> + await asyncio.sleep(3) > > What about sleeping first before connecting? > As it'll give a chance for the server to start properly, MySQL server > may take several seconds to restart. ping(reconnect=True, attempts=3, delay=3) should handle this better. I'll send a v2 later. -- Ammar Faizi ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v1 2/2] daemon: telegram: Handle MySQL error 2022-10-27 15:08 ` [PATCH v1 2/2] daemon: telegram: Handle MySQL error Ammar Faizi 2022-10-28 9:21 ` Alviro Iskandar Setiawan @ 2022-10-28 9:24 ` Alviro Iskandar Setiawan 2022-10-28 9:32 ` Ammar Faizi 1 sibling, 1 reply; 42+ messages in thread From: Alviro Iskandar Setiawan @ 2022-10-28 9:24 UTC (permalink / raw) To: Ammar Faizi; +Cc: GNU/Weeb Mailing List, Muhammad Rizki On Thu, Oct 27, 2022 at 10:08 PM Ammar Faizi wrote: > async def __run(self): > print("[__run]: Running...") > - for url in self.db.get_atom_urls(): > - try: > + try: > + for url in self.db.get_atom_urls(): > await self.__handle_atom_url(url) > - except: > - print(traceback.format_exc()) > + except connector.errors.OperationalError as e: > + await self.handle_db_error(e) > + except connector.errors.DatabaseError as e: > + await self.handle_db_error(e) > + except: > + print(traceback.format_exc()) I think this reconnect() handling should be done in each DB method, not here, otherwise we'll potentially have a half-inserted data and may get it wrong. -- Viro ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v1 2/2] daemon: telegram: Handle MySQL error 2022-10-28 9:24 ` Alviro Iskandar Setiawan @ 2022-10-28 9:32 ` Ammar Faizi 2022-10-28 9:40 ` Alviro Iskandar Setiawan 0 siblings, 1 reply; 42+ messages in thread From: Ammar Faizi @ 2022-10-28 9:32 UTC (permalink / raw) To: Alviro Iskandar Setiawan; +Cc: GNU/Weeb Mailing List, Muhammad Rizki On 10/28/22 4:24 PM, Alviro Iskandar Setiawan wrote: > I think this reconnect() handling should be done in each DB method, > not here, otherwise we'll potentially have a half-inserted data and > may get it wrong. By using a transaction, such a situation cannot happen. It already fulfills the ACID requirements. But I doubt we've fully handled that properly. This needs extra code review, especially in that critical section. -- Ammar Faizi ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v1 2/2] daemon: telegram: Handle MySQL error 2022-10-28 9:32 ` Ammar Faizi @ 2022-10-28 9:40 ` Alviro Iskandar Setiawan 2022-10-28 9:43 ` Ammar Faizi 0 siblings, 1 reply; 42+ messages in thread From: Alviro Iskandar Setiawan @ 2022-10-28 9:40 UTC (permalink / raw) To: Ammar Faizi; +Cc: GNU/Weeb Mailing List, Muhammad Rizki On Fri, Oct 28, 2022 at 4:32 PM Ammar Faizi wrote: > On 10/28/22 4:24 PM, Alviro Iskandar Setiawan wrote: >> I think this reconnect() handling should be done in each DB method, >> not here, otherwise we'll potentially have a half-inserted data and >> may get it wrong. > > By using a transaction, such a situation cannot happen. It already > fulfills the ACID requirements. But I doubt we've fully handled that > properly. This needs extra code review, especially in that critical > section. Turning on autocommit for everything is usually a hint that you did ACID wrong, maybe not you, but Rizki. -- Viro ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v1 2/2] daemon: telegram: Handle MySQL error 2022-10-28 9:40 ` Alviro Iskandar Setiawan @ 2022-10-28 9:43 ` Ammar Faizi 2022-10-28 16:29 ` Ammar Faizi 0 siblings, 1 reply; 42+ messages in thread From: Ammar Faizi @ 2022-10-28 9:43 UTC (permalink / raw) To: Alviro Iskandar Setiawan; +Cc: GNU/Weeb Mailing List, Muhammad Rizki On 10/28/22 4:40 PM, Alviro Iskandar Setiawan wrote: > On Fri, Oct 28, 2022 at 4:32 PM Ammar Faizi wrote: >> On 10/28/22 4:24 PM, Alviro Iskandar Setiawan wrote: >>> I think this reconnect() handling should be done in each DB method, >>> not here, otherwise we'll potentially have a half-inserted data and >>> may get it wrong. >> >> By using a transaction, such a situation cannot happen. It already >> fulfills the ACID requirements. But I doubt we've fully handled that >> properly. This needs extra code review, especially in that critical >> section. > > Turning on autocommit for everything is usually a hint that you did > ACID wrong, maybe not you, but Rizki. Maybe, that's why I said "I doubt". But I don't have time to review it for now, will do that later. -- Ammar Faizi ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v1 2/2] daemon: telegram: Handle MySQL error 2022-10-28 9:43 ` Ammar Faizi @ 2022-10-28 16:29 ` Ammar Faizi 2022-10-28 16:46 ` Alviro Iskandar Setiawan 2022-10-28 17:02 ` Muhammad Rizki 0 siblings, 2 replies; 42+ messages in thread From: Ammar Faizi @ 2022-10-28 16:29 UTC (permalink / raw) To: Alviro Iskandar Setiawan; +Cc: GNU/Weeb Mailing List, Muhammad Rizki On 10/28/22 4:43 PM, Ammar Faizi wrote: > On 10/28/22 4:40 PM, Alviro Iskandar Setiawan wrote: >> On Fri, Oct 28, 2022 at 4:32 PM Ammar Faizi wrote: >>> On 10/28/22 4:24 PM, Alviro Iskandar Setiawan wrote: >>>> I think this reconnect() handling should be done in each DB method, >>>> not here, otherwise we'll potentially have a half-inserted data and >>>> may get it wrong. >>> >>> By using a transaction, such a situation cannot happen. It already >>> fulfills the ACID requirements. But I doubt we've fully handled that >>> properly. This needs extra code review, especially in that critical >>> section. >> >> Turning on autocommit for everything is usually a hint that you did >> ACID wrong, maybe not you, but Rizki. > > Maybe, that's why I said "I doubt". But I don't have time to review it > for now, will do that later. I've just checked it. And yes, we have haven't fulfilled the ACID requirements here. While the following __send_mail method is holding the mutex while it's operating, it doesn't perform a rollback() operation if the subsequent SQL queries fail. So yes, having autocommit = True for everything is absolutely the wrong thing to do. We have to fix this issue at some point. It's currently working fine, only the corner case, that's when the MySQL hits error in the middle of __send_mail function. I believe there are more issues like this somewhere else. But this is the most hot path to get spotted. We also have to teach Rizki about this so he can fix it himself. # @__must_hold(self.mutexes.send_to_tg) async def __send_mail(self, url, mail, tg_chat_id): email_msg_id = utils.get_email_msg_id(mail) if not email_msg_id: # # It doesn't have a Message-Id. # A malformed email. Skip! # return False email_id = self.__mail_id_from_db(email_msg_id, tg_chat_id) if not email_id: # # Email has already been sent to Telegram. # Skip! # return False text, files, is_patch = utils.create_template(mail, Platform.TELEGRAM) reply_to = self.get_reply(mail, tg_chat_id) url = str(re.sub(r"/raw$", "", url)) if is_patch: m: "Message" = await self.client.send_patch_email( mail, tg_chat_id, text, reply_to, url ) else: text = "#ml\n" + text m: "Message" = await self.client.send_text_email( tg_chat_id, text,reply_to, url ) self.db.save_telegram_mail(email_id, m.chat.id, m.id) for d, f in files: await m.reply_document(f"{d}/{f}", file_name=f) await asyncio.sleep(1) utils.remove_patch(files) return True -- Ammar Faizi ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v1 2/2] daemon: telegram: Handle MySQL error 2022-10-28 16:29 ` Ammar Faizi @ 2022-10-28 16:46 ` Alviro Iskandar Setiawan 2022-10-28 16:54 ` Ammar Faizi 2022-10-28 18:10 ` Muhammad Rizki 2022-10-28 17:02 ` Muhammad Rizki 1 sibling, 2 replies; 42+ messages in thread From: Alviro Iskandar Setiawan @ 2022-10-28 16:46 UTC (permalink / raw) To: Ammar Faizi; +Cc: GNU/Weeb Mailing List, Muhammad Rizki On Fri, Oct 28, 2022 at 11:29 PM Ammar Faizi wrote: > I believe there are more issues like this somewhere else. But this is > the most hot path to get spotted. We also have to teach Rizki about this > so he can fix it himself. Yes. > if is_patch: > m: "Message" = await self.client.send_patch_email( > mail, tg_chat_id, text, reply_to, url > ) > else: > text = "#ml\n" + text > m: "Message" = await self.client.send_text_email( > tg_chat_id, text,reply_to, url > ) > > self.db.save_telegram_mail(email_id, m.chat.id, m.id) > for d, f in files: > await m.reply_document(f"{d}/{f}", file_name=f) > await asyncio.sleep(1) > > utils.remove_patch(files) > > return True Even this part is also problematic, if you hit an error before utils.remove_patch(), the patch file won't get removed because exception will stop the current execution and throw the exception to the previous call stack. I hate try and catch style, because it's very easy to miss an error like this. I prefer the way C handles error manually via return value like: ret = func_call(); if (ret < 0) goto clean_up; But that's obviously not the Python style. Python prefers using exceptions which make the caller may not be aware of this cleanup strategy! -- Viro ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v1 2/2] daemon: telegram: Handle MySQL error 2022-10-28 16:46 ` Alviro Iskandar Setiawan @ 2022-10-28 16:54 ` Ammar Faizi 2022-10-28 18:10 ` Muhammad Rizki 1 sibling, 0 replies; 42+ messages in thread From: Ammar Faizi @ 2022-10-28 16:54 UTC (permalink / raw) To: Alviro Iskandar Setiawan; +Cc: GNU/Weeb Mailing List, Muhammad Rizki On 10/28/22 11:46 PM, Alviro Iskandar Setiawan wrote: > Even this part is also problematic, if you hit an error before > utils.remove_patch(), the patch file won't get removed because > exception will stop the current execution and throw the exception to > the previous call stack. > > I hate try and catch style, because it's very easy to miss an error > like this. I prefer the way C handles error manually via return value > like: > > ret = func_call(); > if (ret < 0) > goto clean_up; > > But that's obviously not the Python style. Python prefers using > exceptions which make the caller may not be aware of this cleanup > strategy! Ditto. -- Ammar Faizi ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v1 2/2] daemon: telegram: Handle MySQL error 2022-10-28 16:46 ` Alviro Iskandar Setiawan 2022-10-28 16:54 ` Ammar Faizi @ 2022-10-28 18:10 ` Muhammad Rizki 2022-10-28 18:26 ` Alviro Iskandar Setiawan 1 sibling, 1 reply; 42+ messages in thread From: Muhammad Rizki @ 2022-10-28 18:10 UTC (permalink / raw) To: Alviro Iskandar Setiawan, Ammar Faizi; +Cc: GNU/Weeb Mailing List On 28/10/22 23.46, Alviro Iskandar Setiawan wrote: > Even this part is also problematic, if you hit an error before > utils.remove_patch(), the patch file won't get removed because > exception will stop the current execution and throw the exception to > the previous call stack. You are right. So we just check every value from the DB method and if it's None just return like goto? > > I hate try and catch style, because it's very easy to miss an error > like this. I prefer the way C handles error manually via return value > like: > > ret = func_call(); > if (ret < 0) > goto clean_up; > I've improved the remove_patch() earlier using the glob UNIX style path to check if all temp dirs is exists then remove them all. What do you think? ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v1 2/2] daemon: telegram: Handle MySQL error 2022-10-28 18:10 ` Muhammad Rizki @ 2022-10-28 18:26 ` Alviro Iskandar Setiawan 2022-10-28 18:52 ` Muhammad Rizki 0 siblings, 1 reply; 42+ messages in thread From: Alviro Iskandar Setiawan @ 2022-10-28 18:26 UTC (permalink / raw) To: Muhammad Rizki; +Cc: Ammar Faizi, GNU/Weeb Mailing List On Sat, Oct 29, 2022 at 1:10 AM Muhammad Rizki wrote: > On 28/10/22 23.46, Alviro Iskandar Setiawan wrote: >> Even this part is also problematic, if you hit an error before >> utils.remove_patch(), the patch file won't get removed because >> exception will stop the current execution and throw the exception to >> the previous call stack. > > You are right. So we just check every value from the DB method and if > it's None just return like goto? You don't use goto in Python. In an OOP style, the cleanup usually happens in the destructor, or in a "try with finally" statement. >> >> I hate try and catch style, because it's very easy to miss an error >> like this. I prefer the way C handles error manually via return value >> like: >> >> ret = func_call(); >> if (ret < 0) >> goto clean_up; >> > > I've improved the remove_patch() earlier using the glob UNIX style path > to check if all temp dirs is exists then remove them all. What do you think? It doesn't address the issue. You still don't understand the underlying issue behind your remove_patch() placement. You have this: for d, f in files: await m.reply_document(f"{d}/{f}", file_name=f) await asyncio.sleep(1) utils.remove_patch(files) What happens if you follow that for loop, then m.reply_document() throws an exception? The answer is: utils.remove_patch(files) will *not* be executed because it will throw the exception to the previous call stack, and if the previous call stack doesn't have a "try and except" statement, it will throw the exception to the previous call stack again, until at some point it is handled by and "try and except". If it never hits a "try and except" statement, the application will terminate. You don't do the cleanup if: - An error happens *after you create the patch file*. - But before the remove_patch() is executed. There are 2 possible solutions, either using a "try with finally" statement, or using a destructor wrapped in a class. ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v1 2/2] daemon: telegram: Handle MySQL error 2022-10-28 18:26 ` Alviro Iskandar Setiawan @ 2022-10-28 18:52 ` Muhammad Rizki 2022-10-28 19:08 ` Alviro Iskandar Setiawan 2022-10-28 19:16 ` Ammar Faizi 0 siblings, 2 replies; 42+ messages in thread From: Muhammad Rizki @ 2022-10-28 18:52 UTC (permalink / raw) To: Alviro Iskandar Setiawan; +Cc: Ammar Faizi, GNU/Weeb Mailing List On 29/10/22 01.26, Alviro Iskandar Setiawan wrote: > On Sat, Oct 29, 2022 at 1:10 AM Muhammad Rizki wrote: >> On 28/10/22 23.46, Alviro Iskandar Setiawan wrote: >>> Even this part is also problematic, if you hit an error before >>> utils.remove_patch(), the patch file won't get removed because >>> exception will stop the current execution and throw the exception to >>> the previous call stack. >> >> You are right. So we just check every value from the DB method and if >> it's None just return like goto? > > You don't use goto in Python. In an OOP style, the cleanup usually > happens in the destructor, or in a "try with finally" statement. > >>> >>> I hate try and catch style, because it's very easy to miss an error >>> like this. I prefer the way C handles error manually via return value >>> like: >>> >>> ret = func_call(); >>> if (ret < 0) >>> goto clean_up; >>> >> >> I've improved the remove_patch() earlier using the glob UNIX style path >> to check if all temp dirs is exists then remove them all. What do you think? > > It doesn't address the issue. You still don't understand the > underlying issue behind your remove_patch() placement. > > You have this: > > for d, f in files: > await m.reply_document(f"{d}/{f}", file_name=f) > await asyncio.sleep(1) > > utils.remove_patch(files) > > What happens if you follow that for loop, then m.reply_document() > throws an exception? It will throw an exception before executing the remove_patch(), but, the problem is not "if m.reply_document() throw an exception", but the files variable from the utils.create_template() is the problem if throw an exception, I think it will be throw a local variable error. Here is my improved remove_patch(): def remove_patch( tmp: Union[str, list] = None, platform: Platform = Platform.TELEGRAM ): if isinstance(tmp, str): return shutil.rmtree(tmp) # check if the tmp is None or an empty list or empty str # if they are empty or None then remove using glob UNIX # style path which is if empty will not throw an error if not bool(tmp): platform = platform.TELEGRAM.value store_dir = os.getenv("STORAGE_DIR", "storage") for d in glob.glob(f"{platform}/{store_dir}/*/"): shutil.rmtree(d) return for d,_ in tmp: shutil.rmtree(d) > There are 2 possible solutions, either using a "try with finally" > statement, or using a destructor wrapped in a class. I will do it if this PATCH series is done and will do it in logger PATCH series later. I wait for sir Ammar create the v2 of this series and pull it after this PATCH series is applied to the master branch. ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v1 2/2] daemon: telegram: Handle MySQL error 2022-10-28 18:52 ` Muhammad Rizki @ 2022-10-28 19:08 ` Alviro Iskandar Setiawan 2022-10-28 19:15 ` Muhammad Rizki 2022-10-28 19:16 ` Ammar Faizi 1 sibling, 1 reply; 42+ messages in thread From: Alviro Iskandar Setiawan @ 2022-10-28 19:08 UTC (permalink / raw) To: Muhammad Rizki; +Cc: Ammar Faizi, GNU/Weeb Mailing List On Sat, Oct 29, 2022 at 1:52 AM Muhammad Rizki wrote: > It will throw an exception before executing the remove_patch(), but, the > problem is not "if m.reply_document() throw an exception", but the files > variable from the utils.create_template() is the problem if throw an > exception, I think it will be throw a local variable error. "if m.reply_document() throws an exception" is a REAL PROBLEM. - m.reply_document() performs a network I/O. Therefore it may FAIL. - send_patch_email() performs a network I/O. Therefore it may FAIL. - send_text_email() performs a network I/O. Therefore it may FAIL. - MySQL queries perform a network I/O. Therefore they may FAIL. - whatever... If one of them fails, remove_patch() *is not executed*. > Here is my improved remove_patch(): > > > def remove_patch( > tmp: Union[str, list] = None, > platform: Platform = Platform.TELEGRAM > ): > if isinstance(tmp, str): > return shutil.rmtree(tmp) > > # check if the tmp is None or an empty list or empty str > # if they are empty or None then remove using glob UNIX > # style path which is if empty will not throw an error > if not bool(tmp): > platform = platform.TELEGRAM.value > store_dir = os.getenv("STORAGE_DIR", "storage") > for d in glob.glob(f"{platform}/{store_dir}/*/"): > shutil.rmtree(d) > return This is insane. That listener is running asynchronously, and you delete all temporary files without holding any mutex. At that time, another async job *may still be using* the files you batch-delete in this code. ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v1 2/2] daemon: telegram: Handle MySQL error 2022-10-28 19:08 ` Alviro Iskandar Setiawan @ 2022-10-28 19:15 ` Muhammad Rizki 2022-10-28 19:29 ` Alviro Iskandar Setiawan 0 siblings, 1 reply; 42+ messages in thread From: Muhammad Rizki @ 2022-10-28 19:15 UTC (permalink / raw) To: Alviro Iskandar Setiawan; +Cc: Ammar Faizi, GNU/Weeb Mailing List On 29/10/22 02.08, Alviro Iskandar Setiawan wrote: > >> Here is my improved remove_patch(): >> >> >> def remove_patch( >> tmp: Union[str, list] = None, >> platform: Platform = Platform.TELEGRAM >> ): >> if isinstance(tmp, str): >> return shutil.rmtree(tmp) >> >> # check if the tmp is None or an empty list or empty str >> # if they are empty or None then remove using glob UNIX >> # style path which is if empty will not throw an error >> if not bool(tmp): >> platform = platform.TELEGRAM.value >> store_dir = os.getenv("STORAGE_DIR", "storage") >> for d in glob.glob(f"{platform}/{store_dir}/*/"): >> shutil.rmtree(d) >> return > > This is insane. That listener is running asynchronously, and you > delete all temporary files without holding any mutex. At that time, > another async job *may still be using* the files you batch-delete in > this code. Sorry, you mention remove_patch() which function from? Telegram bot listener __send_mail() ? if yes, that function is already handled by mutex, see it on telegram/mailer/listener.py of __handle_mail(), in line 77 it is already hold. ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v1 2/2] daemon: telegram: Handle MySQL error 2022-10-28 19:15 ` Muhammad Rizki @ 2022-10-28 19:29 ` Alviro Iskandar Setiawan 2022-10-28 19:34 ` Ammar Faizi 2022-10-28 19:39 ` Muhammad Rizki 0 siblings, 2 replies; 42+ messages in thread From: Alviro Iskandar Setiawan @ 2022-10-28 19:29 UTC (permalink / raw) To: Muhammad Rizki; +Cc: Ammar Faizi, GNU/Weeb Mailing List On Sat, Oct 29, 2022 at 2:15 AM Muhammad Rizki <[email protected]> wrote: > Sorry, you mention remove_patch() which function from? Telegram bot > listener __send_mail() ? if yes, that function is already handled by > mutex, see it on telegram/mailer/listener.py of __handle_mail(), in line > 77 it is already hold. Is this function only called from one place? If that's a guarantee, I'm fine with that. But why delete all files, it's the caller's problem that it doesn't call the function properly. ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v1 2/2] daemon: telegram: Handle MySQL error 2022-10-28 19:29 ` Alviro Iskandar Setiawan @ 2022-10-28 19:34 ` Ammar Faizi 2022-10-28 19:36 ` Ammar Faizi 2022-10-28 19:39 ` Muhammad Rizki 1 sibling, 1 reply; 42+ messages in thread From: Ammar Faizi @ 2022-10-28 19:34 UTC (permalink / raw) To: Alviro Iskandar Setiawan, Muhammad Rizki; +Cc: GNU/Weeb Mailing List On 10/29/22 2:29 AM, Alviro Iskandar Setiawan wrote: > On Sat, Oct 29, 2022 at 2:15 AM Muhammad Rizki <[email protected]> wrote: >> Sorry, you mention remove_patch() which function from? Telegram bot >> listener __send_mail() ? if yes, that function is already handled by >> mutex, see it on telegram/mailer/listener.py of __handle_mail(), in line >> 77 it is already hold. > > Is this function only called from one place? > If that's a guarantee, I'm fine with that. But why delete all files, > it's the caller's problem that it doesn't call the function properly. It's also called from daemon/telegram/packages/client.py. That place doesn't hold the mutex anyway. -- Ammar Faizi ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v1 2/2] daemon: telegram: Handle MySQL error 2022-10-28 19:34 ` Ammar Faizi @ 2022-10-28 19:36 ` Ammar Faizi 2022-10-28 19:38 ` Ammar Faizi 0 siblings, 1 reply; 42+ messages in thread From: Ammar Faizi @ 2022-10-28 19:36 UTC (permalink / raw) To: Alviro Iskandar Setiawan, Muhammad Rizki; +Cc: GNU/Weeb Mailing List On 10/29/22 2:34 AM, Ammar Faizi wrote: > On 10/29/22 2:29 AM, Alviro Iskandar Setiawan wrote: >> On Sat, Oct 29, 2022 at 2:15 AM Muhammad Rizki <[email protected]> wrote: >>> Sorry, you mention remove_patch() which function from? Telegram bot >>> listener __send_mail() ? if yes, that function is already handled by >>> mutex, see it on telegram/mailer/listener.py of __handle_mail(), in line >>> 77 it is already hold. >> >> Is this function only called from one place? >> If that's a guarantee, I'm fine with that. But why delete all files, >> it's the caller's problem that it doesn't call the function properly. > > It's also called from daemon/telegram/packages/client.py. > > That place doesn't hold the mutex anyway. Correction, that place may hold or may not hold the mutex, depending where they are called. Another place that calls the remove_patch() is daemon/telegram/packages/plugins/commands/scrape.py This one always doesn't hold the mutex. -- Ammar Faizi ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v1 2/2] daemon: telegram: Handle MySQL error 2022-10-28 19:36 ` Ammar Faizi @ 2022-10-28 19:38 ` Ammar Faizi 2022-10-28 19:44 ` Muhammad Rizki 0 siblings, 1 reply; 42+ messages in thread From: Ammar Faizi @ 2022-10-28 19:38 UTC (permalink / raw) To: Alviro Iskandar Setiawan, Muhammad Rizki; +Cc: GNU/Weeb Mailing List On 10/29/22 2:36 AM, Ammar Faizi wrote: > On 10/29/22 2:34 AM, Ammar Faizi wrote: >> On 10/29/22 2:29 AM, Alviro Iskandar Setiawan wrote: >>> On Sat, Oct 29, 2022 at 2:15 AM Muhammad Rizki <[email protected]> wrote: >>>> Sorry, you mention remove_patch() which function from? Telegram bot >>>> listener __send_mail() ? if yes, that function is already handled by >>>> mutex, see it on telegram/mailer/listener.py of __handle_mail(), in line >>>> 77 it is already hold. >>> >>> Is this function only called from one place? >>> If that's a guarantee, I'm fine with that. But why delete all files, >>> it's the caller's problem that it doesn't call the function properly. >> >> It's also called from daemon/telegram/packages/client.py. >> >> That place doesn't hold the mutex anyway. > > Correction, that place may hold or may not hold the mutex, depending > where they are called. Another place that calls the remove_patch() is > > daemon/telegram/packages/plugins/commands/scrape.py > > This one always doesn't hold the mutex. I think deleting all files doesn't make so much sense to me. Just delete the specific created file only. Why you can't do that? Rizki, any comment? -- Ammar Faizi ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v1 2/2] daemon: telegram: Handle MySQL error 2022-10-28 19:38 ` Ammar Faizi @ 2022-10-28 19:44 ` Muhammad Rizki 0 siblings, 0 replies; 42+ messages in thread From: Muhammad Rizki @ 2022-10-28 19:44 UTC (permalink / raw) To: Ammar Faizi, Alviro Iskandar Setiawan; +Cc: GNU/Weeb Mailing List On 29/10/22 02.38, Ammar Faizi wrote: > > Just delete the specific created file only. Why you can't do that? > Rizki, any comment? > I would like to research it first later, I will return the generated temp dir and `return` it if after the gen_temp() is executed will throw an exception. ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v1 2/2] daemon: telegram: Handle MySQL error 2022-10-28 19:29 ` Alviro Iskandar Setiawan 2022-10-28 19:34 ` Ammar Faizi @ 2022-10-28 19:39 ` Muhammad Rizki 2022-10-28 19:44 ` Alviro Iskandar Setiawan 1 sibling, 1 reply; 42+ messages in thread From: Muhammad Rizki @ 2022-10-28 19:39 UTC (permalink / raw) To: Alviro Iskandar Setiawan; +Cc: Ammar Faizi, GNU/Weeb Mailing List On 29/10/22 02.29, Alviro Iskandar Setiawan wrote: > On Sat, Oct 29, 2022 at 2:15 AM Muhammad Rizki <[email protected]> wrote: >> Sorry, you mention remove_patch() which function from? Telegram bot >> listener __send_mail() ? if yes, that function is already handled by >> mutex, see it on telegram/mailer/listener.py of __handle_mail(), in line >> 77 it is already hold. > > Is this function only called from one place? Which function? if __send_mail() yeah it only once called, the remove_patch() doesn't called once. ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v1 2/2] daemon: telegram: Handle MySQL error 2022-10-28 19:39 ` Muhammad Rizki @ 2022-10-28 19:44 ` Alviro Iskandar Setiawan 2022-10-28 19:46 ` Muhammad Rizki 0 siblings, 1 reply; 42+ messages in thread From: Alviro Iskandar Setiawan @ 2022-10-28 19:44 UTC (permalink / raw) To: Muhammad Rizki; +Cc: Ammar Faizi, GNU/Weeb Mailing List On Sat, Oct 29, 2022 at 2:39 AM Muhammad Rizki wrote: > Which function? remove_patch(). > if __send_mail() yeah it only once called, the > remove_patch() doesn't called once. That's literally a hole. The problem is remove_patch() may be called asynchronously together with a job that doesn't hold the mutex. - job_a is still using the file - job_b doesn't know anything about job_a, but it removes all files blindly. job_b doesn't know anything about mutex in job_a. -- Viro ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v1 2/2] daemon: telegram: Handle MySQL error 2022-10-28 19:44 ` Alviro Iskandar Setiawan @ 2022-10-28 19:46 ` Muhammad Rizki 2022-10-28 19:53 ` Alviro Iskandar Setiawan 0 siblings, 1 reply; 42+ messages in thread From: Muhammad Rizki @ 2022-10-28 19:46 UTC (permalink / raw) To: Alviro Iskandar Setiawan; +Cc: Ammar Faizi, GNU/Weeb Mailing List On 29/10/22 02.44, Alviro Iskandar Setiawan wrote: > On Sat, Oct 29, 2022 at 2:39 AM Muhammad Rizki wrote: >> Which function? > > remove_patch(). > >> if __send_mail() yeah it only once called, the >> remove_patch() doesn't called once. > > That's literally a hole. The problem is remove_patch() may be called > asynchronously together with a job that doesn't hold the mutex. > > - job_a is still using the file > - job_b doesn't know anything about job_a, but it removes all files > blindly. job_b doesn't know anything about mutex in job_a. > > -- Viro So, you are talking about calling a remote_patch() all together if doesn't hold it with mutex? ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v1 2/2] daemon: telegram: Handle MySQL error 2022-10-28 19:46 ` Muhammad Rizki @ 2022-10-28 19:53 ` Alviro Iskandar Setiawan 0 siblings, 0 replies; 42+ messages in thread From: Alviro Iskandar Setiawan @ 2022-10-28 19:53 UTC (permalink / raw) To: Muhammad Rizki; +Cc: Ammar Faizi, GNU/Weeb Mailing List On Sat, Oct 29, 2022 at 2:46 AM Muhammad Rizki wrote: > So, you are talking about calling a remote_patch() all together if > doesn't hold it with mutex? Yes, exactly. What else did you expect? -- Viro ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v1 2/2] daemon: telegram: Handle MySQL error 2022-10-28 18:52 ` Muhammad Rizki 2022-10-28 19:08 ` Alviro Iskandar Setiawan @ 2022-10-28 19:16 ` Ammar Faizi 2022-10-28 19:19 ` Muhammad Rizki 1 sibling, 1 reply; 42+ messages in thread From: Ammar Faizi @ 2022-10-28 19:16 UTC (permalink / raw) To: Muhammad Rizki, Alviro Iskandar Setiawan; +Cc: GNU/Weeb Mailing List On 10/29/22 1:52 AM, Muhammad Rizki wrote: > def remove_patch( > tmp: Union[str, list] = None, > platform: Platform = Platform.TELEGRAM > ): > if isinstance(tmp, str): > return shutil.rmtree(tmp) > > # check if the tmp is None or an empty list or empty str isinstance("", str) is True, so this won't be reached if you pass an empty string. It'll early return at "if isinstance("", str)". I think it's the caller responsibility not to pass an invalid file name. If the caller passes an invalid @tmp value, then you code it wrong. -- Ammar Faizi ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v1 2/2] daemon: telegram: Handle MySQL error 2022-10-28 19:16 ` Ammar Faizi @ 2022-10-28 19:19 ` Muhammad Rizki 2022-10-28 19:19 ` Ammar Faizi 0 siblings, 1 reply; 42+ messages in thread From: Muhammad Rizki @ 2022-10-28 19:19 UTC (permalink / raw) To: Ammar Faizi, Alviro Iskandar Setiawan; +Cc: GNU/Weeb Mailing List On 29/10/22 02.16, Ammar Faizi wrote: > On 10/29/22 1:52 AM, Muhammad Rizki wrote: >> def remove_patch( >> tmp: Union[str, list] = None, >> platform: Platform = Platform.TELEGRAM >> ): >> if isinstance(tmp, str): >> return shutil.rmtree(tmp) >> >> # check if the tmp is None or an empty list or empty str > > isinstance("", str) is True, so this won't be reached if you pass an > empty string. It'll early return at "if isinstance("", str)". > > I think it's the caller responsibility not to pass an invalid file > name. If the caller passes an invalid @tmp value, then you code it > wrong. > So, just swap the `if tmp is None` to the first place, then ` if isinstance()`? ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v1 2/2] daemon: telegram: Handle MySQL error 2022-10-28 19:19 ` Muhammad Rizki @ 2022-10-28 19:19 ` Ammar Faizi 2022-10-28 19:22 ` Muhammad Rizki 0 siblings, 1 reply; 42+ messages in thread From: Ammar Faizi @ 2022-10-28 19:19 UTC (permalink / raw) To: Muhammad Rizki, Alviro Iskandar Setiawan; +Cc: GNU/Weeb Mailing List On 10/29/22 2:19 AM, Muhammad Rizki wrote: > So, just swap the `if tmp is None` to the first place, then ` if isinstance()`? Why would anyone ever call remove_patch(None)? -- Ammar Faizi ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v1 2/2] daemon: telegram: Handle MySQL error 2022-10-28 19:19 ` Ammar Faizi @ 2022-10-28 19:22 ` Muhammad Rizki 2022-10-28 19:33 ` Ammar Faizi 0 siblings, 1 reply; 42+ messages in thread From: Muhammad Rizki @ 2022-10-28 19:22 UTC (permalink / raw) To: Ammar Faizi, Alviro Iskandar Setiawan; +Cc: GNU/Weeb Mailing List On 29/10/22 02.19, Ammar Faizi wrote: > On 10/29/22 2:19 AM, Muhammad Rizki wrote: >> So, just swap the `if tmp is None` to the first place, then ` if >> isinstance()`? > > Why would anyone ever call remove_patch(None)? > I create that statement because: ``` # @__must_hold(self.mutexes.send_to_tg) async def __send_mail(self, url, mail, tg_chat_id): email_msg_id = utils.get_email_msg_id(mail) if not email_msg_id: # # It doesn't have a Message-Id. # A malformed email. Skip! # return False email_id = self.__mail_id_from_db(email_msg_id, tg_chat_id) if not email_id: # # Email has already been sent to Telegram. # Skip! # return False try: text, files, is_patch = utils.create_template(mail, Platform.TELEGRAM) except: # ensure the created tmp dir is removed utils.remove_patch() exc_str = utils.catch_err() self.client.logger.warning(exc_str) await self.client.send_log_file(url) # return, to avoid local variable error return reply_to = self.get_reply(mail, tg_chat_id) url = str(re.sub(r"/raw$", "", url)) if is_patch: ... ``` ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v1 2/2] daemon: telegram: Handle MySQL error 2022-10-28 19:22 ` Muhammad Rizki @ 2022-10-28 19:33 ` Ammar Faizi 2022-10-28 19:38 ` Muhammad Rizki 0 siblings, 1 reply; 42+ messages in thread From: Ammar Faizi @ 2022-10-28 19:33 UTC (permalink / raw) To: Muhammad Rizki, Alviro Iskandar Setiawan; +Cc: GNU/Weeb Mailing List On 10/29/22 2:22 AM, Muhammad Rizki wrote: > I create that statement because: [...] > try: > text, files, is_patch = utils.create_template(mail, Platform.TELEGRAM) > except: > > # ensure the created tmp dir is removed > utils.remove_patch() > > exc_str = utils.catch_err() > self.client.logger.warning(exc_str) > await self.client.send_log_file(url) > > # return, to avoid local variable error > return This is ugly, will you do the: exc_str = utils.catch_err() self.client.logger.warning(exc_str) await self.client.send_log_file(url) all over the place when error may happen? -- Ammar Faizi ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v1 2/2] daemon: telegram: Handle MySQL error 2022-10-28 19:33 ` Ammar Faizi @ 2022-10-28 19:38 ` Muhammad Rizki 0 siblings, 0 replies; 42+ messages in thread From: Muhammad Rizki @ 2022-10-28 19:38 UTC (permalink / raw) To: Ammar Faizi, Alviro Iskandar Setiawan; +Cc: GNU/Weeb Mailing List On 29/10/22 02.33, Ammar Faizi wrote: > On 10/29/22 2:22 AM, Muhammad Rizki wrote: >> I create that statement because: > [...] >> try: >> text, files, is_patch = utils.create_template(mail, >> Platform.TELEGRAM) >> except: >> >> # ensure the created tmp dir is removed >> utils.remove_patch() >> >> exc_str = utils.catch_err() >> self.client.logger.warning(exc_str) >> await self.client.send_log_file(url) >> >> # return, to avoid local variable error >> return > > This is ugly, will you do the: > > exc_str = utils.catch_err() > self.client.logger.warning(exc_str) > await self.client.send_log_file(url) > > all over the place when error may happen? > IDK, this is still debugging method. I don't have any idea when "all over the place when error may happen". ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v1 2/2] daemon: telegram: Handle MySQL error 2022-10-28 16:29 ` Ammar Faizi 2022-10-28 16:46 ` Alviro Iskandar Setiawan @ 2022-10-28 17:02 ` Muhammad Rizki 2022-10-28 17:19 ` Ammar Faizi 1 sibling, 1 reply; 42+ messages in thread From: Muhammad Rizki @ 2022-10-28 17:02 UTC (permalink / raw) To: Ammar Faizi, Alviro Iskandar Setiawan; +Cc: GNU/Weeb Mailing List On 28/10/22 23.29, Ammar Faizi wrote: > So yes, having autocommit = True for everything is absolutely the wrong So, you want me to remove the autocommit = True and manual commit each we want to insert and delete? ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v1 2/2] daemon: telegram: Handle MySQL error 2022-10-28 17:02 ` Muhammad Rizki @ 2022-10-28 17:19 ` Ammar Faizi 2022-10-28 18:15 ` Muhammad Rizki 0 siblings, 1 reply; 42+ messages in thread From: Ammar Faizi @ 2022-10-28 17:19 UTC (permalink / raw) To: Muhammad Rizki; +Cc: Alviro Iskandar Setiawan, GNU/Weeb Mailing List On Sat, Oct 29, 2022 at 12:02 AM Muhammad Rizki <[email protected]> wrote: > So, you want me to remove the autocommit = True and manual commit each > we want to insert and delete? Yes, but note that, not only manual commit, you have to make sure the operation is atomic. For example: begin_transaction() insert_a -> this is a success insert_b -> this is a success insert_c -> this is a fail, goto rollback commit() -> this doesn't happen rollback: rollback() // insert_a and insert_b are undone. Those 3 inserts are atomic and consistent. If one fails, all operations must also fail. No half-insert, no unisolated select, no missing cleanup, no temp file not deleted due to error in the middle operation, etc. -- Ammar Faizi ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v1 2/2] daemon: telegram: Handle MySQL error 2022-10-28 17:19 ` Ammar Faizi @ 2022-10-28 18:15 ` Muhammad Rizki 2022-10-28 18:18 ` Muhammad Rizki 2022-10-28 19:46 ` Ammar Faizi 0 siblings, 2 replies; 42+ messages in thread From: Muhammad Rizki @ 2022-10-28 18:15 UTC (permalink / raw) To: Ammar Faizi; +Cc: Alviro Iskandar Setiawan, GNU/Weeb Mailing List On 29/10/22 00.19, Ammar Faizi wrote: > > For example: > begin_transaction() > insert_a -> this is a success > insert_b -> this is a success > insert_c -> this is a fail, goto rollback > commit() -> this doesn't happen > > rollback: > rollback() // insert_a and insert_b are undone. This pseudo-code seems like C style. Anyway, the rollback() is from the MySQL function? > > Those 3 inserts are atomic and consistent. If one fails, all > operations must also fail. No half-insert, no unisolated select, no > missing cleanup, no temp file not deleted due to error in the middle > operation, etc. > This is a little bit confused me to implement it. 🤔 ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v1 2/2] daemon: telegram: Handle MySQL error 2022-10-28 18:15 ` Muhammad Rizki @ 2022-10-28 18:18 ` Muhammad Rizki 2022-10-28 19:49 ` Ammar Faizi 2022-10-28 19:46 ` Ammar Faizi 1 sibling, 1 reply; 42+ messages in thread From: Muhammad Rizki @ 2022-10-28 18:18 UTC (permalink / raw) To: Ammar Faizi; +Cc: Alviro Iskandar Setiawan, GNU/Weeb Mailing List On 29/10/22 01.15, Muhammad Rizki wrote: > On 29/10/22 00.19, Ammar Faizi wrote: >> >> For example: >> begin_transaction() >> insert_a -> this is a success >> insert_b -> this is a success >> insert_c -> this is a fail, goto rollback >> commit() -> this doesn't happen >> >> rollback: >> rollback() // insert_a and insert_b are undone. > > This pseudo-code seems like C style. Anyway, the rollback() is from the > MySQL function? > >> >> Those 3 inserts are atomic and consistent. If one fails, all >> operations must also fail. No half-insert, no unisolated select, no >> missing cleanup, no temp file not deleted due to error in the middle >> operation, etc. >> > > This is a little bit confused me to implement it. 🤔 Anyway, I've ever wanted to refactor the DB using the SQLAlchemy and want to implement the clean-arch method too, but, IDK when to do that, maybe I could just re-create the daemon in my local and make the version 2 of it. ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v1 2/2] daemon: telegram: Handle MySQL error 2022-10-28 18:18 ` Muhammad Rizki @ 2022-10-28 19:49 ` Ammar Faizi 2022-10-28 19:55 ` Muhammad Rizki 0 siblings, 1 reply; 42+ messages in thread From: Ammar Faizi @ 2022-10-28 19:49 UTC (permalink / raw) To: Muhammad Rizki; +Cc: Alviro Iskandar Setiawan, GNU/Weeb Mailing List On 10/29/22 1:18 AM, Muhammad Rizki wrote: > Anyway, I've ever wanted to refactor the DB using the SQLAlchemy and want to implement the clean-arch method too, but, IDK when to do that, maybe I could just re-create the daemon in my local and make the version 2 of it. Not now, maybe next year. For now, let's learn about ACID and how to handle error properly first. -- Ammar Faizi ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v1 2/2] daemon: telegram: Handle MySQL error 2022-10-28 19:49 ` Ammar Faizi @ 2022-10-28 19:55 ` Muhammad Rizki 0 siblings, 0 replies; 42+ messages in thread From: Muhammad Rizki @ 2022-10-28 19:55 UTC (permalink / raw) To: Ammar Faizi; +Cc: Alviro Iskandar Setiawan, GNU/Weeb Mailing List On 29/10/22 02.49, Ammar Faizi wrote: > On 10/29/22 1:18 AM, Muhammad Rizki wrote: >> Anyway, I've ever wanted to refactor the DB using the SQLAlchemy and >> want to implement the clean-arch method too, but, IDK when to do that, >> maybe I could just re-create the daemon in my local and make the >> version 2 of it. > > Not now, maybe next year. > > For now, let's learn about ACID and how to handle error > properly first. > Okay, just teach me how to do it, I really never do the ACID method, so yeah, please understand a bit. ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v1 2/2] daemon: telegram: Handle MySQL error 2022-10-28 18:15 ` Muhammad Rizki 2022-10-28 18:18 ` Muhammad Rizki @ 2022-10-28 19:46 ` Ammar Faizi 1 sibling, 0 replies; 42+ messages in thread From: Ammar Faizi @ 2022-10-28 19:46 UTC (permalink / raw) To: Muhammad Rizki; +Cc: Alviro Iskandar Setiawan, GNU/Weeb Mailing List On 10/29/22 1:15 AM, Muhammad Rizki wrote: > Anyway, the rollback() is from the MySQL function? Yes. Just a quick google. Please read: https://pynative.com/python-mysql-transaction-management-using-commit-rollback/ -- Ammar Faizi ^ permalink raw reply [flat|nested] 42+ messages in thread
end of thread, other threads:[~2022-10-28 19:55 UTC | newest] Thread overview: 42+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2022-10-27 15:08 [PATCH v1 0/2] Automatic recovery from a MySQL restart Ammar Faizi 2022-10-27 15:08 ` [PATCH v1 1/2] daemon: telegram: db: Allow the caller to reconnect Ammar Faizi 2022-10-27 16:52 ` Muhammad Rizki 2022-10-27 16:54 ` Muhammad Rizki 2022-10-28 5:48 ` Ammar Faizi 2022-10-27 15:08 ` [PATCH v1 2/2] daemon: telegram: Handle MySQL error Ammar Faizi 2022-10-28 9:21 ` Alviro Iskandar Setiawan 2022-10-28 9:28 ` Ammar Faizi 2022-10-28 9:24 ` Alviro Iskandar Setiawan 2022-10-28 9:32 ` Ammar Faizi 2022-10-28 9:40 ` Alviro Iskandar Setiawan 2022-10-28 9:43 ` Ammar Faizi 2022-10-28 16:29 ` Ammar Faizi 2022-10-28 16:46 ` Alviro Iskandar Setiawan 2022-10-28 16:54 ` Ammar Faizi 2022-10-28 18:10 ` Muhammad Rizki 2022-10-28 18:26 ` Alviro Iskandar Setiawan 2022-10-28 18:52 ` Muhammad Rizki 2022-10-28 19:08 ` Alviro Iskandar Setiawan 2022-10-28 19:15 ` Muhammad Rizki 2022-10-28 19:29 ` Alviro Iskandar Setiawan 2022-10-28 19:34 ` Ammar Faizi 2022-10-28 19:36 ` Ammar Faizi 2022-10-28 19:38 ` Ammar Faizi 2022-10-28 19:44 ` Muhammad Rizki 2022-10-28 19:39 ` Muhammad Rizki 2022-10-28 19:44 ` Alviro Iskandar Setiawan 2022-10-28 19:46 ` Muhammad Rizki 2022-10-28 19:53 ` Alviro Iskandar Setiawan 2022-10-28 19:16 ` Ammar Faizi 2022-10-28 19:19 ` Muhammad Rizki 2022-10-28 19:19 ` Ammar Faizi 2022-10-28 19:22 ` Muhammad Rizki 2022-10-28 19:33 ` Ammar Faizi 2022-10-28 19:38 ` Muhammad Rizki 2022-10-28 17:02 ` Muhammad Rizki 2022-10-28 17:19 ` Ammar Faizi 2022-10-28 18:15 ` Muhammad Rizki 2022-10-28 18:18 ` Muhammad Rizki 2022-10-28 19:49 ` Ammar Faizi 2022-10-28 19:55 ` Muhammad Rizki 2022-10-28 19:46 ` Ammar Faizi
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox