Group all migration events into one row migrate:vN (if version changed) OR migrate:msgspec
- Log as a single event - Logging config has debug parameter to lower level to DEBUG, showing migration diffs
This commit is contained in:
+1
-1
@@ -106,5 +106,5 @@ def fake_clock() -> datetime:
|
|||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
configure_logging()
|
configure_logging(debug=True)
|
||||||
asyncio.run(main())
|
asyncio.run(main())
|
||||||
|
|||||||
+3
-2
@@ -76,8 +76,9 @@ history.
|
|||||||
- In-memory data is defined by an application `msgspec.Struct` type.
|
- In-memory data is defined by an application `msgspec.Struct` type.
|
||||||
- Kanta round-trips through plain builtins for persistence and diffing.
|
- Kanta round-trips through plain builtins for persistence and diffing.
|
||||||
- Dict keys are serialized as strings (`str_keys=True`) for stable JSON form.
|
- Dict keys are serialized as strings (`str_keys=True`) for stable JSON form.
|
||||||
- Normalization changes introduced by struct decode/encode are logged as
|
- Normalization changes introduced by struct decode/encode are logged together
|
||||||
`migrate:msgspec` when they produce a diff.
|
with migrations as `migrate:vN`, or as `migrate:msgspec` when no migration
|
||||||
|
ran but normalization still produces a diff.
|
||||||
|
|
||||||
## Transaction Semantics
|
## Transaction Semantics
|
||||||
|
|
||||||
|
|||||||
+32
-33
@@ -124,21 +124,6 @@ class KantaImpl(PersistenceMixin, Generic[T]):
|
|||||||
if not changed:
|
if not changed:
|
||||||
return
|
return
|
||||||
|
|
||||||
for info in changed:
|
|
||||||
if info.diff:
|
|
||||||
emit_event(
|
|
||||||
LogEvent(
|
|
||||||
kind="change",
|
|
||||||
logger=migration_log,
|
|
||||||
level=logging.DEBUG,
|
|
||||||
kanta=self._kanta,
|
|
||||||
action=info.name,
|
|
||||||
diff=info.diff,
|
|
||||||
previous=info.before,
|
|
||||||
),
|
|
||||||
self.callback_registry.logemit_handlers,
|
|
||||||
)
|
|
||||||
|
|
||||||
descriptions = [f"{m.name} ({m.description})" for m in changed]
|
descriptions = [f"{m.name} ({m.description})" for m in changed]
|
||||||
emit_event(
|
emit_event(
|
||||||
LogEvent(
|
LogEvent(
|
||||||
@@ -237,10 +222,6 @@ class KantaImpl(PersistenceMixin, Generic[T]):
|
|||||||
rr.version = migration_result.version
|
rr.version = migration_result.version
|
||||||
|
|
||||||
migrations_ran = rr.version != previous_version
|
migrations_ran = rr.version != previous_version
|
||||||
migration_state_changed = (
|
|
||||||
state_before_migrations is not None
|
|
||||||
and state_before_migrations != rr.state
|
|
||||||
)
|
|
||||||
|
|
||||||
self.snapshot.ts = (
|
self.snapshot.ts = (
|
||||||
datetime.fromtimestamp(rr.last_snapshot_mtime, UTC)
|
datetime.fromtimestamp(rr.last_snapshot_mtime, UTC)
|
||||||
@@ -268,16 +249,39 @@ class KantaImpl(PersistenceMixin, Generic[T]):
|
|||||||
if self.readonly:
|
if self.readonly:
|
||||||
self.statedict = copy.deepcopy(normalized)
|
self.statedict = copy.deepcopy(normalized)
|
||||||
else:
|
else:
|
||||||
if migrations_ran and migration_state_changed:
|
# One record per open: migration changes and normalization are
|
||||||
self.queue_change(
|
# grouped into migrate:vN, or migrate:msgspec when only the
|
||||||
f"migrate:v{self.version}",
|
# serialization drifted.
|
||||||
rr.state,
|
previous = self.statedict
|
||||||
mtime=False,
|
action = (
|
||||||
)
|
f"migrate:v{self.version}" if migrations_ran else "migrate:msgspec"
|
||||||
msgspec_record = self.queue_change(
|
|
||||||
"migrate:msgspec", normalized, mtime=False
|
|
||||||
)
|
)
|
||||||
if migrations_ran or msgspec_record is not None:
|
record = self.queue_change(action, normalized, mtime=False)
|
||||||
|
if (
|
||||||
|
record is not None
|
||||||
|
and log is not False
|
||||||
|
and not (migrations_ran and self.callback_registry.has("logmigr"))
|
||||||
|
):
|
||||||
|
logger = (
|
||||||
|
log if isinstance(log, logging.Logger) else migration_logger
|
||||||
|
)
|
||||||
|
emit_event(
|
||||||
|
LogEvent(
|
||||||
|
kind="change",
|
||||||
|
logger=logger,
|
||||||
|
level=logging.DEBUG,
|
||||||
|
kanta=self._kanta,
|
||||||
|
action=action,
|
||||||
|
diff=record.diff,
|
||||||
|
previous=previous,
|
||||||
|
),
|
||||||
|
self.callback_registry.logemit_handlers,
|
||||||
|
)
|
||||||
|
if migrations_ran and migration_result is not None:
|
||||||
|
await self._handle_migration_log(
|
||||||
|
migration_result, previous_version, log
|
||||||
|
)
|
||||||
|
if migrations_ran or record is not None:
|
||||||
self.snapshot.request_force()
|
self.snapshot.request_force()
|
||||||
await self.flush()
|
await self.flush()
|
||||||
self.snapshot.maybe_write(
|
self.snapshot.maybe_write(
|
||||||
@@ -287,11 +291,6 @@ class KantaImpl(PersistenceMixin, Generic[T]):
|
|||||||
m=self.mtime,
|
m=self.mtime,
|
||||||
now=self.now,
|
now=self.now,
|
||||||
)
|
)
|
||||||
|
|
||||||
if migrations_ran and migration_result is not None:
|
|
||||||
await self._handle_migration_log(
|
|
||||||
migration_result, previous_version, log
|
|
||||||
)
|
|
||||||
elif self.readonly:
|
elif self.readonly:
|
||||||
self.opened = False
|
self.opened = False
|
||||||
self.file.close()
|
self.file.close()
|
||||||
|
|||||||
+5
-1
@@ -480,6 +480,7 @@ def configure_logging(
|
|||||||
migration: bool = True,
|
migration: bool = True,
|
||||||
transaction: bool = True,
|
transaction: bool = True,
|
||||||
diff: bool = True,
|
diff: bool = True,
|
||||||
|
debug: bool = False,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Configure Kanta's default logging output.
|
"""Configure Kanta's default logging output.
|
||||||
|
|
||||||
@@ -497,6 +498,9 @@ def configure_logging(
|
|||||||
only transaction headers are printed and diff formatting is
|
only transaction headers are printed and diff formatting is
|
||||||
skipped. Per transaction this is controlled by the ``logdiff``
|
skipped. Per transaction this is controlled by the ``logdiff``
|
||||||
argument of :meth:`Kanta.transaction`.
|
argument of :meth:`Kanta.transaction`.
|
||||||
|
debug: Whether to set the ``kanta`` logger level to ``DEBUG`` instead
|
||||||
|
of ``INFO``. This reveals debug-level output such as migration
|
||||||
|
diffs, which are hidden by default.
|
||||||
|
|
||||||
This helper is not called automatically; applications that want Kanta's
|
This helper is not called automatically; applications that want Kanta's
|
||||||
default output can call it, but most applications will configure logging
|
default output can call it, but most applications will configure logging
|
||||||
@@ -521,4 +525,4 @@ def configure_logging(
|
|||||||
handler = logging.StreamHandler(sys.stderr)
|
handler = logging.StreamHandler(sys.stderr)
|
||||||
handler.setFormatter(logging.Formatter("%(message)s"))
|
handler.setFormatter(logging.Formatter("%(message)s"))
|
||||||
target.addHandler(handler)
|
target.addHandler(handler)
|
||||||
target.setLevel(logging.INFO)
|
target.setLevel(logging.DEBUG if debug else logging.INFO)
|
||||||
|
|||||||
Reference in New Issue
Block a user