dwh 0.5.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -0
- data/README.md +3 -3
- data/lib/dwh/adapters/duck_db.rb +6 -5
- data/lib/dwh/factory.rb +53 -30
- data/lib/dwh/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: fd8f7098719fc4a93610c3295a6bcbbf14e8487f07cb8dec86c9b95df7805b4e
|
|
4
|
+
data.tar.gz: a18967ab069c582d12bd3eaaa7d97f5242e3380c9f348e1fd2809da4c82ace88
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 27599ae5ac8ddd1db8fddf7d9714318eb36f98cb66ff73ed71026dddb0ab4f97e8f2f060dde216e6217485b1627cdc5ce7031254aca92da20395334a61d9bb57
|
|
7
|
+
data.tar.gz: a560342b601b038bd8e1d6d30708822433b09d83bef6094a3a1839e5cd0b09dae4bdbffce45e945f43ec1b6fc6e894e1def9d1c3d2a4c76d38a6a2c7cac3f3b6
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [0.5.1] - 2026-08-03
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- **Factory#shutdown**: `case pool.class` never matched (every call fell into `else`); undefined `c` NameError when closing live connections; symbol branch called `delete` on the pool instead of the map. Shutdown now matches on the argument, removes the map entry before closing, and tolerates unknown names.
|
|
8
|
+
- **Factory#pool**: check-then-set race could orphan a pool under concurrent first-use; creation is now mutex-guarded.
|
|
9
|
+
- **Factory#start_reaper**: no longer checks out a connection just to log stats (which created connections and reset idle clocks); Idle/Available labels use `pool.idle` / `pool.available`; iterates a snapshot; rescues `PoolShuttingDownError` per pool so a retired pool cannot kill the reaper thread.
|
|
10
|
+
- **DuckDb#close**: use `@connection&.disconnect` so closing an already-closed adapter does not open a new connection.
|
|
11
|
+
- **DuckDb.close_all**: close then clear instead of deleting while iterating (which could skip entries).
|
|
12
|
+
|
|
3
13
|
## [0.5.0] - 2026-06-19
|
|
4
14
|
|
|
5
15
|
### Added
|
data/README.md
CHANGED
|
@@ -33,11 +33,11 @@ The adapter only has 5 core methods (6 including the connection method). A YAML
|
|
|
33
33
|
- **PostgreSQL** - Full-featured RDBMS with advanced SQL support
|
|
34
34
|
- **MySQL** - Popular open-source database
|
|
35
35
|
- **SQL Server** - Microsoft's enterprise database
|
|
36
|
+
- **ClickHouse** - High performance analytical database
|
|
37
|
+
- **Databricks** - Lakehouse SQL warehouse
|
|
36
38
|
|
|
37
39
|
## Integrations Coming Soon
|
|
38
40
|
|
|
39
|
-
- **ClickHouse** - High performance analytical db
|
|
40
|
-
- **Databricks** - Big data compute engine
|
|
41
41
|
- **MotherDuck** - Hosted DuckDB service
|
|
42
42
|
|
|
43
43
|
## Quick Start
|
|
@@ -137,4 +137,4 @@ This project is available as open source under the terms of the MIT License.
|
|
|
137
137
|
|
|
138
138
|
## Version
|
|
139
139
|
|
|
140
|
-
Current version: 0.
|
|
140
|
+
Current version: 0.5.0
|
data/lib/dwh/adapters/duck_db.rb
CHANGED
|
@@ -56,10 +56,9 @@ module DWH
|
|
|
56
56
|
# we open one instance but many connections. Use this
|
|
57
57
|
# method to close them all.
|
|
58
58
|
def self.close_all
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
end
|
|
59
|
+
# Iterate values then clear — mutating the hash while each-ing skips entries.
|
|
60
|
+
databases.each_value(&:close)
|
|
61
|
+
databases.clear
|
|
63
62
|
end
|
|
64
63
|
|
|
65
64
|
# This disconnects the current connection but
|
|
@@ -68,7 +67,9 @@ module DWH
|
|
|
68
67
|
#
|
|
69
68
|
# (see Adapter#close)
|
|
70
69
|
def close
|
|
71
|
-
connection
|
|
70
|
+
# Use @connection directly: #connection opens a new handle when nil,
|
|
71
|
+
# which would re-open just to close.
|
|
72
|
+
@connection&.disconnect
|
|
72
73
|
@connection = nil
|
|
73
74
|
end
|
|
74
75
|
|
data/lib/dwh/factory.rb
CHANGED
|
@@ -46,6 +46,11 @@ module DWH
|
|
|
46
46
|
@pools ||= {}
|
|
47
47
|
end
|
|
48
48
|
|
|
49
|
+
# Mutex guarding pool creation / map mutation.
|
|
50
|
+
def pool_mutex
|
|
51
|
+
@pool_mutex ||= Mutex.new
|
|
52
|
+
end
|
|
53
|
+
|
|
49
54
|
# The canonical way of creating an adapter instance
|
|
50
55
|
# in DWH.
|
|
51
56
|
# @param adapter_name [String, Symbol]
|
|
@@ -64,62 +69,80 @@ module DWH
|
|
|
64
69
|
# Create a pool of connections for a given name and adapter.
|
|
65
70
|
# Returns existing pool if it was already created.
|
|
66
71
|
#
|
|
67
|
-
# @param name [String] custom name for your pool
|
|
72
|
+
# @param name [String, Symbol] custom name for your pool (stored as String)
|
|
68
73
|
# @param adapter_name [String, Symbol]
|
|
69
74
|
# @param config [Hash] connection options
|
|
70
75
|
# @param timeout [Integer] pool checkout time out
|
|
71
76
|
# @param size [Integer] size of the pool
|
|
72
77
|
def pool(name, adapter_name, config, timeout: 5, size: 10)
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
+
name = name.to_s
|
|
79
|
+
pool_mutex.synchronize do
|
|
80
|
+
if pools.key?(name)
|
|
81
|
+
pools[name]
|
|
82
|
+
else
|
|
83
|
+
pools[name] = ConnectionPool.new(size: size, timeout: timeout) do
|
|
84
|
+
create(adapter_name, config)
|
|
85
|
+
end
|
|
78
86
|
end
|
|
79
87
|
end
|
|
80
88
|
end
|
|
81
89
|
|
|
82
90
|
# Shutdown a specific pool or all pools
|
|
83
|
-
# @param pool [String, ConnectionPool, nil] pool or name of pool
|
|
91
|
+
# @param pool [String, Symbol, ConnectionPool, nil] pool or name of pool
|
|
84
92
|
# or nil to shut everything down
|
|
85
93
|
def shutdown(pool = nil)
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
94
|
+
# Mutate the map under the same mutex as pool creation so a concurrent
|
|
95
|
+
# create cannot be orphaned by @pools = {} / delete racing it.
|
|
96
|
+
# Close outside the lock — ConnectionPool#shutdown can wait on check-in.
|
|
97
|
+
to_close = pool_mutex.synchronize do
|
|
98
|
+
case pool
|
|
99
|
+
when String, Symbol
|
|
100
|
+
# Delete first so a raising close cannot leave a dead pool in the map.
|
|
101
|
+
removed = pools.delete(pool.to_s)
|
|
102
|
+
removed ? [removed] : []
|
|
103
|
+
when ConnectionPool
|
|
104
|
+
key = pools.key(pool)
|
|
105
|
+
pools.delete(key) if key
|
|
106
|
+
[pool]
|
|
107
|
+
else
|
|
108
|
+
closing = pools.values
|
|
109
|
+
@pools = {}
|
|
110
|
+
closing
|
|
99
111
|
end
|
|
100
|
-
@pools = {}
|
|
101
112
|
end
|
|
113
|
+
to_close.each { |p| p.shutdown { it.close } }
|
|
102
114
|
end
|
|
103
115
|
|
|
104
116
|
# Start reaper that will periodically clean up
|
|
105
117
|
# unused or idle connections.
|
|
106
118
|
# @param frequency [Integer] defaults to 300 seconds
|
|
119
|
+
# @return [Thread] the reaper thread
|
|
107
120
|
def start_reaper(frequency = 300)
|
|
108
121
|
logger.info 'Starting DB Adapter reaper process'
|
|
109
122
|
Thread.new do
|
|
110
123
|
loop do
|
|
111
|
-
|
|
112
|
-
logger.info "DB POOL FOR #{name} STATS:"
|
|
113
|
-
pool.with do
|
|
114
|
-
logger.info "\tSize: #{pool.size}"
|
|
115
|
-
logger.info "\tIdle: #{pool.available}"
|
|
116
|
-
logger.info "\tAvailable: #{pool.available}"
|
|
117
|
-
end
|
|
118
|
-
pool.reap(frequency) { it.close }
|
|
119
|
-
end
|
|
124
|
+
reaper_tick(frequency)
|
|
120
125
|
sleep frequency
|
|
121
126
|
end
|
|
122
127
|
end
|
|
123
128
|
end
|
|
129
|
+
|
|
130
|
+
# One reaper cycle: log pool stats (without checking out) and reap idle connections.
|
|
131
|
+
# Safe to call from tests; rescues per-pool so a shut-down pool cannot kill the loop.
|
|
132
|
+
# @param frequency [Integer] idle threshold passed to ConnectionPool#reap
|
|
133
|
+
def reaper_tick(frequency = 300)
|
|
134
|
+
# Snapshot so concurrent shutdown deletions do not mutate while we iterate.
|
|
135
|
+
pools.to_a.each do |name, pool|
|
|
136
|
+
logger.info "DB POOL FOR #{name} STATS:"
|
|
137
|
+
logger.info "\tSize: #{pool.size}"
|
|
138
|
+
logger.info "\tIdle: #{pool.idle}"
|
|
139
|
+
logger.info "\tAvailable: #{pool.available}"
|
|
140
|
+
pool.reap(frequency) { it.close }
|
|
141
|
+
rescue ConnectionPool::PoolShuttingDownError => e
|
|
142
|
+
logger.info "Skipping reaper for pool #{name}: #{e.class}"
|
|
143
|
+
rescue StandardError => e
|
|
144
|
+
logger.error "Reaper error for pool #{name}: #{e.class}: #{e.message}"
|
|
145
|
+
end
|
|
146
|
+
end
|
|
124
147
|
end
|
|
125
148
|
end
|
data/lib/dwh/version.rb
CHANGED