]> git.netwichtig.de Git - user/henk/code/ruby/rbot.git/blob - lib/rbot/dbhash.rb
+ core/config: sort config search result
[user/henk/code/ruby/rbot.git] / lib / rbot / dbhash.rb
1 #-- vim:sw=2:et
2 #++
3 #
4 # :title: Berkeley DB interface
5
6 begin
7   require 'bdb'
8 rescue LoadError
9   fatal "rbot couldn't load the bdb module, perhaps you need to install it? try: http://www.ruby-lang.org/en/raa-list.rhtml?name=bdb"
10 rescue Exception => e
11   fatal "rbot couldn't load the bdb module: #{e.pretty_inspect}"
12 end
13
14 if not defined? BDB
15   exit 2
16 end
17
18 if BDB::VERSION_MAJOR < 4
19   fatal "Your bdb (Berkeley DB) version #{BDB::VERSION} is too old!"
20   fatal "rbot will only run with bdb version 4 or higher, please upgrade."
21   fatal "For maximum reliability, upgrade to version 4.2 or higher."
22   raise BDB::Fatal, BDB::VERSION + " is too old"
23 end
24
25 if BDB::VERSION_MAJOR == 4 and BDB::VERSION_MINOR < 2
26   warning "Your bdb (Berkeley DB) version #{BDB::VERSION} may not be reliable."
27   warning "If possible, try upgrade version 4.2 or later."
28 end
29
30 # make BTree lookups case insensitive
31 module BDB
32   class CIBtree < Btree
33     def bdb_bt_compare(a, b)
34       if a == nil || b == nil
35         warning "CIBTree: comparing #{a.inspect} (#{self[a].inspect}) with #{b.inspect} (#{self[b].inspect})"
36       end
37       (a||'').downcase <=> (b||'').downcase
38     end
39   end
40 end
41
42 module Irc
43
44   # DBHash is for tying a hash to disk (using bdb).
45   # Call it with an identifier, for example "mydata". It'll look for
46   # mydata.db, if it exists, it will load and reference that db.
47   # Otherwise it'll create and empty db called mydata.db
48   class DBHash
49
50     # absfilename:: use +key+ as an actual filename, don't prepend the bot's
51     #               config path and don't append ".db"
52     def initialize(bot, key, absfilename=false)
53       @bot = bot
54       @key = key
55       if absfilename && File.exist?(key)
56         # db already exists, use it
57         @db = DBHash.open_db(key)
58       elsif File.exist?(@bot.botclass + "/#{key}.db")
59         # db already exists, use it
60         @db = DBHash.open_db(@bot.botclass + "/#{key}.db")
61       elsif absfilename
62         # create empty db
63         @db = DBHash.create_db(key)
64       else
65         # create empty db
66         @db = DBHash.create_db(@bot.botclass + "/#{key}.db")
67       end
68     end
69
70     def method_missing(method, *args, &block)
71       return @db.send(method, *args, &block)
72     end
73
74     def DBHash.create_db(name)
75       debug "DBHash: creating empty db #{name}"
76       return BDB::Hash.open(name, nil, 
77       BDB::CREATE | BDB::EXCL, 0600)
78     end
79
80     def DBHash.open_db(name)
81       debug "DBHash: opening existing db #{name}"
82       return BDB::Hash.open(name, nil, "r+", 0600)
83     end
84
85   end
86
87
88   # DBTree is a BTree equivalent of DBHash, with case insensitive lookups.
89   class DBTree
90     @@env=nil
91     # TODO: make this customizable
92     # Note that it must be at least four times lg_bsize
93     @@lg_max = 8*1024*1024
94     # absfilename:: use +key+ as an actual filename, don't prepend the bot's
95     #               config path and don't append ".db"
96     def initialize(bot, key, absfilename=false)
97       @bot = bot
98       @key = key
99       if @@env.nil?
100         begin
101           @@env = BDB::Env.open("#{@bot.botclass}", BDB::INIT_TRANSACTION | BDB::CREATE | BDB::RECOVER, "set_lg_max" => @@lg_max)
102           debug "DBTree: environment opened with max log size #{@@env.conf['lg_max']}"
103         rescue => e
104           debug "DBTree: failed to open environment: #{e.pretty_inspect}. Retrying ..."
105           @@env = BDB::Env.open("#{@bot.botclass}", BDB::INIT_TRANSACTION | BDB::CREATE |  BDB::RECOVER)
106         end
107         #@@env = BDB::Env.open("#{@bot.botclass}", BDB::CREATE | BDB::INIT_MPOOL | BDB::RECOVER)
108       end
109
110       if absfilename && File.exist?(key)
111         # db already exists, use it
112         @db = DBTree.open_db(key)
113       elsif absfilename
114         # create empty db
115         @db = DBTree.create_db(key)
116       elsif File.exist?(@bot.botclass + "/#{key}.db")
117         # db already exists, use it
118         @db = DBTree.open_db(@bot.botclass + "/#{key}.db")
119       else
120         # create empty db
121         @db = DBTree.create_db(@bot.botclass + "/#{key}.db")
122       end
123     end
124
125     def method_missing(method, *args, &block)
126       return @db.send(method, *args, &block)
127     end
128
129     def DBTree.create_db(name)
130       debug "DBTree: creating empty db #{name}"
131       return @@env.open_db(BDB::CIBtree, name, nil, BDB::CREATE | BDB::EXCL, 0600)
132     end
133
134     def DBTree.open_db(name)
135       debug "DBTree: opening existing db #{name}"
136       return @@env.open_db(BDB::CIBtree, name, nil, "r+", 0600)
137     end
138
139     def DBTree.cleanup_logs()
140       begin
141         debug "DBTree: checkpointing ..."
142         @@env.checkpoint
143       rescue Exception => e
144         debug "Failed: #{e.pretty_inspect}"
145       end
146       begin
147         debug "DBTree: flushing log ..."
148         @@env.log_flush
149         logs = @@env.log_archive(BDB::ARCH_ABS)
150         debug "DBTree: deleting archivable logs: #{logs.join(', ')}."
151         logs.each { |log|
152           File.delete(log)
153         }
154       rescue Exception => e
155         debug "Failed: #{e.pretty_inspect}"
156       end
157     end
158
159     def DBTree.stats()
160       begin
161         debug "General stats:"
162         debug @@env.stat
163         debug "Lock stats:"
164         debug @@env.lock_stat
165         debug "Log stats:"
166         debug @@env.log_stat
167         debug "Txn stats:"
168         debug @@env.txn_stat
169       rescue
170         debug "Couldn't dump stats"
171       end
172     end
173
174     def DBTree.cleanup_env()
175       begin
176         debug "DBTree: checking transactions ..."
177         has_active_txn = @@env.txn_stat["st_nactive"] > 0
178         if has_active_txn
179           warning "DBTree: not all transactions completed!"
180         end
181         DBTree.cleanup_logs
182         debug "DBTree: closing environment #{@@env}"
183         path = @@env.home
184         @@env.close
185         @@env = nil
186         if has_active_txn
187           debug "DBTree: keeping file because of incomplete transactions"
188         else
189           debug "DBTree: cleaning up environment in #{path}"
190           BDB::Env.remove("#{path}")
191         end
192       rescue Exception => e
193         error "failed to clean up environment: #{e.pretty_inspect}"
194       end
195     end
196
197   end
198
199 end