* Add alembic DB schema management (#86) * Use alembic * add creation helper * example migration tool * Store UTC int time in DB (#81) * use UTC int time * Remove old index notes script -- no longer needed * modify alembic to support cleaner migrations * add /version json endpoint * move technical docs * remove old migrate script * add readme in docs: * more doc tidy * rm * update api docs * ignore other database files * health endpoint * alembic log format * break out api calls in to their own file to reduce footprint * ruff and docs * vuln * Improves arguments in mvrun.py * Set dbcleanup.log location configurable * mvrun work * fallback if missing config * remove unused loop * improve migrations and fix logging problem with mqtt * Container using slim/uv * auto build containers * symlink * fix symlink * checkout and containerfile * make /app owned by ap0p * Traceroute Return Path logged and displayed (#97) * traceroute returns are now logged and /packetlist now graphs the correct data for a return route * now using alembic to update schema * HOWTO - Alembic --------- Co-authored-by: Joel Krauska <jkrauska@gmail.com> * DB Backups * backups and cleanups are different * ruff * Docker Docs * setup-dev * graphviz for dot in Container * Summary of 3.0.0 stuff * Alembic was blocking mqtt logs * Add us first/last timestamps to node table too * Worked on /api/packet. Needed to modify - Store.py to read the new time data - api.py to present the new time data - firehose.html chat.html and map.html now use the new apis and the time is the browser local time * Worked on /api/packet. Needed to modify - Store.py to read the new time data - api.py to present the new time data - firehose.html chat.html and map.html now use the new apis and the time is the browser local time * Improves container build (#94) * Worked on /api/packet. Needed to modify - Store.py to read the new time data - api.py to present the new time data - firehose.html chat.html and map.html now use the new apis and the time is the browser local time * Worked on /api/packet. Needed to modify - Store.py to read the new time data - api.py to present the new time data - firehose.html chat.html and map.html now use the new apis and the time is the browser local time * Worked on /api/packet. Needed to modify - Added new api endpoint /api/packets_seen - Modified web.py and store.py to support changes to APIs. - Started to work on new_node.html and new_packet.html for presentation of data. * Worked on /api/packet. Needed to modify - Added new api endpoint /api/packets_seen - Modified web.py and store.py to support changes to APIs. - Started to work on new_node.html and new_packet.html for presentation of data. * Finishing up all the pages for the 3.0 release. Now all pages are functional. * Finishing up all the pages for the 3.0 release. Now all pages are functional. * fix ruff format * more ruff * Finishing up all the pages for the 3.0 release. Now all pages are functional. * Finishing up all the pages for the 3.0 release. Now all pages are functional. * pyproject.toml requirements * use sys.executable * fix 0 epoch dates in /chat * Make the robots do our bidding * another compatibility fix when _us is empty and we need to sort by BOTH old and new * Finishing up all the pages for the 3.0 release. Now all pages are functional. * Finishing up all the pages for the 3.0 release. Now all pages are functional. * Remamed new_node to node. shorter and descriptive. * Remamed new_node to node. shorter and descriptive. * Remamed new_node to node. shorter and descriptive. * Remamed new_node to node. shorter and descriptive. * Remamed new_node to node. shorter and descriptive. * Remamed new_node to node. shorter and descriptive. * More changes... almost ready for release. Ranamed 2 pages for easy or reading. * Fix the net page as it was not showing the date information * Fix the net page as it was not showing the date information * Fix the net page as it was not showing the date information * Fix the net page as it was not showing the date information * ruff --------- Co-authored-by: Óscar García Amor <ogarcia@connectical.com> Co-authored-by: Jim Schrempp <jschrempp@users.noreply.github.com> Co-authored-by: Pablo Revilla <pablorevilla@gmail.com>
5.7 KiB
High-Resolution Timestamp Migration
This document describes the implementation of GitHub issue #55: storing high-resolution timestamps as integers in the database for improved performance and query efficiency.
Overview
The meshview database now stores timestamps in two formats:
- TEXT format (
import_time): Human-readable ISO8601 format with microseconds (e.g.,2025-03-12 04:15:56.058038) - INTEGER format (
import_time_us): Microseconds since Unix epoch (1970-01-01 00:00:00 UTC)
The dual format approach provides:
- Backward compatibility: Existing
import_timeTEXT columns remain unchanged - Performance: Fast integer comparisons and math operations
- Precision: Microsecond resolution for accurate timing
- Efficiency: Compact storage and fast indexed lookups
Database Changes
New Columns Added
Three tables have new import_time_us columns:
-
packet.import_time_us (INTEGER)
- Stores when the packet was imported into the database
- Indexed for fast queries
-
packet_seen.import_time_us (INTEGER)
- Stores when the packet_seen record was imported
- Indexed for performance
-
traceroute.import_time_us (INTEGER)
- Stores when the traceroute was imported
- Indexed for fast lookups
New Indexes
The following indexes were created for optimal query performance:
CREATE INDEX idx_packet_import_time_us ON packet(import_time_us DESC);
CREATE INDEX idx_packet_from_node_time_us ON packet(from_node_id, import_time_us DESC);
CREATE INDEX idx_packet_seen_import_time_us ON packet_seen(import_time_us);
CREATE INDEX idx_traceroute_import_time_us ON traceroute(import_time_us);
Migration Process
For Existing Databases
Run the migration script to add the new columns and populate them from existing data:
python migrate_add_timestamp_us.py [database_path]
If no path is provided, it defaults to packets.db in the current directory.
The migration script:
- Checks if migration is needed (idempotent)
- Adds
import_time_uscolumns to the three tables - Populates the new columns from existing
import_timevalues - Creates indexes for optimal performance
- Verifies the migration completed successfully
For New Databases
New databases created with the updated schema will automatically include the import_time_us columns. The MQTT store module populates both columns when inserting new records.
Code Changes
Models (meshview/models.py)
The ORM models now include the new import_time_us fields:
class Packet(Base):
import_time: Mapped[datetime] = mapped_column(nullable=True)
import_time_us: Mapped[int] = mapped_column(BigInteger, nullable=True)
MQTT Store (meshview/mqtt_store.py)
The data ingestion logic now populates both timestamp columns using UTC time:
now = datetime.datetime.now(datetime.timezone.utc)
now_us = int(now.timestamp() * 1_000_000)
# Both columns are populated
import_time=now,
import_time_us=now_us,
Important: All new timestamps use UTC (Coordinated Universal Time) for consistency across time zones.
Using the New Timestamps
Example Queries
Query packets from the last 7 days:
-- Old way (slower)
SELECT * FROM packet
WHERE import_time >= datetime('now', '-7 days');
-- New way (faster)
SELECT * FROM packet
WHERE import_time_us >= (strftime('%s', 'now', '-7 days') * 1000000);
Query packets in a specific time range:
SELECT * FROM packet
WHERE import_time_us BETWEEN 1759254380000000 AND 1759254390000000;
Calculate time differences (in microseconds):
SELECT
id,
(import_time_us - LAG(import_time_us) OVER (ORDER BY import_time_us)) / 1000000.0 as seconds_since_last
FROM packet
LIMIT 10;
Converting Timestamps
From datetime to microseconds (UTC):
import datetime
now = datetime.datetime.now(datetime.timezone.utc)
now_us = int(now.timestamp() * 1_000_000)
From microseconds to datetime:
import datetime
timestamp_us = 1759254380813451
dt = datetime.datetime.fromtimestamp(timestamp_us / 1_000_000)
In SQL queries:
-- Datetime to microseconds
SELECT CAST((strftime('%s', import_time) || substr(import_time, 21, 6)) AS INTEGER);
-- Microseconds to datetime (approximate)
SELECT datetime(import_time_us / 1000000, 'unixepoch');
Performance Benefits
The integer timestamp columns provide significant performance improvements:
- Faster comparisons: Integer comparisons are much faster than string/datetime comparisons
- Smaller index size: Integer indexes are more compact than datetime indexes
- Range queries: BETWEEN operations on integers are highly optimized
- Math operations: Easy to calculate time differences, averages, etc.
- Sorting: Integer sorting is faster than datetime sorting
Backward Compatibility
The original import_time TEXT columns remain unchanged:
- Existing code continues to work
- Human-readable timestamps still available
- Gradual migration to new columns possible
- No breaking changes for existing queries
Future Work
Future improvements could include:
- Migrating queries to use
import_time_uscolumns - Deprecating the TEXT
import_timecolumns (after transition period) - Adding helper functions for timestamp conversion
- Creating views that expose both formats
Testing
The migration was tested on a production database with:
- 132,466 packet records
- 1,385,659 packet_seen records
- 28,414 traceroute records
All records were successfully migrated with microsecond precision preserved.
References
- GitHub Issue: #55 - Storing High-Resolution Timestamps in SQLite
- SQLite datetime functions: https://www.sqlite.org/lang_datefunc.html
- Python datetime module: https://docs.python.org/3/library/datetime.html