Skip to content

Commit 6587115

Browse files
author
David Erb
committed
adds explicit apply_revisions
1 parent 2030acf commit 6587115

File tree

1 file changed

+69
-45
lines changed

1 file changed

+69
-45
lines changed

src/dls_normsql/aiosqlite.py

Lines changed: 69 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
logger = logging.getLogger(__name__)
2525

2626
connect_lock = asyncio.Lock()
27+
apply_revisions_lock = asyncio.Lock()
2728

2829

2930
# ----------------------------------------------------------------------------------------
@@ -60,7 +61,10 @@ def __init__(self, specification):
6061

6162
self.__tables = {}
6263

63-
self.LATEST_REVISION = 1
64+
# Deriving class has not established its latest revision?
65+
if not hasattr(self, "LATEST_REVISION") or self.LATEST_REVISION is None:
66+
# Presume it is 1.
67+
self.LATEST_REVISION = 1
6468

6569
self.__backup_restore_lock = asyncio.Lock()
6670

@@ -100,8 +104,8 @@ async def connect(self):
100104
# rows = await self.query("SELECT * from mainTable", why="main table check")
101105

102106
await self.__connection.create_function("regexp", 2, sqlite_regexp_callback)
103-
logger.debug("created regexp function")
104107

108+
# Let the base class contribute its table definitions to the in-memory list.
105109
await self.add_table_definitions()
106110

107111
if should_create_schemas:
@@ -111,42 +115,59 @@ async def connect(self):
111115
)
112116
# TODO: Set permission on sqlite file from configuration.
113117
os.chmod(self.__filename, 0o666)
114-
else:
115-
try:
116-
records = await self.query(
117-
f"SELECT number FROM {Tablenames.REVISION}",
118-
why="get database revision",
119-
)
120-
if len(records) == 0:
121-
old_revision = 0
122-
else:
123-
old_revision = records[0]["number"]
124-
except Exception as exception:
125-
logger.warning(
126-
f"could not get revision, presuming legacy database with no table: {exception}"
127-
)
128-
old_revision = 0
129-
130-
if old_revision < self.LATEST_REVISION:
131-
logger.debug(
132-
f"need to update old revision {old_revision}"
133-
f" to latest revision {self.LATEST_REVISION}"
134-
)
135-
for revision in range(old_revision, self.LATEST_REVISION):
136-
logger.debug(f"updating to revision {revision+1}")
137-
await self.apply_revision(revision + 1)
138-
await self.update(
139-
Tablenames.REVISION,
140-
{"number": self.LATEST_REVISION},
141-
"1 = 1",
142-
why="update database revision",
143-
)
144118

145119
# Emit the name of the database file for positive confirmation on console.
146120
logger.info(
147121
f"{callsign(self)} database file is {self.__filename} revision {self.LATEST_REVISION}"
148122
)
149123

124+
# ----------------------------------------------------------------------------------------
125+
async def apply_revisions(self):
126+
"""
127+
Apply revision updates to databse if needed.
128+
"""
129+
130+
# TODO: Consider how to lock database while running applying_revisions.
131+
# TODO: Establish transaction arouund apply_revisions with rollback if error.
132+
async with apply_revisions_lock:
133+
try:
134+
records = await self.query(
135+
f"SELECT number FROM {Tablenames.REVISION}",
136+
why="get database revision",
137+
)
138+
if len(records) == 0:
139+
old_revision = 0
140+
else:
141+
old_revision = records[0]["number"]
142+
except Exception as exception:
143+
logger.warning(
144+
f"could not get revision, presuming legacy database with no table: {exception}"
145+
)
146+
old_revision = 0
147+
148+
if old_revision < self.LATEST_REVISION:
149+
# Backup before applying revisions.
150+
logger.debug(
151+
f"[BKREVL] backing up before updating to revision {self.LATEST_REVISION}"
152+
)
153+
154+
await self.backup()
155+
156+
for revision in range(old_revision, self.LATEST_REVISION):
157+
logger.debug(f"updating to revision {revision+1}")
158+
await self.apply_revision(revision + 1)
159+
await self.update(
160+
Tablenames.REVISION,
161+
{"number": self.LATEST_REVISION},
162+
"1 = 1",
163+
why="update database revision",
164+
)
165+
else:
166+
logger.debug(
167+
f"[BKREVL] no need to update old revision {old_revision}"
168+
f" which matches latest revision {self.LATEST_REVISION}"
169+
)
170+
150171
# ----------------------------------------------------------------------------------------
151172
async def apply_revision(self, revision):
152173
logger.debug(f"updating to revision {revision}")
@@ -469,25 +490,26 @@ async def backup(self):
469490
Back up database to timestamped location.
470491
"""
471492

472-
# Prune all the restores which were orphaned.
473-
directory = self.__backup_directory
493+
async with self.__backup_restore_lock:
494+
# Prune all the restores which were orphaned.
495+
directory = self.__backup_directory
496+
if directory is None:
497+
raise RuntimeError("no backup directory supplied in confirmation")
474498

475-
basename, suffix = os.path.splitext(os.path.basename(self.__filename))
499+
basename, suffix = os.path.splitext(os.path.basename(self.__filename))
476500

477-
filenames = glob.glob(f"{directory}/{basename}.*{suffix}")
501+
filenames = glob.glob(f"{directory}/{basename}.*{suffix}")
478502

479-
filenames.sort(reverse=True)
503+
filenames.sort(reverse=True)
480504

481-
logger.debug(f"[BACKPRU] {self.__last_restore} is last restore")
482-
for restore in range(self.__last_restore):
483-
logger.debug(
484-
f"[BACKPRU] removing {restore}-th restore {filenames[restore]}"
485-
)
486-
os.remove(filenames[restore])
505+
for restore in range(self.__last_restore):
506+
logger.debug(
507+
f"[BACKPRU] removing {restore}-th restore {filenames[restore]}"
508+
)
509+
os.remove(filenames[restore])
487510

488-
self.__last_restore = 0
511+
self.__last_restore = 0
489512

490-
async with self.__backup_restore_lock:
491513
timestamp = isodatetime_filename()
492514
to_filename = f"{directory}/{basename}.{timestamp}{suffix}"
493515

@@ -509,6 +531,8 @@ async def restore(self, nth):
509531

510532
async with self.__backup_restore_lock:
511533
directory = self.__backup_directory
534+
if directory is None:
535+
raise RuntimeError("no backup directory supplied in confirmation")
512536

513537
basename, suffix = os.path.splitext(os.path.basename(self.__filename))
514538

0 commit comments

Comments
 (0)