Files
purple-electrumwallet/electrum/sql_db.py
T

77 lines
2.4 KiB
Python
Raw Normal View History

2019-03-06 09:56:22 +01:00
import os
import queue
import threading
2019-06-18 13:49:31 +02:00
import asyncio
2019-06-27 09:03:34 +02:00
import sqlite3
2019-03-06 09:56:22 +01:00
from .logging import Logger
from .util import test_read_write_permissions
2019-03-06 09:56:22 +01:00
def sql(func):
2021-09-27 10:31:44 +02:00
"""wrapper for sql methods
returns an awaitable asyncio.Future
"""
def wrapper(self: 'SqlDB', *args, **kwargs):
2019-03-06 09:56:22 +01:00
assert threading.currentThread() != self.sql_thread
2021-03-15 20:37:49 +01:00
f = self.asyncio_loop.create_future()
2019-03-06 09:56:22 +01:00
self.db_requests.put((f, func, args, kwargs))
2019-06-18 13:49:31 +02:00
return f
2019-03-06 09:56:22 +01:00
return wrapper
class SqlDB(Logger):
2021-09-27 10:31:44 +02:00
def __init__(self, asyncio_loop: asyncio.BaseEventLoop, path, commit_interval=None):
Logger.__init__(self)
2020-04-16 10:58:40 +02:00
self.asyncio_loop = asyncio_loop
2020-11-11 11:03:31 +01:00
self.stopping = False
self.stopped_event = asyncio.Event()
2019-03-06 09:56:22 +01:00
self.path = path
test_read_write_permissions(path)
2019-06-18 13:49:31 +02:00
self.commit_interval = commit_interval
2019-03-06 09:56:22 +01:00
self.db_requests = queue.Queue()
self.sql_thread = threading.Thread(target=self.run_sql)
self.sql_thread.start()
2020-11-11 11:03:31 +01:00
def stop(self):
self.stopping = True
2020-05-13 15:13:09 +02:00
def filesize(self):
return os.stat(self.path).st_size
2019-03-06 09:56:22 +01:00
def run_sql(self):
2019-06-18 13:49:31 +02:00
self.logger.info("SQL thread started")
2019-06-27 09:03:34 +02:00
self.conn = sqlite3.connect(self.path)
self.logger.info("Creating database")
self.create_database()
2019-06-18 13:49:31 +02:00
i = 0
2020-11-11 11:03:31 +01:00
while not self.stopping and self.asyncio_loop.is_running():
2019-03-06 09:56:22 +01:00
try:
future, func, args, kwargs = self.db_requests.get(timeout=0.1)
except queue.Empty:
continue
try:
result = func(self, *args, **kwargs)
except BaseException as e:
self.asyncio_loop.call_soon_threadsafe(future.set_exception, e)
2019-03-06 09:56:22 +01:00
continue
2019-06-18 13:49:31 +02:00
if not future.cancelled():
self.asyncio_loop.call_soon_threadsafe(future.set_result, result)
2019-06-18 13:49:31 +02:00
# note: in sweepstore session.commit() is called inside
# the sql-decorated methods, so commiting to disk is awaited
if self.commit_interval:
i = (i + 1) % self.commit_interval
if i == 0:
2019-06-27 09:03:34 +02:00
self.conn.commit()
2019-03-06 09:56:22 +01:00
# write
2019-06-27 09:03:34 +02:00
self.conn.commit()
self.conn.close()
self.logger.info("SQL thread terminated")
self.asyncio_loop.call_soon_threadsafe(self.stopped_event.set)
def create_database(self):
raise NotImplementedError()