Skip to content

Commit c5a421a

Browse files
Add immune_tables config option (#68)
1 parent 4370521 commit c5a421a

File tree

7 files changed

+52
-103
lines changed

7 files changed

+52
-103
lines changed

poetry.lock

Lines changed: 4 additions & 37 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ packages = [
2323
[tool.poetry.dependencies]
2424
python = "^3.8"
2525
aiohttp = "^3.7.4"
26-
aiomysql = "^0.0.21"
2726
asyncpg = "0.23.0"
2827
datamodel-code-generator = "^0.11.1"
2928
"ruamel.yaml" = "^0.17.2"

src/dipdup/config.py

Lines changed: 9 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -52,29 +52,6 @@ def connection_string(self) -> str:
5252
return f'{self.kind}://{self.path}'
5353

5454

55-
@dataclass
56-
class MySQLDatabaseConfig:
57-
"""MySQL database connection config
58-
59-
:param host: Host
60-
:param port: Port
61-
:param user: User
62-
:param password: Password
63-
:param database: Database name
64-
"""
65-
66-
kind: Literal['mysql']
67-
host: str
68-
port: int
69-
user: str
70-
database: str
71-
password: str = ''
72-
73-
@property
74-
def connection_string(self) -> str:
75-
return f'{self.kind}://{self.user}:{self.password}@{self.host}:{self.port}/{self.database}'
76-
77-
7855
@dataclass
7956
class PostgresDatabaseConfig:
8057
"""Postgres database connection config
@@ -85,6 +62,7 @@ class PostgresDatabaseConfig:
8562
:param password: Password
8663
:param database: Database name
8764
:param schema_name: Schema name
65+
:param immune_tables: List of tables to preserve during reindexing
8866
"""
8967

9068
kind: Literal['postgres']
@@ -94,11 +72,18 @@ class PostgresDatabaseConfig:
9472
database: str
9573
schema_name: str = 'public'
9674
password: str = ''
75+
immune_tables: Optional[List[str]] = None
9776

9877
@property
9978
def connection_string(self) -> str:
10079
return f'{self.kind}://{self.user}:{self.password}@{self.host}:{self.port}/{self.database}?schema={self.schema_name}'
10180

81+
@validator('immune_tables')
82+
def valid_immune_tables(cls, v):
83+
if v and 'dipdup_state' in v:
84+
raise ConfigurationError('`dipdup_state` table can\'t be immune')
85+
return v
86+
10287

10388
@dataclass
10489
class ContractConfig:
@@ -635,7 +620,6 @@ class DipDupConfig:
635620
:param templates: Mapping of template aliases and index templates
636621
:param database: Database config
637622
:param hasura: Hasura config
638-
:param configuration: Dynamic configuration parameters
639623
"""
640624

641625
spec_version: str
@@ -644,7 +628,7 @@ class DipDupConfig:
644628
contracts: Dict[str, ContractConfig] = Field(default_factory=dict)
645629
indexes: Dict[str, IndexConfigT] = Field(default_factory=dict)
646630
templates: Optional[Dict[str, IndexConfigTemplateT]] = None
647-
database: Union[SqliteDatabaseConfig, MySQLDatabaseConfig, PostgresDatabaseConfig] = SqliteDatabaseConfig(kind='sqlite')
631+
database: Union[SqliteDatabaseConfig, PostgresDatabaseConfig] = SqliteDatabaseConfig(kind='sqlite')
648632
hasura: Optional[HasuraConfig] = None
649633

650634
def __post_init_post_parse__(self):

src/dipdup/context.py

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
1+
import os
2+
import sys
13
from typing import Any, Dict, Optional
24

3-
from dipdup.config import ContractConfig, DipDupConfig, StaticTemplateConfig
5+
from tortoise import Tortoise
6+
from tortoise.transactions import in_transaction
7+
8+
from dipdup.config import ContractConfig, DipDupConfig, PostgresDatabaseConfig, StaticTemplateConfig
49
from dipdup.datasources import DatasourceT
510
from dipdup.exceptions import ConfigurationError
6-
from dipdup.utils import FormattedLogger, reindex, restart
11+
from dipdup.utils import FormattedLogger
712

813

914
# TODO: Dataclasses are cool, everyone loves them. Resolve issue with pydantic in HandlerContext.
@@ -34,11 +39,36 @@ def reset(self) -> None:
3439
def updated(self) -> bool:
3540
return self._updated
3641

42+
async def restart(self) -> None:
43+
"""Restart preserving CLI arguments"""
44+
# NOTE: Remove --reindex from arguments to avoid reindexing loop
45+
if '--reindex' in sys.argv:
46+
sys.argv.remove('--reindex')
47+
os.execl(sys.executable, sys.executable, *sys.argv)
48+
3749
async def reindex(self) -> None:
38-
await reindex()
50+
"""Drop all tables or whole database and restart with the same CLI arguments"""
51+
if isinstance(self.config.database, PostgresDatabaseConfig):
52+
exclude_expression = ''
53+
if self.config.database.immune_tables:
54+
immune_tables = [f"'{t}'" for t in self.config.database.immune_tables]
55+
exclude_expression = f' AND tablename NOT IN ({",".join(immune_tables)})'
3956

40-
async def restart(self) -> None:
41-
await restart()
57+
async with in_transaction() as conn:
58+
await conn.execute_script(
59+
f'''
60+
DO $$ DECLARE
61+
r RECORD;
62+
BEGIN
63+
FOR r IN (SELECT tablename FROM pg_tables WHERE schemaname = current_schema(){exclude_expression}) LOOP
64+
EXECUTE 'DROP TABLE IF EXISTS ' || quote_ident(r.tablename) || ' CASCADE';
65+
END LOOP;
66+
END $$;
67+
'''
68+
)
69+
else:
70+
await Tortoise._drop_databases()
71+
await self.restart()
4272

4373
def add_contract(self, name: str, address: str, typename: Optional[str] = None) -> None:
4474
if name in self.config.contracts:

src/dipdup/dipdup.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,7 @@ async def _initialize_database(self, reindex: bool = False) -> None:
255255
# TODO: Move higher
256256
if reindex:
257257
self._logger.warning('Started with `--reindex` argument, reindexing')
258-
await utils.reindex()
258+
await self._ctx.reindex()
259259

260260
try:
261261
schema_state = await State.get_or_none(index_type=IndexType.schema, index_name=connection_name)
@@ -268,7 +268,7 @@ async def _initialize_database(self, reindex: bool = False) -> None:
268268
await schema_state.save()
269269
elif schema_state.hash != schema_hash:
270270
self._logger.warning('Schema hash mismatch, reindexing')
271-
await utils.reindex()
271+
await self._ctx.reindex()
272272

273273
sql_path = join(self._config.package_path, 'sql')
274274
if not exists(sql_path):

src/dipdup/index.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
from dipdup.context import HandlerContext
2020
from dipdup.datasources.tzkt.datasource import BigMapFetcher, OperationFetcher, TzktDatasource
2121
from dipdup.models import BigMapAction, BigMapData, BigMapDiff, OperationData, Origination, State, TemporaryState, Transaction
22-
from dipdup.utils import FormattedLogger, in_global_transaction, reindex
22+
from dipdup.utils import FormattedLogger, in_global_transaction
2323

2424
OperationGroup = namedtuple('OperationGroup', ('hash', 'counter'))
2525

@@ -82,7 +82,7 @@ async def _initialize_index_state(self) -> None:
8282

8383
elif state.hash != index_hash:
8484
self._logger.warning('Config hash mismatch, reindexing')
85-
await reindex()
85+
await self._ctx.reindex()
8686

8787
self._state = state
8888

src/dipdup/utils.py

Lines changed: 0 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
import asyncio
22
import logging
3-
import os
43
import re
5-
import sys
64
import time
75
from contextlib import asynccontextmanager
86
from logging import Logger
@@ -118,35 +116,6 @@ async def http_request(session: aiohttp.ClientSession, method: str, **kwargs):
118116
return await response.json()
119117

120118

121-
async def restart() -> None:
122-
"""Restart preserving CLI arguments"""
123-
# NOTE: Remove --reindex from arguments to avoid reindexing loop
124-
if '--reindex' in sys.argv:
125-
sys.argv.remove('--reindex')
126-
os.execl(sys.executable, sys.executable, *sys.argv)
127-
128-
129-
async def reindex() -> None:
130-
"""Drop all tables or whole database and restart with the same CLI arguments"""
131-
if isinstance(Tortoise._connections['default'], AsyncpgDBClient):
132-
async with in_transaction() as conn:
133-
await conn.execute_script(
134-
'''
135-
DO $$ DECLARE
136-
r RECORD;
137-
BEGIN
138-
FOR r IN (SELECT tablename FROM pg_tables WHERE schemaname = current_schema()) LOOP
139-
EXECUTE 'DROP TABLE IF EXISTS ' || quote_ident(r.tablename) || ' CASCADE';
140-
END LOOP;
141-
END $$;
142-
'''
143-
)
144-
else:
145-
await Tortoise._drop_databases()
146-
# NOTE: Tortoise can't recover after dropping database for some reason, restart.
147-
await restart()
148-
149-
150119
class FormattedLogger(Logger):
151120
def __init__(
152121
self,

0 commit comments

Comments
 (0)