cic-eth: Make nonce separate task
This commit is contained in:
@@ -103,6 +103,9 @@ def status_str(v, bits_only=False):
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
if v == 0:
|
||||
return 'NONE'
|
||||
|
||||
for i in range(16):
|
||||
b = (1 << i)
|
||||
if (b & 0xffff) & v:
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
"""Nonce reservation
|
||||
|
||||
Revision ID: 3b693afd526a
|
||||
Revises: f738d9962fdf
|
||||
Create Date: 2021-03-05 07:09:50.898728
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '3b693afd526a'
|
||||
down_revision = 'f738d9962fdf'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.create_table(
|
||||
'nonce_task_reservation',
|
||||
sa.Column('id', sa.Integer, primary_key=True),
|
||||
sa.Column('nonce', sa.Integer, nullable=False),
|
||||
sa.Column('key', sa.String, nullable=False),
|
||||
sa.Column('date_created', sa.DateTime, nullable=False),
|
||||
)
|
||||
|
||||
|
||||
def downgrade():
|
||||
op.drop_table('nonce_task_reservation')
|
||||
@@ -0,0 +1,30 @@
|
||||
"""Nonce reservation
|
||||
|
||||
Revision ID: 3b693afd526a
|
||||
Revises: f738d9962fdf
|
||||
Create Date: 2021-03-05 07:09:50.898728
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '3b693afd526a'
|
||||
down_revision = 'f738d9962fdf'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.create_table(
|
||||
'nonce_task_reservation',
|
||||
sa.Column('id', sa.Integer, primary_key=True),
|
||||
sa.Column('nonce', sa.Integer, nullable=False),
|
||||
sa.Column('key', sa.String, nullable=False),
|
||||
sa.Column('date_created', sa.DateTime, nullable=False),
|
||||
)
|
||||
|
||||
|
||||
def downgrade():
|
||||
op.drop_table('nonce_task_reservation')
|
||||
@@ -54,7 +54,7 @@ class SessionBase(Model):
|
||||
|
||||
|
||||
@staticmethod
|
||||
def connect(dsn, pool_size=8, debug=False):
|
||||
def connect(dsn, pool_size=16, debug=False):
|
||||
"""Create new database connection engine and connect to database backend.
|
||||
|
||||
:param dsn: DSN string defining connection.
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
# standard imports
|
||||
import logging
|
||||
import datetime
|
||||
|
||||
# third-party imports
|
||||
from sqlalchemy import Column, String, Integer
|
||||
from sqlalchemy import Column, String, Integer, DateTime
|
||||
|
||||
# local imports
|
||||
from .base import SessionBase
|
||||
from cic_eth.error import (
|
||||
InitializationError,
|
||||
IntegrityError,
|
||||
)
|
||||
|
||||
logg = logging.getLogger()
|
||||
|
||||
@@ -37,23 +42,43 @@ class Nonce(SessionBase):
|
||||
|
||||
|
||||
@staticmethod
|
||||
def __get(session, address):
|
||||
r = session.execute("SELECT nonce FROM nonce WHERE address_hex = '{}'".format(address))
|
||||
def __get(conn, address):
|
||||
r = conn.execute("SELECT nonce FROM nonce WHERE address_hex = '{}'".format(address))
|
||||
nonce = r.fetchone()
|
||||
session.flush()
|
||||
if nonce == None:
|
||||
return None
|
||||
return nonce[0]
|
||||
|
||||
|
||||
@staticmethod
|
||||
def __set(session, address, nonce):
|
||||
session.execute("UPDATE nonce set nonce = {} WHERE address_hex = '{}'".format(nonce, address))
|
||||
session.flush()
|
||||
def __set(conn, address, nonce):
|
||||
conn.execute("UPDATE nonce set nonce = {} WHERE address_hex = '{}'".format(nonce, address))
|
||||
|
||||
|
||||
@staticmethod
|
||||
def next(address, initial_if_not_exists=0, session=None):
|
||||
def __init(conn, address, nonce):
|
||||
conn.execute("INSERT INTO nonce (nonce, address_hex) VALUES ({}, '{}')".format(nonce, address))
|
||||
|
||||
|
||||
@staticmethod
|
||||
def init(address, nonce=0, session=None):
|
||||
session = SessionBase.bind_session(session)
|
||||
|
||||
q = session.query(Nonce)
|
||||
q = q.filter(Nonce.address_hex==address)
|
||||
o = q.first()
|
||||
if o != None:
|
||||
session.flush()
|
||||
raise InitializationError('nonce on {} already exists ({})'.format(address, o.nonce))
|
||||
session.flush()
|
||||
Nonce.__init(session, address, nonce)
|
||||
|
||||
SessionBase.release_session(session)
|
||||
|
||||
|
||||
# TODO: Incrementing nonce MUST be done by separate tasks.
|
||||
@staticmethod
|
||||
def next(address, initial_if_not_exists=0):
|
||||
"""Generate next nonce for the given address.
|
||||
|
||||
If there is no previous nonce record for the address, the nonce may be initialized to a specified value, or 0 if no value has been given.
|
||||
@@ -65,32 +90,96 @@ class Nonce(SessionBase):
|
||||
:returns: Nonce
|
||||
:rtype: number
|
||||
"""
|
||||
session = SessionBase.bind_session(session)
|
||||
#session = SessionBase.bind_session(session)
|
||||
|
||||
SessionBase.release_session(session)
|
||||
|
||||
session.begin_nested()
|
||||
#conn = Nonce.engine.connect()
|
||||
#session.begin_nested()
|
||||
conn = Nonce.engine.connect()
|
||||
if Nonce.transactional:
|
||||
#session.execute('BEGIN')
|
||||
session.execute('LOCK TABLE nonce IN SHARE ROW EXCLUSIVE MODE')
|
||||
session.flush()
|
||||
nonce = Nonce.__get(session, address)
|
||||
conn.execute('BEGIN')
|
||||
conn.execute('LOCK TABLE nonce IN SHARE ROW EXCLUSIVE MODE')
|
||||
logg.debug('locking nonce table for address {}'.format(address))
|
||||
nonce = Nonce.__get(conn, address)
|
||||
logg.debug('get nonce {} for address {}'.format(nonce, address))
|
||||
if nonce == None:
|
||||
nonce = initial_if_not_exists
|
||||
session.execute("INSERT INTO nonce (nonce, address_hex) VALUES ({}, '{}')".format(nonce, address))
|
||||
session.flush()
|
||||
logg.debug('setting default nonce to {} for address {}'.format(nonce, address))
|
||||
Nonce.__set(session, address, nonce+1)
|
||||
#if Nonce.transactional:
|
||||
#session.execute('COMMIT')
|
||||
#session.execute('UNLOCK TABLE nonce')
|
||||
#conn.close()
|
||||
session.commit()
|
||||
# session.commit()
|
||||
Nonce.__init(conn, address, nonce)
|
||||
Nonce.__set(conn, address, nonce+1)
|
||||
if Nonce.transactional:
|
||||
conn.execute('COMMIT')
|
||||
logg.debug('unlocking nonce table for address {}'.format(address))
|
||||
conn.close()
|
||||
#session.commit()
|
||||
|
||||
SessionBase.release_session(session)
|
||||
#SessionBase.release_session(session)
|
||||
return nonce
|
||||
|
||||
|
||||
class NonceReservation(SessionBase):
|
||||
|
||||
__tablename__ = 'nonce_task_reservation'
|
||||
|
||||
nonce = Column(Integer)
|
||||
key = Column(String)
|
||||
date_created = Column(DateTime, default=datetime.datetime.utcnow)
|
||||
|
||||
|
||||
@staticmethod
|
||||
def peek(key, session=None):
|
||||
session = SessionBase.bind_session(session)
|
||||
|
||||
q = session.query(NonceReservation)
|
||||
q = q.filter(NonceReservation.key==key)
|
||||
o = q.first()
|
||||
|
||||
nonce = None
|
||||
if o != None:
|
||||
nonce = o.nonce
|
||||
|
||||
session.flush()
|
||||
|
||||
SessionBase.release_session(session)
|
||||
|
||||
return nonce
|
||||
|
||||
|
||||
@staticmethod
|
||||
def release(key, session=None):
|
||||
|
||||
session = SessionBase.bind_session(session)
|
||||
|
||||
nonce = NonceReservation.peek(key, session=session)
|
||||
|
||||
q = session.query(NonceReservation)
|
||||
q = q.filter(NonceReservation.key==key)
|
||||
o = q.first()
|
||||
|
||||
if o == None:
|
||||
raise IntegrityError('nonce for key {}'.format(nonce))
|
||||
SessionBase.release_session(session)
|
||||
|
||||
session.delete(o)
|
||||
session.flush()
|
||||
|
||||
SessionBase.release_session(session)
|
||||
|
||||
return nonce
|
||||
|
||||
|
||||
@staticmethod
|
||||
def next(address, key, session=None):
|
||||
session = SessionBase.bind_session(session)
|
||||
|
||||
if NonceReservation.peek(key, session) != None:
|
||||
raise IntegrityError('nonce for key {}'.format(key))
|
||||
|
||||
nonce = Nonce.next(address)
|
||||
|
||||
o = NonceReservation()
|
||||
o.nonce = nonce
|
||||
o.key = key
|
||||
session.add(o)
|
||||
|
||||
SessionBase.release_session(session)
|
||||
|
||||
return nonce
|
||||
|
||||
@@ -400,7 +400,7 @@ class Otx(SessionBase):
|
||||
raise TxStateChangeError('CANCEL cannot be set on an entry with FINAL state set ({})'.format(status_str(self.status)))
|
||||
|
||||
if confirmed:
|
||||
if not self.status & StatusBits.OBSOLETE:
|
||||
if self.status > 0 and not self.status & StatusBits.OBSOLETE:
|
||||
raise TxStateChangeError('CANCEL can only be set on an entry marked OBSOLETE ({})'.format(status_str(self.status)))
|
||||
self.__set_status(StatusEnum.CANCELLED, session)
|
||||
else:
|
||||
|
||||
@@ -143,7 +143,7 @@ class TxCache(SessionBase):
|
||||
self.block_number = block_number
|
||||
self.tx_index = tx_index
|
||||
# not automatically set in sqlite, it seems:
|
||||
self.date_created = datetime.datetime.now()
|
||||
self.date_created = datetime.datetime.utcnow()
|
||||
self.date_updated = self.date_created
|
||||
self.date_checked = self.date_created
|
||||
|
||||
|
||||
Reference in New Issue
Block a user