-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdatabase_manager.rb
More file actions
135 lines (114 loc) · 4.26 KB
/
database_manager.rb
File metadata and controls
135 lines (114 loc) · 4.26 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
require 'sequel'
Sequel.extension :migration
require_relative 'metadata_manager'
module DatabaseManager
extend self
def setup_route_database(route, route_name)
db_path = "sqlite://" + route[:mbtiles_file]
db = Sequel.connect(db_path, max_connections: 20)
configure_sqlite_pragmas(db)
create_tables(db)
apply_migrations(db)
route[:db] = db
integrate_wal_files(db, route_name)
MetadataManager.sync_metadata(db, route, route_name)
format_value = db[:metadata].where(name: 'format').get(:value)
route[:content_type] = format_value ? "image/#{format_value}" : 'image/png'
tile_size_value = db[:metadata].where(name: 'tileSize').get(:value)
route[:tile_size] = tile_size_value ? tile_size_value.to_i : nil
route[:locks] = Hash.new { |h,k| h[k] = Mutex.new }
db
end
def vacuum_all_databases(routes) = routes.each { |name, route| vacuum_database(route[:db], name) }
def vacuum_database(db, name = nil)
name_str = name ? " for #{name}" : ""
LOGGER.info("Starting VACUUM operation#{name_str}...")
start_time = Time.now
db.run "VACUUM"
duration = Time.now - start_time
LOGGER.info("VACUUM completed#{name_str} in #{duration.round(2)}s")
rescue => e
LOGGER.error("VACUUM failed#{name_str}: #{e.message}")
end
def integrate_wal_files(db, name = nil)
name_str = name ? " for #{name}" : ""
LOGGER.info("Integrating WAL files#{name_str} on startup...")
begin
db.run "PRAGMA wal_checkpoint(RESTART)"
db.run "PRAGMA wal_checkpoint(TRUNCATE)"
LOGGER.info("WAL integration completed#{name_str}")
rescue => e
LOGGER.error("Failed to integrate WAL#{name_str}: #{e.message}")
end
end
def record_miss(route, z, x, y, reason, details, status, body)
tile_row = (1 << z) - 1 - y
route[:db][:misses].where(
zoom_level: z,
tile_column: x,
tile_row: tile_row
).delete
route[:db][:misses].insert(
zoom_level: z,
tile_column: x,
tile_row: tile_row,
ts: Time.now.to_i,
reason: reason,
details: details,
status: status,
response_body: Sequel.blob(body || '')
)
end
private
def configure_sqlite_pragmas(db)
db.run "PRAGMA page_size=4096" # or 8192/16384; set once
db.run "PRAGMA journal_mode=WAL"
db.run "PRAGMA synchronous=NORMAL"
db.run "PRAGMA locking_mode=NORMAL"
db.run "PRAGMA busy_timeout=10000"
db.run "PRAGMA temp_store=MEMORY"
db.run "PRAGMA cache_size=-131072" # ~128 MiB
db.run "PRAGMA mmap_size=536870912" # 512 MiB
db.run "PRAGMA wal_autocheckpoint=1000"
end
def create_tables(db)
db.create_table?(:metadata){ String :name, null:false; String :value; unique :name }
db.create_table?(:tiles){
Integer :zoom_level, null:false
Integer :tile_column, null:false
Integer :tile_row, null:false
File :tile_data, null:false
Integer :generated, default: 0 # 0=original, 1=generated, 2=needs_regeneration
DateTime :updated_at, default: Sequel.lit("datetime('now', 'utc')")
unique [:zoom_level,:tile_column,:tile_row], name: :tile_index
index :zoom_level, name: :idx_tiles_zoom_level
index [:zoom_level, Sequel.function(:length, :tile_data)], name: :idx_tiles_zoom_size
index [:zoom_level, :generated], name: :idx_tiles_zoom_generated
index [:zoom_level, :updated_at], name: :idx_tiles_zoom_updated
}
db.create_table?(:misses){
Integer :zoom_level, null: false
Integer :tile_column, null: false
Integer :tile_row, null: false
Integer :ts, null: false
String :reason
String :details
Integer :status
File :response_body
primary_key [:zoom_level, :tile_column, :tile_row], name: :misses_pk
index [:zoom_level, :status], name: :idx_misses_zoom_status
index :ts, name: :idx_misses_ts
}
end
def apply_migrations(db)
migrations_path = File.join(__dir__, 'migrations')
return unless Dir.exist?(migrations_path) && !Dir[File.join(migrations_path, '*.rb')].empty?
begin
Sequel::Migrator.run(db, migrations_path, table: :schema_info)
LOGGER.info("Migrations applied successfully")
rescue => e
LOGGER.error("Migration failed: #{e.message}")
raise
end
end
end