qpid-cpp-store-debian-0.16/0000755000176300017630000000000011761423670014542 5ustar cajuscajusqpid-cpp-store-debian-0.16/bootstrap0000755000176300017630000000177411234402777016516 0ustar cajuscajus#!/bin/bash # Copyright (C) 2007, 2008, 2009 Red Hat Inc. # # This file is part of the Qpid async store library msgstore.so. # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 # USA # # The GNU Lesser General Public License is available in the file COPYING. set -e aclocal -I m4 autoheader libtoolize --automake automake --add-missing autoconf qpid-cpp-store-debian-0.16/tools/0000755000176300017630000000000011761422436015701 5ustar cajuscajusqpid-cpp-store-debian-0.16/tools/resize0000755000176300017630000004221111560047111017116 0ustar cajuscajus#!/usr/bin/env python """ Copyright (c) 2007, 2008 Red Hat, Inc. This file is part of the Qpid async store library msgstore.so. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA The GNU Lesser General Public License is available in the file COPYING. """ from qpidstore import jerr, jrnl, janal import glob, optparse, os, sys, time #== class Resize ============================================================== class Resize(object): """ Creates a new store journal and copies records from old journal to new. The new journal may be of different size from the old one. The records are packed into the new journal (ie only remaining enqueued records and associated transactions - if any - are copied over without spaces between them). The default action is to push the old journal down into a 'bak' sub-directory and then create a new journal of the same size and pack it with the records from the old. However, it is possible to suppress the pushdown (using --no-pushdown), in which case either a new journal id (using --new-base-filename) or an old journal id (usnig --old-base-filename) must be supplied. In the former case,a new journal will be created using the new base file name alongside the old one. In the latter case, the old journal will be renamed to the supplied name, and the new one will take the default. Note that both can be specified together with the --no-pushdown option. To resize the journal, use the optional --num-jfiles and/or --jfile-size parameters. These should be large enough to write all the records or an error will result. If the size is large enough to write all records, but too small to keep below the enqueue threshold, a warning will be printed. Note that as any valid size will be accepted, a journal can also be shrunk, as long as it is sufficiently big to accept the transferred records. """ BAK_DIR = "bak" JFILE_SIZE_PGS_MIN = 1 JFILE_SIZE_PGS_MAX = 32768 NUM_JFILES_MIN = 4 NUM_JFILES_MAX = 64 def __init__(self): """Constructor""" self._opts = None self._jdir = None self._fname = None self._fnum = None self._file = None self._file_rec_wr_cnt = None self._filler_wr_cnt = None self._last_rec_fid = None self._last_rec_offs = None self._rec_wr_cnt = None self._jrnl_info = None self._jrnl_analysis = None self._jrnl_reader = None self._process_args() self._jrnl_info = jrnl.JrnlInfo(self._jdir, self._opts.bfn) # FIXME: This is a hack... find an elegant way of getting the file size to jrec! jrnl.JRNL_FILE_SIZE = self._jrnl_info.get_jrnl_file_size_bytes() self._jrnl_analysis = janal.JrnlAnalyzer(self._jrnl_info) self._jrnl_reader = janal.JrnlReader(self._jrnl_info, self._jrnl_analysis, self._opts.qflag, self._opts.rflag, self._opts.vflag) def run(self): """Perform the action of resizing the journal""" if not self._opts.qflag: print self._jrnl_analysis self._jrnl_reader.run() if self._opts.vflag: print self._jrnl_info if not self._opts.qflag: print self._jrnl_reader.report(self._opts.vflag, self._opts.rflag) self._handle_old_files() self._create_new_files() if not self._opts.qflag: print "Transferred %d records to new journal." % self._rec_wr_cnt self._chk_free() def _chk_free(self): """Check if sufficient space is available in resized journal to be able to enqueue. Raise a warning if not.""" if self._last_rec_fid == None or self._last_rec_offs == None: return wr_capacity_bytes = self._last_rec_fid * self._jrnl_info.get_jrnl_data_size_bytes() + self._last_rec_offs tot_capacity_bytes = self._jrnl_info.get_tot_jrnl_data_size_bytes() percent_full = 100.0 * wr_capacity_bytes / tot_capacity_bytes if percent_full > 80.0: raise jerr.JWarning("WARNING: Journal %s is %2.1f%% full and will likely not allow enqueuing of new records" " until some existing records are dequeued." % (self._jrnl_info.get_jrnl_id(), percent_full)) def _create_new_files(self): """Create new journal files""" # Assemble records to be transfered master_record_list = {} txn_record_list = self._jrnl_reader.txn_obj_list() if self._opts.vflag and self._jrnl_reader.emap().size() > 0: print "* Assembling %d records from emap" % self._jrnl_reader.emap().size() for tup in self._jrnl_reader.emap().get_rec_list(): hdr = tup[1] hdr.flags &= ~jrnl.Hdr.OWI_MASK # Turn off owi master_record_list[long(hdr.rid)] = hdr if hdr.xidsize > 0 and hdr.xid in txn_record_list: txn_hdr = txn_record_list[hdr.xid] del(txn_record_list[hdr.xid]) txn_hdr.flags &= ~jrnl.Hdr.OWI_MASK # Turn off owi master_record_list[long(txn_hdr.rid)] = txn_hdr if self._opts.vflag and self._jrnl_reader.tmap().size() > 0: print "* Assembling %d records from tmap" % self._jrnl_reader.tmap().size() for xid in self._jrnl_reader.tmap().xids(): for l in self._jrnl_reader.tmap().get(xid): hdr = l[1] hdr.flags &= ~jrnl.Hdr.OWI_MASK # Turn off owi master_record_list[hdr.rid] = hdr rid_list = master_record_list.keys() rid_list.sort() # get base filename bfn = self._opts.bfn if self._opts.nbfn != None: bfn = self._opts.nbfn # write jinf file self._jrnl_info.resize(self._opts.njf, self._opts.jfs) self._jrnl_info.write(self._jdir, bfn) # write records if self._opts.vflag: print "* Transferring records to new journal files" fro = self._jrnl_info.get_jrnl_sblk_size_bytes() while len(rid_list) > 0: hdr = master_record_list[rid_list.pop(0)] rec = hdr.encode() pos = 0 while pos < len(rec): if self._file == None or self._file.tell() >= self._jrnl_info.get_jrnl_file_size_bytes(): if self._file == None: rid = hdr.rid elif len(rid_list) == 0: rid = 0 else: rid = rid_list[0] if not self._rotate_file(rid, fro): raise jerr.JournalSpaceExceededError() if len(rec) - pos <= self._jrnl_info.get_jrnl_file_size_bytes() - self._file.tell(): self._file.write(rec[pos:]) self._fill_file(jrnl.Utils.size_in_bytes_to_blk(self._file.tell(), self._jrnl_info.get_jrnl_dblk_size_bytes())) pos = len(rec) fro = self._jrnl_info.get_jrnl_sblk_size_bytes() else: flen = self._jrnl_info.get_jrnl_file_size_bytes() - self._file.tell() self._file.write(rec[pos:pos + flen]) pos += flen rem = len(rec) - pos if rem <= self._jrnl_info.get_jrnl_data_size_bytes(): fro = (jrnl.Utils.size_in_bytes_to_blk(self._jrnl_info.get_jrnl_sblk_size_bytes() + rem, self._jrnl_info.get_jrnl_dblk_size_bytes())) else: fro = 0 self._rec_wr_cnt += 1 self._file_rec_wr_cnt += 1 self._fill_file(add_filler_recs = True) while self._rotate_file(): pass def _fill_file(self, to_posn = None, add_filler_recs = False): """Fill a file to a known offset""" if self._file == None: return if add_filler_recs: nfr = int(jrnl.Utils.rem_bytes_in_blk(self._file, self._jrnl_info.get_jrnl_sblk_size_bytes()) / self._jrnl_info.get_jrnl_dblk_size_bytes()) if nfr > 0: self._filler_wr_cnt = nfr for i in range(0, nfr): self._file.write("RHMx") self._fill_file(jrnl.Utils.size_in_bytes_to_blk(self._file.tell(), self._jrnl_info.get_jrnl_dblk_size_bytes())) self._last_rec_fid = self._fnum self._last_rec_offs = self._file.tell() if to_posn == None: to_posn = self._jrnl_info.get_jrnl_file_size_bytes() elif to_posn > self._jrnl_info.get_jrnl_file_size_bytes(): raise jerr.FillExceedsFileSizeError(to_posn, self._jrnl_info.get_jrnl_file_size_bytes()) diff = to_posn - self._file.tell() self._file.write(str("\0" * diff)) #DEBUG if self._file.tell() != to_posn: raise jerr.FillSizeError(self._file.tell(), to_posn) def _rotate_file(self, rid = None, fro = None): """Switch to the next logical file""" if self._file != None: self._file.close() if self._opts.vflag: if self._file_rec_wr_cnt == 0: print " (empty)" elif self._filler_wr_cnt == None: print " (%d records)" % self._file_rec_wr_cnt else: print " (%d records + %d filler(s))" % (self._file_rec_wr_cnt, self._filler_wr_cnt) if self._fnum == None: self._fnum = 0 self._rec_wr_cnt = 0 elif self._fnum == self._jrnl_info.get_num_jrnl_files() - 1: return False else: self._fnum += 1 self._file_rec_wr_cnt = 0 self._fname = os.path.join(self._jrnl_info.get_jrnl_dir(), "%s.%04x.jdat" % (self._jrnl_info.get_jrnl_base_name(), self._fnum)) if self._opts.vflag: print "* Opening file %s" % self._fname, self._file = open(self._fname, "w") if rid == None or fro == None: self._fill_file() else: now = time.time() fhdr = jrnl.FileHdr(0, "RHMf", jrnl.Hdr.HDR_VER, int(jrnl.Hdr.BIG_ENDIAN), 0, rid) fhdr.init(self._file, 0, self._fnum, self._fnum, fro, int(now), 1000000000*(now - int(now))) self._file.write(fhdr.encode()) self._fill_file(self._jrnl_info.get_jrnl_sblk_size_bytes()) return True def _handle_old_files(self): """Push old journal down into a backup directory""" target_dir = self._jdir if not self._opts.npd: target_dir = os.path.join(self._jdir, self.BAK_DIR) if os.path.exists(target_dir): if self._opts.vflag: print "* Pushdown directory %s exists, deleting content" % target_dir for fname in glob.glob(os.path.join(target_dir, "*")): os.unlink(fname) else: if self._opts.vflag: print "* Creating new pushdown directory %s" % target_dir os.mkdir(target_dir) if not self._opts.npd or self._opts.obfn != None: if self._opts.obfn != None and self._opts.vflag: print "* Renaming old journal files using base name %s" % self._opts.obfn # .jdat files for fname in glob.glob(os.path.join(self._jdir, "%s.*.jdat" % self._opts.bfn)): tbfn = os.path.basename(fname) if self._opts.obfn != None: per1 = tbfn.rfind(".") if per1 >= 0: per2 = tbfn.rfind(".", 0, per1) if per2 >= 0: tbfn = "%s%s" % (self._opts.obfn, tbfn[per2:]) os.rename(fname, os.path.join(target_dir, tbfn)) # .jinf file self._jrnl_info.write(target_dir, self._opts.obfn) os.unlink(os.path.join(self._jdir, "%s.jinf" % self._opts.bfn)) def _print_options(self): """Print program options""" if self._opts.vflag: print "Journal dir: %s" % self._jdir print "Options: Base filename: %s" % self._opts.bfn print " New base filename: %s" % self._opts.nbfn print " Old base filename: %s" % self._opts.obfn print " Pushdown: %s" % self._opts.npd print " No. journal files: %d" % self._opts.njf print " Journal file size: %d 64kiB blocks" % self._opts.jfs print " Show records flag: %s" % self._opts.rflag print " Verbose flag: %s" % True print def _process_args(self): """Process the command-line arguments""" opt = optparse.OptionParser(usage="%prog [options] DIR", version="%prog 1.0") opt.add_option("-b", "--base-filename", action="store", dest="bfn", default="JournalData", help="Base filename for old journal files") opt.add_option("-B", "--new-base-filename", action="store", dest="nbfn", help="Base filename for new journal files") opt.add_option("-n", "--no-pushdown", action="store_true", dest="npd", help="Suppress pushdown of old files into \"bak\" dir; old files will remain in existing dir") opt.add_option("-N", "--num-jfiles", action="store", type="int", dest="njf", default=8, help="Number of files for new journal (%d-%d)" % (self.NUM_JFILES_MIN, self.NUM_JFILES_MAX)) opt.add_option("-o", "--old-base-filename", action="store", dest="obfn", help="Base filename for old journal files") opt.add_option("-q", "--quiet", action="store_true", dest="qflag", help="Quiet (suppress all non-error output)") opt.add_option("-r", "--records", action="store_true", dest="rflag", help="Print remaining records and transactions") opt.add_option("-s", "--jfile-size-pgs", action="store", type="int", dest="jfs", default=24, help="Size of each new journal file in 64kiB blocks (%d-%d)" % (self.JFILE_SIZE_PGS_MIN, self.JFILE_SIZE_PGS_MAX)) opt.add_option("-v", "--verbose", action="store_true", dest="vflag", help="Verbose output") (self._opts, args) = opt.parse_args() if len(args) == 0: opt.error("No journal directory argument") elif len(args) > 1: opt.error("Too many positional arguments: %s" % args) if self._opts.qflag and self._opts.rflag: opt.error("Quiet (-q/--quiet) and record (-r/--records) options are mutually exclusive") if self._opts.qflag and self._opts.vflag: opt.error("Quiet (-q/--quiet) and verbose (-v/--verbose) options are mutually exclusive") if self._opts.njf != None and (self._opts.njf < self.NUM_JFILES_MIN or self._opts.njf > self.NUM_JFILES_MAX): opt.error("Number of files (%d) is out of range (%d-%d)" % (self._opts.njf, self.NUM_JFILES_MIN, self.NUM_JFILES_MAX)) if self._opts.jfs != None and (self._opts.jfs < self.JFILE_SIZE_PGS_MIN or self._opts.jfs > self.JFILE_SIZE_PGS_MAX): opt.error("File size (%d) is out of range (%d-%d)" % (self._opts.jfs, self.JFILE_SIZE_PGS_MIN, self.JFILE_SIZE_PGS_MAX)) if self._opts.npd != None and (self._opts.nbfn == None and self._opts.obfn == None): opt.error("If (-n/--no-pushdown) is used, then at least one of (-B/--new-base-filename) and" " (-o/--old-base-filename) must be used.") self._jdir = args[0] if not os.path.exists(self._jdir): opt.error("Journal path \"%s\" does not exist" % self._jdir) self._print_options() #============================================================================== # main program #============================================================================== if __name__ == "__main__": R = Resize() try: R.run() except Exception, e: sys.exit(e) qpid-cpp-store-debian-0.16/tools/qpidstore/0000755000176300017630000000000011761422455017714 5ustar cajuscajusqpid-cpp-store-debian-0.16/tools/qpidstore/__init__.py0000644000176300017630000000157111562255465022035 0ustar cajuscajus""" Copyright (c) 2007, 2008 Red Hat, Inc. This file is part of the Qpid async store library msgstore.so. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA The GNU Lesser General Public License is available in the file COPYING. """ qpid-cpp-store-debian-0.16/tools/qpidstore/jrnl.py0000644000176300017630000007710211562255465021246 0ustar cajuscajus""" Copyright (c) 2007, 2008 Red Hat, Inc. This file is part of the Qpid async store library msgstore.so. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA The GNU Lesser General Public License is available in the file COPYING. """ import jerr import os.path, sys, xml.parsers.expat from struct import pack, unpack, calcsize from time import gmtime, strftime # TODO: Get rid of these! Use jinf instance instead DBLK_SIZE = 128 SBLK_SIZE = 4 * DBLK_SIZE # TODO - this is messy - find a better way to handle this # This is a global, but is set directly by the calling program JRNL_FILE_SIZE = None #== class Utils ====================================================================== class Utils(object): """Class containing utility functions for dealing with the journal""" __printchars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!\"#$%&'()*+,-./:;<=>?@[\\]^_`{\|}~ " # The @staticmethod declarations are not supported in RHEL4 (python 2.3.x) # When RHEL4 support ends, restore these declarations and remove the older # staticmethod() declaration. #@staticmethod def format_data(dsize, data): """Format binary data for printing""" if data == None: return "" if Utils._is_printable(data): datastr = Utils._split_str(data) else: datastr = Utils._hex_split_str(data) if dsize != len(data): raise jerr.DataSizeError(dsize, len(data), datastr) return "data(%d)=\"%s\" " % (dsize, datastr) format_data = staticmethod(format_data) #@staticmethod def format_xid(xid, xidsize=None): """Format binary XID for printing""" if xid == None and xidsize != None: if xidsize > 0: raise jerr.XidSizeError(xidsize, 0, None) return "" if Utils._is_printable(xid): xidstr = Utils._split_str(xid) else: xidstr = Utils._hex_split_str(xid) if xidsize == None: xidsize = len(xid) elif xidsize != len(xid): raise jerr.XidSizeError(xidsize, len(xid), xidstr) return "xid(%d)=\"%s\" " % (xidsize, xidstr) format_xid = staticmethod(format_xid) #@staticmethod def inv_str(string): """Perform a binary 1's compliment (invert all bits) on a binary string""" istr = "" for index in range(0, len(string)): istr += chr(~ord(string[index]) & 0xff) return istr inv_str = staticmethod(inv_str) #@staticmethod def load(fhandle, klass): """Load a record of class klass from a file""" args = Utils._load_args(fhandle, klass) subclass = klass.discriminate(args) result = subclass(*args) # create instance of record if subclass != klass: result.init(fhandle, *Utils._load_args(fhandle, subclass)) result.skip(fhandle) return result load = staticmethod(load) #@staticmethod def load_file_data(fhandle, size, data): """Load the data portion of a message from file""" if size == 0: return (data, True) if data == None: loaded = 0 else: loaded = len(data) foverflow = fhandle.tell() + size - loaded > JRNL_FILE_SIZE if foverflow: rsize = JRNL_FILE_SIZE - fhandle.tell() else: rsize = size - loaded fbin = fhandle.read(rsize) if data == None: data = unpack("%ds" % (rsize), fbin)[0] else: data = data + unpack("%ds" % (rsize), fbin)[0] return (data, not foverflow) load_file_data = staticmethod(load_file_data) #@staticmethod def rem_bytes_in_blk(fhandle, blk_size): """Return the remaining bytes in a block""" foffs = fhandle.tell() return Utils.size_in_bytes_to_blk(foffs, blk_size) - foffs rem_bytes_in_blk = staticmethod(rem_bytes_in_blk) #@staticmethod def size_in_blks(size, blk_size): """Return the size in terms of data blocks""" return int((size + blk_size - 1) / blk_size) size_in_blks = staticmethod(size_in_blks) #@staticmethod def size_in_bytes_to_blk(size, blk_size): """Return the bytes remaining until the next block boundary""" return Utils.size_in_blks(size, blk_size) * blk_size size_in_bytes_to_blk = staticmethod(size_in_bytes_to_blk) #@staticmethod def _hex_split_str(in_str, split_size = 50): """Split a hex string into two parts separated by an ellipsis""" if len(in_str) <= split_size: return Utils._hex_str(in_str, 0, len(in_str)) # if len(in_str) > split_size + 25: # return Utils._hex_str(in_str, 0, 10) + " ... " + Utils._hex_str(in_str, 55, 65) + " ... " + \ # Utils._hex_str(in_str, len(in_str)-10, len(in_str)) return Utils._hex_str(in_str, 0, 10) + " ... " + Utils._hex_str(in_str, len(in_str)-10, len(in_str)) _hex_split_str = staticmethod(_hex_split_str) #@staticmethod def _hex_str(in_str, begin, end): """Return a binary string as a hex string""" hstr = "" for index in range(begin, end): if Utils._is_printable(in_str[index]): hstr += in_str[index] else: hstr += "\\%02x" % ord(in_str[index]) return hstr _hex_str = staticmethod(_hex_str) #@staticmethod def _is_printable(in_str): """Return True if in_str in printable; False otherwise.""" return in_str.strip(Utils.__printchars) == "" _is_printable = staticmethod(_is_printable) #@staticmethod def _load_args(fhandle, klass): """Load the arguments from class klass""" size = calcsize(klass.FORMAT) foffs = fhandle.tell(), fbin = fhandle.read(size) if len(fbin) != size: raise jerr.UnexpectedEndOfFileError(size, len(fbin)) return foffs + unpack(klass.FORMAT, fbin) _load_args = staticmethod(_load_args) #@staticmethod def _split_str(in_str, split_size = 50): """Split a string into two parts separated by an ellipsis if it is longer than split_size""" if len(in_str) < split_size: return in_str return in_str[:25] + " ... " + in_str[-25:] _split_str = staticmethod(_split_str) #== class Hdr ================================================================= class Hdr: """Class representing the journal header records""" FORMAT = "=4sBBHQ" HDR_VER = 1 OWI_MASK = 0x01 BIG_ENDIAN = sys.byteorder == "big" REC_BOUNDARY = DBLK_SIZE def __init__(self, foffs, magic, ver, endn, flags, rid): """Constructor""" # Sizeable.__init__(self) self.foffs = foffs self.magic = magic self.ver = ver self.endn = endn self.flags = flags self.rid = long(rid) def __str__(self): """Return string representation of this header""" if self.empty(): return "0x%08x: " % (self.foffs) if self.magic[-1] == "x": return "0x%08x: [\"%s\"]" % (self.foffs, self.magic) if self.magic[-1] in ["a", "c", "d", "e", "f", "x"]: return "0x%08x: [\"%s\" v=%d e=%d f=0x%04x rid=0x%x]" % (self.foffs, self.magic, self.ver, self.endn, self.flags, self.rid) return "0x%08x: " % (self.foffs, self.magic) #@staticmethod def discriminate(args): """Use the last char in the header magic to determine the header type""" return _CLASSES.get(args[1][-1], Hdr) discriminate = staticmethod(discriminate) def empty(self): """Return True if this record is empty (ie has a magic of 0x0000""" return self.magic == "\x00"*4 def encode(self): """Encode the header into a binary string""" return pack(Hdr.FORMAT, self.magic, self.ver, self.endn, self.flags, self.rid) def owi(self): """Return the OWI (overwrite indicator) for this header""" return self.flags & self.OWI_MASK != 0 def skip(self, fhandle): """Read and discard the remainder of this record""" fhandle.read(Utils.rem_bytes_in_blk(fhandle, self.REC_BOUNDARY)) def check(self): """Check that this record is valid""" if self.empty() or self.magic[:3] != "RHM" or self.magic[3] not in ["a", "c", "d", "e", "f", "x"]: return True if self.magic[-1] != "x": if self.ver != self.HDR_VER: raise jerr.InvalidHeaderVersionError(self.HDR_VER, self.ver) if bool(self.endn) != self.BIG_ENDIAN: raise jerr.EndianMismatchError(self.BIG_ENDIAN) return False #== class FileHdr ============================================================= class FileHdr(Hdr): """Class for file headers, found at the beginning of journal files""" FORMAT = "=2H4x3Q" REC_BOUNDARY = SBLK_SIZE def __str__(self): """Return a string representation of the this FileHdr instance""" return "%s fid=%d lid=%d fro=0x%08x t=%s" % (Hdr.__str__(self), self.fid, self.lid, self.fro, self.timestamp_str()) def encode(self): """Encode this class into a binary string""" return Hdr.encode(self) + pack(FileHdr.FORMAT, self.fid, self.lid, self.fro, self.time_sec, self.time_ns) def init(self, fhandle, foffs, fid, lid, fro, time_sec, time_ns): """Initialize this instance to known values""" self.fid = fid self.lid = lid self.fro = fro self.time_sec = time_sec self.time_ns = time_ns def timestamp(self): """Get the timestamp of this record as a tuple (secs, nsecs)""" return (self.time_sec, self.time_ns) def timestamp_str(self): """Get the timestamp of this record in string format""" time = gmtime(self.time_sec) fstr = "%%a %%b %%d %%H:%%M:%%S.%09d %%Y" % (self.time_ns) return strftime(fstr, time) #== class DeqRec ============================================================== class DeqRec(Hdr): """Class for a dequeue record""" FORMAT = "=QQ" def __str__(self): """Return a string representation of the this DeqRec instance""" return "%s %sdrid=0x%x" % (Hdr.__str__(self), Utils.format_xid(self.xid, self.xidsize), self.deq_rid) def init(self, fhandle, foffs, deq_rid, xidsize): """Initialize this instance to known values""" self.deq_rid = deq_rid self.xidsize = xidsize self.xid = None self.deq_tail = None self.xid_complete = False self.tail_complete = False self.tail_bin = None self.tail_offs = 0 self.load(fhandle) def encode(self): """Encode this class into a binary string""" buf = Hdr.encode(self) + pack(DeqRec.FORMAT, self.deq_rid, self.xidsize) if self.xidsize > 0: fmt = "%ds" % (self.xidsize) buf += pack(fmt, self.xid) buf += self.deq_tail.encode() return buf def load(self, fhandle): """Load the remainder of this record (after the header has been loaded""" if self.xidsize == 0: self.xid_complete = True self.tail_complete = True else: if not self.xid_complete: (self.xid, self.xid_complete) = Utils.load_file_data(fhandle, self.xidsize, self.xid) if self.xid_complete and not self.tail_complete: ret = Utils.load_file_data(fhandle, calcsize(RecTail.FORMAT), self.tail_bin) self.tail_bin = ret[0] if ret[1]: self.deq_tail = RecTail(self.tail_offs, *unpack(RecTail.FORMAT, self.tail_bin)) magic_err = self.deq_tail.magic_inv != Utils.inv_str(self.magic) rid_err = self.deq_tail.rid != self.rid if magic_err or rid_err: raise jerr.InvalidRecordTailError(magic_err, rid_err, self) self.skip(fhandle) self.tail_complete = ret[1] return self.complete() def complete(self): """Returns True if the entire record is loaded, False otherwise""" return self.xid_complete and self.tail_complete #== class TxnRec ============================================================== class TxnRec(Hdr): """Class for a transaction commit/abort record""" FORMAT = "=Q" def __str__(self): """Return a string representation of the this TxnRec instance""" return "%s %s" % (Hdr.__str__(self), Utils.format_xid(self.xid, self.xidsize)) def init(self, fhandle, foffs, xidsize): """Initialize this instance to known values""" self.xidsize = xidsize self.xid = None self.tx_tail = None self.xid_complete = False self.tail_complete = False self.tail_bin = None self.tail_offs = 0 self.load(fhandle) def encode(self): """Encode this class into a binary string""" return Hdr.encode(self) + pack(TxnRec.FORMAT, self.xidsize) + pack("%ds" % self.xidsize, self.xid) + \ self.tx_tail.encode() def load(self, fhandle): """Load the remainder of this record (after the header has been loaded""" if not self.xid_complete: ret = Utils.load_file_data(fhandle, self.xidsize, self.xid) self.xid = ret[0] self.xid_complete = ret[1] if self.xid_complete and not self.tail_complete: ret = Utils.load_file_data(fhandle, calcsize(RecTail.FORMAT), self.tail_bin) self.tail_bin = ret[0] if ret[1]: self.tx_tail = RecTail(self.tail_offs, *unpack(RecTail.FORMAT, self.tail_bin)) magic_err = self.tx_tail.magic_inv != Utils.inv_str(self.magic) rid_err = self.tx_tail.rid != self.rid if magic_err or rid_err: raise jerr.InvalidRecordTailError(magic_err, rid_err, self) self.skip(fhandle) self.tail_complete = ret[1] return self.complete() def complete(self): """Returns True if the entire record is loaded, False otherwise""" return self.xid_complete and self.tail_complete #== class EnqRec ============================================================== class EnqRec(Hdr): """Class for a enqueue record""" FORMAT = "=QQ" TRANSIENT_MASK = 0x10 EXTERN_MASK = 0x20 def __str__(self): """Return a string representation of the this EnqRec instance""" return "%s %s%s %s %s" % (Hdr.__str__(self), Utils.format_xid(self.xid, self.xidsize), Utils.format_data(self.dsize, self.data), self.enq_tail, self.print_flags()) def encode(self): """Encode this class into a binary string""" buf = Hdr.encode(self) + pack(EnqRec.FORMAT, self.xidsize, self.dsize) if self.xidsize > 0: buf += pack("%ds" % self.xidsize, self.xid) if self.dsize > 0: buf += pack("%ds" % self.dsize, self.data) if self.xidsize > 0 or self.dsize > 0: buf += self.enq_tail.encode() return buf def init(self, fhandle, foffs, xidsize, dsize): """Initialize this instance to known values""" self.xidsize = xidsize self.dsize = dsize self.transient = self.flags & self.TRANSIENT_MASK > 0 self.extern = self.flags & self.EXTERN_MASK > 0 self.xid = None self.data = None self.enq_tail = None self.xid_complete = False self.data_complete = False self.tail_complete = False self.tail_bin = None self.tail_offs = 0 self.load(fhandle) def load(self, fhandle): """Load the remainder of this record (after the header has been loaded""" if not self.xid_complete: ret = Utils.load_file_data(fhandle, self.xidsize, self.xid) self.xid = ret[0] self.xid_complete = ret[1] if self.xid_complete and not self.data_complete: if self.extern: self.data_complete = True else: ret = Utils.load_file_data(fhandle, self.dsize, self.data) self.data = ret[0] self.data_complete = ret[1] if self.data_complete and not self.tail_complete: ret = Utils.load_file_data(fhandle, calcsize(RecTail.FORMAT), self.tail_bin) self.tail_bin = ret[0] if ret[1]: self.enq_tail = RecTail(self.tail_offs, *unpack(RecTail.FORMAT, self.tail_bin)) magic_err = self.enq_tail.magic_inv != Utils.inv_str(self.magic) rid_err = self.enq_tail.rid != self.rid if magic_err or rid_err: raise jerr.InvalidRecordTailError(magic_err, rid_err, self) self.skip(fhandle) self.tail_complete = ret[1] return self.complete() def complete(self): """Returns True if the entire record is loaded, False otherwise""" return self.xid_complete and self.data_complete and self.tail_complete def print_flags(self): """Utility function to decode the flags field in the header and print a string representation""" fstr = "" if self.transient: fstr = "*TRANSIENT" if self.extern: if len(fstr) > 0: fstr += ",EXTERNAL" else: fstr = "*EXTERNAL" if len(fstr) > 0: fstr += "*" return fstr #== class RecTail ============================================================= class RecTail: """Class for a record tail - for all records where either an XID or data separate the header from the end of the record""" FORMAT = "=4sQ" def __init__(self, foffs, magic_inv, rid): """Initialize this instance to known values""" self.foffs = foffs self.magic_inv = magic_inv self.rid = long(rid) def __str__(self): """Return a string representation of the this RecTail instance""" magic = Utils.inv_str(self.magic_inv) return "[\"%s\" rid=0x%x]" % (magic, self.rid) def encode(self): """Encode this class into a binary string""" return pack(RecTail.FORMAT, self.magic_inv, self.rid) #== class JrnlInfo ============================================================ class JrnlInfo(object): """ This object reads and writes journal information files (.jinf). Methods are provided to read a file, query its properties and reset just those properties necessary for normalizing and resizing a journal. Normalizing: resetting the directory and/or base filename to different values. This is necessary if a set of journal files is copied from one location to another before being restored, as the value of the path in the file no longer matches the actual path. Resizing: If the journal geometry parameters (size and number of journal files) changes, then the .jinf file must reflect these changes, as this file is the source of information for journal recovery. NOTE: Data size vs File size: There are methods which return the data size and file size of the journal files. +-------------+--------------------/ /----------+ | File header | File data | +-------------+--------------------/ /----------+ | | | | |<---------- Data size ---------->| |<------------------ File Size ---------------->| Data size: The size of the data content of the journal, ie that part which stores the data records. File size: The actual disk size of the journal including data and the file header which precedes the data. The file header is fixed to 1 sblk, so file size = jrnl size + sblk size. """ def __init__(self, jdir, bfn = "JournalData"): """Constructor""" self.__jdir = jdir self.__bfn = bfn self.__jinf_dict = {} self._read_jinf() def __str__(self): """Create a string containing all of the journal info contained in the jinf file""" ostr = "Journal info file %s:\n" % os.path.join(self.__jdir, "%s.jinf" % self.__bfn) for key, val in self.__jinf_dict.iteritems(): ostr += " %s = %s\n" % (key, val) return ostr def normalize(self, jdir = None, bfn = None): """Normalize the directory (ie reset the directory path to match the actual current location) for this jinf file""" if jdir == None: self.__jinf_dict["directory"] = self.__jdir else: self.__jdir = jdir self.__jinf_dict["directory"] = jdir if bfn != None: self.__bfn = bfn self.__jinf_dict["base_filename"] = bfn def resize(self, num_jrnl_files = None, jrnl_file_size = None): """Reset the journal size information to allow for resizing the journal""" if num_jrnl_files != None: self.__jinf_dict["number_jrnl_files"] = num_jrnl_files if jrnl_file_size != None: self.__jinf_dict["jrnl_file_size_sblks"] = jrnl_file_size * self.get_jrnl_dblk_size_bytes() def write(self, jdir = None, bfn = None): """Write the .jinf file""" self.normalize(jdir, bfn) if not os.path.exists(self.get_jrnl_dir()): os.makedirs(self.get_jrnl_dir()) fhandle = open(os.path.join(self.get_jrnl_dir(), "%s.jinf" % self.get_jrnl_base_name()), "w") fhandle.write("\n") fhandle.write("\n") fhandle.write(" \n" % self.get_jrnl_version()) fhandle.write(" \n") fhandle.write(" \n" % self.get_jrnl_id()) fhandle.write(" \n" % self.get_jrnl_dir()) fhandle.write(" \n" % self.get_jrnl_base_name()) fhandle.write(" \n") fhandle.write(" \n") fhandle.write(" \n" % self.get_creation_time()[0]) fhandle.write(" \n" % self.get_creation_time()[1]) fhandle.write(" \n" % self.get_creation_time_str()) fhandle.write(" \n") fhandle.write(" \n") fhandle.write(" \n" % self.get_num_jrnl_files()) fhandle.write(" \n" % str.lower(str(self.get_auto_expand()))) fhandle.write(" \n" % self.get_jrnl_data_size_sblks()) fhandle.write(" \n" % self.get_jrnl_sblk_size_dblks()) fhandle.write(" \n" % self.get_jrnl_dblk_size_bytes()) fhandle.write(" \n") fhandle.write(" \n") fhandle.write(" \n" % self.get_wr_buf_pg_size_sblks()) fhandle.write(" \n" % self.get_num_wr_buf_pgs()) fhandle.write(" \n" % self.get_rd_buf_pg_size_sblks()) fhandle.write(" \n" % self.get_num_rd_buf_pgs()) fhandle.write(" \n") fhandle.write("\n") fhandle.close() # Journal ID def get_jrnl_version(self): """Get the journal version""" return self.__jinf_dict["journal_version"] def get_jrnl_id(self): """Get the journal id""" return self.__jinf_dict["id_string"] def get_current_dir(self): """Get the current directory of the store (as opposed to that value saved in the .jinf file)""" return self.__jdir def get_jrnl_dir(self): """Get the journal directory stored in the .jinf file""" return self.__jinf_dict["directory"] def get_jrnl_base_name(self): """Get the base filename - that string used to name the journal files -nnnn.jdat and .jinf""" return self.__jinf_dict["base_filename"] # Journal creation time def get_creation_time(self): """Get journal creation time as a tuple (secs, nsecs)""" return (self.__jinf_dict["seconds"], self.__jinf_dict["nanoseconds"]) def get_creation_time_str(self): """Get journal creation time as a string""" return self.__jinf_dict["string"] # --- Files and geometry --- def get_num_jrnl_files(self): """Get number of data files in the journal""" return self.__jinf_dict["number_jrnl_files"] def get_auto_expand(self): """Return True if auto-expand is enabled; False otherwise""" return self.__jinf_dict["auto_expand"] def get_jrnl_sblk_size_dblks(self): """Get the journal softblock size in dblks""" return self.__jinf_dict["JRNL_SBLK_SIZE"] def get_jrnl_sblk_size_bytes(self): """Get the journal softblock size in bytes""" return self.get_jrnl_sblk_size_dblks() * self.get_jrnl_dblk_size_bytes() def get_jrnl_dblk_size_bytes(self): """Get the journal datablock size in bytes""" return self.__jinf_dict["JRNL_DBLK_SIZE"] def get_jrnl_data_size_sblks(self): """Get the data capacity (excluding the file headers) for one journal file in softblocks""" return self.__jinf_dict["jrnl_file_size_sblks"] def get_jrnl_data_size_dblks(self): """Get the data capacity (excluding the file headers) for one journal file in datablocks""" return self.get_jrnl_data_size_sblks() * self.get_jrnl_sblk_size_dblks() def get_jrnl_data_size_bytes(self): """Get the data capacity (excluding the file headers) for one journal file in bytes""" return self.get_jrnl_data_size_dblks() * self.get_jrnl_dblk_size_bytes() def get_jrnl_file_size_sblks(self): """Get the size of one journal file on disk (including the file headers) in softblocks""" return self.get_jrnl_data_size_sblks() + 1 def get_jrnl_file_size_dblks(self): """Get the size of one journal file on disk (including the file headers) in datablocks""" return self.get_jrnl_file_size_sblks() * self.get_jrnl_sblk_size_dblks() def get_jrnl_file_size_bytes(self): """Get the size of one journal file on disk (including the file headers) in bytes""" return self.get_jrnl_file_size_dblks() * self.get_jrnl_dblk_size_bytes() def get_tot_jrnl_data_size_sblks(self): """Get the size of the entire jouranl's data capacity (excluding the file headers) for all files together in softblocks""" return self.get_num_jrnl_files() * self.get_jrnl_data_size_bytes() def get_tot_jrnl_data_size_dblks(self): """Get the size of the entire jouranl's data capacity (excluding the file headers) for all files together in datablocks""" return self.get_num_jrnl_files() * self.get_jrnl_data_size_dblks() def get_tot_jrnl_data_size_bytes(self): """Get the size of the entire jouranl's data capacity (excluding the file headers) for all files together in bytes""" return self.get_num_jrnl_files() * self.get_jrnl_data_size_bytes() # Read and write buffers def get_wr_buf_pg_size_sblks(self): """Get the size of the write buffer pages in softblocks""" return self.__jinf_dict["wcache_pgsize_sblks"] def get_wr_buf_pg_size_dblks(self): """Get the size of the write buffer pages in datablocks""" return self.get_wr_buf_pg_size_sblks() * self.get_jrnl_sblk_size_dblks() def get_wr_buf_pg_size_bytes(self): """Get the size of the write buffer pages in bytes""" return self.get_wr_buf_pg_size_dblks() * self.get_jrnl_dblk_size_bytes() def get_num_wr_buf_pgs(self): """Get the number of write buffer pages""" return self.__jinf_dict["wcache_num_pages"] def get_rd_buf_pg_size_sblks(self): """Get the size of the read buffer pages in softblocks""" return self.__jinf_dict["JRNL_RMGR_PAGE_SIZE"] def get_rd_buf_pg_size_dblks(self): """Get the size of the read buffer pages in datablocks""" return self.get_rd_buf_pg_size_sblks * self.get_jrnl_sblk_size_dblks() def get_rd_buf_pg_size_bytes(self): """Get the size of the read buffer pages in bytes""" return self.get_rd_buf_pg_size_dblks * self.get_jrnl_dblk_size_bytes() def get_num_rd_buf_pgs(self): """Get the number of read buffer pages""" return self.__jinf_dict["JRNL_RMGR_PAGES"] def _read_jinf(self): """Read and initialize this instance from an existing jinf file located at the directory named in the constructor - called by the constructor""" fhandle = open(os.path.join(self.__jdir, "%s.jinf" % self.__bfn), "r") parser = xml.parsers.expat.ParserCreate() parser.StartElementHandler = self._handle_xml_start_elt parser.CharacterDataHandler = self._handle_xml_char_data parser.EndElementHandler = self._handle_xml_end_elt parser.ParseFile(fhandle) fhandle.close() def _handle_xml_start_elt(self, name, attrs): """Callback for handling XML start elements. Used by the XML parser.""" # bool values if name == "auto_expand": self.__jinf_dict[name] = attrs["value"] == "true" # long values elif name == "seconds" or \ name == "nanoseconds": self.__jinf_dict[name] = long(attrs["value"]) # int values elif name == "journal_version" or \ name == "number_jrnl_files" or \ name == "jrnl_file_size_sblks" or \ name == "JRNL_SBLK_SIZE" or \ name == "JRNL_DBLK_SIZE" or \ name == "wcache_pgsize_sblks" or \ name == "wcache_num_pages" or \ name == "JRNL_RMGR_PAGE_SIZE" or \ name == "JRNL_RMGR_PAGES": self.__jinf_dict[name] = int(attrs["value"]) # strings elif "value" in attrs: self.__jinf_dict[name] = attrs["value"] def _handle_xml_char_data(self, data): """Callback for handling character data (ie within ...). The jinf file does not use this in its data. Used by the XML parser.""" pass def _handle_xml_end_elt(self, name): """Callback for handling XML end elements. Used by XML parser.""" pass #============================================================================== _CLASSES = { "a": TxnRec, "c": TxnRec, "d": DeqRec, "e": EnqRec, "f": FileHdr } if __name__ == "__main__": print "This is a library, and cannot be executed." qpid-cpp-store-debian-0.16/tools/qpidstore/jerr.py0000644000176300017630000002300311562255465021232 0ustar cajuscajus""" Copyright (c) 2007, 2008 Red Hat, Inc. This file is part of the Qpid async store library msgstore.so. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA The GNU Lesser General Public License is available in the file COPYING. """ # == Warnings ================================================================= class JWarning(Exception): """Class to convey a warning""" def __init__(self, err): """Constructor""" Exception.__init__(self, err) # == Errors =================================================================== class AllJrnlFilesEmptyCsvError(Exception): """All journal files are empty (never been written)""" def __init__(self, tnum, exp_num_msgs): """Constructor""" Exception.__init__(self, "[CSV %d] All journal files are empty, but test expects %d msg(s)." % (tnum, exp_num_msgs)) class AlreadyLockedError(Exception): """Error class for trying to lock a record that is already locked""" def __init__(self, rid): """Constructor""" Exception.__init__(self, "Locking record which is already locked in EnqMap: rid=0x%x" % rid) class BadFileNumberError(Exception): """Error class for incorrect or unexpected file number""" def __init__(self, file_num): """Constructor""" Exception.__init__(self, "Bad file number %d" % file_num) class DataSizeError(Exception): """Error class for data size mismatch""" def __init__(self, exp_size, act_size, data_str): """Constructor""" Exception.__init__(self, "Inconsistent data size: expected:%d; actual:%d; data=\"%s\"" % (exp_size, act_size, data_str)) class DeleteLockedRecordError(Exception): """Error class for deleting a locked record from the enqueue map""" def __init__(self, rid): """Constructor""" Exception.__init__(self, "Deleting locked record from EnqMap: rid=0x%s" % rid) class DequeueNonExistentEnqueueError(Exception): """Error class for attempting to dequeue a non-existent enqueue record (rid)""" def __init__(self, deq_rid): """Constructor""" Exception.__init__(self, "Dequeuing non-existent enqueue record: rid=0x%s" % deq_rid) class DuplicateRidError(Exception): """Error class for placing duplicate rid into enqueue map""" def __init__(self, rid): """Constructor""" Exception.__init__(self, "Adding duplicate record to EnqMap: rid=0x%x" % rid) class EndianMismatchError(Exception): """Error class mismatched record header endian flag""" def __init__(self, exp_endianness): """Constructor""" Exception.__init__(self, "Endian mismatch: expected %s, but current record is %s" % self.endian_str(exp_endianness)) #@staticmethod def endian_str(endianness): """Return a string tuple for the endianness error message""" if endianness: return "big", "little" return "little", "big" endian_str = staticmethod(endian_str) class ExternFlagDataError(Exception): """Error class for the extern flag being set and the internal size > 0""" def __init__(self, hdr): """Constructor""" Exception.__init__(self, "Message data found (msg size > 0) on record with external flag set: hdr=%s" % hdr) class ExternFlagCsvError(Exception): """External flag mismatch between record and CSV test file""" def __init__(self, tnum, exp_extern_flag): """Constructor""" Exception.__init__(self, "[CSV %d] External flag mismatch: expected %s" % (tnum, exp_extern_flag)) class ExternFlagWithDataCsvError(Exception): """External flag set and Message data found""" def __init__(self, tnum): """Constructor""" Exception.__init__(self, "[CSV %d] Message data found on record with external flag set" % tnum) class FillExceedsFileSizeError(Exception): """Internal error from a fill operation which will exceed the specified file size""" def __init__(self, cur_size, file_size): """Constructor""" Exception.__init__(self, "Filling to size %d > max file size %d" % (cur_size, file_size)) class FillSizeError(Exception): """Internal error from a fill operation that did not match the calculated end point in the file""" def __init__(self, cur_posn, exp_posn): """Constructor""" Exception.__init__(self, "Filled to size %d > expected file posn %d" % (cur_posn, exp_posn)) class FirstRecordOffsetMismatch(Exception): """Error class for file header fro mismatch with actual record""" def __init__(self, fro, actual_offs): """Constructor""" Exception.__init__(self, "File header first record offset mismatch: fro=0x%x; actual offs=0x%x" % (fro, actual_offs)) class InvalidHeaderVersionError(Exception): """Error class for invalid record header version""" def __init__(self, exp_ver, act_ver): """Constructor""" Exception.__init__(self, "Invalid header version: expected:%d, actual:%d." % (exp_ver, act_ver)) class InvalidRecordTypeError(Exception): """Error class for any operation using an invalid record type""" def __init__(self, operation, magic, rid): """Constructor""" Exception.__init__(self, "Invalid record type for operation: operation=%s record magic=%s, rid=0x%x" % (operation, magic, rid)) class InvalidRecordTailError(Exception): """Error class for invalid record tail""" def __init__(self, magic_err, rid_err, rec): """Constructor""" Exception.__init__(self, " > %s *INVALID TAIL RECORD (%s)*" % (rec, self.tail_err_str(magic_err, rid_err))) #@staticmethod def tail_err_str(magic_err, rid_err): """Return a string indicating the tail record error(s)""" estr = "" if magic_err: estr = "magic bad" if rid_err: estr += ", " if rid_err: estr += "rid mismatch" return estr tail_err_str = staticmethod(tail_err_str) class NonExistentRecordError(Exception): """Error class for any operation on an non-existent record""" def __init__(self, operation, rid): """Constructor""" Exception.__init__(self, "Operation on non-existent record: operation=%s; rid=0x%x" % (operation, rid)) class NotLockedError(Exception): """Error class for unlocking a record which is not locked in the first place""" def __init__(self, rid): """Constructor""" Exception.__init__(self, "Unlocking record which is not locked in EnqMap: rid=0x%x" % rid) class JournalSpaceExceededError(Exception): """Error class for when journal space of resized journal is too small to contain the transferred records""" def __init__(self): """Constructor""" Exception.__init__(self, "Ran out of journal space while writing records") class MessageLengthCsvError(Exception): """Message length mismatch between record and CSV test file""" def __init__(self, tnum, exp_msg_len, actual_msg_len): """Constructor""" Exception.__init__(self, "[CSV %d] Message length mismatch: expected %d; found %d" % (tnum, exp_msg_len, actual_msg_len)) class NumMsgsCsvError(Exception): """Number of messages found mismatched with CSV file""" def __init__(self, tnum, exp_num_msgs, actual_num_msgs): """Constructor""" Exception.__init__(self, "[CSV %s] Incorrect number of messages: expected %d, found %d" % (tnum, exp_num_msgs, actual_num_msgs)) class TransactionCsvError(Exception): """Transaction mismatch between record and CSV file""" def __init__(self, tnum, exp_transactional): """Constructor""" Exception.__init__(self, "[CSV %d] Transaction mismatch: expected %s" % (tnum, exp_transactional)) class UnexpectedEndOfFileError(Exception): """Error class for unexpected end-of-file during reading""" def __init__(self, exp_size, curr_offs): """Constructor""" Exception.__init__(self, "Unexpected end-of-file: expected file size:%d; current offset:%d" % (exp_size, curr_offs)) class XidLengthCsvError(Exception): """Message Xid length mismatch between record and CSV file""" def __init__(self, tnum, exp_xid_len, actual_msg_len): """Constructor""" Exception.__init__(self, "[CSV %d] Message XID mismatch: expected %d; found %d" % (tnum, exp_xid_len, actual_msg_len)) class XidSizeError(Exception): """Error class for Xid size mismatch""" def __init__(self, exp_size, act_size, xid_str): """Constructor""" Exception.__init__(self, "Inconsistent xid size: expected:%d; actual:%d; xid=\"%s\"" % (exp_size, act_size, xid_str)) # ============================================================================= if __name__ == "__main__": print "This is a library, and cannot be executed." qpid-cpp-store-debian-0.16/tools/qpidstore/janal.py0000644000176300017630000005473011701656157021366 0ustar cajuscajus""" Copyright (c) 2007, 2008 Red Hat, Inc. This file is part of the Qpid async store library msgstore.so. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA The GNU Lesser General Public License is available in the file COPYING. """ import jerr, jrnl import os.path, sys #== class EnqMap ============================================================== class EnqMap(object): """Class for maintaining a map of enqueued records, indexing the rid against hdr, fid and transaction lock""" def __init__(self): """Constructor""" self.__map = {} def __str__(self): """Print the contents of the map""" return self.report(True, True) def add(self, fid, hdr, lock = False): """Add a new record into the map""" if hdr.rid in self.__map: raise jerr.DuplicateRidError(hdr.rid) self.__map[hdr.rid] = [fid, hdr, lock] def contains(self, rid): """Return True if the map contains the given rid""" return rid in self.__map def delete(self, rid): """Delete the rid and its associated data from the map""" if rid in self.__map: if self.get_lock(rid): raise jerr.DeleteLockedRecordError(rid) del self.__map[rid] else: raise jerr.JWarning("ERROR: Deleting non-existent rid from EnqMap: rid=0x%x" % rid) def get(self, rid): """Return a list [fid, hdr, lock] for the given rid""" if self.contains(rid): return self.__map[rid] return None def get_fid(self, rid): """Return the fid for the given rid""" if self.contains(rid): return self.__map[rid][0] return None def get_hdr(self, rid): """Return the header record for the given rid""" if self.contains(rid): return self.__map[rid][1] return None def get_lock(self, rid): """Return the transaction lock value for the given rid""" if self.contains(rid): return self.__map[rid][2] return None def get_rec_list(self): """Return a list of tuples (fid, hdr, lock) for all entries in the map""" return self.__map.values() def lock(self, rid): """Set the transaction lock for a given rid to True""" if rid in self.__map: if not self.__map[rid][2]: # locked self.__map[rid][2] = True else: raise jerr.AlreadyLockedError(rid) else: raise jerr.JWarning("ERROR: Locking non-existent rid in EnqMap: rid=0x%x" % rid) def report(self, show_stats, show_records): """Return a string containing a text report for all records in the map""" if len(self.__map) == 0: return "No enqueued records found." rstr = "%d enqueued records found" % len(self.__map) if show_records: rstr += ":" rid_list = self.__map.keys() rid_list.sort() for rid in rid_list: if self.__map[rid][2]: lock_str = " [LOCKED]" else: lock_str = "" rstr += "\n lfid=%d %s %s" % (rec[0], rec[1], lock_str) else: rstr += "." return rstr def rids(self): """Return a list of rids in the map""" return self.__map.keys() def size(self): """Return the number of entries in the map""" return len(self.__map) def unlock(self, rid): """Set the transaction lock for a given rid to False""" if rid in self.__map: if self.__map[rid][2]: self.__map[rid][2] = False else: raise jerr.NotLockedError(rid) else: raise jerr.NonExistentRecordError("unlock", rid) #== class TxnMap ============================================================== class TxnMap(object): """Transaction map, which maps xids to a list of outstanding actions""" def __init__(self, emap): """Constructor, requires an existing EnqMap instance""" self.__emap = emap self.__map = {} def __str__(self): """Print the contents of the map""" return self.report(True, True) def add(self, fid, hdr): """Add a new transactional record into the map""" if isinstance(hdr, jrnl.DeqRec): try: self.__emap.lock(hdr.deq_rid) except jerr.JWarning: # Not in emap, look for rid in tmap l = self.find_rid(hdr.deq_rid, hdr.xid) if l != None: if l[2]: raise jerr.AlreadyLockedError(hdr.deq_rid) l[2] = True if hdr.xid in self.__map: self.__map[hdr.xid].append([fid, hdr, False]) # append to existing list else: self.__map[hdr.xid] = [[fid, hdr, False]] # create new list def contains(self, xid): """Return True if the xid exists in the map; False otherwise""" return xid in self.__map def delete(self, hdr): """Remove a transaction record from the map using either a commit or abort header""" if hdr.magic[-1] == "c": return self._commit(hdr.xid) if hdr.magic[-1] == "a": self._abort(hdr.xid) else: raise jerr.InvalidRecordTypeError("delete from TxnMap", hdr.magic, hdr.rid) def find_rid(self, rid, xid_hint = None): """ Search for and return map list with supplied rid. If xid_hint is supplied, try that xid first""" if xid_hint != None and self.contains(xid_hint): for l in self.__map[xid_hint]: if l[1].rid == rid: return l for xid in self.__map.iterkeys(): if xid_hint == None or xid != xid_hint: for l in self.__map[xid]: if l[1].rid == rid: return l def get(self, xid): """Return a list of operations for the given xid""" if self.contains(xid): return self.__map[xid] def report(self, show_stats, show_records): """Return a string containing a text report for all records in the map""" if len(self.__map) == 0: return "No outstanding transactions found." rstr = "%d outstanding transactions found" % len(self.__map) if show_records: rstr += ":" for xid, tup in self.__map.iteritems(): rstr += "\n xid=%s:" % jrnl.Utils.format_xid(xid) for i in tup: rstr += "\n %s" % str(i[1]) else: rstr += "." return rstr def size(self): """Return the number of xids in the map""" return len(self.__map) def xids(self): """Return a list of xids in the map""" return self.__map.keys() def _abort(self, xid): """Perform an abort operation for the given xid record""" for fid, hdr, lock in self.__map[xid]: if isinstance(hdr, jrnl.DeqRec): self.__emap.unlock(hdr.deq_rid) del self.__map[xid] def _commit(self, xid): """Perform a commit operation for the given xid record""" mismatch_list = [] for fid, hdr, lock in self.__map[xid]: if isinstance(hdr, jrnl.EnqRec): self.__emap.add(fid, hdr, lock) # Transfer enq to emap else: if self.__emap.contains(hdr.deq_rid): self.__emap.unlock(hdr.deq_rid) self.__emap.delete(hdr.deq_rid) else: mismatch_list.append("0x%x" % hdr.deq_rid) del self.__map[xid] return mismatch_list #== class JrnlAnalyzer ======================================================== class JrnlAnalyzer(object): """ This class analyzes a set of journal files and determines which is the last to be written (the newest file), and hence which should be the first to be read for recovery (the oldest file). The analysis is performed on construction; the contents of the JrnlInfo object passed provide the recovery details. """ def __init__(self, jinf): """Constructor""" self.__oldest = None self.__jinf = jinf self.__flist = self._analyze() def __str__(self): """String representation of this JrnlAnalyzer instance, will print out results of analysis.""" ostr = "Journal files analyzed in directory %s (* = earliest full):\n" % self.__jinf.get_current_dir() if self.is_empty(): ostr += " \n" else: for tup in self.__flist: tmp = " " if tup[0] == self.__oldest[0]: tmp = "*" ostr += " %s %s: owi=%-5s rid=0x%x, fro=0x%x ts=%s\n" % (tmp, os.path.basename(tup[1]), tup[2], tup[3], tup[4], tup[5]) for i in range(self.__flist[-1][0] + 1, self.__jinf.get_num_jrnl_files()): ostr += " %s.%04x.jdat: \n" % (self.__jinf.get_jrnl_base_name(), i) return ostr # Analysis def get_oldest_file(self): """Return a tuple (ordnum, jfn, owi, rid, fro, timestamp) for the oldest data file found in the journal""" return self.__oldest def get_oldest_file_index(self): """Return the ordinal number of the oldest data file found in the journal""" if self.is_empty(): return None return self.__oldest[0] def is_empty(self): """Return true if the analysis found that the journal file has never been written to""" return len(self.__flist) == 0 def _analyze(self): """Perform the journal file analysis by reading and comparing the file headers of each journal data file""" owi_found = False flist = [] for i in range(0, self.__jinf.get_num_jrnl_files()): jfn = os.path.join(self.__jinf.get_current_dir(), "%s.%04x.jdat" % (self.__jinf.get_jrnl_base_name(), i)) fhandle = open(jfn) fhdr = jrnl.Utils.load(fhandle, jrnl.Hdr) if fhdr.empty(): break this_tup = (i, jfn, fhdr.owi(), fhdr.rid, fhdr.fro, fhdr.timestamp_str()) flist.append(this_tup) if i == 0: init_owi = fhdr.owi() self.__oldest = this_tup elif fhdr.owi() != init_owi and not owi_found: self.__oldest = this_tup owi_found = True return flist #== class JrnlReader ==================================================== class JrnlReader(object): """ This class contains an Enqueue Map (emap), a transaction map (tmap) and a transaction object list (txn_obj_list) which are populated by reading the journals from the oldest to the newest and analyzing each record. The JrnlInfo and JrnlAnalyzer objects supplied on construction provide the information used for the recovery. The analysis is performed on construction. """ def __init__(self, jinfo, jra, qflag = False, rflag = False, vflag = False): """Constructor, which reads all """ self._jinfo = jinfo self._jra = jra self._qflag = qflag self._rflag = rflag self._vflag = vflag # test callback functions for CSV tests self._csv_store_chk = None self._csv_start_cb = None self._csv_enq_cb = None self._csv_deq_cb = None self._csv_txn_cb = None self._csv_end_cb = None self._emap = EnqMap() self._tmap = TxnMap(self._emap) self._txn_obj_list = {} self._file = None self._file_hdr = None self._file_num = None self._first_rec_flag = None self._fro = None self._last_file_flag = None self._start_file_num = None self._file_hdr_owi = None self._warning = [] self._abort_cnt = 0 self._commit_cnt = 0 self._msg_cnt = 0 self._rec_cnt = 0 self._txn_msg_cnt = 0 def __str__(self): """Print out all the undequeued records""" return self.report(True, self._rflag) def emap(self): """Get the enqueue map""" return self._emap def get_abort_cnt(self): """Get the cumulative number of transactional aborts found""" return self._abort_cnt def get_commit_cnt(self): """Get the cumulative number of transactional commits found""" return self._commit_cnt def get_msg_cnt(self): """Get the cumulative number of messages found""" return self._msg_cnt def get_rec_cnt(self): """Get the cumulative number of journal records (including fillers) found""" return self._rec_cnt def is_last_file(self): """Return True if the last file is being read""" return self._last_file_flag def report(self, show_stats = True, show_records = False): """Return a string containing a report on the file analysis""" rstr = self._emap.report(show_stats, show_records) + "\n" + self._tmap.report(show_stats, show_records) #TODO - print size analysis here - ie how full, sparse, est. space remaining before enq threshold return rstr def run(self): """Perform the read of the journal""" if self._csv_start_cb != None and self._csv_start_cb(self._csv_store_chk): return if self._jra.is_empty(): return stop = self._advance_jrnl_file(*self._jra.get_oldest_file()) while not stop and not self._get_next_record(): pass if self._csv_end_cb != None and self._csv_end_cb(self._csv_store_chk): return if not self._qflag: print def set_callbacks(self, csv_store_chk, csv_start_cb = None, csv_enq_cb = None, csv_deq_cb = None, csv_txn_cb = None, csv_end_cb = None): """Set callbacks for checks to be made at various points while reading the journal""" self._csv_store_chk = csv_store_chk self._csv_start_cb = csv_start_cb self._csv_enq_cb = csv_enq_cb self._csv_deq_cb = csv_deq_cb self._csv_txn_cb = csv_txn_cb self._csv_end_cb = csv_end_cb def tmap(self): """Return the transaction map""" return self._tmap def get_txn_msg_cnt(self): """Get the cumulative transactional message count""" return self._txn_msg_cnt def txn_obj_list(self): """Get a cumulative list of transaction objects (commits and aborts)""" return self._txn_obj_list def _advance_jrnl_file(self, *oldest_file_info): """Rotate to using the next journal file. Return False if the operation was successful, True if there are no more files to read.""" fro_seek_flag = False if len(oldest_file_info) > 0: self._start_file_num = self._file_num = oldest_file_info[0] self._fro = oldest_file_info[4] fro_seek_flag = True # jump to fro to start reading if not self._qflag and not self._rflag: if self._vflag: print "Recovering journals..." else: print "Recovering journals", if self._file != None and self._is_file_full(): self._file.close() self._file_num = self._incr_file_num() if self._file_num == self._start_file_num: return True if self._start_file_num == 0: self._last_file_flag = self._file_num == self._jinfo.get_num_jrnl_files() - 1 else: self._last_file_flag = self._file_num == self._start_file_num - 1 if self._file_num < 0 or self._file_num >= self._jinfo.get_num_jrnl_files(): raise jerr.BadFileNumberError(self._file_num) jfn = os.path.join(self._jinfo.get_current_dir(), "%s.%04x.jdat" % (self._jinfo.get_jrnl_base_name(), self._file_num)) self._file = open(jfn) self._file_hdr = jrnl.Utils.load(self._file, jrnl.Hdr) if fro_seek_flag and self._file.tell() != self._fro: self._file.seek(self._fro) self._first_rec_flag = True if not self._qflag: if self._rflag: print jfn, ": ", self._file_hdr elif self._vflag: print "* Reading %s" % jfn else: print ".", sys.stdout.flush() return False def _check_owi(self, hdr): """Return True if the header's owi indicator matches that of the file header record; False otherwise. This can indicate whether the last record in a file has been read and now older records which have not yet been overwritten are now being read.""" return self._file_hdr_owi == hdr.owi() def _is_file_full(self): """Return True if the current file is full (no more write space); false otherwise""" return self._file.tell() >= self._jinfo.get_jrnl_file_size_bytes() def _get_next_record(self): """Get the next record in the file for analysis""" if self._is_file_full(): if self._advance_jrnl_file(): return True try: hdr = jrnl.Utils.load(self._file, jrnl.Hdr) except: return True if hdr.empty(): return True if hdr.check(): return True self._rec_cnt += 1 self._file_hdr_owi = self._file_hdr.owi() if self._first_rec_flag: if self._file_hdr.fro != hdr.foffs: raise jerr.FirstRecordOffsetMismatch(self._file_hdr.fro, hdr.foffs) else: if self._rflag: print " * fro ok: 0x%x" % self._file_hdr.fro self._first_rec_flag = False stop = False if isinstance(hdr, jrnl.EnqRec): stop = self._handle_enq_rec(hdr) elif isinstance(hdr, jrnl.DeqRec): stop = self._handle_deq_rec(hdr) elif isinstance(hdr, jrnl.TxnRec): stop = self._handle_txn_rec(hdr) wstr = "" for warn in self._warning: wstr += " (%s)" % warn if self._rflag: print " > %s %s" % (hdr, wstr) self._warning = [] return stop def _handle_deq_rec(self, hdr): """Process a dequeue ("RHMd") record""" if self._load_rec(hdr): return True # Check OWI flag if not self._check_owi(hdr): self._warning.append("WARNING: OWI mismatch - could be overwrite boundary.") return True # Test hook if self._csv_deq_cb != None and self._csv_deq_cb(self._csv_store_chk, hdr): return True try: if hdr.xid == None: self._emap.delete(hdr.deq_rid) else: self._tmap.add(self._file_hdr.fid, hdr) except jerr.JWarning, warn: self._warning.append(str(warn)) return False def _handle_enq_rec(self, hdr): """Process a dequeue ("RHMe") record""" if self._load_rec(hdr): return True # Check extern flag if hdr.extern and hdr.data != None: raise jerr.ExternFlagDataError(hdr) # Check OWI flag if not self._check_owi(hdr): self._warning.append("WARNING: OWI mismatch - could be overwrite boundary.") return True # Test hook if self._csv_enq_cb != None and self._csv_enq_cb(self._csv_store_chk, hdr): return True if hdr.xid == None: self._emap.add(self._file_hdr.fid, hdr) else: self._txn_msg_cnt += 1 self._tmap.add(self._file_hdr.fid, hdr) self._msg_cnt += 1 return False def _handle_txn_rec(self, hdr): """Process a transaction ("RHMa or RHMc") record""" if self._load_rec(hdr): return True # Check OWI flag if not self._check_owi(hdr): self._warning.append("WARNING: OWI mismatch - could be overwrite boundary.") return True # Test hook if self._csv_txn_cb != None and self._csv_txn_cb(self._csv_store_chk, hdr): return True if hdr.magic[-1] == "a": self._abort_cnt += 1 else: self._commit_cnt += 1 if self._tmap.contains(hdr.xid): mismatched_rids = self._tmap.delete(hdr) if mismatched_rids != None and len(mismatched_rids) > 0: self._warning.append("WARNING: transactional dequeues not found in enqueue map; rids=%s" % mismatched_rids) else: self._warning.append("WARNING: %s not found in transaction map" % jrnl.Utils.format_xid(hdr.xid)) if hdr.magic[-1] == "c": # commits only self._txn_obj_list[hdr.xid] = hdr return False def _incr_file_num(self): """Increment the number of files read with wraparound (ie after file n-1, go to 0)""" self._file_num += 1 if self._file_num >= self._jinfo.get_num_jrnl_files(): self._file_num = 0 return self._file_num def _load_rec(self, hdr): """Load a single record for the given header. There may be arbitrarily large xids and data components.""" while not hdr.complete(): if self._advance_jrnl_file(): return True hdr.load(self._file) return False # ============================================================================= if __name__ == "__main__": print "This is a library, and cannot be executed." qpid-cpp-store-debian-0.16/tools/store_chk0000755000176300017630000003626711560047111017614 0ustar cajuscajus#!/usr/bin/env python """ Copyright (c) 2007, 2008 Red Hat, Inc. This file is part of the Qpid async store library msgstore.so. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA The GNU Lesser General Public License is available in the file COPYING. """ from qpidstore import jerr, jrnl, janal import optparse, os, sys #== class StoreChk ============================================================ class StoreChk(object): """ This class: 1. Reads a journal jinf file, and from its info: 2. Analyzes the journal data files to determine which is the last to be written, then 3. Reads and analyzes all the records in the journal files. The only public method is run() which kicks off the analysis. """ def __init__(self): """Constructor""" # params self.opts = None self._jdir = None # recovery analysis objects # self._jrnl_info = None # self.jrnl_rdr = None self._process_args() self._jrnl_info = jrnl.JrnlInfo(self._jdir, self.opts.bfn) # FIXME: This is a hack... find an elegant way of getting the file size to jrec! jrnl.JRNL_FILE_SIZE = self._jrnl_info.get_jrnl_file_size_bytes() self.jrnl_anal = janal.JrnlAnalyzer(self._jrnl_info) self.jrnl_rdr = janal.JrnlReader(self._jrnl_info, self.jrnl_anal, self.opts.qflag, self.opts.rflag, self.opts.vflag) def run(self): """Run the store check""" if not self.opts.qflag: print self._jrnl_info print self.jrnl_anal self.jrnl_rdr.run() self._report() def _report(self): """Print the results of the store check""" if not self.opts.qflag: print print " === REPORT ====" print print "Records: %8d non-transactional" % \ (self.jrnl_rdr.get_msg_cnt() - self.jrnl_rdr.get_txn_msg_cnt()) print " %8d transactional" % self.jrnl_rdr.get_txn_msg_cnt() print " %8d total" % self.jrnl_rdr.get_msg_cnt() print print "Transactions: %8d aborts" % self.jrnl_rdr.get_abort_cnt() print " %8d commits" % self.jrnl_rdr.get_commit_cnt() print " %8d total" % (self.jrnl_rdr.get_abort_cnt() + self.jrnl_rdr.get_commit_cnt()) print if self.jrnl_rdr.emap().size() > 0: print "Remaining enqueued records (sorted by rid): " rid_list = self.jrnl_rdr.emap().rids() rid_list.sort() for rid in rid_list: l = self.jrnl_rdr.emap().get(rid) locked = "" if l[2]: locked += " (locked)" print " fid=%d %s%s" % (l[0], l[1], locked) print "WARNING: Enqueue-Dequeue mismatch, %d enqueued records remain." % self.jrnl_rdr.emap().size() else: print "No remaining enqueued records found (emap empty)." print if self.jrnl_rdr.tmap().size() > 0: txn_rec_cnt = 0 print "Incomplete transactions: " for xid in self.jrnl_rdr.tmap().xids(): jrnl.Utils.format_xid(xid) recs = self.jrnl_rdr.tmap().get(xid) for l in recs: print " fid=%d %s" % (l[0], l[1]) print " Total: %d records for %s" % (len(recs), jrnl.Utils.format_xid(xid)) print txn_rec_cnt += len(recs) print "WARNING: Incomplete transactions found, %d xids remain containing a total of %d records." % \ (self.jrnl_rdr.tmap().size(), txn_rec_cnt) else: print "No incomplete transactions found (tmap empty)." print print "%d enqueues, %d journal records processed." % \ (self.jrnl_rdr.get_msg_cnt(), self.jrnl_rdr.get_rec_cnt()) def _process_args(self): """Process the command-line arguments""" opt = optparse.OptionParser(usage="%prog [options] DIR", version="%prog 1.0") opt.add_option("-b", "--base-filename", action="store", dest="bfn", default="JournalData", help="Base filename for old journal files") opt.add_option("-q", "--quiet", action="store_true", dest="qflag", help="Quiet (suppress all non-error output)") opt.add_option("-r", "--records", action="store_true", dest="rflag", help="Print all records and transactions (including consumed/closed)") opt.add_option("-v", "--verbose", action="store_true", dest="vflag", help="Verbose output") (self.opts, args) = opt.parse_args() if len(args) == 0: opt.error("No journal directory argument") elif len(args) > 1: opt.error("Too many positional arguments: %s" % args) if self.opts.qflag and self.opts.rflag: opt.error("Quiet (-q/--quiet) and record (-r/--records) options are mutually exclusive") if self.opts.qflag and self.opts.vflag: opt.error("Quiet (-q/--quiet) and verbose (-v/--verbose) options are mutually exclusive") self._jdir = args[0] if not os.path.exists(self._jdir): opt.error("Journal path \"%s\" does not exist" % self._jdir) #== class CsvStoreChk ========================================================= class CsvStoreChk(StoreChk): """ This class, in addition to analyzing a journal, can compare the journal footprint (ie enqueued/dequeued/transaction record counts) to expected values from a CSV file. This can be used for additional automated testing, and is currently in use in the long store tests for journal encode testing. """ # CSV file cols TEST_NUM_COL = 0 NUM_MSGS_COL = 5 MIN_MSG_SIZE_COL = 7 MAX_MSG_SIZE_COL = 8 MIN_XID_SIZE_COL = 9 MAX_XID_SIZE_COL = 10 AUTO_DEQ_COL = 11 TRANSIENT_COL = 12 EXTERN_COL = 13 COMMENT_COL = 20 def __init__(self): """Constructor""" StoreChk.__init__(self) # csv params self.num_msgs = None self.msg_len = None self.auto_deq = None self.xid_len = None self.transient = None self.extern = None self._warning = [] self.jrnl_rdr.set_callbacks(self, CsvStoreChk._csv_pre_run_chk, CsvStoreChk._csv_enq_chk, CsvStoreChk._csv_deq_chk, CsvStoreChk._csv_txn_chk, CsvStoreChk._csv_post_run_chk) self._get_csv_test() def _get_csv_test(self): """Get a test from the CSV reader""" if self.opts.csvfn != None and self.opts.tnum != None: tparams = self._read_csv_file(self.opts.csvfn, self.opts.tnum) if tparams == None: print "ERROR: Test %d not found in CSV file \"%s\"" % (self.opts.tnum, self.opts.csvfn) sys.exit(1) self.num_msgs = tparams["num_msgs"] if tparams["min_size"] == tparams["max_size"]: self.msg_len = tparams["max_size"] else: self.msg_len = 0 self.auto_deq = tparams["auto_deq"] if tparams["xid_min_size"] == tparams["xid_max_size"]: self.xid_len = tparams["xid_max_size"] else: self.xid_len = 0 self.transient = tparams["transient"] self.extern = tparams["extern"] def _read_csv_file(self, filename, tnum): """Read the CSV test parameter file""" try: csvf = open(filename, "r") except IOError: print "ERROR: Unable to open CSV file \"%s\"" % filename sys.exit(1) for line in csvf: str_list = line.strip().split(",") if len(str_list[0]) > 0 and str_list[0][0] != "\"": try: if (int(str_list[self.TEST_NUM_COL]) == tnum): return { "num_msgs": int(str_list[self.NUM_MSGS_COL]), "min_size": int(str_list[self.MIN_MSG_SIZE_COL]), "max_size": int(str_list[self.MAX_MSG_SIZE_COL]), "auto_deq": not (str_list[self.AUTO_DEQ_COL] == "FALSE" or str_list[self.AUTO_DEQ_COL] == "0"), "xid_min_size": int(str_list[self.MIN_XID_SIZE_COL]), "xid_max_size": int(str_list[self.MAX_XID_SIZE_COL]), "transient": not (str_list[self.TRANSIENT_COL] == "FALSE" or str_list[self.TRANSIENT_COL] == "0"), "extern": not (str_list[self.EXTERN_COL] == "FALSE" or str_list[self.EXTERN_COL] == "0"), "comment": str_list[self.COMMENT_COL] } except Exception: pass return None def _process_args(self): """Process command-line arguments""" opt = optparse.OptionParser(usage="%prog [options] DIR", version="%prog 1.0") opt.add_option("-b", "--base-filename", action="store", dest="bfn", default="JournalData", help="Base filename for old journal files") opt.add_option("-c", "--csv-filename", action="store", dest="csvfn", help="CSV filename containing test parameters") opt.add_option("-q", "--quiet", action="store_true", dest="qflag", help="Quiet (suppress all non-error output)") opt.add_option("-r", "--records", action="store_true", dest="rflag", help="Print all records and transactions (including consumed/closed)") opt.add_option("-t", "--test-num", action="store", type="int", dest="tnum", help="Test number from CSV file - only valid if CSV file named") opt.add_option("-v", "--verbose", action="store_true", dest="vflag", help="Verbose output") (self.opts, args) = opt.parse_args() if len(args) == 0: opt.error("No journal directory argument") elif len(args) > 1: opt.error("Too many positional arguments: %s" % args) if self.opts.qflag and self.opts.rflag: opt.error("Quiet (-q/--quiet) and record (-r/--records) options are mutually exclusive") if self.opts.qflag and self.opts.vflag: opt.error("Quiet (-q/--quiet) and verbose (-v/--verbose) options are mutually exclusive") self._jdir = args[0] if not os.path.exists(self._jdir): opt.error("Journal path \"%s\" does not exist" % self._jdir) # Callbacks for checking against CSV test parameters. Return False if ok, True to raise error. #@staticmethod def _csv_pre_run_chk(csv_store_chk): """Check performed before a test runs""" if csv_store_chk.num_msgs == None: return if csv_store_chk.jrnl_anal.is_empty() and csv_store_chk.num_msgs > 0: raise jerr.AllJrnlFilesEmptyCsvError(csv_store_chk.get_opts().tnum, csv_store_chk.num_msgs) return False _csv_pre_run_chk = staticmethod(_csv_pre_run_chk) #@staticmethod def _csv_enq_chk(csv_store_chk, hdr): """Check performed before each enqueue operation""" #if csv_store_chk.num_msgs == None: return # if csv_store_chk.extern != None: if csv_store_chk.extern != hdr.extern: raise jerr.ExternFlagCsvError(csv_store_chk.opts.tnum, csv_store_chk.extern) if hdr.extern and hdr.data != None: raise jerr.ExternFlagWithDataCsvError(csv_store_chk.opts.tnum) if csv_store_chk.msg_len != None and csv_store_chk.msg_len > 0 and hdr.data != None and \ len(hdr.data) != csv_store_chk.msg_len: raise jerr.MessageLengthCsvError(csv_store_chk.opts.tnum, csv_store_chk.msg_len, len(hdr.data)) if csv_store_chk.xid_len != None and csv_store_chk.xid_len > 0 and len(hdr.xid) != csv_store_chk.xid_len: raise jerr.XidLengthCsvError(csv_store_chk.opts.tnum, csv_store_chk.xid_len, len(hdr.xid)) if csv_store_chk.transient != None and hdr.transient != csv_store_chk.transient: raise jerr.TransactionCsvError(csv_store_chk.opts.tnum, csv_store_chk.transient) return False _csv_enq_chk = staticmethod(_csv_enq_chk) #@staticmethod def _csv_deq_chk(csv_store_chk, hdr): """Check performed before each dequeue operation""" if csv_store_chk.auto_deq != None and not csv_store_chk.auto_deq: raise jerr.JWarning("[CSV %d] WARNING: Dequeue record rid=%d found in non-dequeue test - ignoring." % (csv_store_chk.opts.tnum, hdr.rid)) #self._warning.append("[CSV %d] WARNING: Dequeue record rid=%d found in non-dequeue test - ignoring." % # (csv_store_chk.opts.tnum, hdr.rid)) return False _csv_deq_chk = staticmethod(_csv_deq_chk) #@staticmethod def _csv_txn_chk(csv_store_chk, hdr): """Check performed before each transaction commit/abort""" return False _csv_txn_chk = staticmethod(_csv_txn_chk) #@staticmethod def _csv_post_run_chk(csv_store_chk): """Cehck performed after the completion of the test""" # Exclude this check if lastFileFlag is set - the count may be less than the number of msgs sent because # of journal overwriting if csv_store_chk.num_msgs != None and not csv_store_chk.jrnl_rdr.is_last_file() and \ csv_store_chk.num_msgs != csv_store_chk.jrnl_rdr.get_msg_cnt(): raise jerr.NumMsgsCsvError(csv_store_chk.opts.tnum, csv_store_chk.num_msgs, csv_store_chk.jrnl_rdr.get_msg_cnt()) return False _csv_post_run_chk = staticmethod(_csv_post_run_chk) #============================================================================== # main program #============================================================================== if __name__ == "__main__": M = CsvStoreChk() try: M.run() except Exception, e: sys.exit(e) qpid-cpp-store-debian-0.16/tools/Makefile.am0000644000176300017630000000220611562255465017742 0ustar cajuscajus# Copyright (c) 2007, 2008 Red Hat, Inc. # # This file is part of the Qpid async store library msgstore.so. # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 # USA # # The GNU Lesser General Public License is available in the file COPYING. qpidexecdir = $(libexecdir)/qpid qpidexec_SCRIPTS = resize store_chk pkgpyexec_qpiddir = $(pyexecdir)/qpidstore pkgpyexec_qpid_PYTHON = \ qpidstore/__init__.py \ qpidstore/jerr.py \ qpidstore/jrnl.py \ qpidstore/janal.py qpid-cpp-store-debian-0.16/m4/0000755000176300017630000000000011761422405015055 5ustar cajuscajusqpid-cpp-store-debian-0.16/m4/extensions.m40000644000176300017630000000373711130424755017530 0ustar cajuscajus# serial 5 -*- Autoconf -*- # Enable extensions on systems that normally disable them. # Copyright (C) 2003, 2006, 2008 Free Software Foundation, Inc. # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This definition of AC_USE_SYSTEM_EXTENSIONS is stolen from CVS # Autoconf. Perhaps we can remove this once we can assume Autoconf # 2.61 or later everywhere, but since CVS Autoconf mutates rapidly # enough in this area it's likely we'll need to redefine # AC_USE_SYSTEM_EXTENSIONS for quite some time. # AC_USE_SYSTEM_EXTENSIONS # ------------------------ # Enable extensions on systems that normally disable them, # typically due to standards-conformance issues. m4_ifdef([AC_USE_SYSTEM_EXTENSIONS], [], [ AC_DEFUN([AC_USE_SYSTEM_EXTENSIONS], [ AC_BEFORE([$0], [AC_COMPILE_IFELSE]) AC_BEFORE([$0], [AC_RUN_IFELSE]) AC_REQUIRE([AC_GNU_SOURCE]) AC_REQUIRE([AC_AIX]) AC_REQUIRE([AC_MINIX]) AH_VERBATIM([__EXTENSIONS__], [/* Enable extensions on Solaris. */ #ifndef __EXTENSIONS__ # undef __EXTENSIONS__ #endif #ifndef _POSIX_PTHREAD_SEMANTICS # undef _POSIX_PTHREAD_SEMANTICS #endif #ifndef _TANDEM_SOURCE # undef _TANDEM_SOURCE #endif]) AC_CACHE_CHECK([whether it is safe to define __EXTENSIONS__], [ac_cv_safe_to_define___extensions__], [AC_COMPILE_IFELSE( [AC_LANG_PROGRAM([ # define __EXTENSIONS__ 1 AC_INCLUDES_DEFAULT])], [ac_cv_safe_to_define___extensions__=yes], [ac_cv_safe_to_define___extensions__=no])]) test $ac_cv_safe_to_define___extensions__ = yes && AC_DEFINE([__EXTENSIONS__]) AC_DEFINE([_POSIX_PTHREAD_SEMANTICS]) AC_DEFINE([_TANDEM_SOURCE]) ])]) # gl_USE_SYSTEM_EXTENSIONS # ------------------------ # Enable extensions on systems that normally disable them, # typically due to standards-conformance issues. AC_DEFUN([gl_USE_SYSTEM_EXTENSIONS], [AC_REQUIRE([AC_USE_SYSTEM_EXTENSIONS])]) qpid-cpp-store-debian-0.16/m4/compiler-flags.m40000644000176300017630000000140310542551676020232 0ustar cajuscajus# serial 3 # Find valid warning flags for the C Compiler. -*-Autoconf-*- dnl Copyright (C) 2001, 2002, 2006 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. dnl Written by Jesse Thilo. AC_DEFUN([gl_COMPILER_FLAGS], [AC_MSG_CHECKING(whether compiler accepts $1) AC_SUBST(COMPILER_FLAGS) ac_save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $1" ac_save_CXXFLAGS="$CXXFLAGS" CXXFLAGS="$CXXFLAGS $1" AC_TRY_COMPILE(, [int x;], COMPILER_FLAGS="$COMPILER_FLAGS $1" AC_MSG_RESULT(yes), AC_MSG_RESULT(no)) CFLAGS="$ac_save_CFLAGS" CXXFLAGS="$ac_save_CXXFLAGS" ]) qpid-cpp-store-debian-0.16/m4/clock_time.m40000644000176300017630000000245510542551676017447 0ustar cajuscajus# clock_time.m4 serial 8 dnl Copyright (C) 2002-2006 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. # Check for clock_gettime and clock_settime, and set LIB_CLOCK_GETTIME. # For a program named, say foo, you should add a line like the following # in the corresponding Makefile.am file: # foo_LDADD = $(LDADD) $(LIB_CLOCK_GETTIME) AC_DEFUN([gl_CLOCK_TIME], [ dnl Persuade glibc and Solaris to declare these functions. AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS]) # Solaris 2.5.1 needs -lposix4 to get the clock_gettime function. # Solaris 7 prefers the library name -lrt to the obsolescent name -lposix4. # Save and restore LIBS so e.g., -lrt, isn't added to it. Otherwise, *all* # programs in the package would end up linked with that potentially-shared # library, inducing unnecessary run-time overhead. gl_saved_libs=$LIBS AC_SEARCH_LIBS(clock_gettime, [rt posix4], [test "$ac_cv_search_clock_gettime" = "none required" || LIB_CLOCK_GETTIME=$ac_cv_search_clock_gettime]) AC_SUBST([LIB_CLOCK_GETTIME]) AC_CHECK_FUNCS(clock_gettime clock_settime) LIBS=$gl_saved_libs ]) qpid-cpp-store-debian-0.16/m4/cppunit.m40000644000176300017630000000613110542551676017013 0ustar cajuscajusdnl dnl AM_PATH_CPPUNIT(MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]) dnl AC_DEFUN([AM_PATH_CPPUNIT], [ AC_ARG_WITH(cppunit-prefix,[ --with-cppunit-prefix=PFX Prefix where CppUnit is installed (optional)], cppunit_config_prefix="$withval", cppunit_config_prefix="") AC_ARG_WITH(cppunit-exec-prefix,[ --with-cppunit-exec-prefix=PFX Exec prefix where CppUnit is installed (optional)], cppunit_config_exec_prefix="$withval", cppunit_config_exec_prefix="") if test x$cppunit_config_exec_prefix != x ; then cppunit_config_args="$cppunit_config_args --exec-prefix=$cppunit_config_exec_prefix" if test x${CPPUNIT_CONFIG+set} != xset ; then CPPUNIT_CONFIG=$cppunit_config_exec_prefix/bin/cppunit-config fi fi if test x$cppunit_config_prefix != x ; then cppunit_config_args="$cppunit_config_args --prefix=$cppunit_config_prefix" if test x${CPPUNIT_CONFIG+set} != xset ; then CPPUNIT_CONFIG=$cppunit_config_prefix/bin/cppunit-config fi fi AC_PATH_PROG(CPPUNIT_CONFIG, cppunit-config, no) cppunit_version_min=$1 AC_MSG_CHECKING(for Cppunit - version >= $cppunit_version_min) no_cppunit="" if test "$CPPUNIT_CONFIG" = "no" ; then AC_MSG_RESULT(no) no_cppunit=yes else CPPUNIT_CFLAGS=`$CPPUNIT_CONFIG --cflags` CPPUNIT_LIBS=`$CPPUNIT_CONFIG --libs` cppunit_version=`$CPPUNIT_CONFIG --version` cppunit_major_version=`echo $cppunit_version | \ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'` cppunit_minor_version=`echo $cppunit_version | \ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'` cppunit_micro_version=`echo $cppunit_version | \ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'` cppunit_major_min=`echo $cppunit_version_min | \ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'` if test "x${cppunit_major_min}" = "x" ; then cppunit_major_min=0 fi cppunit_minor_min=`echo $cppunit_version_min | \ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'` if test "x${cppunit_minor_min}" = "x" ; then cppunit_minor_min=0 fi cppunit_micro_min=`echo $cppunit_version_min | \ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'` if test "x${cppunit_micro_min}" = "x" ; then cppunit_micro_min=0 fi cppunit_version_proper=`expr \ $cppunit_major_version \> $cppunit_major_min \| \ $cppunit_major_version \= $cppunit_major_min \& \ $cppunit_minor_version \> $cppunit_minor_min \| \ $cppunit_major_version \= $cppunit_major_min \& \ $cppunit_minor_version \= $cppunit_minor_min \& \ $cppunit_micro_version \>= $cppunit_micro_min ` if test "$cppunit_version_proper" = "1" ; then AC_MSG_RESULT([$cppunit_major_version.$cppunit_minor_version.$cppunit_micro_version]) else AC_MSG_RESULT(no) no_cppunit=yes fi fi if test "x$no_cppunit" = x ; then ifelse([$2], , :, [$2]) else CPPUNIT_CFLAGS="" CPPUNIT_LIBS="" ifelse([$3], , :, [$3]) fi AC_SUBST(CPPUNIT_CFLAGS) AC_SUBST(CPPUNIT_LIBS) ]) qpid-cpp-store-debian-0.16/etc/0000755000176300017630000000000011761423615015314 5ustar cajuscajusqpid-cpp-store-debian-0.16/etc/rhmd.conf0000644000176300017630000000172011142067437017114 0ustar cajuscajus# Copyright (c) 2007, 2008 Red Hat, Inc. # # This file is part of the Qpid async store library msgstore.so. # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 # USA # # The GNU Lesser General Public License is available in the file COPYING. # Configuration file for qpidd load-module=libbdbstore.so.0 qpid-cpp-store-debian-0.16/lib/0000755000176300017630000000000011761423566015314 5ustar cajuscajusqpid-cpp-store-debian-0.16/lib/PreparedTransaction.h0000644000176300017630000000465411201111007021413 0ustar cajuscajus/* Copyright (c) 2007, 2008 Red Hat, Inc. This file is part of the Qpid async store library msgstore.so. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA The GNU Lesser General Public License is available in the file COPYING. */ #ifndef _PreparedTransaction_ #define _PreparedTransaction_ #include #include #include #include #include #include namespace mrg{ namespace msgstore{ typedef u_int64_t queue_id; typedef u_int64_t message_id; class LockedMappings { public: typedef boost::shared_ptr shared_ptr; typedef std::map map; typedef std::pair idpair; typedef std::list::iterator iterator; void add(queue_id queue, message_id message); bool isLocked(queue_id queue, message_id message); std::size_t size() { return locked.size(); } iterator begin() { return locked.begin(); } iterator end() { return locked.end(); } static void add(LockedMappings::map& map, std::string& key, queue_id queue, message_id message); private: std::list locked; }; struct PreparedTransaction { typedef boost::ptr_list list; const std::string xid; const LockedMappings::shared_ptr enqueues; const LockedMappings::shared_ptr dequeues; PreparedTransaction(const std::string& xid, LockedMappings::shared_ptr enqueues, LockedMappings::shared_ptr dequeues); bool isLocked(queue_id queue, message_id message); static bool isLocked(PreparedTransaction::list& txns, queue_id queue, message_id message); static PreparedTransaction::list::iterator getLockedPreparedTransaction(PreparedTransaction::list& txns, queue_id queue, message_id message); }; }} #endif qpid-cpp-store-debian-0.16/lib/MessageStoreImpl.cpp0000644000176300017630000021554411750751726021256 0ustar cajuscajus/* Copyright (c) 2007, 2008, 2009, 2010 Red Hat, Inc. This file is part of the Qpid async store library msgstore.so. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA The GNU Lesser General Public License is available in the file COPYING. */ #include "MessageStoreImpl.h" #include "BindingDbt.h" #include "BufferValue.h" #include "IdDbt.h" #include "jrnl/txn_map.hpp" #include "qpid/framing/FieldValue.h" #include "qpid/log/Statement.h" #include "qmf/com/redhat/rhm/store/Package.h" #include "StoreException.h" #include #include #define MAX_AIO_SLEEPS 100000 // tot: ~1 sec #define AIO_SLEEP_TIME_US 10 // 0.01 ms namespace _qmf = qmf::com::redhat::rhm::store; namespace mrg { namespace msgstore { const std::string MessageStoreImpl::storeTopLevelDir("rhm"); // Sets the top-level store dir name // FIXME aconway 2010-03-09: was 10 qpid::sys::Duration MessageStoreImpl::defJournalGetEventsTimeout(1 * qpid::sys::TIME_MSEC); // 10ms qpid::sys::Duration MessageStoreImpl::defJournalFlushTimeout(500 * qpid::sys::TIME_MSEC); // 0.5s qpid::sys::Mutex TxnCtxt::globalSerialiser; MessageStoreImpl::TplRecoverStruct::TplRecoverStruct(const u_int64_t _rid, const bool _deq_flag, const bool _commit_flag, const bool _tpc_flag) : rid(_rid), deq_flag(_deq_flag), commit_flag(_commit_flag), tpc_flag(_tpc_flag) {} MessageStoreImpl::MessageStoreImpl(qpid::sys::Timer& timer_, const char* envpath) : numJrnlFiles(0), autoJrnlExpand(false), autoJrnlExpandMaxFiles(0), jrnlFsizeSblks(0), truncateFlag(false), wCachePgSizeSblks(0), wCacheNumPages(0), tplNumJrnlFiles(0), tplJrnlFsizeSblks(0), tplWCachePgSizeSblks(0), tplWCacheNumPages(0), highestRid(0), isInit(false), envPath(envpath), timer(timer_), mgmtObject(0), agent(0) {} u_int16_t MessageStoreImpl::chkJrnlNumFilesParam(const u_int16_t param, const std::string paramName) { u_int16_t p = param; if (p < JRNL_MIN_NUM_FILES) { p = JRNL_MIN_NUM_FILES; QPID_LOG(warning, "parameter " << paramName << " (" << param << ") is below allowable minimum (" << JRNL_MIN_NUM_FILES << "); changing this parameter to minimum value."); } else if (p > JRNL_MAX_NUM_FILES) { p = JRNL_MAX_NUM_FILES; QPID_LOG(warning, "parameter " << paramName << " (" << param << ") is above allowable maximum (" << JRNL_MAX_NUM_FILES << "); changing this parameter to maximum value."); } return p; } u_int32_t MessageStoreImpl::chkJrnlFileSizeParam(const u_int32_t param, const std::string paramName, const u_int32_t wCachePgSizeSblks) { u_int32_t p = param; u_int32_t min = JRNL_MIN_FILE_SIZE / JRNL_RMGR_PAGE_SIZE; u_int32_t max = JRNL_MAX_FILE_SIZE / JRNL_RMGR_PAGE_SIZE; if (p < min) { p = min; QPID_LOG(warning, "parameter " << paramName << " (" << param << ") is below allowable minimum (" << min << "); changing this parameter to minimum value."); } else if (p > max) { p = max; QPID_LOG(warning, "parameter " << paramName << " (" << param << ") is above allowable maximum (" << max << "); changing this parameter to maximum value."); } if (wCachePgSizeSblks > p * JRNL_RMGR_PAGE_SIZE) { std::ostringstream oss; oss << "Cannot create store with file size less than write page cache size. [file size = " << p << " (" << (p * JRNL_RMGR_PAGE_SIZE / 2) << " kB); write page cache = " << (wCachePgSizeSblks / 2) << " kB]"; THROW_STORE_EXCEPTION(oss.str()); } return p; } u_int32_t MessageStoreImpl::chkJrnlWrPageCacheSize(const u_int32_t param, const std::string paramName, const u_int16_t jrnlFsizePgs) { u_int32_t p = param; switch (p) { case 1: case 2: case 4: case 8: case 16: case 32: case 64: case 128: if (jrnlFsizePgs == 1) { p = 64; QPID_LOG(warning, "parameter " << paramName << " (" << param << ") cannot set a page size greater than the journal file size; changing this parameter to the journal file size (" << p << ")"); } break; default: if (p == 0) { // For zero value, use default p = JRNL_WMGR_DEF_PAGE_SIZE * JRNL_DBLK_SIZE * JRNL_SBLK_SIZE / 1024; QPID_LOG(warning, "parameter " << paramName << " (" << param << ") must be a power of 2 between 1 and 128; changing this parameter to default value (" << p << ")"); } else { // For any positive value, use closest value if (p < 6) p = 4; else if (p < 12) p = 8; else if (p < 24) p = 16; else if (p < 48) p = 32; else if (p < 96) p = 64; else p = 128; QPID_LOG(warning, "parameter " << paramName << " (" << param << ") must be a power of 2 between 1 and 128; changing this parameter to closest allowable value (" << p << ")"); } } return p; } u_int16_t MessageStoreImpl::getJrnlWrNumPages(const u_int32_t wrPageSizeKib) { u_int32_t wrPageSizeSblks = wrPageSizeKib * 1024 / JRNL_DBLK_SIZE / JRNL_SBLK_SIZE; // convert from KiB to number sblks u_int32_t defTotWCacheSize = JRNL_WMGR_DEF_PAGE_SIZE * JRNL_WMGR_DEF_PAGES; // in sblks. Currently 2014 sblks (1 MiB). switch (wrPageSizeKib) { case 1: case 2: case 4: // 256 KiB total cache return defTotWCacheSize / wrPageSizeSblks / 4; case 8: case 16: // 512 KiB total cache return defTotWCacheSize / wrPageSizeSblks / 2; default: // 32, 64, 128 // 1 MiB total cache return defTotWCacheSize / wrPageSizeSblks; } } void MessageStoreImpl::chkJrnlAutoExpandOptions(const StoreOptions* opts, bool& autoJrnlExpand, u_int16_t& autoJrnlExpandMaxFiles, const std::string& autoJrnlExpandMaxFilesParamName, const u_int16_t numJrnlFiles, const std::string& numJrnlFilesParamName) { if (!opts->autoJrnlExpand) { // auto-expand disabled autoJrnlExpand = false; autoJrnlExpandMaxFiles = 0; return; } u_int16_t p = opts->autoJrnlExpandMaxFiles; if (numJrnlFiles == JRNL_MAX_NUM_FILES) { // num-jfiles at max; disable auto-expand autoJrnlExpand = false; autoJrnlExpandMaxFiles = 0; QPID_LOG(warning, "parameter " << autoJrnlExpandMaxFilesParamName << " (" << p << ") must be higher than parameter " << numJrnlFilesParamName << " (" << numJrnlFiles << ") which is at the maximum allowable value; disabling auto-expand."); return; } if (p > JRNL_MAX_NUM_FILES) { // auto-expand-max-jfiles higher than max allowable, adjust autoJrnlExpand = true; autoJrnlExpandMaxFiles = JRNL_MAX_NUM_FILES; QPID_LOG(warning, "parameter " << autoJrnlExpandMaxFilesParamName << " (" << p << ") is above allowable maximum (" << JRNL_MAX_NUM_FILES << "); changing this parameter to maximum value."); return; } if (p && p == defAutoJrnlExpandMaxFiles && numJrnlFiles != defTplNumJrnlFiles) { // num-jfiles is different from the default AND max-auto-expand-jfiles is still at default // change value of max-auto-expand-jfiles autoJrnlExpand = true; if (2 * numJrnlFiles <= JRNL_MAX_NUM_FILES) { autoJrnlExpandMaxFiles = 2 * numJrnlFiles <= JRNL_MAX_NUM_FILES ? 2 * numJrnlFiles : JRNL_MAX_NUM_FILES; QPID_LOG(warning, "parameter " << autoJrnlExpandMaxFilesParamName << " adjusted from its default value (" << defAutoJrnlExpandMaxFiles << ") to twice that of parameter " << numJrnlFilesParamName << " (" << autoJrnlExpandMaxFiles << ")."); } else { autoJrnlExpandMaxFiles = 2 * numJrnlFiles <= JRNL_MAX_NUM_FILES ? 2 * numJrnlFiles : JRNL_MAX_NUM_FILES; QPID_LOG(warning, "parameter " << autoJrnlExpandMaxFilesParamName << " adjusted from its default to maximum allowable value (" << JRNL_MAX_NUM_FILES << ") because of the value of " << numJrnlFilesParamName << " (" << numJrnlFiles << ")."); } return; } // No adjustments req'd, set values autoJrnlExpand = true; autoJrnlExpandMaxFiles = p; } void MessageStoreImpl::initManagement (qpid::broker::Broker* broker) { if (broker != 0) { agent = broker->getManagementAgent(); if (agent != 0) { _qmf::Package packageInitializer(agent); mgmtObject = new _qmf::Store(agent, this, broker); mgmtObject->set_location(storeDir); mgmtObject->set_defaultInitialFileCount(numJrnlFiles); mgmtObject->set_defaultDataFileSize(jrnlFsizeSblks / JRNL_RMGR_PAGE_SIZE); mgmtObject->set_tplIsInitialized(false); mgmtObject->set_tplDirectory(getTplBaseDir()); mgmtObject->set_tplWritePageSize(tplWCachePgSizeSblks * JRNL_SBLK_SIZE * JRNL_DBLK_SIZE); mgmtObject->set_tplWritePages(tplWCacheNumPages); mgmtObject->set_tplInitialFileCount(tplNumJrnlFiles); mgmtObject->set_tplDataFileSize(tplJrnlFsizeSblks * JRNL_SBLK_SIZE * JRNL_DBLK_SIZE); mgmtObject->set_tplCurrentFileCount(tplNumJrnlFiles); agent->addObject(mgmtObject, 0, true); // Initialize all existing queues (ie those recovered before management was initialized) for (JournalListMapItr i=journalList.begin(); i!=journalList.end(); i++) { i->second->initManagement(agent); } } } } bool MessageStoreImpl::init(const qpid::Options* options) { // Extract and check options const StoreOptions* opts = static_cast(options); u_int16_t numJrnlFiles = chkJrnlNumFilesParam(opts->numJrnlFiles, "num-jfiles"); u_int32_t jrnlFsizePgs = chkJrnlFileSizeParam(opts->jrnlFsizePgs, "jfile-size-pgs"); u_int32_t jrnlWrCachePageSizeKib = chkJrnlWrPageCacheSize(opts->wCachePageSizeKib, "wcache-page-size", jrnlFsizePgs); u_int16_t tplNumJrnlFiles = chkJrnlNumFilesParam(opts->tplNumJrnlFiles, "tpl-num-jfiles"); u_int32_t tplJrnlFSizePgs = chkJrnlFileSizeParam(opts->tplJrnlFsizePgs, "tpl-jfile-size-pgs"); u_int32_t tplJrnlWrCachePageSizeKib = chkJrnlWrPageCacheSize(opts->tplWCachePageSizeKib, "tpl-wcache-page-size", tplJrnlFSizePgs); bool autoJrnlExpand; u_int16_t autoJrnlExpandMaxFiles; chkJrnlAutoExpandOptions(opts, autoJrnlExpand, autoJrnlExpandMaxFiles, "auto-expand-max-jfiles", numJrnlFiles, "num-jfiles"); // Pass option values to init(...) return init(opts->storeDir, numJrnlFiles, jrnlFsizePgs, opts->truncateFlag, jrnlWrCachePageSizeKib, tplNumJrnlFiles, tplJrnlFSizePgs, tplJrnlWrCachePageSizeKib, autoJrnlExpand, autoJrnlExpandMaxFiles); } // These params, taken from options, are assumed to be correct and verified bool MessageStoreImpl::init(const std::string& dir, u_int16_t jfiles, u_int32_t jfileSizePgs, const bool truncateFlag, u_int32_t wCachePageSizeKib, u_int16_t tplJfiles, u_int32_t tplJfileSizePgs, u_int32_t tplWCachePageSizeKib, bool autoJExpand, u_int16_t autoJExpandMaxFiles) { if (isInit) return true; // Set geometry members (converting to correct units where req'd) numJrnlFiles = jfiles; jrnlFsizeSblks = jfileSizePgs * JRNL_RMGR_PAGE_SIZE; wCachePgSizeSblks = wCachePageSizeKib * 1024 / JRNL_DBLK_SIZE / JRNL_SBLK_SIZE; // convert from KiB to number sblks wCacheNumPages = getJrnlWrNumPages(wCachePageSizeKib); tplNumJrnlFiles = tplJfiles; tplJrnlFsizeSblks = tplJfileSizePgs * JRNL_RMGR_PAGE_SIZE; tplWCachePgSizeSblks = tplWCachePageSizeKib * 1024 / JRNL_DBLK_SIZE / JRNL_SBLK_SIZE; // convert from KiB to number sblks tplWCacheNumPages = getJrnlWrNumPages(tplWCachePageSizeKib); autoJrnlExpand = autoJExpand; autoJrnlExpandMaxFiles = autoJExpandMaxFiles; if (dir.size()>0) storeDir = dir; if (truncateFlag) truncateInit(false); else init(); QPID_LOG(notice, "Store module initialized; store-dir=" << dir); QPID_LOG(info, "> Default files per journal: " << jfiles); // TODO: Uncomment these lines when auto-expand is enabled. // QPID_LOG(info, "> Auto-expand " << (autoJrnlExpand ? "enabled" : "disabled")); // if (autoJrnlExpand) QPID_LOG(info, "> Max auto-expand journal files: " << autoJrnlExpandMaxFiles); QPID_LOG(info, "> Default journal file size: " << jfileSizePgs << " (wpgs)"); QPID_LOG(info, "> Default write cache page size: " << wCachePageSizeKib << " (KiB)"); QPID_LOG(info, "> Default number of write cache pages: " << wCacheNumPages); QPID_LOG(info, "> TPL files per journal: " << tplNumJrnlFiles); QPID_LOG(info, "> TPL journal file size: " << tplJfileSizePgs << " (wpgs)"); QPID_LOG(info, "> TPL write cache page size: " << tplWCachePageSizeKib << " (KiB)"); QPID_LOG(info, "> TPL number of write cache pages: " << tplWCacheNumPages); return isInit; } void MessageStoreImpl::init() { const int retryMax = 3; int bdbRetryCnt = 0; do { if (bdbRetryCnt++ > 0) { closeDbs(); ::usleep(1000000); // 1 sec delay QPID_LOG(error, "Previoius BDB store initialization failed, retrying (" << bdbRetryCnt << " of " << retryMax << ")..."); } try { journal::jdir::create_dir(getBdbBaseDir()); dbenv.reset(new DbEnv(0)); dbenv->set_errpfx("msgstore"); dbenv->set_lg_regionmax(256000); // default = 65000 dbenv->open(getBdbBaseDir().c_str(), DB_THREAD | DB_CREATE | DB_INIT_TXN | DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_USE_ENVIRON | DB_RECOVER, 0); // Databases are constructed here instead of the constructor so that the DB_RECOVER flag can be used // against the database environment. Recover can only be performed if no databases have been created // against the environment at the time of recovery, as recovery invalidates the environment. queueDb.reset(new Db(dbenv.get(), 0)); dbs.push_back(queueDb); configDb.reset(new Db(dbenv.get(), 0)); dbs.push_back(configDb); exchangeDb.reset(new Db(dbenv.get(), 0)); dbs.push_back(exchangeDb); mappingDb.reset(new Db(dbenv.get(), 0)); dbs.push_back(mappingDb); bindingDb.reset(new Db(dbenv.get(), 0)); dbs.push_back(bindingDb); generalDb.reset(new Db(dbenv.get(), 0)); dbs.push_back(generalDb); TxnCtxt txn; txn.begin(dbenv.get(), false); try { open(queueDb, txn.get(), "queues.db", false); open(configDb, txn.get(), "config.db", false); open(exchangeDb, txn.get(), "exchanges.db", false); open(mappingDb, txn.get(), "mappings.db", true); open(bindingDb, txn.get(), "bindings.db", true); open(generalDb, txn.get(), "general.db", false); txn.commit(); } catch (...) { txn.abort(); throw; } // NOTE: during normal initialization, agent == 0 because the store is initialized before the management infrastructure. // However during a truncated initialization in a cluster, agent != 0. We always pass 0 as the agent for the // TplStore to keep things consistent in a cluster. See https://bugzilla.redhat.com/show_bug.cgi?id=681026 tplStorePtr.reset(new TplJournalImpl(timer, "TplStore", getTplBaseDir(), "tpl", defJournalGetEventsTimeout, defJournalFlushTimeout, 0)); isInit = true; } catch (const DbException& e) { if (e.get_errno() == DB_VERSION_MISMATCH) { QPID_LOG(error, "Database environment mismatch: This version of db4 does not match that which created the store database.: " << e.what()); THROW_STORE_EXCEPTION_2("Database environment mismatch: This version of db4 does not match that which created the store database. " "(If recovery is not important, delete the contents of the store directory. Otherwise, try upgrading the database using " "db_upgrade or using db_recover - but the db4-utils package must also be installed to use these utilities.)", e); } QPID_LOG(error, "BDB exception occurred while initializing store: " << e.what()); if (bdbRetryCnt >= retryMax) THROW_STORE_EXCEPTION_2("BDB exception occurred while initializing store", e); } catch (const StoreException&) { throw; } catch (const journal::jexception& e) { QPID_LOG(error, "Journal Exception occurred while initializing store: " << e); THROW_STORE_EXCEPTION_2("Journal Exception occurred while initializing store", e.what()); } catch (...) { QPID_LOG(error, "Unknown exception occurred while initializing store."); throw; } } while (!isInit); } void MessageStoreImpl::finalize() { if (tplStorePtr.get() && tplStorePtr->is_ready()) tplStorePtr->stop(true); { qpid::sys::Mutex::ScopedLock sl(journalListLock); for (JournalListMapItr i = journalList.begin(); i != journalList.end(); i++) { JournalImpl* jQueue = i->second; jQueue->resetDeleteCallback(); if (jQueue->is_ready()) jQueue->stop(true); } } if (mgmtObject != 0) { mgmtObject->resourceDestroy(); mgmtObject = 0; } } void MessageStoreImpl::truncateInit(const bool saveStoreContent) { if (isInit) { { qpid::sys::Mutex::ScopedLock sl(journalListLock); if (journalList.size()) { // check no queues exist std::ostringstream oss; oss << "truncateInit() called with " << journalList.size() << " queues still in existence"; THROW_STORE_EXCEPTION(oss.str()); } } closeDbs(); dbs.clear(); if (tplStorePtr->is_ready()) tplStorePtr->stop(true); dbenv->close(0); isInit = false; } std::ostringstream oss; oss << storeDir << "/" << storeTopLevelDir; if (saveStoreContent) { std::string dir = mrg::journal::jdir::push_down(storeDir, storeTopLevelDir, "cluster"); QPID_LOG(notice, "Store directory " << oss.str() << " was pushed down (saved) into directory " << dir << "."); } else { mrg::journal::jdir::delete_dir(oss.str().c_str()); QPID_LOG(notice, "Store directory " << oss.str() << " was truncated."); } init(); } void MessageStoreImpl::chkTplStoreInit() { // Prevent multiple threads from late-initializing the TPL qpid::sys::Mutex::ScopedLock sl(tplInitLock); if (!tplStorePtr->is_ready()) { journal::jdir::create_dir(getTplBaseDir()); tplStorePtr->initialize(tplNumJrnlFiles, false, 0, tplJrnlFsizeSblks, tplWCacheNumPages, tplWCachePgSizeSblks); if (mgmtObject != 0) mgmtObject->set_tplIsInitialized(true); } } void MessageStoreImpl::open(db_ptr db, DbTxn* txn, const char* file, bool dupKey) { if(dupKey) db->set_flags(DB_DUPSORT); db->open(txn, file, 0, DB_BTREE, DB_CREATE | DB_THREAD, 0); } void MessageStoreImpl::closeDbs() { for (std::list::iterator i = dbs.begin(); i != dbs.end(); i++) { (*i)->close(0); } dbs.clear(); } MessageStoreImpl::~MessageStoreImpl() { finalize(); try { closeDbs(); } catch (const DbException& e) { QPID_LOG(error, "Error closing BDB databases: " << e.what()); } catch (const journal::jexception& e) { QPID_LOG(error, "Error: " << e.what()); } catch (const std::exception& e) { QPID_LOG(error, "Error: " << e.what()); } catch (...) { QPID_LOG(error, "Unknown error in MessageStoreImpl::~MessageStoreImpl()"); } if (mgmtObject != 0) { mgmtObject->resourceDestroy(); mgmtObject = 0; } } void MessageStoreImpl::create(qpid::broker::PersistableQueue& queue, const qpid::framing::FieldTable& args) { checkInit(); if (queue.getPersistenceId()) { THROW_STORE_EXCEPTION("Queue already created: " + queue.getName()); } JournalImpl* jQueue = 0; qpid::framing::FieldTable::ValuePtr value; u_int16_t localFileCount = numJrnlFiles; bool localAutoExpandFlag = autoJrnlExpand; u_int16_t localAutoExpandMaxFileCount = autoJrnlExpandMaxFiles; u_int32_t localFileSizeSblks = jrnlFsizeSblks; value = args.get("qpid.file_count"); if (value.get() != 0 && !value->empty() && value->convertsTo()) localFileCount = chkJrnlNumFilesParam((u_int16_t) value->get(), "qpid.file_count"); value = args.get("qpid.file_size"); if (value.get() != 0 && !value->empty() && value->convertsTo()) localFileSizeSblks = chkJrnlFileSizeParam((u_int32_t) value->get(), "qpid.file_size", wCachePgSizeSblks) * JRNL_RMGR_PAGE_SIZE; if (queue.getName().size() == 0) { QPID_LOG(error, "Cannot create store for empty (null) queue name - ignoring and attempting to continue."); return; } jQueue = new JournalImpl(timer, queue.getName(), getJrnlDir(queue), std::string("JournalData"), defJournalGetEventsTimeout, defJournalFlushTimeout, agent, boost::bind(&MessageStoreImpl::journalDeleted, this, _1)); { qpid::sys::Mutex::ScopedLock sl(journalListLock); journalList[queue.getName()]=jQueue; } value = args.get("qpid.auto_expand"); if (value.get() != 0 && !value->empty() && value->convertsTo()) localAutoExpandFlag = (bool) value->get(); value = args.get("qpid.auto_expand_max_jfiles"); if (value.get() != 0 && !value->empty() && value->convertsTo()) localAutoExpandMaxFileCount = (u_int16_t) value->get(); queue.setExternalQueueStore(dynamic_cast(jQueue)); try { // init will create the deque's for the init... jQueue->initialize(localFileCount, localAutoExpandFlag, localAutoExpandMaxFileCount, localFileSizeSblks, wCacheNumPages, wCachePgSizeSblks); } catch (const journal::jexception& e) { THROW_STORE_EXCEPTION(std::string("Queue ") + queue.getName() + ": create() failed: " + e.what()); } try { if (!create(queueDb, queueIdSequence, queue)) { THROW_STORE_EXCEPTION("Queue already exists: " + queue.getName()); } } catch (const DbException& e) { THROW_STORE_EXCEPTION_2("Error creating queue named " + queue.getName(), e); } } void MessageStoreImpl::destroy(qpid::broker::PersistableQueue& queue) { checkInit(); destroy(queueDb, queue); deleteBindingsForQueue(queue); qpid::broker::ExternalQueueStore* eqs = queue.getExternalQueueStore(); if (eqs) { JournalImpl* jQueue = static_cast(eqs); jQueue->delete_jrnl_files(); queue.setExternalQueueStore(0); // will delete the journal if exists { qpid::sys::Mutex::ScopedLock sl(journalListLock); journalList.erase(queue.getName()); } } } void MessageStoreImpl::create(const qpid::broker::PersistableExchange& exchange, const qpid::framing::FieldTable& /*args*/) { checkInit(); if (exchange.getPersistenceId()) { THROW_STORE_EXCEPTION("Exchange already created: " + exchange.getName()); } try { if (!create(exchangeDb, exchangeIdSequence, exchange)) { THROW_STORE_EXCEPTION("Exchange already exists: " + exchange.getName()); } } catch (const DbException& e) { THROW_STORE_EXCEPTION_2("Error creating exchange named " + exchange.getName(), e); } } void MessageStoreImpl::destroy(const qpid::broker::PersistableExchange& exchange) { checkInit(); destroy(exchangeDb, exchange); //need to also delete bindings IdDbt key(exchange.getPersistenceId()); bindingDb->del(0, &key, DB_AUTO_COMMIT); } void MessageStoreImpl::create(const qpid::broker::PersistableConfig& general) { checkInit(); if (general.getPersistenceId()) { THROW_STORE_EXCEPTION("General configuration item already created"); } try { if (!create(generalDb, generalIdSequence, general)) { THROW_STORE_EXCEPTION("General configuration already exists"); } } catch (const DbException& e) { THROW_STORE_EXCEPTION_2("Error creating general configuration", e); } } void MessageStoreImpl::destroy(const qpid::broker::PersistableConfig& general) { checkInit(); destroy(generalDb, general); } bool MessageStoreImpl::create(db_ptr db, IdSequence& seq, const qpid::broker::Persistable& p) { u_int64_t id (seq.next()); Dbt key(&id, sizeof(id)); BufferValue value (p); int status; TxnCtxt txn; txn.begin(dbenv.get(), true); try { status = db->put(txn.get(), &key, &value, DB_NOOVERWRITE); txn.commit(); } catch (...) { txn.abort(); throw; } if (status == DB_KEYEXIST) { return false; } else { p.setPersistenceId(id); return true; } } void MessageStoreImpl::destroy(db_ptr db, const qpid::broker::Persistable& p) { qpid::sys::Mutex::ScopedLock sl(bdbLock); IdDbt key(p.getPersistenceId()); db->del(0, &key, DB_AUTO_COMMIT); } void MessageStoreImpl::bind(const qpid::broker::PersistableExchange& e, const qpid::broker::PersistableQueue& q, const std::string& k, const qpid::framing::FieldTable& a) { checkInit(); IdDbt key(e.getPersistenceId()); BindingDbt value(e, q, k, a); TxnCtxt txn; txn.begin(dbenv.get(), true); try { put(bindingDb, txn.get(), key, value); txn.commit(); } catch (...) { txn.abort(); throw; } } void MessageStoreImpl::unbind(const qpid::broker::PersistableExchange& e, const qpid::broker::PersistableQueue& q, const std::string& k, const qpid::framing::FieldTable&) { checkInit(); deleteBinding(e, q, k); } void MessageStoreImpl::recover(qpid::broker::RecoveryManager& registry) { checkInit(); txn_list prepared; recoverLockedMappings(prepared); queue_index queues;//id->queue exchange_index exchanges;//id->exchange message_index messages;//id->message TxnCtxt txn; txn.begin(dbenv.get(), false); try { //read all queues, calls recoversMessages recoverQueues(txn, registry, queues, prepared, messages); //recover exchange & bindings: recoverExchanges(txn, registry, exchanges); recoverBindings(txn, exchanges, queues); //recover general-purpose configuration recoverGeneral(txn, registry); txn.commit(); } catch (const DbException& e) { txn.abort(); THROW_STORE_EXCEPTION_2("Error on recovery", e); } catch (...) { txn.abort(); throw; } //recover transactions: for (txn_list::iterator i = prepared.begin(); i != prepared.end(); i++) { const PreparedTransaction pt = *i; if (mgmtObject != 0) { mgmtObject->inc_tplTransactionDepth(); mgmtObject->inc_tplTxnPrepares(); } std::string xid = pt.xid; // Restore data token state in TxnCtxt TplRecoverMapCitr citr = tplRecoverMap.find(xid); if (citr == tplRecoverMap.end()) THROW_STORE_EXCEPTION("XID not found in tplRecoverMap"); // If a record is found that is dequeued but not committed/aborted from tplStore, then a complete() call // was interrupted part way through committing/aborting the impacted queues. Complete this process. bool incomplTplTxnFlag = citr->second.deq_flag; if (citr->second.tpc_flag) { // Dtx (2PC) transaction TPCTxnCtxt* tpcc = new TPCTxnCtxt(xid, &messageIdSequence); std::auto_ptr txn(tpcc); tpcc->recoverDtok(citr->second.rid, xid); tpcc->prepare(tplStorePtr.get()); qpid::broker::RecoverableTransaction::shared_ptr dtx; if (!incomplTplTxnFlag) dtx = registry.recoverTransaction(xid, txn); if (pt.enqueues.get()) { for (LockedMappings::iterator j = pt.enqueues->begin(); j != pt.enqueues->end(); j++) { tpcc->addXidRecord(queues[j->first]->getExternalQueueStore()); if (!incomplTplTxnFlag) dtx->enqueue(queues[j->first], messages[j->second]); } } if (pt.dequeues.get()) { for (LockedMappings::iterator j = pt.dequeues->begin(); j != pt.dequeues->end(); j++) { tpcc->addXidRecord(queues[j->first]->getExternalQueueStore()); if (!incomplTplTxnFlag) dtx->dequeue(queues[j->first], messages[j->second]); } } if (incomplTplTxnFlag) { tpcc->complete(citr->second.commit_flag); } } else { // Local (1PC) transaction boost::shared_ptr opcc(new TxnCtxt(xid, &messageIdSequence)); opcc->recoverDtok(citr->second.rid, xid); opcc->prepare(tplStorePtr.get()); if (pt.enqueues.get()) { for (LockedMappings::iterator j = pt.enqueues->begin(); j != pt.enqueues->end(); j++) { opcc->addXidRecord(queues[j->first]->getExternalQueueStore()); } } if (pt.dequeues.get()) { for (LockedMappings::iterator j = pt.dequeues->begin(); j != pt.dequeues->end(); j++) { opcc->addXidRecord(queues[j->first]->getExternalQueueStore()); } } if (incomplTplTxnFlag) { opcc->complete(citr->second.commit_flag); } else { completed(*opcc.get(), citr->second.commit_flag); } } } registry.recoveryComplete(); } void MessageStoreImpl::recoverQueues(TxnCtxt& txn, qpid::broker::RecoveryManager& registry, queue_index& queue_index, txn_list& prepared, message_index& messages) { Cursor queues; queues.open(queueDb, txn.get()); u_int64_t maxQueueId(1); IdDbt key; Dbt value; //read all queues while (queues.next(key, value)) { qpid::framing::Buffer buffer(reinterpret_cast(value.get_data()), value.get_size()); //create a Queue instance qpid::broker::RecoverableQueue::shared_ptr queue = registry.recoverQueue(buffer); //set the persistenceId and update max as required queue->setPersistenceId(key.id); const std::string queueName = queue->getName().c_str(); JournalImpl* jQueue = 0; if (queueName.size() == 0) { QPID_LOG(error, "Cannot recover empty (null) queue name - ignoring and attempting to continue."); break; } jQueue = new JournalImpl(timer, queueName, getJrnlHashDir(queueName), std::string("JournalData"), defJournalGetEventsTimeout, defJournalFlushTimeout, agent, boost::bind(&MessageStoreImpl::journalDeleted, this, _1)); { qpid::sys::Mutex::ScopedLock sl(journalListLock); journalList[queueName] = jQueue; } queue->setExternalQueueStore(dynamic_cast(jQueue)); try { long rcnt = 0L; // recovered msg count long idcnt = 0L; // in-doubt msg count u_int64_t thisHighestRid = 0ULL; jQueue->recover(numJrnlFiles, autoJrnlExpand, autoJrnlExpandMaxFiles, jrnlFsizeSblks, wCacheNumPages, wCachePgSizeSblks, &prepared, thisHighestRid, key.id); // start recovery if (highestRid == 0ULL) highestRid = thisHighestRid; else if (thisHighestRid - highestRid < 0x8000000000000000ULL) // RFC 1982 comparison for unsigned 64-bit highestRid = thisHighestRid; recoverMessages(txn, registry, queue, prepared, messages, rcnt, idcnt); QPID_LOG(info, "Recovered queue \"" << queueName << "\": " << rcnt << " messages recovered; " << idcnt << " messages in-doubt."); jQueue->recover_complete(); // start journal. } catch (const journal::jexception& e) { THROW_STORE_EXCEPTION(std::string("Queue ") + queueName + ": recoverQueues() failed: " + e.what()); } //read all messages: done on a per queue basis if using Journal queue_index[key.id] = queue; maxQueueId = std::max(key.id, maxQueueId); } // NOTE: highestRid is set by both recoverQueues() and recoverTplStore() as // the messageIdSequence is used for both queue journals and the tpl journal. messageIdSequence.reset(highestRid + 1); QPID_LOG(info, "Most recent persistence id found: 0x" << std::hex << highestRid << std::dec); queueIdSequence.reset(maxQueueId + 1); } void MessageStoreImpl::recoverExchanges(TxnCtxt& txn, qpid::broker::RecoveryManager& registry, exchange_index& index) { //TODO: this is a copy&paste from recoverQueues - refactor! Cursor exchanges; exchanges.open(exchangeDb, txn.get()); u_int64_t maxExchangeId(1); IdDbt key; Dbt value; //read all exchanges while (exchanges.next(key, value)) { qpid::framing::Buffer buffer(reinterpret_cast(value.get_data()), value.get_size()); //create a Exchange instance qpid::broker::RecoverableExchange::shared_ptr exchange = registry.recoverExchange(buffer); if (exchange) { //set the persistenceId and update max as required exchange->setPersistenceId(key.id); index[key.id] = exchange; } maxExchangeId = std::max(key.id, maxExchangeId); } exchangeIdSequence.reset(maxExchangeId + 1); } void MessageStoreImpl::recoverBindings(TxnCtxt& txn, exchange_index& exchanges, queue_index& queues) { Cursor bindings; bindings.open(bindingDb, txn.get()); IdDbt key; Dbt value; while (bindings.next(key, value)) { qpid::framing::Buffer buffer(reinterpret_cast(value.get_data()), value.get_size()); if (buffer.available() < 8) { QPID_LOG(error, "Not enough data for binding: " << buffer.available()); THROW_STORE_EXCEPTION("Not enough data for binding"); } uint64_t queueId = buffer.getLongLong(); std::string queueName; std::string routingkey; qpid::framing::FieldTable args; buffer.getShortString(queueName); buffer.getShortString(routingkey); buffer.get(args); exchange_index::iterator exchange = exchanges.find(key.id); queue_index::iterator queue = queues.find(queueId); if (exchange != exchanges.end() && queue != queues.end()) { //could use the recoverable queue here rather than the name... exchange->second->bind(queueName, routingkey, args); } else { //stale binding, delete it QPID_LOG(warning, "Deleting stale binding"); bindings->del(0); } } } void MessageStoreImpl::recoverGeneral(TxnCtxt& txn, qpid::broker::RecoveryManager& registry) { Cursor items; items.open(generalDb, txn.get()); u_int64_t maxGeneralId(1); IdDbt key; Dbt value; //read all items while (items.next(key, value)) { qpid::framing::Buffer buffer(reinterpret_cast(value.get_data()), value.get_size()); //create instance qpid::broker::RecoverableConfig::shared_ptr config = registry.recoverConfig(buffer); //set the persistenceId and update max as required config->setPersistenceId(key.id); maxGeneralId = std::max(key.id, maxGeneralId); } generalIdSequence.reset(maxGeneralId + 1); } void MessageStoreImpl::recoverMessages(TxnCtxt& /*txn*/, qpid::broker::RecoveryManager& recovery, qpid::broker::RecoverableQueue::shared_ptr& queue, txn_list& prepared, message_index& messages, long& rcnt, long& idcnt) { size_t preambleLength = sizeof(u_int32_t)/*header size*/; JournalImpl* jc = static_cast(queue->getExternalQueueStore()); DataTokenImpl dtok; size_t readSize = 0; unsigned msg_count = 0; // TODO: This optimization to skip reading if there are no enqueued messages to read // breaks the python system test in phase 6 with "Exception: Cannot write lock file" // Figure out what is breaking. //bool read = jc->get_enq_cnt() > 0; bool read = true; void* dbuff = NULL; size_t dbuffSize = 0; void* xidbuff = NULL; size_t xidbuffSize = 0; bool transientFlag = false; bool externalFlag = false; dtok.set_wstate(DataTokenImpl::ENQ); // Read the message from the Journal. try { unsigned aio_sleep_cnt = 0; while (read) { mrg::journal::iores res = jc->read_data_record(&dbuff, dbuffSize, &xidbuff, xidbuffSize, transientFlag, externalFlag, &dtok); readSize = dtok.dsize(); switch (res) { case mrg::journal::RHM_IORES_SUCCESS: { msg_count++; qpid::broker::RecoverableMessage::shared_ptr msg; char* data = (char*)dbuff; unsigned headerSize; if (externalFlag) { msg = getExternMessage(recovery, dtok.rid(), headerSize); // large message external to jrnl } else { headerSize = qpid::framing::Buffer(data, preambleLength).getLong(); qpid::framing::Buffer headerBuff(data+ preambleLength, headerSize); /// do we want read size or header size ???? msg = recovery.recoverMessage(headerBuff); } msg->setPersistenceId(dtok.rid()); // At some future point if delivery attempts are stored, then this call would // become optional depending on that information. msg->setRedelivered(); u_int32_t contentOffset = headerSize + preambleLength; u_int64_t contentSize = readSize - contentOffset; if (msg->loadContent(contentSize) && !externalFlag) { //now read the content qpid::framing::Buffer contentBuff(data + contentOffset, contentSize); msg->decodeContent(contentBuff); } PreparedTransaction::list::iterator i = PreparedTransaction::getLockedPreparedTransaction(prepared, queue->getPersistenceId(), dtok.rid()); if (i == prepared.end()) { // not in prepared list rcnt++; queue->recover(msg); } else { u_int64_t rid = dtok.rid(); std::string xid(i->xid); TplRecoverMapCitr citr = tplRecoverMap.find(xid); if (citr == tplRecoverMap.end()) THROW_STORE_EXCEPTION("XID not found in tplRecoverMap"); // deq present in prepared list: this xid is part of incomplete txn commit/abort // or this is a 1PC txn that must be rolled forward if (citr->second.deq_flag || !citr->second.tpc_flag) { if (jc->is_enqueued(rid, true)) { // Enqueue is non-tx, dequeue tx assert(jc->is_locked(rid)); // This record MUST be locked by a txn dequeue if (!citr->second.commit_flag) { rcnt++; queue->recover(msg); // recover message in abort case only } } else { // Enqueue and/or dequeue tx journal::txn_map& tmap = jc->get_txn_map(); journal::txn_data_list txnList = tmap.get_tdata_list(xid); // txnList will be empty if xid not found bool enq = false; bool deq = false; for (journal::tdl_itr j = txnList.begin(); j_enq_flag && j->_rid == rid) enq = true; else if (!j->_enq_flag && j->_drid == rid) deq = true; } if (enq && !deq && citr->second.commit_flag) { rcnt++; queue->recover(msg); // recover txn message in commit case only } } } else { idcnt++; messages[rid] = msg; } } dtok.reset(); dtok.set_wstate(DataTokenImpl::ENQ); if (xidbuff) ::free(xidbuff); else if (dbuff) ::free(dbuff); aio_sleep_cnt = 0; break; } case mrg::journal::RHM_IORES_PAGE_AIOWAIT: if (++aio_sleep_cnt > MAX_AIO_SLEEPS) THROW_STORE_EXCEPTION("Timeout waiting for AIO in MessageStoreImpl::recoverMessages()"); ::usleep(AIO_SLEEP_TIME_US); break; case mrg::journal::RHM_IORES_EMPTY: read = false; break; // done with all messages. (add call in jrnl to test that _emap is empty.) default: std::ostringstream oss; oss << "recoverMessages(): Queue: " << queue->getName() << ": Unexpected return from journal read: " << mrg::journal::iores_str(res); THROW_STORE_EXCEPTION(oss.str()); } // switch } // while } catch (const journal::jexception& e) { THROW_STORE_EXCEPTION(std::string("Queue ") + queue->getName() + ": recoverMessages() failed: " + e.what()); } } qpid::broker::RecoverableMessage::shared_ptr MessageStoreImpl::getExternMessage(qpid::broker::RecoveryManager& /*recovery*/, uint64_t /*messageId*/, unsigned& /*headerSize*/) { throw mrg::journal::jexception(mrg::journal::jerrno::JERR__NOTIMPL, "MessageStoreImpl", "getExternMessage"); } int MessageStoreImpl::enqueueMessage(TxnCtxt& txn, IdDbt& msgId, qpid::broker::RecoverableMessage::shared_ptr& msg, queue_index& index, txn_list& prepared, message_index& messages) { Cursor mappings; mappings.open(mappingDb, txn.get()); IdDbt value; int count(0); for (int status = mappings->get(&msgId, &value, DB_SET); status == 0; status = mappings->get(&msgId, &value, DB_NEXT_DUP)) { if (index.find(value.id) == index.end()) { QPID_LOG(warning, "Recovered message for queue that no longer exists"); mappings->del(0); } else { qpid::broker::RecoverableQueue::shared_ptr queue = index[value.id]; if (PreparedTransaction::isLocked(prepared, value.id, msgId.id)) { messages[msgId.id] = msg; } else { queue->recover(msg); } count++; } } mappings.close(); return count; } void MessageStoreImpl::readTplStore() { tplRecoverMap.clear(); journal::txn_map& tmap = tplStorePtr->get_txn_map(); DataTokenImpl dtok; void* dbuff = NULL; size_t dbuffSize = 0; void* xidbuff = NULL; size_t xidbuffSize = 0; bool transientFlag = false; bool externalFlag = false; bool done = false; try { unsigned aio_sleep_cnt = 0; while (!done) { dtok.reset(); dtok.set_wstate(DataTokenImpl::ENQ); mrg::journal::iores res = tplStorePtr->read_data_record(&dbuff, dbuffSize, &xidbuff, xidbuffSize, transientFlag, externalFlag, &dtok); switch (res) { case mrg::journal::RHM_IORES_SUCCESS: { // Every TPL record contains both data and an XID assert(dbuffSize>0); assert(xidbuffSize>0); std::string xid(static_cast(xidbuff), xidbuffSize); bool is2PC = *(static_cast(dbuff)) != 0; // Check transaction details; add to recover map journal::txn_data_list txnList = tmap.get_tdata_list(xid); // txnList will be empty if xid not found if (!txnList.empty()) { // xid found in tmap unsigned enqCnt = 0; unsigned deqCnt = 0; u_int64_t rid = 0; // Assume commit (roll forward) in cases where only prepare has been called - ie only enqueue record exists. // Note: will apply to both 1PC and 2PC transactions. bool commitFlag = true; for (journal::tdl_itr j = txnList.begin(); j_enq_flag) { rid = j->_rid; enqCnt++; } else { commitFlag = j->_commit_flag; deqCnt++; } } assert(enqCnt == 1); assert(deqCnt <= 1); tplRecoverMap.insert(TplRecoverMapPair(xid, TplRecoverStruct(rid, deqCnt == 1, commitFlag, is2PC))); } ::free(xidbuff); aio_sleep_cnt = 0; break; } case mrg::journal::RHM_IORES_PAGE_AIOWAIT: if (++aio_sleep_cnt > MAX_AIO_SLEEPS) THROW_STORE_EXCEPTION("Timeout waiting for AIO in MessageStoreImpl::recoverTplStore()"); ::usleep(AIO_SLEEP_TIME_US); break; case mrg::journal::RHM_IORES_EMPTY: done = true; break; // done with all messages. (add call in jrnl to test that _emap is empty.) default: std::ostringstream oss; oss << "readTplStore(): Unexpected result from journal read: " << mrg::journal::iores_str(res); THROW_STORE_EXCEPTION(oss.str()); } // switch } } catch (const journal::jexception& e) { THROW_STORE_EXCEPTION(std::string("TPL recoverTplStore() failed: ") + e.what()); } } void MessageStoreImpl::recoverTplStore() { if (journal::jdir::exists(tplStorePtr->jrnl_dir() + tplStorePtr->base_filename() + ".jinf")) { u_int64_t thisHighestRid = 0ULL; tplStorePtr->recover(tplNumJrnlFiles, false, 0, tplJrnlFsizeSblks, tplWCachePgSizeSblks, tplWCacheNumPages, 0, thisHighestRid, 0); if (highestRid == 0ULL) highestRid = thisHighestRid; else if (thisHighestRid - highestRid < 0x8000000000000000ULL) // RFC 1982 comparison for unsigned 64-bit highestRid = thisHighestRid; // Load tplRecoverMap by reading the TPL store readTplStore(); tplStorePtr->recover_complete(); // start journal. } } void MessageStoreImpl::recoverLockedMappings(txn_list& txns) { if (!tplStorePtr->is_ready()) recoverTplStore(); // Abort unprepared xids and populate the locked maps for (TplRecoverMapCitr i = tplRecoverMap.begin(); i != tplRecoverMap.end(); i++) { LockedMappings::shared_ptr enq_ptr; enq_ptr.reset(new LockedMappings); LockedMappings::shared_ptr deq_ptr; deq_ptr.reset(new LockedMappings); txns.push_back(new PreparedTransaction(i->first, enq_ptr, deq_ptr)); } } void MessageStoreImpl::collectPreparedXids(std::set& xids) { if (tplStorePtr->is_ready()) { tplStorePtr->read_reset(); readTplStore(); } else { recoverTplStore(); } for (TplRecoverMapCitr i = tplRecoverMap.begin(); i != tplRecoverMap.end(); i++) { // Discard all txns that are to be rolled forward/back and 1PC transactions if (!i->second.deq_flag && i->second.tpc_flag) xids.insert(i->first); } } void MessageStoreImpl::stage(const boost::intrusive_ptr& /*msg*/) { throw mrg::journal::jexception(mrg::journal::jerrno::JERR__NOTIMPL, "MessageStoreImpl", "stage"); } void MessageStoreImpl::destroy(qpid::broker::PersistableMessage& /*msg*/) { throw mrg::journal::jexception(mrg::journal::jerrno::JERR__NOTIMPL, "MessageStoreImpl", "destroy"); } void MessageStoreImpl::appendContent(const boost::intrusive_ptr& /*msg*/, const std::string& /*data*/) { throw mrg::journal::jexception(mrg::journal::jerrno::JERR__NOTIMPL, "MessageStoreImpl", "appendContent"); } void MessageStoreImpl::loadContent(const qpid::broker::PersistableQueue& queue, const boost::intrusive_ptr& msg, std::string& data, u_int64_t offset, u_int32_t length) { checkInit(); u_int64_t messageId (msg->getPersistenceId()); if (messageId != 0) { try { JournalImpl* jc = static_cast(queue.getExternalQueueStore()); if (jc && jc->is_enqueued(messageId) ) { if (!jc->loadMsgContent(messageId, data, length, offset)) { std::ostringstream oss; oss << "Queue " << queue.getName() << ": loadContent() failed: Message " << messageId << " is extern"; THROW_STORE_EXCEPTION(oss.str()); } } else { std::ostringstream oss; oss << "Queue " << queue.getName() << ": loadContent() failed: Message " << messageId << " not enqueued"; THROW_STORE_EXCEPTION(oss.str()); } } catch (const journal::jexception& e) { THROW_STORE_EXCEPTION(std::string("Queue ") + queue.getName() + ": loadContent() failed: " + e.what()); } } else { THROW_STORE_EXCEPTION("Cannot load content. Message not known to store!"); } } void MessageStoreImpl::flush(const qpid::broker::PersistableQueue& queue) { if (queue.getExternalQueueStore() == 0) return; checkInit(); std::string qn = queue.getName(); try { JournalImpl* jc = static_cast(queue.getExternalQueueStore()); if (jc) { // TODO: check if this result should be used... /*mrg::journal::iores res =*/ jc->flush(); } } catch (const journal::jexception& e) { THROW_STORE_EXCEPTION(std::string("Queue ") + qn + ": flush() failed: " + e.what() ); } } void MessageStoreImpl::enqueue(qpid::broker::TransactionContext* ctxt, const boost::intrusive_ptr& msg, const qpid::broker::PersistableQueue& queue) { checkInit(); u_int64_t queueId (queue.getPersistenceId()); u_int64_t messageId (msg->getPersistenceId()); if (queueId == 0) { THROW_STORE_EXCEPTION("Queue not created: " + queue.getName()); } TxnCtxt implicit; TxnCtxt* txn = 0; if (ctxt) { txn = check(ctxt); } else { txn = &implicit; } bool newId = false; if (messageId == 0) { messageId = messageIdSequence.next(); msg->setPersistenceId(messageId); newId = true; } store(&queue, txn, msg, newId); // add queue* to the txn map.. if (ctxt) txn->addXidRecord(queue.getExternalQueueStore()); } u_int64_t MessageStoreImpl::msgEncode(std::vector& buff, const boost::intrusive_ptr& message) { u_int32_t headerSize = message->encodedHeaderSize(); u_int64_t size = message->encodedSize() + sizeof(u_int32_t); try { buff = std::vector(size); } // long + headers + content catch (const std::exception& e) { std::ostringstream oss; oss << "Unable to allocate memory for encoding message; requested size: " << size << "; error: " << e.what(); THROW_STORE_EXCEPTION(oss.str()); } qpid::framing::Buffer buffer(&buff[0],size); buffer.putLong(headerSize); message->encode(buffer); return size; } void MessageStoreImpl::store(const qpid::broker::PersistableQueue* queue, TxnCtxt* txn, const boost::intrusive_ptr& message, bool /*newId*/) { std::vector buff; u_int64_t size = msgEncode(buff, message); try { if (queue) { boost::intrusive_ptr dtokp(new DataTokenImpl); dtokp->addRef(); dtokp->setSourceMessage(message); dtokp->set_external_rid(true); dtokp->set_rid(message->getPersistenceId()); // set the messageID into the Journal header (record-id) JournalImpl* jc = static_cast(queue->getExternalQueueStore()); if (txn->getXid().empty()) { if (message->isContentReleased()) { jc->enqueue_extern_data_record(size, dtokp.get(), !message->isPersistent()); } else { jc->enqueue_data_record(&buff[0], size, size, dtokp.get(), !message->isPersistent()); } } else { if (message->isContentReleased()) { jc->enqueue_extern_txn_data_record(size, dtokp.get(), txn->getXid(), !message->isPersistent()); } else { jc->enqueue_txn_data_record(&buff[0], size, size, dtokp.get(), txn->getXid(), !message->isPersistent()); } } } else { THROW_STORE_EXCEPTION(std::string("MessageStoreImpl::store() failed: queue NULL.")); } } catch (const journal::jexception& e) { THROW_STORE_EXCEPTION(std::string("Queue ") + queue->getName() + ": MessageStoreImpl::store() failed: " + e.what()); } } void MessageStoreImpl::dequeue(qpid::broker::TransactionContext* ctxt, const boost::intrusive_ptr& msg, const qpid::broker::PersistableQueue& queue) { checkInit(); u_int64_t queueId (queue.getPersistenceId()); u_int64_t messageId (msg->getPersistenceId()); if (queueId == 0) { THROW_STORE_EXCEPTION("Queue \"" + queue.getName() + "\" has null queue Id (has not been created)"); } if (messageId == 0) { THROW_STORE_EXCEPTION("Queue \"" + queue.getName() + "\": Dequeuing message with null persistence Id."); } TxnCtxt implicit; TxnCtxt* txn = 0; if (ctxt) { txn = check(ctxt); } else { txn = &implicit; } // add queue* to the txn map.. if (ctxt) txn->addXidRecord(queue.getExternalQueueStore()); async_dequeue(ctxt, msg, queue); msg->dequeueComplete(); } void MessageStoreImpl::async_dequeue(qpid::broker::TransactionContext* ctxt, const boost::intrusive_ptr& msg, const qpid::broker::PersistableQueue& queue) { boost::intrusive_ptr ddtokp(new DataTokenImpl); ddtokp->setSourceMessage(msg); ddtokp->set_external_rid(true); ddtokp->set_rid(messageIdSequence.next()); ddtokp->set_dequeue_rid(msg->getPersistenceId()); ddtokp->set_wstate(DataTokenImpl::ENQ); std::string tid; if (ctxt) { TxnCtxt* txn = check(ctxt); tid = txn->getXid(); } // Manually increase the ref count, as raw pointers are used beyond this point ddtokp->addRef(); try { JournalImpl* jc = static_cast(queue.getExternalQueueStore()); if (tid.empty()) { jc->dequeue_data_record(ddtokp.get()); } else { jc->dequeue_txn_data_record(ddtokp.get(), tid); } } catch (const journal::jexception& e) { ddtokp->release(); THROW_STORE_EXCEPTION(std::string("Queue ") + queue.getName() + ": async_dequeue() failed: " + e.what()); } } u_int32_t MessageStoreImpl::outstandingQueueAIO(const qpid::broker::PersistableQueue& /*queue*/) { checkInit(); return 0; } void MessageStoreImpl::completed(TxnCtxt& txn, bool commit) { try { chkTplStoreInit(); // Late initialize (if needed) // Nothing to do if not prepared if (txn.getDtok()->is_enqueued()) { txn.incrDtokRef(); DataTokenImpl* dtokp = txn.getDtok(); dtokp->set_dequeue_rid(dtokp->rid()); dtokp->set_rid(messageIdSequence.next()); tplStorePtr->dequeue_txn_data_record(txn.getDtok(), txn.getXid(), commit); } txn.complete(commit); if (mgmtObject != 0) { mgmtObject->dec_tplTransactionDepth(); if (commit) mgmtObject->inc_tplTxnCommits(); else mgmtObject->inc_tplTxnAborts(); } } catch (const std::exception& e) { QPID_LOG(error, "Error completing xid " << txn.getXid() << ": " << e.what()); throw; } } std::auto_ptr MessageStoreImpl::begin() { checkInit(); // pass sequence number for c/a return std::auto_ptr(new TxnCtxt(&messageIdSequence)); } std::auto_ptr MessageStoreImpl::begin(const std::string& xid) { checkInit(); IdSequence* jtx = &messageIdSequence; // pass sequence number for c/a return std::auto_ptr(new TPCTxnCtxt(xid, jtx)); } void MessageStoreImpl::prepare(qpid::broker::TPCTransactionContext& ctxt) { checkInit(); TxnCtxt* txn = dynamic_cast(&ctxt); if(!txn) throw qpid::broker::InvalidTransactionContextException(); localPrepare(txn); } void MessageStoreImpl::localPrepare(TxnCtxt* ctxt) { try { chkTplStoreInit(); // Late initialize (if needed) // This sync is required to ensure multi-queue atomicity - ie all txn data // must hit the disk on *all* queues before the TPL prepare (enq) is written. ctxt->sync(); ctxt->incrDtokRef(); DataTokenImpl* dtokp = ctxt->getDtok(); dtokp->set_external_rid(true); dtokp->set_rid(messageIdSequence.next()); char tpcFlag = static_cast(ctxt->isTPC()); tplStorePtr->enqueue_txn_data_record(&tpcFlag, sizeof(char), sizeof(char), dtokp, ctxt->getXid(), false); ctxt->prepare(tplStorePtr.get()); // make sure all the data is written to disk before returning ctxt->sync(); if (mgmtObject != 0) { mgmtObject->inc_tplTransactionDepth(); mgmtObject->inc_tplTxnPrepares(); } } catch (const std::exception& e) { QPID_LOG(error, "Error preparing xid " << ctxt->getXid() << ": " << e.what()); throw; } } void MessageStoreImpl::commit(qpid::broker::TransactionContext& ctxt) { checkInit(); TxnCtxt* txn(check(&ctxt)); if (!txn->isTPC()) { if (txn->impactedQueuesEmpty()) return; localPrepare(dynamic_cast(txn)); } completed(*dynamic_cast(txn), true); } void MessageStoreImpl::abort(qpid::broker::TransactionContext& ctxt) { checkInit(); TxnCtxt* txn(check(&ctxt)); if (!txn->isTPC()) { if (txn->impactedQueuesEmpty()) return; localPrepare(dynamic_cast(txn)); } completed(*dynamic_cast(txn), false); } TxnCtxt* MessageStoreImpl::check(qpid::broker::TransactionContext* ctxt) { TxnCtxt* txn = dynamic_cast(ctxt); if(!txn) throw qpid::broker::InvalidTransactionContextException(); return txn; } void MessageStoreImpl::put(db_ptr db, DbTxn* txn, Dbt& key, Dbt& value) { try { int status = db->put(txn, &key, &value, DB_NODUPDATA); if (status == DB_KEYEXIST) { THROW_STORE_EXCEPTION("duplicate data"); } else if (status) { THROW_STORE_EXCEPTION(DbEnv::strerror(status)); } } catch (const DbException& e) { THROW_STORE_EXCEPTION(e.what()); } } void MessageStoreImpl::deleteBindingsForQueue(const qpid::broker::PersistableQueue& queue) { TxnCtxt txn; txn.begin(dbenv.get(), true); try { { Cursor bindings; bindings.open(bindingDb, txn.get()); IdDbt key; Dbt value; while (bindings.next(key, value)) { qpid::framing::Buffer buffer(reinterpret_cast(value.get_data()), value.get_size()); if (buffer.available() < 8) { THROW_STORE_EXCEPTION("Not enough data for binding"); } uint64_t queueId = buffer.getLongLong(); if (queue.getPersistenceId() == queueId) { bindings->del(0); QPID_LOG(debug, "Deleting binding for " << queue.getName() << " " << key.id << "->" << queueId); } } } txn.commit(); } catch (const std::exception& e) { txn.abort(); THROW_STORE_EXCEPTION_2("Error deleting bindings", e.what()); } catch (...) { txn.abort(); throw; } QPID_LOG(debug, "Deleted all bindings for " << queue.getName() << ":" << queue.getPersistenceId()); } void MessageStoreImpl::deleteBinding(const qpid::broker::PersistableExchange& exchange, const qpid::broker::PersistableQueue& queue, const std::string& bkey) { TxnCtxt txn; txn.begin(dbenv.get(), true); try { { Cursor bindings; bindings.open(bindingDb, txn.get()); IdDbt key(exchange.getPersistenceId()); Dbt value; for (int status = bindings->get(&key, &value, DB_SET); status == 0; status = bindings->get(&key, &value, DB_NEXT_DUP)) { qpid::framing::Buffer buffer(reinterpret_cast(value.get_data()), value.get_size()); if (buffer.available() < 8) { THROW_STORE_EXCEPTION("Not enough data for binding"); } uint64_t queueId = buffer.getLongLong(); if (queue.getPersistenceId() == queueId) { std::string q; std::string k; buffer.getShortString(q); buffer.getShortString(k); if (bkey == k) { bindings->del(0); QPID_LOG(debug, "Deleting binding for " << queue.getName() << " " << key.id << "->" << queueId); } } } } txn.commit(); } catch (const std::exception& e) { txn.abort(); THROW_STORE_EXCEPTION_2("Error deleting bindings", e.what()); } catch (...) { txn.abort(); throw; } } std::string MessageStoreImpl::getJrnlBaseDir() { std::ostringstream dir; dir << storeDir << "/" << storeTopLevelDir << "/jrnl/" ; return dir.str(); } std::string MessageStoreImpl::getBdbBaseDir() { std::ostringstream dir; dir << storeDir << "/" << storeTopLevelDir << "/dat/" ; return dir.str(); } std::string MessageStoreImpl::getTplBaseDir() { std::ostringstream dir; dir << storeDir << "/" << storeTopLevelDir << "/tpl/" ; return dir.str(); } std::string MessageStoreImpl::getJrnlDir(const qpid::broker::PersistableQueue& queue) //for exmaple /var/rhm/ + queueDir/ { return getJrnlHashDir(queue.getName().c_str()); } u_int32_t MessageStoreImpl::bHash(const std::string str) { // Daniel Bernstein hash fn u_int32_t h = 0; for (std::string::const_iterator i = str.begin(); i < str.end(); i++) h = 33*h + *i; return h; } std::string MessageStoreImpl::getJrnlHashDir(const std::string& queueName) //for exmaple /var/rhm/ + queueDir/ { std::stringstream dir; dir << getJrnlBaseDir() << std::hex << std::setfill('0') << std::setw(4); dir << (bHash(queueName.c_str()) % 29); // Use a prime number for better distribution across dirs dir << "/" << queueName << "/"; return dir.str(); } std::string MessageStoreImpl::getStoreDir() const { return storeDir; } void MessageStoreImpl::journalDeleted(JournalImpl& j) { qpid::sys::Mutex::ScopedLock sl(journalListLock); journalList.erase(j.id()); } MessageStoreImpl::StoreOptions::StoreOptions(const std::string& name) : qpid::Options(name), numJrnlFiles(defNumJrnlFiles), autoJrnlExpand(defAutoJrnlExpand), autoJrnlExpandMaxFiles(defAutoJrnlExpandMaxFiles), jrnlFsizePgs(defJrnlFileSizePgs), truncateFlag(defTruncateFlag), wCachePageSizeKib(defWCachePageSize), tplNumJrnlFiles(defTplNumJrnlFiles), tplJrnlFsizePgs(defTplJrnlFileSizePgs), tplWCachePageSizeKib(defTplWCachePageSize) { std::ostringstream oss1; oss1 << "Default number of files for each journal instance (queue). [Allowable values: " << JRNL_MIN_NUM_FILES << " - " << JRNL_MAX_NUM_FILES << "]"; std::ostringstream oss2; oss2 << "Default size for each journal file in multiples of read pages (1 read page = 64KiB). [Allowable values: " << JRNL_MIN_FILE_SIZE / JRNL_RMGR_PAGE_SIZE << " - " << JRNL_MAX_FILE_SIZE / JRNL_RMGR_PAGE_SIZE << "]"; std::ostringstream oss3; oss3 << "Number of files for transaction prepared list journal instance. [Allowable values: " << JRNL_MIN_NUM_FILES << " - " << JRNL_MAX_NUM_FILES << "]"; std::ostringstream oss4; oss4 << "Size of each transaction prepared list journal file in multiples of read pages (1 read page = 64KiB) [Allowable values: " << JRNL_MIN_FILE_SIZE / JRNL_RMGR_PAGE_SIZE << " - " << JRNL_MAX_FILE_SIZE / JRNL_RMGR_PAGE_SIZE << "]"; addOptions() ("store-dir", qpid::optValue(storeDir, "DIR"), "Store directory location for persistence (instead of using --data-dir value). " "Required if --no-data-dir is also used.") ("num-jfiles", qpid::optValue(numJrnlFiles, "N"), oss1.str().c_str()) ("jfile-size-pgs", qpid::optValue(jrnlFsizePgs, "N"), oss2.str().c_str()) // TODO: Uncomment these lines when auto-expand is enabled. // ("auto-expand", qpid::optValue(autoJrnlExpand, "yes|no"), // "If yes|true|1, allows journal to auto-expand by adding additional journal files as needed. " // "If no|false|0, the number of journal files will remain fixed (num-jfiles).") // ("max-auto-expand-jfiles", qpid::optValue(autoJrnlExpandMaxFiles, "N"), // "Maximum number of journal files allowed from auto-expanding; must be greater than --num-jfiles parameter.") ("truncate", qpid::optValue(truncateFlag, "yes|no"), "If yes|true|1, will truncate the store (discard any existing records). If no|false|0, will preserve " "the existing store files for recovery.") ("wcache-page-size", qpid::optValue(wCachePageSizeKib, "N"), "Size of the pages in the write page cache in KiB. " "Allowable values - powers of 2: 1, 2, 4, ... , 128. " "Lower values decrease latency at the expense of throughput.") ("tpl-num-jfiles", qpid::optValue(tplNumJrnlFiles, "N"), oss3.str().c_str()) ("tpl-jfile-size-pgs", qpid::optValue(tplJrnlFsizePgs, "N"), oss4.str().c_str()) ("tpl-wcache-page-size", qpid::optValue(tplWCachePageSizeKib, "N"), "Size of the pages in the transaction prepared list write page cache in KiB. " "Allowable values - powers of 2: 1, 2, 4, ... , 128. " "Lower values decrease latency at the expense of throughput.") ; } }} qpid-cpp-store-debian-0.16/lib/TxnCtxt.cpp0000644000176300017630000001145611432777354017445 0ustar cajuscajus#include "TxnCtxt.h" #include #include "jrnl/jexception.hpp" #include "StoreException.h" namespace mrg { namespace msgstore { void TxnCtxt::completeTxn(bool commit) { sync(); for (ipqItr i = impactedQueues.begin(); i != impactedQueues.end(); i++) { commitTxn(static_cast(*i), commit); } impactedQueues.clear(); if (preparedXidStorePtr) commitTxn(preparedXidStorePtr, commit); } void TxnCtxt::commitTxn(JournalImpl* jc, bool commit) { if (jc && loggedtx) { /* if using journal */ boost::intrusive_ptr dtokp(new DataTokenImpl); dtokp->addRef(); dtokp->set_external_rid(true); dtokp->set_rid(loggedtx->next()); try { if (commit) { jc->txn_commit(dtokp.get(), getXid()); sync(); } else { jc->txn_abort(dtokp.get(), getXid()); } } catch (const journal::jexception& e) { THROW_STORE_EXCEPTION(std::string("Error commit") + e.what()); } } } // static uuid_t TxnCtxt::uuid; // static IdSequence TxnCtxt::uuidSeq; // static bool TxnCtxt::staticInit = TxnCtxt::setUuid(); // static bool TxnCtxt::setUuid() { ::uuid_generate(uuid); return true; } TxnCtxt::TxnCtxt(IdSequence* _loggedtx) : loggedtx(_loggedtx), dtokp(new DataTokenImpl), preparedXidStorePtr(0), txn(0) { if (loggedtx) { // // Human-readable tid: 53 bytes // // uuit_t is a char[16] // tid.reserve(53); // u_int64_t* u1 = (u_int64_t*)uuid; // u_int64_t* u2 = (u_int64_t*)(uuid + sizeof(u_int64_t)); // std::stringstream s; // s << "tid:" << std::hex << std::setfill('0') << std::setw(16) << uuidSeq.next() << ":" << std::setw(16) << *u1 << std::setw(16) << *u2; // tid.assign(s.str()); // Binary tid: 24 bytes tid.reserve(24); u_int64_t c = uuidSeq.next(); tid.append((char*)&c, sizeof(c)); tid.append((char*)&uuid, sizeof(uuid)); } } TxnCtxt::TxnCtxt(std::string _tid, IdSequence* _loggedtx) : loggedtx(_loggedtx), dtokp(new DataTokenImpl), preparedXidStorePtr(0), tid(_tid), txn(0) {} TxnCtxt::~TxnCtxt() { abort(); } void TxnCtxt::sync() { if (loggedtx) { try { for (ipqItr i = impactedQueues.begin(); i != impactedQueues.end(); i++) jrnl_flush(static_cast(*i)); if (preparedXidStorePtr) jrnl_flush(preparedXidStorePtr); for (ipqItr i = impactedQueues.begin(); i != impactedQueues.end(); i++) jrnl_sync(static_cast(*i), &journal::jcntl::_aio_cmpl_timeout); if (preparedXidStorePtr) jrnl_sync(preparedXidStorePtr, &journal::jcntl::_aio_cmpl_timeout); } catch (const journal::jexception& e) { THROW_STORE_EXCEPTION(std::string("Error during txn sync: ") + e.what()); } } } void TxnCtxt::jrnl_flush(JournalImpl* jc) { if (jc && !(jc->is_txn_synced(getXid()))) jc->flush(); } void TxnCtxt::jrnl_sync(JournalImpl* jc, timespec* timeout) { if (!jc || jc->is_txn_synced(getXid())) return; while (jc->get_wr_aio_evt_rem()) { if (jc->get_wr_events(timeout) == journal::jerrno::AIO_TIMEOUT && timeout) THROW_STORE_EXCEPTION(std::string("Error: timeout waiting for TxnCtxt::jrnl_sync()")); } } void TxnCtxt::begin(DbEnv* env, bool sync) { int err; try { err = env->txn_begin(0, &txn, 0); } catch (const DbException&) { txn = 0; throw; } if (err != 0) { std::ostringstream oss; oss << "Error: Env::txn_begin() returned error code: " << err; THROW_STORE_EXCEPTION(oss.str()); } if (sync) globalHolder = AutoScopedLock(new qpid::sys::Mutex::ScopedLock(globalSerialiser)); } void TxnCtxt::commit() { if (txn) { txn->commit(0); txn = 0; globalHolder.reset(); } } void TxnCtxt::abort(){ if (txn) { txn->abort(); txn = 0; globalHolder.reset(); } } DbTxn* TxnCtxt::get() { return txn; } bool TxnCtxt::isTPC() { return false; } const std::string& TxnCtxt::getXid() { return tid; } void TxnCtxt::addXidRecord(qpid::broker::ExternalQueueStore* queue) { impactedQueues.insert(queue); } void TxnCtxt::complete(bool commit) { completeTxn(commit); } bool TxnCtxt::impactedQueuesEmpty() { return impactedQueues.empty(); } DataTokenImpl* TxnCtxt::getDtok() { return dtokp.get(); } void TxnCtxt::incrDtokRef() { dtokp->addRef(); } void TxnCtxt::recoverDtok(const u_int64_t rid, const std::string xid) { dtokp->set_rid(rid); dtokp->set_wstate(DataTokenImpl::ENQ); dtokp->set_xid(xid); dtokp->set_external_rid(true); } TPCTxnCtxt::TPCTxnCtxt(const std::string& _xid, IdSequence* _loggedtx) : TxnCtxt(_loggedtx), xid(_xid) {} }} qpid-cpp-store-debian-0.16/lib/BufferValue.cpp0000644000176300017630000000273711514124615020224 0ustar cajuscajus/* Copyright (c) 2007, 2008 Red Hat, Inc. This file is part of the Qpid async store library msgstore.so. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA The GNU Lesser General Public License is available in the file COPYING. */ #include "BufferValue.h" namespace mrg { namespace msgstore { BufferValue::BufferValue(u_int32_t size, u_int64_t offset) : data(new char[size]), buffer(data, size) { set_data(data); set_size(size); set_flags(DB_DBT_USERMEM | DB_DBT_PARTIAL); set_doff(offset); set_dlen(size); set_ulen(size); } BufferValue::BufferValue(const qpid::broker::Persistable& p) : data(new char[p.encodedSize()]), buffer(data, p.encodedSize()) { p.encode(buffer); set_data(data); set_size(p.encodedSize()); } BufferValue::~BufferValue() { delete [] data; } }} qpid-cpp-store-debian-0.16/lib/gen/0000755000176300017630000000000011761423521016054 5ustar cajuscajusqpid-cpp-store-debian-0.16/lib/gen/qmf/0000755000176300017630000000000011761423522016640 5ustar cajuscajusqpid-cpp-store-debian-0.16/lib/gen/qmf/com/0000755000176300017630000000000011761423524017420 5ustar cajuscajusqpid-cpp-store-debian-0.16/lib/gen/qmf/com/redhat/0000755000176300017630000000000011761423525020670 5ustar cajuscajusqpid-cpp-store-debian-0.16/lib/gen/qmf/com/redhat/rhm/0000755000176300017630000000000011761423526021457 5ustar cajuscajusqpid-cpp-store-debian-0.16/lib/gen/qmf/com/redhat/rhm/store/0000755000176300017630000000000011761423556022616 5ustar cajuscajusqpid-cpp-store-debian-0.16/lib/gen/qmf/com/redhat/rhm/store/EventFull.h0000644000176300017630000000370511354735417024700 0ustar cajuscajus #ifndef _MANAGEMENT_FULL_ #define _MANAGEMENT_FULL_ // // Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. // // This source file was created by a code generator. // Please do not edit. #include "qpid/management/ManagementEvent.h" namespace qmf { namespace com { namespace redhat { namespace rhm { namespace store { class EventFull : public ::qpid::management::ManagementEvent { private: static void writeSchema (std::string& schema); static std::string packageName; static std::string eventName; static uint8_t md5Sum[MD5_LEN]; const std::string& jrnlId; const std::string& what; public: writeSchemaCall_t getWriteSchemaCall(void) { return writeSchema; } EventFull(const std::string& _jrnlId, const std::string& _what); ~EventFull() {}; static void registerSelf(::qpid::management::ManagementAgent* agent); std::string& getPackageName() const { return packageName; } std::string& getEventName() const { return eventName; } uint8_t* getMd5Sum() const { return md5Sum; } uint8_t getSeverity() const { return 3; } void encode(std::string& buffer) const; void mapEncode(::qpid::types::Variant::Map& map) const; }; }}}}} #endif /*!_MANAGEMENT_FULL_*/ qpid-cpp-store-debian-0.16/lib/gen/qmf/com/redhat/rhm/store/EventEnqThresholdExceeded.cpp0000644000176300017630000000711011357660733030353 0ustar cajuscajus // // Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. // // This source file was created by a code generator. // Please do not edit. #include "qpid/management/Manageable.h" #include "qpid/management/Buffer.h" #include "qpid/types/Variant.h" #include "qpid/amqp_0_10/Codecs.h" #include "qpid/management/ManagementAgent.h" #include "EventEnqThresholdExceeded.h" using namespace qmf::com::redhat::rhm::store; using qpid::management::ManagementAgent; using qpid::management::Manageable; using qpid::management::ManagementObject; using qpid::management::Args; using qpid::management::Mutex; using std::string; string EventEnqThresholdExceeded::packageName = string ("com.redhat.rhm.store"); string EventEnqThresholdExceeded::eventName = string ("enqThresholdExceeded"); uint8_t EventEnqThresholdExceeded::md5Sum[16] = {0x5b,0x1f,0xd4,0x87,0x9c,0xf6,0x1e,0xc3,0xdc,0x3e,0xc4,0x4,0x49,0xf3,0xa8,0xf3}; EventEnqThresholdExceeded::EventEnqThresholdExceeded (const std::string& _jrnlId, const std::string& _what) : jrnlId(_jrnlId), what(_what) {} namespace { const string NAME("name"); const string TYPE("type"); const string DESC("desc"); const string ARGCOUNT("argCount"); const string ARGS("args"); } void EventEnqThresholdExceeded::registerSelf(ManagementAgent* agent) { agent->registerEvent(packageName, eventName, md5Sum, writeSchema); } void EventEnqThresholdExceeded::writeSchema (std::string& schema) { const int _bufSize = 65536; char _msgChars[_bufSize]; ::qpid::management::Buffer buf(_msgChars, _bufSize); ::qpid::types::Variant::Map ft; // Schema class header: buf.putOctet (CLASS_KIND_EVENT); buf.putShortString (packageName); // Package Name buf.putShortString (eventName); // Event Name buf.putBin128 (md5Sum); // Schema Hash buf.putShort (2); // Argument Count // Arguments ft.clear(); ft[NAME] = "jrnlId"; ft[TYPE] = TYPE_SSTR; ft[DESC] = "Journal Id"; buf.putMap(ft); ft.clear(); ft[NAME] = "what"; ft[TYPE] = TYPE_SSTR; ft[DESC] = "Description of event"; buf.putMap(ft); { uint32_t _len = buf.getPosition(); buf.reset(); buf.getRawData(schema, _len); } } void EventEnqThresholdExceeded::encode(std::string& _sBuf) const { const int _bufSize=65536; char _msgChars[_bufSize]; ::qpid::management::Buffer buf(_msgChars, _bufSize); buf.putShortString(jrnlId); buf.putShortString(what); uint32_t _bufLen = buf.getPosition(); buf.reset(); buf.getRawData(_sBuf, _bufLen); } void EventEnqThresholdExceeded::mapEncode(::qpid::types::Variant::Map& map) const { using namespace ::qpid::types; map["jrnlId"] = ::qpid::types::Variant(jrnlId); map["what"] = ::qpid::types::Variant(what); } qpid-cpp-store-debian-0.16/lib/gen/qmf/com/redhat/rhm/store/EventFull.cpp0000644000176300017630000000663111357660733025235 0ustar cajuscajus // // Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. // // This source file was created by a code generator. // Please do not edit. #include "qpid/management/Manageable.h" #include "qpid/management/Buffer.h" #include "qpid/types/Variant.h" #include "qpid/amqp_0_10/Codecs.h" #include "qpid/management/ManagementAgent.h" #include "EventFull.h" using namespace qmf::com::redhat::rhm::store; using qpid::management::ManagementAgent; using qpid::management::Manageable; using qpid::management::ManagementObject; using qpid::management::Args; using qpid::management::Mutex; using std::string; string EventFull::packageName = string ("com.redhat.rhm.store"); string EventFull::eventName = string ("full"); uint8_t EventFull::md5Sum[16] = {0x23,0x4c,0x70,0xc1,0xb0,0xeb,0x4e,0x5d,0x72,0x37,0x56,0x60,0xc3,0x10,0x78,0x68}; EventFull::EventFull (const std::string& _jrnlId, const std::string& _what) : jrnlId(_jrnlId), what(_what) {} namespace { const string NAME("name"); const string TYPE("type"); const string DESC("desc"); const string ARGCOUNT("argCount"); const string ARGS("args"); } void EventFull::registerSelf(ManagementAgent* agent) { agent->registerEvent(packageName, eventName, md5Sum, writeSchema); } void EventFull::writeSchema (std::string& schema) { const int _bufSize = 65536; char _msgChars[_bufSize]; ::qpid::management::Buffer buf(_msgChars, _bufSize); ::qpid::types::Variant::Map ft; // Schema class header: buf.putOctet (CLASS_KIND_EVENT); buf.putShortString (packageName); // Package Name buf.putShortString (eventName); // Event Name buf.putBin128 (md5Sum); // Schema Hash buf.putShort (2); // Argument Count // Arguments ft.clear(); ft[NAME] = "jrnlId"; ft[TYPE] = TYPE_SSTR; ft[DESC] = "Journal Id"; buf.putMap(ft); ft.clear(); ft[NAME] = "what"; ft[TYPE] = TYPE_SSTR; ft[DESC] = "Description of event"; buf.putMap(ft); { uint32_t _len = buf.getPosition(); buf.reset(); buf.getRawData(schema, _len); } } void EventFull::encode(std::string& _sBuf) const { const int _bufSize=65536; char _msgChars[_bufSize]; ::qpid::management::Buffer buf(_msgChars, _bufSize); buf.putShortString(jrnlId); buf.putShortString(what); uint32_t _bufLen = buf.getPosition(); buf.reset(); buf.getRawData(_sBuf, _bufLen); } void EventFull::mapEncode(::qpid::types::Variant::Map& map) const { using namespace ::qpid::types; map["jrnlId"] = ::qpid::types::Variant(jrnlId); map["what"] = ::qpid::types::Variant(what); } qpid-cpp-store-debian-0.16/lib/gen/qmf/com/redhat/rhm/store/Journal.h0000644000176300017630000003704611466365520024411 0ustar cajuscajus #ifndef _MANAGEMENT_JOURNAL_ #define _MANAGEMENT_JOURNAL_ // // Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. // // This source file was created by a code generator. // Please do not edit. #include "qpid/management/ManagementObject.h" namespace qpid { namespace management { class ManagementAgent; } } namespace qmf { namespace com { namespace redhat { namespace rhm { namespace store { class Journal : public ::qpid::management::ManagementObject { private: static std::string packageName; static std::string className; static uint8_t md5Sum[MD5_LEN]; // Properties ::qpid::management::ObjectId queueRef; std::string name; std::string directory; std::string baseFileName; uint32_t writePageSize; uint32_t writePages; uint32_t readPageSize; uint32_t readPages; uint16_t initialFileCount; bool autoExpand; uint16_t currentFileCount; uint16_t maxFileCount; uint32_t dataFileSize; // Statistics uint32_t recordDepth; uint32_t recordDepthHigh; uint32_t recordDepthLow; uint32_t outstandingAIOs; uint32_t outstandingAIOsHigh; uint32_t outstandingAIOsLow; uint32_t freeFileCount; uint32_t freeFileCountHigh; uint32_t freeFileCountLow; uint32_t availableFileCount; uint32_t availableFileCountHigh; uint32_t availableFileCountLow; uint32_t writePageCacheDepth; uint32_t writePageCacheDepthHigh; uint32_t writePageCacheDepthLow; uint32_t readPageCacheDepth; uint32_t readPageCacheDepthHigh; uint32_t readPageCacheDepthLow; // Per-Thread Statistics struct PerThreadStats { uint64_t enqueues; uint64_t dequeues; uint32_t txn; uint64_t txnEnqueues; uint64_t txnDequeues; uint64_t txnCommits; uint64_t txnAborts; uint64_t writeWaitFailures; uint64_t writeBusyFailures; uint64_t readRecordCount; uint64_t readBusyFailures; }; struct PerThreadStats** perThreadStatsArray; inline struct PerThreadStats* getThreadStats() { int idx = getThreadIndex(); struct PerThreadStats* threadStats = perThreadStatsArray[idx]; if (threadStats == 0) { threadStats = new(PerThreadStats); perThreadStatsArray[idx] = threadStats; threadStats->enqueues = 0; threadStats->dequeues = 0; threadStats->txn = 0; threadStats->txnEnqueues = 0; threadStats->txnDequeues = 0; threadStats->txnCommits = 0; threadStats->txnAborts = 0; threadStats->writeWaitFailures = 0; threadStats->writeBusyFailures = 0; threadStats->readRecordCount = 0; threadStats->readBusyFailures = 0; } return threadStats; } void aggregatePerThreadStats(struct PerThreadStats*) const; public: static void writeSchema(std::string& schema); void mapEncodeValues(::qpid::types::Variant::Map& map, bool includeProperties=true, bool includeStatistics=true); void mapDecodeValues(const ::qpid::types::Variant::Map& map); void doMethod(std::string& methodName, const ::qpid::types::Variant::Map& inMap, ::qpid::types::Variant::Map& outMap, const std::string& userId); std::string getKey() const; uint32_t writePropertiesSize() const; void readProperties(const std::string& buf); void writeProperties(std::string& buf) const; void writeStatistics(std::string& buf, bool skipHeaders = false); void doMethod(std::string& methodName, const std::string& inBuf, std::string& outBuf, const std::string& userId); writeSchemaCall_t getWriteSchemaCall() { return writeSchema; } Journal(::qpid::management::ManagementAgent* agent, ::qpid::management::Manageable* coreObject); ~Journal(); void setReference(::qpid::management::ObjectId objectId) { queueRef = objectId; } static void registerSelf(::qpid::management::ManagementAgent* agent); std::string& getPackageName() const { return packageName; } std::string& getClassName() const { return className; } uint8_t* getMd5Sum() const { return md5Sum; } // Method IDs static const uint32_t METHOD_EXPAND = 1; // Accessor Methods inline void set_queueRef (const ::qpid::management::ObjectId& val) { ::qpid::management::Mutex::ScopedLock mutex(accessLock); queueRef = val; configChanged = true; } inline const ::qpid::management::ObjectId& get_queueRef() { ::qpid::management::Mutex::ScopedLock mutex(accessLock); return queueRef; } inline void set_name (const std::string& val) { ::qpid::management::Mutex::ScopedLock mutex(accessLock); name = val; configChanged = true; } inline const std::string& get_name() { ::qpid::management::Mutex::ScopedLock mutex(accessLock); return name; } inline void set_directory (const std::string& val) { ::qpid::management::Mutex::ScopedLock mutex(accessLock); directory = val; configChanged = true; } inline const std::string& get_directory() { ::qpid::management::Mutex::ScopedLock mutex(accessLock); return directory; } inline void set_baseFileName (const std::string& val) { ::qpid::management::Mutex::ScopedLock mutex(accessLock); baseFileName = val; configChanged = true; } inline const std::string& get_baseFileName() { ::qpid::management::Mutex::ScopedLock mutex(accessLock); return baseFileName; } inline void set_writePageSize (uint32_t val) { ::qpid::management::Mutex::ScopedLock mutex(accessLock); writePageSize = val; configChanged = true; } inline uint32_t get_writePageSize() { ::qpid::management::Mutex::ScopedLock mutex(accessLock); return writePageSize; } inline void set_writePages (uint32_t val) { ::qpid::management::Mutex::ScopedLock mutex(accessLock); writePages = val; configChanged = true; } inline uint32_t get_writePages() { ::qpid::management::Mutex::ScopedLock mutex(accessLock); return writePages; } inline void set_readPageSize (uint32_t val) { ::qpid::management::Mutex::ScopedLock mutex(accessLock); readPageSize = val; configChanged = true; } inline uint32_t get_readPageSize() { ::qpid::management::Mutex::ScopedLock mutex(accessLock); return readPageSize; } inline void set_readPages (uint32_t val) { ::qpid::management::Mutex::ScopedLock mutex(accessLock); readPages = val; configChanged = true; } inline uint32_t get_readPages() { ::qpid::management::Mutex::ScopedLock mutex(accessLock); return readPages; } inline void set_initialFileCount (uint16_t val) { ::qpid::management::Mutex::ScopedLock mutex(accessLock); initialFileCount = val; configChanged = true; } inline uint16_t get_initialFileCount() { ::qpid::management::Mutex::ScopedLock mutex(accessLock); return initialFileCount; } inline void set_autoExpand (bool val) { ::qpid::management::Mutex::ScopedLock mutex(accessLock); autoExpand = val; configChanged = true; } inline bool get_autoExpand() { ::qpid::management::Mutex::ScopedLock mutex(accessLock); return autoExpand; } inline void set_currentFileCount (uint16_t val) { ::qpid::management::Mutex::ScopedLock mutex(accessLock); currentFileCount = val; configChanged = true; } inline uint16_t get_currentFileCount() { ::qpid::management::Mutex::ScopedLock mutex(accessLock); return currentFileCount; } inline void set_maxFileCount (uint16_t val) { ::qpid::management::Mutex::ScopedLock mutex(accessLock); maxFileCount = val; configChanged = true; } inline uint16_t get_maxFileCount() { ::qpid::management::Mutex::ScopedLock mutex(accessLock); return maxFileCount; } inline void set_dataFileSize (uint32_t val) { ::qpid::management::Mutex::ScopedLock mutex(accessLock); dataFileSize = val; configChanged = true; } inline uint32_t get_dataFileSize() { ::qpid::management::Mutex::ScopedLock mutex(accessLock); return dataFileSize; } inline void inc_recordDepth (uint32_t by = 1) { ::qpid::management::Mutex::ScopedLock mutex(accessLock); recordDepth += by; if (recordDepthHigh < recordDepth) recordDepthHigh = recordDepth; instChanged = true; } inline void dec_recordDepth (uint32_t by = 1) { ::qpid::management::Mutex::ScopedLock mutex(accessLock); recordDepth -= by; if (recordDepthLow > recordDepth) recordDepthLow = recordDepth; instChanged = true; } inline void inc_enqueues (uint64_t by = 1) { getThreadStats()->enqueues += by; instChanged = true; } inline void dec_enqueues (uint64_t by = 1) { getThreadStats()->enqueues -= by; instChanged = true; } inline void inc_dequeues (uint64_t by = 1) { getThreadStats()->dequeues += by; instChanged = true; } inline void dec_dequeues (uint64_t by = 1) { getThreadStats()->dequeues -= by; instChanged = true; } inline void inc_txn (uint32_t by = 1) { getThreadStats()->txn += by; instChanged = true; } inline void dec_txn (uint32_t by = 1) { getThreadStats()->txn -= by; instChanged = true; } inline void inc_txnEnqueues (uint64_t by = 1) { getThreadStats()->txnEnqueues += by; instChanged = true; } inline void dec_txnEnqueues (uint64_t by = 1) { getThreadStats()->txnEnqueues -= by; instChanged = true; } inline void inc_txnDequeues (uint64_t by = 1) { getThreadStats()->txnDequeues += by; instChanged = true; } inline void dec_txnDequeues (uint64_t by = 1) { getThreadStats()->txnDequeues -= by; instChanged = true; } inline void inc_txnCommits (uint64_t by = 1) { getThreadStats()->txnCommits += by; instChanged = true; } inline void dec_txnCommits (uint64_t by = 1) { getThreadStats()->txnCommits -= by; instChanged = true; } inline void inc_txnAborts (uint64_t by = 1) { getThreadStats()->txnAborts += by; instChanged = true; } inline void dec_txnAborts (uint64_t by = 1) { getThreadStats()->txnAborts -= by; instChanged = true; } inline void inc_outstandingAIOs (uint32_t by = 1) { ::qpid::management::Mutex::ScopedLock mutex(accessLock); outstandingAIOs += by; if (outstandingAIOsHigh < outstandingAIOs) outstandingAIOsHigh = outstandingAIOs; instChanged = true; } inline void dec_outstandingAIOs (uint32_t by = 1) { ::qpid::management::Mutex::ScopedLock mutex(accessLock); outstandingAIOs -= by; if (outstandingAIOsLow > outstandingAIOs) outstandingAIOsLow = outstandingAIOs; instChanged = true; } inline void inc_freeFileCount (uint32_t by = 1) { ::qpid::management::Mutex::ScopedLock mutex(accessLock); freeFileCount += by; if (freeFileCountHigh < freeFileCount) freeFileCountHigh = freeFileCount; instChanged = true; } inline void dec_freeFileCount (uint32_t by = 1) { ::qpid::management::Mutex::ScopedLock mutex(accessLock); freeFileCount -= by; if (freeFileCountLow > freeFileCount) freeFileCountLow = freeFileCount; instChanged = true; } inline void inc_availableFileCount (uint32_t by = 1) { ::qpid::management::Mutex::ScopedLock mutex(accessLock); availableFileCount += by; if (availableFileCountHigh < availableFileCount) availableFileCountHigh = availableFileCount; instChanged = true; } inline void dec_availableFileCount (uint32_t by = 1) { ::qpid::management::Mutex::ScopedLock mutex(accessLock); availableFileCount -= by; if (availableFileCountLow > availableFileCount) availableFileCountLow = availableFileCount; instChanged = true; } inline void inc_writeWaitFailures (uint64_t by = 1) { getThreadStats()->writeWaitFailures += by; instChanged = true; } inline void dec_writeWaitFailures (uint64_t by = 1) { getThreadStats()->writeWaitFailures -= by; instChanged = true; } inline void inc_writeBusyFailures (uint64_t by = 1) { getThreadStats()->writeBusyFailures += by; instChanged = true; } inline void dec_writeBusyFailures (uint64_t by = 1) { getThreadStats()->writeBusyFailures -= by; instChanged = true; } inline void inc_readRecordCount (uint64_t by = 1) { getThreadStats()->readRecordCount += by; instChanged = true; } inline void dec_readRecordCount (uint64_t by = 1) { getThreadStats()->readRecordCount -= by; instChanged = true; } inline void inc_readBusyFailures (uint64_t by = 1) { getThreadStats()->readBusyFailures += by; instChanged = true; } inline void dec_readBusyFailures (uint64_t by = 1) { getThreadStats()->readBusyFailures -= by; instChanged = true; } inline void inc_writePageCacheDepth (uint32_t by = 1) { ::qpid::management::Mutex::ScopedLock mutex(accessLock); writePageCacheDepth += by; if (writePageCacheDepthHigh < writePageCacheDepth) writePageCacheDepthHigh = writePageCacheDepth; instChanged = true; } inline void dec_writePageCacheDepth (uint32_t by = 1) { ::qpid::management::Mutex::ScopedLock mutex(accessLock); writePageCacheDepth -= by; if (writePageCacheDepthLow > writePageCacheDepth) writePageCacheDepthLow = writePageCacheDepth; instChanged = true; } inline void inc_readPageCacheDepth (uint32_t by = 1) { ::qpid::management::Mutex::ScopedLock mutex(accessLock); readPageCacheDepth += by; if (readPageCacheDepthHigh < readPageCacheDepth) readPageCacheDepthHigh = readPageCacheDepth; instChanged = true; } inline void dec_readPageCacheDepth (uint32_t by = 1) { ::qpid::management::Mutex::ScopedLock mutex(accessLock); readPageCacheDepth -= by; if (readPageCacheDepthLow > readPageCacheDepth) readPageCacheDepthLow = readPageCacheDepth; instChanged = true; } }; }}}}} #endif /*!_MANAGEMENT_JOURNAL_*/ qpid-cpp-store-debian-0.16/lib/gen/qmf/com/redhat/rhm/store/Store.h0000644000176300017630000002345211466365520024067 0ustar cajuscajus #ifndef _MANAGEMENT_STORE_ #define _MANAGEMENT_STORE_ // // Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. // // This source file was created by a code generator. // Please do not edit. #include "qpid/management/ManagementObject.h" namespace qpid { namespace management { class ManagementAgent; } } namespace qmf { namespace com { namespace redhat { namespace rhm { namespace store { class Store : public ::qpid::management::ManagementObject { private: static std::string packageName; static std::string className; static uint8_t md5Sum[MD5_LEN]; // Properties ::qpid::management::ObjectId brokerRef; std::string location; uint16_t defaultInitialFileCount; uint32_t defaultDataFileSize; bool tplIsInitialized; std::string tplDirectory; uint32_t tplWritePageSize; uint32_t tplWritePages; uint16_t tplInitialFileCount; uint32_t tplDataFileSize; uint32_t tplCurrentFileCount; // Statistics uint32_t tplTransactionDepth; uint32_t tplTransactionDepthHigh; uint32_t tplTransactionDepthLow; uint32_t tplOutstandingAIOs; uint32_t tplOutstandingAIOsHigh; uint32_t tplOutstandingAIOsLow; // Per-Thread Statistics struct PerThreadStats { uint64_t tplTxnPrepares; uint64_t tplTxnCommits; uint64_t tplTxnAborts; }; struct PerThreadStats** perThreadStatsArray; inline struct PerThreadStats* getThreadStats() { int idx = getThreadIndex(); struct PerThreadStats* threadStats = perThreadStatsArray[idx]; if (threadStats == 0) { threadStats = new(PerThreadStats); perThreadStatsArray[idx] = threadStats; threadStats->tplTxnPrepares = 0; threadStats->tplTxnCommits = 0; threadStats->tplTxnAborts = 0; } return threadStats; } void aggregatePerThreadStats(struct PerThreadStats*) const; public: static void writeSchema(std::string& schema); void mapEncodeValues(::qpid::types::Variant::Map& map, bool includeProperties=true, bool includeStatistics=true); void mapDecodeValues(const ::qpid::types::Variant::Map& map); void doMethod(std::string& methodName, const ::qpid::types::Variant::Map& inMap, ::qpid::types::Variant::Map& outMap, const std::string& userId); std::string getKey() const; uint32_t writePropertiesSize() const; void readProperties(const std::string& buf); void writeProperties(std::string& buf) const; void writeStatistics(std::string& buf, bool skipHeaders = false); void doMethod(std::string& methodName, const std::string& inBuf, std::string& outBuf, const std::string& userId); writeSchemaCall_t getWriteSchemaCall() { return writeSchema; } Store(::qpid::management::ManagementAgent* agent, ::qpid::management::Manageable* coreObject, ::qpid::management::Manageable* _parent); ~Store(); static void registerSelf(::qpid::management::ManagementAgent* agent); std::string& getPackageName() const { return packageName; } std::string& getClassName() const { return className; } uint8_t* getMd5Sum() const { return md5Sum; } // Method IDs // Accessor Methods inline void set_brokerRef (const ::qpid::management::ObjectId& val) { ::qpid::management::Mutex::ScopedLock mutex(accessLock); brokerRef = val; configChanged = true; } inline const ::qpid::management::ObjectId& get_brokerRef() { ::qpid::management::Mutex::ScopedLock mutex(accessLock); return brokerRef; } inline void set_location (const std::string& val) { ::qpid::management::Mutex::ScopedLock mutex(accessLock); location = val; configChanged = true; } inline const std::string& get_location() { ::qpid::management::Mutex::ScopedLock mutex(accessLock); return location; } inline void set_defaultInitialFileCount (uint16_t val) { ::qpid::management::Mutex::ScopedLock mutex(accessLock); defaultInitialFileCount = val; configChanged = true; } inline uint16_t get_defaultInitialFileCount() { ::qpid::management::Mutex::ScopedLock mutex(accessLock); return defaultInitialFileCount; } inline void set_defaultDataFileSize (uint32_t val) { ::qpid::management::Mutex::ScopedLock mutex(accessLock); defaultDataFileSize = val; configChanged = true; } inline uint32_t get_defaultDataFileSize() { ::qpid::management::Mutex::ScopedLock mutex(accessLock); return defaultDataFileSize; } inline void set_tplIsInitialized (bool val) { ::qpid::management::Mutex::ScopedLock mutex(accessLock); tplIsInitialized = val; configChanged = true; } inline bool get_tplIsInitialized() { ::qpid::management::Mutex::ScopedLock mutex(accessLock); return tplIsInitialized; } inline void set_tplDirectory (const std::string& val) { ::qpid::management::Mutex::ScopedLock mutex(accessLock); tplDirectory = val; configChanged = true; } inline const std::string& get_tplDirectory() { ::qpid::management::Mutex::ScopedLock mutex(accessLock); return tplDirectory; } inline void set_tplWritePageSize (uint32_t val) { ::qpid::management::Mutex::ScopedLock mutex(accessLock); tplWritePageSize = val; configChanged = true; } inline uint32_t get_tplWritePageSize() { ::qpid::management::Mutex::ScopedLock mutex(accessLock); return tplWritePageSize; } inline void set_tplWritePages (uint32_t val) { ::qpid::management::Mutex::ScopedLock mutex(accessLock); tplWritePages = val; configChanged = true; } inline uint32_t get_tplWritePages() { ::qpid::management::Mutex::ScopedLock mutex(accessLock); return tplWritePages; } inline void set_tplInitialFileCount (uint16_t val) { ::qpid::management::Mutex::ScopedLock mutex(accessLock); tplInitialFileCount = val; configChanged = true; } inline uint16_t get_tplInitialFileCount() { ::qpid::management::Mutex::ScopedLock mutex(accessLock); return tplInitialFileCount; } inline void set_tplDataFileSize (uint32_t val) { ::qpid::management::Mutex::ScopedLock mutex(accessLock); tplDataFileSize = val; configChanged = true; } inline uint32_t get_tplDataFileSize() { ::qpid::management::Mutex::ScopedLock mutex(accessLock); return tplDataFileSize; } inline void set_tplCurrentFileCount (uint32_t val) { ::qpid::management::Mutex::ScopedLock mutex(accessLock); tplCurrentFileCount = val; configChanged = true; } inline uint32_t get_tplCurrentFileCount() { ::qpid::management::Mutex::ScopedLock mutex(accessLock); return tplCurrentFileCount; } inline void inc_tplTransactionDepth (uint32_t by = 1) { ::qpid::management::Mutex::ScopedLock mutex(accessLock); tplTransactionDepth += by; if (tplTransactionDepthHigh < tplTransactionDepth) tplTransactionDepthHigh = tplTransactionDepth; instChanged = true; } inline void dec_tplTransactionDepth (uint32_t by = 1) { ::qpid::management::Mutex::ScopedLock mutex(accessLock); tplTransactionDepth -= by; if (tplTransactionDepthLow > tplTransactionDepth) tplTransactionDepthLow = tplTransactionDepth; instChanged = true; } inline void inc_tplTxnPrepares (uint64_t by = 1) { getThreadStats()->tplTxnPrepares += by; instChanged = true; } inline void dec_tplTxnPrepares (uint64_t by = 1) { getThreadStats()->tplTxnPrepares -= by; instChanged = true; } inline void inc_tplTxnCommits (uint64_t by = 1) { getThreadStats()->tplTxnCommits += by; instChanged = true; } inline void dec_tplTxnCommits (uint64_t by = 1) { getThreadStats()->tplTxnCommits -= by; instChanged = true; } inline void inc_tplTxnAborts (uint64_t by = 1) { getThreadStats()->tplTxnAborts += by; instChanged = true; } inline void dec_tplTxnAborts (uint64_t by = 1) { getThreadStats()->tplTxnAborts -= by; instChanged = true; } inline void inc_tplOutstandingAIOs (uint32_t by = 1) { ::qpid::management::Mutex::ScopedLock mutex(accessLock); tplOutstandingAIOs += by; if (tplOutstandingAIOsHigh < tplOutstandingAIOs) tplOutstandingAIOsHigh = tplOutstandingAIOs; instChanged = true; } inline void dec_tplOutstandingAIOs (uint32_t by = 1) { ::qpid::management::Mutex::ScopedLock mutex(accessLock); tplOutstandingAIOs -= by; if (tplOutstandingAIOsLow > tplOutstandingAIOs) tplOutstandingAIOsLow = tplOutstandingAIOs; instChanged = true; } }; }}}}} #endif /*!_MANAGEMENT_STORE_*/ qpid-cpp-store-debian-0.16/lib/gen/qmf/com/redhat/rhm/store/EventRecovered.cpp0000644000176300017630000001153711357660733026252 0ustar cajuscajus // // Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. // // This source file was created by a code generator. // Please do not edit. #include "qpid/management/Manageable.h" #include "qpid/management/Buffer.h" #include "qpid/types/Variant.h" #include "qpid/amqp_0_10/Codecs.h" #include "qpid/management/ManagementAgent.h" #include "EventRecovered.h" using namespace qmf::com::redhat::rhm::store; using qpid::management::ManagementAgent; using qpid::management::Manageable; using qpid::management::ManagementObject; using qpid::management::Args; using qpid::management::Mutex; using std::string; string EventRecovered::packageName = string ("com.redhat.rhm.store"); string EventRecovered::eventName = string ("recovered"); uint8_t EventRecovered::md5Sum[16] = {0x23,0x28,0x44,0x5a,0x72,0xb0,0x1e,0xda,0x7d,0xff,0x0,0x9,0x1,0x6d,0xa8,0xd4}; EventRecovered::EventRecovered (const std::string& _jrnlId, const uint32_t _fileSize, const uint16_t _numFiles, const uint32_t _numEnq, const uint32_t _numTxn, const uint32_t _numTxnEnq, const uint32_t _numTxnDeq) : jrnlId(_jrnlId), fileSize(_fileSize), numFiles(_numFiles), numEnq(_numEnq), numTxn(_numTxn), numTxnEnq(_numTxnEnq), numTxnDeq(_numTxnDeq) {} namespace { const string NAME("name"); const string TYPE("type"); const string DESC("desc"); const string ARGCOUNT("argCount"); const string ARGS("args"); } void EventRecovered::registerSelf(ManagementAgent* agent) { agent->registerEvent(packageName, eventName, md5Sum, writeSchema); } void EventRecovered::writeSchema (std::string& schema) { const int _bufSize = 65536; char _msgChars[_bufSize]; ::qpid::management::Buffer buf(_msgChars, _bufSize); ::qpid::types::Variant::Map ft; // Schema class header: buf.putOctet (CLASS_KIND_EVENT); buf.putShortString (packageName); // Package Name buf.putShortString (eventName); // Event Name buf.putBin128 (md5Sum); // Schema Hash buf.putShort (7); // Argument Count // Arguments ft.clear(); ft[NAME] = "jrnlId"; ft[TYPE] = TYPE_SSTR; ft[DESC] = "Journal Id"; buf.putMap(ft); ft.clear(); ft[NAME] = "fileSize"; ft[TYPE] = TYPE_U32; ft[DESC] = "Journal file size in bytes"; buf.putMap(ft); ft.clear(); ft[NAME] = "numFiles"; ft[TYPE] = TYPE_U16; ft[DESC] = "Number of journal files"; buf.putMap(ft); ft.clear(); ft[NAME] = "numEnq"; ft[TYPE] = TYPE_U32; ft[DESC] = "Number of recovered enqueues"; buf.putMap(ft); ft.clear(); ft[NAME] = "numTxn"; ft[TYPE] = TYPE_U32; ft[DESC] = "Number of recovered transactions"; buf.putMap(ft); ft.clear(); ft[NAME] = "numTxnEnq"; ft[TYPE] = TYPE_U32; ft[DESC] = "Number of recovered transactional enqueues"; buf.putMap(ft); ft.clear(); ft[NAME] = "numTxnDeq"; ft[TYPE] = TYPE_U32; ft[DESC] = "Number of recovered transactional dequeues"; buf.putMap(ft); { uint32_t _len = buf.getPosition(); buf.reset(); buf.getRawData(schema, _len); } } void EventRecovered::encode(std::string& _sBuf) const { const int _bufSize=65536; char _msgChars[_bufSize]; ::qpid::management::Buffer buf(_msgChars, _bufSize); buf.putShortString(jrnlId); buf.putLong(fileSize); buf.putShort(numFiles); buf.putLong(numEnq); buf.putLong(numTxn); buf.putLong(numTxnEnq); buf.putLong(numTxnDeq); uint32_t _bufLen = buf.getPosition(); buf.reset(); buf.getRawData(_sBuf, _bufLen); } void EventRecovered::mapEncode(::qpid::types::Variant::Map& map) const { using namespace ::qpid::types; map["jrnlId"] = ::qpid::types::Variant(jrnlId); map["fileSize"] = ::qpid::types::Variant(fileSize); map["numFiles"] = ::qpid::types::Variant(numFiles); map["numEnq"] = ::qpid::types::Variant(numEnq); map["numTxn"] = ::qpid::types::Variant(numTxn); map["numTxnEnq"] = ::qpid::types::Variant(numTxnEnq); map["numTxnDeq"] = ::qpid::types::Variant(numTxnDeq); } qpid-cpp-store-debian-0.16/lib/gen/qmf/com/redhat/rhm/store/Package.cpp0000644000176300017630000000261011161720772024646 0ustar cajuscajus // // Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. // // This source file was created by a code generator. // Please do not edit. #include "Package.h" #include "Store.h" #include "Journal.h" #include "EventEnqThresholdExceeded.h" #include "EventCreated.h" #include "EventFull.h" #include "EventRecovered.h" using namespace qmf::com::redhat::rhm::store; Package::Package (::qpid::management::ManagementAgent* agent) { Store::registerSelf(agent); Journal::registerSelf(agent); EventEnqThresholdExceeded::registerSelf(agent); EventCreated::registerSelf(agent); EventFull::registerSelf(agent); EventRecovered::registerSelf(agent); } qpid-cpp-store-debian-0.16/lib/gen/qmf/com/redhat/rhm/store/Journal.cpp0000644000176300017630000006450211466365520024741 0ustar cajuscajus // // Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. // // This source file was created by a code generator. // Please do not edit. #include "qpid/management/Manageable.h" #include "qpid/management/Buffer.h" #include "qpid/types/Variant.h" #include "qpid/amqp_0_10/Codecs.h" #include "qpid/management/ManagementAgent.h" #include "Journal.h" #include "ArgsJournalExpand.h" #include #include using namespace qmf::com::redhat::rhm::store; using qpid::management::ManagementAgent; using qpid::management::Manageable; using qpid::management::ManagementObject; using qpid::management::Args; using qpid::management::Mutex; using std::string; string Journal::packageName = string ("com.redhat.rhm.store"); string Journal::className = string ("journal"); uint8_t Journal::md5Sum[MD5_LEN] = {0x9,0xc2,0xe2,0xfc,0xcc,0x2e,0x72,0xaa,0x3f,0xce,0x36,0x28,0xce,0x81,0xc,0xd4}; Journal::Journal (ManagementAgent*, Manageable* _core) : ManagementObject(_core) { queueRef = ::qpid::management::ObjectId(); name = ""; directory = ""; baseFileName = ""; writePageSize = 0; writePages = 0; readPageSize = 0; readPages = 0; initialFileCount = 0; autoExpand = 0; currentFileCount = 0; maxFileCount = 0; dataFileSize = 0; recordDepth = 0; recordDepthHigh = 0; recordDepthLow = 0; outstandingAIOs = 0; outstandingAIOsHigh = 0; outstandingAIOsLow = 0; freeFileCount = 0; freeFileCountHigh = 0; freeFileCountLow = 0; availableFileCount = 0; availableFileCountHigh = 0; availableFileCountLow = 0; writePageCacheDepth = 0; writePageCacheDepthHigh = 0; writePageCacheDepthLow = 0; readPageCacheDepth = 0; readPageCacheDepthHigh = 0; readPageCacheDepthLow = 0; perThreadStatsArray = new struct PerThreadStats*[maxThreads]; for (int idx = 0; idx < maxThreads; idx++) perThreadStatsArray[idx] = 0; } Journal::~Journal () { for (int idx = 0; idx < maxThreads; idx++) if (perThreadStatsArray[idx] != 0) delete perThreadStatsArray[idx]; delete[] perThreadStatsArray; } namespace { const string NAME("name"); const string TYPE("type"); const string ACCESS("access"); const string IS_INDEX("index"); const string IS_OPTIONAL("optional"); const string UNIT("unit"); const string MIN("min"); const string MAX("max"); const string MAXLEN("maxlen"); const string DESC("desc"); const string ARGCOUNT("argCount"); const string ARGS("args"); const string DIR("dir"); const string DEFAULT("default"); } void Journal::registerSelf(ManagementAgent* agent) { agent->registerClass(packageName, className, md5Sum, writeSchema); } void Journal::writeSchema (std::string& schema) { const int _bufSize=65536; char _msgChars[_bufSize]; ::qpid::management::Buffer buf(_msgChars, _bufSize); ::qpid::types::Variant::Map ft; // Schema class header: buf.putOctet (CLASS_KIND_TABLE); buf.putShortString (packageName); // Package Name buf.putShortString (className); // Class Name buf.putBin128 (md5Sum); // Schema Hash buf.putShort (13); // Config Element Count buf.putShort (29); // Inst Element Count buf.putShort (1); // Method Count // Properties ft.clear(); ft[NAME] = "queueRef"; ft[TYPE] = TYPE_REF; ft[ACCESS] = ACCESS_RO; ft[IS_INDEX] = 0; ft[IS_OPTIONAL] = 0; buf.putMap(ft); ft.clear(); ft[NAME] = "name"; ft[TYPE] = TYPE_SSTR; ft[ACCESS] = ACCESS_RO; ft[IS_INDEX] = 1; ft[IS_OPTIONAL] = 0; buf.putMap(ft); ft.clear(); ft[NAME] = "directory"; ft[TYPE] = TYPE_SSTR; ft[ACCESS] = ACCESS_RO; ft[IS_INDEX] = 0; ft[IS_OPTIONAL] = 0; ft[DESC] = "Directory containing journal files"; buf.putMap(ft); ft.clear(); ft[NAME] = "baseFileName"; ft[TYPE] = TYPE_SSTR; ft[ACCESS] = ACCESS_RO; ft[IS_INDEX] = 0; ft[IS_OPTIONAL] = 0; ft[DESC] = "Base filename prefix for journal"; buf.putMap(ft); ft.clear(); ft[NAME] = "writePageSize"; ft[TYPE] = TYPE_U32; ft[ACCESS] = ACCESS_RO; ft[IS_INDEX] = 0; ft[IS_OPTIONAL] = 0; ft[UNIT] = "byte"; ft[DESC] = "Page size in write-page-cache"; buf.putMap(ft); ft.clear(); ft[NAME] = "writePages"; ft[TYPE] = TYPE_U32; ft[ACCESS] = ACCESS_RO; ft[IS_INDEX] = 0; ft[IS_OPTIONAL] = 0; ft[UNIT] = "wpage"; ft[DESC] = "Number of pages in write-page-cache"; buf.putMap(ft); ft.clear(); ft[NAME] = "readPageSize"; ft[TYPE] = TYPE_U32; ft[ACCESS] = ACCESS_RO; ft[IS_INDEX] = 0; ft[IS_OPTIONAL] = 0; ft[UNIT] = "byte"; ft[DESC] = "Page size in read-page-cache"; buf.putMap(ft); ft.clear(); ft[NAME] = "readPages"; ft[TYPE] = TYPE_U32; ft[ACCESS] = ACCESS_RO; ft[IS_INDEX] = 0; ft[IS_OPTIONAL] = 0; ft[UNIT] = "rpage"; ft[DESC] = "Number of pages in read-page-cache"; buf.putMap(ft); ft.clear(); ft[NAME] = "initialFileCount"; ft[TYPE] = TYPE_U16; ft[ACCESS] = ACCESS_RO; ft[IS_INDEX] = 0; ft[IS_OPTIONAL] = 0; ft[UNIT] = "file"; ft[DESC] = "Number of files initially allocated to this journal"; buf.putMap(ft); ft.clear(); ft[NAME] = "autoExpand"; ft[TYPE] = TYPE_BOOL; ft[ACCESS] = ACCESS_RO; ft[IS_INDEX] = 0; ft[IS_OPTIONAL] = 0; ft[DESC] = "Auto-expand enabled"; buf.putMap(ft); ft.clear(); ft[NAME] = "currentFileCount"; ft[TYPE] = TYPE_U16; ft[ACCESS] = ACCESS_RO; ft[IS_INDEX] = 0; ft[IS_OPTIONAL] = 0; ft[UNIT] = "file"; ft[DESC] = "Number of files currently allocated to this journal"; buf.putMap(ft); ft.clear(); ft[NAME] = "maxFileCount"; ft[TYPE] = TYPE_U16; ft[ACCESS] = ACCESS_RO; ft[IS_INDEX] = 0; ft[IS_OPTIONAL] = 0; ft[UNIT] = "file"; ft[DESC] = "Max number of files allowed for this journal"; buf.putMap(ft); ft.clear(); ft[NAME] = "dataFileSize"; ft[TYPE] = TYPE_U32; ft[ACCESS] = ACCESS_RO; ft[IS_INDEX] = 0; ft[IS_OPTIONAL] = 0; ft[UNIT] = "byte"; ft[DESC] = "Size of each journal data file"; buf.putMap(ft); // Statistics ft.clear(); ft[NAME] = "recordDepth"; ft[TYPE] = TYPE_U32; ft[UNIT] = "record"; ft[DESC] = "Number of currently enqueued records (durable messages)"; buf.putMap(ft); ft.clear(); ft[NAME] = "recordDepthHigh"; ft[TYPE] = TYPE_U32; ft[UNIT] = "record"; ft[DESC] = "Number of currently enqueued records (durable messages) (High)"; buf.putMap(ft); ft.clear(); ft[NAME] = "recordDepthLow"; ft[TYPE] = TYPE_U32; ft[UNIT] = "record"; ft[DESC] = "Number of currently enqueued records (durable messages) (Low)"; buf.putMap(ft); ft.clear(); ft[NAME] = "enqueues"; ft[TYPE] = TYPE_U64; ft[UNIT] = "record"; ft[DESC] = "Total enqueued records on journal"; buf.putMap(ft); ft.clear(); ft[NAME] = "dequeues"; ft[TYPE] = TYPE_U64; ft[UNIT] = "record"; ft[DESC] = "Total dequeued records on journal"; buf.putMap(ft); ft.clear(); ft[NAME] = "txn"; ft[TYPE] = TYPE_U32; ft[UNIT] = "record"; ft[DESC] = "Total open transactions (xids) on journal"; buf.putMap(ft); ft.clear(); ft[NAME] = "txnEnqueues"; ft[TYPE] = TYPE_U64; ft[UNIT] = "record"; ft[DESC] = "Total transactional enqueued records on journal"; buf.putMap(ft); ft.clear(); ft[NAME] = "txnDequeues"; ft[TYPE] = TYPE_U64; ft[UNIT] = "record"; ft[DESC] = "Total transactional dequeued records on journal"; buf.putMap(ft); ft.clear(); ft[NAME] = "txnCommits"; ft[TYPE] = TYPE_U64; ft[UNIT] = "record"; ft[DESC] = "Total transactional commit records on journal"; buf.putMap(ft); ft.clear(); ft[NAME] = "txnAborts"; ft[TYPE] = TYPE_U64; ft[UNIT] = "record"; ft[DESC] = "Total transactional abort records on journal"; buf.putMap(ft); ft.clear(); ft[NAME] = "outstandingAIOs"; ft[TYPE] = TYPE_U32; ft[UNIT] = "aio_op"; ft[DESC] = "Number of currently outstanding AIO requests in Async IO system"; buf.putMap(ft); ft.clear(); ft[NAME] = "outstandingAIOsHigh"; ft[TYPE] = TYPE_U32; ft[UNIT] = "aio_op"; ft[DESC] = "Number of currently outstanding AIO requests in Async IO system (High)"; buf.putMap(ft); ft.clear(); ft[NAME] = "outstandingAIOsLow"; ft[TYPE] = TYPE_U32; ft[UNIT] = "aio_op"; ft[DESC] = "Number of currently outstanding AIO requests in Async IO system (Low)"; buf.putMap(ft); ft.clear(); ft[NAME] = "freeFileCount"; ft[TYPE] = TYPE_U32; ft[UNIT] = "file"; ft[DESC] = "Number of files free on this journal. Includes free files trapped in holes."; buf.putMap(ft); ft.clear(); ft[NAME] = "freeFileCountHigh"; ft[TYPE] = TYPE_U32; ft[UNIT] = "file"; ft[DESC] = "Number of files free on this journal. Includes free files trapped in holes. (High)"; buf.putMap(ft); ft.clear(); ft[NAME] = "freeFileCountLow"; ft[TYPE] = TYPE_U32; ft[UNIT] = "file"; ft[DESC] = "Number of files free on this journal. Includes free files trapped in holes. (Low)"; buf.putMap(ft); ft.clear(); ft[NAME] = "availableFileCount"; ft[TYPE] = TYPE_U32; ft[UNIT] = "file"; ft[DESC] = "Number of files available to be written. Excluding holes"; buf.putMap(ft); ft.clear(); ft[NAME] = "availableFileCountHigh"; ft[TYPE] = TYPE_U32; ft[UNIT] = "file"; ft[DESC] = "Number of files available to be written. Excluding holes (High)"; buf.putMap(ft); ft.clear(); ft[NAME] = "availableFileCountLow"; ft[TYPE] = TYPE_U32; ft[UNIT] = "file"; ft[DESC] = "Number of files available to be written. Excluding holes (Low)"; buf.putMap(ft); ft.clear(); ft[NAME] = "writeWaitFailures"; ft[TYPE] = TYPE_U64; ft[UNIT] = "record"; ft[DESC] = "AIO Wait failures on write"; buf.putMap(ft); ft.clear(); ft[NAME] = "writeBusyFailures"; ft[TYPE] = TYPE_U64; ft[UNIT] = "record"; ft[DESC] = "AIO Busy failures on write"; buf.putMap(ft); ft.clear(); ft[NAME] = "readRecordCount"; ft[TYPE] = TYPE_U64; ft[UNIT] = "record"; ft[DESC] = "Records read from the journal"; buf.putMap(ft); ft.clear(); ft[NAME] = "readBusyFailures"; ft[TYPE] = TYPE_U64; ft[UNIT] = "record"; ft[DESC] = "AIO Busy failures on read"; buf.putMap(ft); ft.clear(); ft[NAME] = "writePageCacheDepth"; ft[TYPE] = TYPE_U32; ft[UNIT] = "wpage"; ft[DESC] = "Current depth of write-page-cache"; buf.putMap(ft); ft.clear(); ft[NAME] = "writePageCacheDepthHigh"; ft[TYPE] = TYPE_U32; ft[UNIT] = "wpage"; ft[DESC] = "Current depth of write-page-cache (High)"; buf.putMap(ft); ft.clear(); ft[NAME] = "writePageCacheDepthLow"; ft[TYPE] = TYPE_U32; ft[UNIT] = "wpage"; ft[DESC] = "Current depth of write-page-cache (Low)"; buf.putMap(ft); ft.clear(); ft[NAME] = "readPageCacheDepth"; ft[TYPE] = TYPE_U32; ft[UNIT] = "rpage"; ft[DESC] = "Current depth of read-page-cache"; buf.putMap(ft); ft.clear(); ft[NAME] = "readPageCacheDepthHigh"; ft[TYPE] = TYPE_U32; ft[UNIT] = "rpage"; ft[DESC] = "Current depth of read-page-cache (High)"; buf.putMap(ft); ft.clear(); ft[NAME] = "readPageCacheDepthLow"; ft[TYPE] = TYPE_U32; ft[UNIT] = "rpage"; ft[DESC] = "Current depth of read-page-cache (Low)"; buf.putMap(ft); // Methods ft.clear(); ft[NAME] = "expand"; ft[ARGCOUNT] = 1; ft[DESC] = "Increase number of files allocated for this journal"; buf.putMap(ft); ft.clear(); ft[NAME] = "by"; ft[TYPE] = TYPE_U32; ft[DIR] = "I"; ft[DESC] = "Number of files to increase journal size by"; buf.putMap(ft); { uint32_t _len = buf.getPosition(); buf.reset(); buf.getRawData(schema, _len); } } void Journal::aggregatePerThreadStats(struct PerThreadStats* totals) const { totals->enqueues = 0; totals->dequeues = 0; totals->txn = 0; totals->txnEnqueues = 0; totals->txnDequeues = 0; totals->txnCommits = 0; totals->txnAborts = 0; totals->writeWaitFailures = 0; totals->writeBusyFailures = 0; totals->readRecordCount = 0; totals->readBusyFailures = 0; for (int idx = 0; idx < maxThreads; idx++) { struct PerThreadStats* threadStats = perThreadStatsArray[idx]; if (threadStats != 0) { totals->enqueues += threadStats->enqueues; totals->dequeues += threadStats->dequeues; totals->txn += threadStats->txn; totals->txnEnqueues += threadStats->txnEnqueues; totals->txnDequeues += threadStats->txnDequeues; totals->txnCommits += threadStats->txnCommits; totals->txnAborts += threadStats->txnAborts; totals->writeWaitFailures += threadStats->writeWaitFailures; totals->writeBusyFailures += threadStats->writeBusyFailures; totals->readRecordCount += threadStats->readRecordCount; totals->readBusyFailures += threadStats->readBusyFailures; } } } uint32_t Journal::writePropertiesSize() const { uint32_t size = writeTimestampsSize(); size += 16; // queueRef size += (1 + name.length()); // name size += (1 + directory.length()); // directory size += (1 + baseFileName.length()); // baseFileName size += 4; // writePageSize size += 4; // writePages size += 4; // readPageSize size += 4; // readPages size += 2; // initialFileCount size += 1; // autoExpand size += 2; // currentFileCount size += 2; // maxFileCount size += 4; // dataFileSize return size; } void Journal::readProperties (const std::string& _sBuf) { char *_tmpBuf = new char[_sBuf.length()]; memcpy(_tmpBuf, _sBuf.data(), _sBuf.length()); ::qpid::management::Buffer buf(_tmpBuf, _sBuf.length()); Mutex::ScopedLock mutex(accessLock); { std::string _tbuf; buf.getRawData(_tbuf, writeTimestampsSize()); readTimestamps(_tbuf); } {std::string _s; buf.getRawData(_s, queueRef.encodedSize()); queueRef.decode(_s);}; buf.getShortString(name); buf.getShortString(directory); buf.getShortString(baseFileName); writePageSize = buf.getLong(); writePages = buf.getLong(); readPageSize = buf.getLong(); readPages = buf.getLong(); initialFileCount = buf.getShort(); autoExpand = buf.getOctet()==1; currentFileCount = buf.getShort(); maxFileCount = buf.getShort(); dataFileSize = buf.getLong(); delete [] _tmpBuf; } void Journal::writeProperties (std::string& _sBuf) const { const int _bufSize=65536; char _msgChars[_bufSize]; ::qpid::management::Buffer buf(_msgChars, _bufSize); Mutex::ScopedLock mutex(accessLock); configChanged = false; { std::string _tbuf; writeTimestamps(_tbuf); buf.putRawData(_tbuf); } {std::string _s; queueRef.encode(_s); buf.putRawData(_s);}; buf.putShortString(name); buf.putShortString(directory); buf.putShortString(baseFileName); buf.putLong(writePageSize); buf.putLong(writePages); buf.putLong(readPageSize); buf.putLong(readPages); buf.putShort(initialFileCount); buf.putOctet(autoExpand?1:0); buf.putShort(currentFileCount); buf.putShort(maxFileCount); buf.putLong(dataFileSize); uint32_t _bufLen = buf.getPosition(); buf.reset(); buf.getRawData(_sBuf, _bufLen); } void Journal::writeStatistics (std::string& _sBuf, bool skipHeaders) { const int _bufSize=65536; char _msgChars[_bufSize]; ::qpid::management::Buffer buf(_msgChars, _bufSize); Mutex::ScopedLock mutex(accessLock); instChanged = false; struct PerThreadStats totals; aggregatePerThreadStats(&totals); if (!skipHeaders) { std::string _tbuf; writeTimestamps (_tbuf); buf.putRawData(_tbuf); } buf.putLong(recordDepth); buf.putLong(recordDepthHigh); buf.putLong(recordDepthLow); buf.putLongLong(totals.enqueues); buf.putLongLong(totals.dequeues); buf.putLong(totals.txn); buf.putLongLong(totals.txnEnqueues); buf.putLongLong(totals.txnDequeues); buf.putLongLong(totals.txnCommits); buf.putLongLong(totals.txnAborts); buf.putLong(outstandingAIOs); buf.putLong(outstandingAIOsHigh); buf.putLong(outstandingAIOsLow); buf.putLong(freeFileCount); buf.putLong(freeFileCountHigh); buf.putLong(freeFileCountLow); buf.putLong(availableFileCount); buf.putLong(availableFileCountHigh); buf.putLong(availableFileCountLow); buf.putLongLong(totals.writeWaitFailures); buf.putLongLong(totals.writeBusyFailures); buf.putLongLong(totals.readRecordCount); buf.putLongLong(totals.readBusyFailures); buf.putLong(writePageCacheDepth); buf.putLong(writePageCacheDepthHigh); buf.putLong(writePageCacheDepthLow); buf.putLong(readPageCacheDepth); buf.putLong(readPageCacheDepthHigh); buf.putLong(readPageCacheDepthLow); // Maintenance of hi-lo statistics recordDepthHigh = recordDepth; recordDepthLow = recordDepth; outstandingAIOsHigh = outstandingAIOs; outstandingAIOsLow = outstandingAIOs; freeFileCountHigh = freeFileCount; freeFileCountLow = freeFileCount; availableFileCountHigh = availableFileCount; availableFileCountLow = availableFileCount; writePageCacheDepthHigh = writePageCacheDepth; writePageCacheDepthLow = writePageCacheDepth; readPageCacheDepthHigh = readPageCacheDepth; readPageCacheDepthLow = readPageCacheDepth; uint32_t _bufLen = buf.getPosition(); buf.reset(); buf.getRawData(_sBuf, _bufLen); } void Journal::doMethod (string& methodName, const string& inStr, string& outStr, const string& userId) { Manageable::status_t status = Manageable::STATUS_UNKNOWN_METHOD; std::string text; bool _matched = false; const int _bufSize=65536; char _msgChars[_bufSize]; ::qpid::management::Buffer outBuf(_msgChars, _bufSize); char *_tmpBuf = new char[inStr.length()]; memcpy(_tmpBuf, inStr.data(), inStr.length()); ::qpid::management::Buffer inBuf(_tmpBuf, inStr.length()); if (methodName == "expand") { _matched = true; ArgsJournalExpand ioArgs; ioArgs.i_by = inBuf.getLong(); bool allow = coreObject->AuthorizeMethod(METHOD_EXPAND, ioArgs, userId); if (allow) status = coreObject->ManagementMethod (METHOD_EXPAND, ioArgs, text); else status = Manageable::STATUS_FORBIDDEN; outBuf.putLong (status); outBuf.putMediumString(::qpid::management::Manageable::StatusText (status, text)); } delete [] _tmpBuf; if (!_matched) { outBuf.putLong(status); outBuf.putShortString(Manageable::StatusText(status, text)); } uint32_t _bufLen = outBuf.getPosition(); outBuf.reset(); outBuf.getRawData(outStr, _bufLen); } std::string Journal::getKey() const { std::stringstream key; key << name; return key.str(); } void Journal::mapEncodeValues (::qpid::types::Variant::Map& _map, bool includeProperties, bool includeStatistics) { using namespace ::qpid::types; Mutex::ScopedLock mutex(accessLock); if (includeProperties) { configChanged = false; _map["queueRef"] = ::qpid::types::Variant(queueRef); _map["name"] = ::qpid::types::Variant(name); _map["directory"] = ::qpid::types::Variant(directory); _map["baseFileName"] = ::qpid::types::Variant(baseFileName); _map["writePageSize"] = ::qpid::types::Variant(writePageSize); _map["writePages"] = ::qpid::types::Variant(writePages); _map["readPageSize"] = ::qpid::types::Variant(readPageSize); _map["readPages"] = ::qpid::types::Variant(readPages); _map["initialFileCount"] = ::qpid::types::Variant(initialFileCount); _map["autoExpand"] = ::qpid::types::Variant(autoExpand); _map["currentFileCount"] = ::qpid::types::Variant(currentFileCount); _map["maxFileCount"] = ::qpid::types::Variant(maxFileCount); _map["dataFileSize"] = ::qpid::types::Variant(dataFileSize); } if (includeStatistics) { instChanged = false; struct PerThreadStats totals; aggregatePerThreadStats(&totals); _map["recordDepth"] = ::qpid::types::Variant(recordDepth); _map["recordDepthHigh"] = ::qpid::types::Variant(recordDepthHigh); _map["recordDepthLow"] = ::qpid::types::Variant(recordDepthLow); _map["enqueues"] = ::qpid::types::Variant(totals.enqueues); _map["dequeues"] = ::qpid::types::Variant(totals.dequeues); _map["txn"] = ::qpid::types::Variant(totals.txn); _map["txnEnqueues"] = ::qpid::types::Variant(totals.txnEnqueues); _map["txnDequeues"] = ::qpid::types::Variant(totals.txnDequeues); _map["txnCommits"] = ::qpid::types::Variant(totals.txnCommits); _map["txnAborts"] = ::qpid::types::Variant(totals.txnAborts); _map["outstandingAIOs"] = ::qpid::types::Variant(outstandingAIOs); _map["outstandingAIOsHigh"] = ::qpid::types::Variant(outstandingAIOsHigh); _map["outstandingAIOsLow"] = ::qpid::types::Variant(outstandingAIOsLow); _map["freeFileCount"] = ::qpid::types::Variant(freeFileCount); _map["freeFileCountHigh"] = ::qpid::types::Variant(freeFileCountHigh); _map["freeFileCountLow"] = ::qpid::types::Variant(freeFileCountLow); _map["availableFileCount"] = ::qpid::types::Variant(availableFileCount); _map["availableFileCountHigh"] = ::qpid::types::Variant(availableFileCountHigh); _map["availableFileCountLow"] = ::qpid::types::Variant(availableFileCountLow); _map["writeWaitFailures"] = ::qpid::types::Variant(totals.writeWaitFailures); _map["writeBusyFailures"] = ::qpid::types::Variant(totals.writeBusyFailures); _map["readRecordCount"] = ::qpid::types::Variant(totals.readRecordCount); _map["readBusyFailures"] = ::qpid::types::Variant(totals.readBusyFailures); _map["writePageCacheDepth"] = ::qpid::types::Variant(writePageCacheDepth); _map["writePageCacheDepthHigh"] = ::qpid::types::Variant(writePageCacheDepthHigh); _map["writePageCacheDepthLow"] = ::qpid::types::Variant(writePageCacheDepthLow); _map["readPageCacheDepth"] = ::qpid::types::Variant(readPageCacheDepth); _map["readPageCacheDepthHigh"] = ::qpid::types::Variant(readPageCacheDepthHigh); _map["readPageCacheDepthLow"] = ::qpid::types::Variant(readPageCacheDepthLow); // Maintenance of hi-lo statistics recordDepthHigh = recordDepth; recordDepthLow = recordDepth; outstandingAIOsHigh = outstandingAIOs; outstandingAIOsLow = outstandingAIOs; freeFileCountHigh = freeFileCount; freeFileCountLow = freeFileCount; availableFileCountHigh = availableFileCount; availableFileCountLow = availableFileCount; writePageCacheDepthHigh = writePageCacheDepth; writePageCacheDepthLow = writePageCacheDepth; readPageCacheDepthHigh = readPageCacheDepth; readPageCacheDepthLow = readPageCacheDepth; } } void Journal::mapDecodeValues (const ::qpid::types::Variant::Map& _map) { ::qpid::types::Variant::Map::const_iterator _i; Mutex::ScopedLock mutex(accessLock); if ((_i = _map.find("queueRef")) != _map.end()) { queueRef = _i->second; } if ((_i = _map.find("name")) != _map.end()) { name = (_i->second).getString(); } if ((_i = _map.find("directory")) != _map.end()) { directory = (_i->second).getString(); } if ((_i = _map.find("baseFileName")) != _map.end()) { baseFileName = (_i->second).getString(); } if ((_i = _map.find("writePageSize")) != _map.end()) { writePageSize = _i->second; } if ((_i = _map.find("writePages")) != _map.end()) { writePages = _i->second; } if ((_i = _map.find("readPageSize")) != _map.end()) { readPageSize = _i->second; } if ((_i = _map.find("readPages")) != _map.end()) { readPages = _i->second; } if ((_i = _map.find("initialFileCount")) != _map.end()) { initialFileCount = _i->second; } if ((_i = _map.find("autoExpand")) != _map.end()) { autoExpand = _i->second; } if ((_i = _map.find("currentFileCount")) != _map.end()) { currentFileCount = _i->second; } if ((_i = _map.find("maxFileCount")) != _map.end()) { maxFileCount = _i->second; } if ((_i = _map.find("dataFileSize")) != _map.end()) { dataFileSize = _i->second; } } void Journal::doMethod (string& methodName, const ::qpid::types::Variant::Map& inMap, ::qpid::types::Variant::Map& outMap, const string& userId) { Manageable::status_t status = Manageable::STATUS_UNKNOWN_METHOD; std::string text; if (methodName == "expand") { ArgsJournalExpand ioArgs; ::qpid::types::Variant::Map::const_iterator _i; if ((_i = inMap.find("by")) != inMap.end()) { ioArgs.i_by = _i->second; } bool allow = coreObject->AuthorizeMethod(METHOD_EXPAND, ioArgs, userId); if (allow) status = coreObject->ManagementMethod (METHOD_EXPAND, ioArgs, text); else status = Manageable::STATUS_FORBIDDEN; outMap["_status_code"] = (uint32_t) status; outMap["_status_text"] = ::qpid::management::Manageable::StatusText(status, text); return; } outMap["_status_code"] = (uint32_t) status; outMap["_status_text"] = Manageable::StatusText(status, text); } qpid-cpp-store-debian-0.16/lib/gen/qmf/com/redhat/rhm/store/ArgsJournalExpand.h0000644000176300017630000000231311357522345026352 0ustar cajuscajus #ifndef _ARGS_JOURNALEXPAND_ #define _ARGS_JOURNALEXPAND_ // // Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. // // This source file was created by a code generator. // Please do not edit. #include "qpid/management/Args.h" #include namespace qmf { namespace com { namespace redhat { namespace rhm { namespace store { class ArgsJournalExpand : public ::qpid::management::Args { public: uint32_t i_by; }; }}}}} #endif /*!_ARGS_JOURNALEXPAND_*/ qpid-cpp-store-debian-0.16/lib/gen/qmf/com/redhat/rhm/store/EventCreated.h0000644000176300017630000000402611354735417025342 0ustar cajuscajus #ifndef _MANAGEMENT_CREATED_ #define _MANAGEMENT_CREATED_ // // Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. // // This source file was created by a code generator. // Please do not edit. #include "qpid/management/ManagementEvent.h" namespace qmf { namespace com { namespace redhat { namespace rhm { namespace store { class EventCreated : public ::qpid::management::ManagementEvent { private: static void writeSchema (std::string& schema); static std::string packageName; static std::string eventName; static uint8_t md5Sum[MD5_LEN]; const std::string& jrnlId; const uint32_t fileSize; const uint16_t numFiles; public: writeSchemaCall_t getWriteSchemaCall(void) { return writeSchema; } EventCreated(const std::string& _jrnlId, const uint32_t _fileSize, const uint16_t _numFiles); ~EventCreated() {}; static void registerSelf(::qpid::management::ManagementAgent* agent); std::string& getPackageName() const { return packageName; } std::string& getEventName() const { return eventName; } uint8_t* getMd5Sum() const { return md5Sum; } uint8_t getSeverity() const { return 5; } void encode(std::string& buffer) const; void mapEncode(::qpid::types::Variant::Map& map) const; }; }}}}} #endif /*!_MANAGEMENT_CREATED_*/ qpid-cpp-store-debian-0.16/lib/gen/qmf/com/redhat/rhm/store/EventCreated.cpp0000644000176300017630000000734111357660733025701 0ustar cajuscajus // // Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. // // This source file was created by a code generator. // Please do not edit. #include "qpid/management/Manageable.h" #include "qpid/management/Buffer.h" #include "qpid/types/Variant.h" #include "qpid/amqp_0_10/Codecs.h" #include "qpid/management/ManagementAgent.h" #include "EventCreated.h" using namespace qmf::com::redhat::rhm::store; using qpid::management::ManagementAgent; using qpid::management::Manageable; using qpid::management::ManagementObject; using qpid::management::Args; using qpid::management::Mutex; using std::string; string EventCreated::packageName = string ("com.redhat.rhm.store"); string EventCreated::eventName = string ("created"); uint8_t EventCreated::md5Sum[16] = {0x6f,0x7,0x35,0xf6,0xdc,0xa3,0x18,0x29,0x23,0xf0,0x19,0x61,0xcc,0xe7,0x1c,0x5b}; EventCreated::EventCreated (const std::string& _jrnlId, const uint32_t _fileSize, const uint16_t _numFiles) : jrnlId(_jrnlId), fileSize(_fileSize), numFiles(_numFiles) {} namespace { const string NAME("name"); const string TYPE("type"); const string DESC("desc"); const string ARGCOUNT("argCount"); const string ARGS("args"); } void EventCreated::registerSelf(ManagementAgent* agent) { agent->registerEvent(packageName, eventName, md5Sum, writeSchema); } void EventCreated::writeSchema (std::string& schema) { const int _bufSize = 65536; char _msgChars[_bufSize]; ::qpid::management::Buffer buf(_msgChars, _bufSize); ::qpid::types::Variant::Map ft; // Schema class header: buf.putOctet (CLASS_KIND_EVENT); buf.putShortString (packageName); // Package Name buf.putShortString (eventName); // Event Name buf.putBin128 (md5Sum); // Schema Hash buf.putShort (3); // Argument Count // Arguments ft.clear(); ft[NAME] = "jrnlId"; ft[TYPE] = TYPE_SSTR; ft[DESC] = "Journal Id"; buf.putMap(ft); ft.clear(); ft[NAME] = "fileSize"; ft[TYPE] = TYPE_U32; ft[DESC] = "Journal file size in bytes"; buf.putMap(ft); ft.clear(); ft[NAME] = "numFiles"; ft[TYPE] = TYPE_U16; ft[DESC] = "Number of journal files"; buf.putMap(ft); { uint32_t _len = buf.getPosition(); buf.reset(); buf.getRawData(schema, _len); } } void EventCreated::encode(std::string& _sBuf) const { const int _bufSize=65536; char _msgChars[_bufSize]; ::qpid::management::Buffer buf(_msgChars, _bufSize); buf.putShortString(jrnlId); buf.putLong(fileSize); buf.putShort(numFiles); uint32_t _bufLen = buf.getPosition(); buf.reset(); buf.getRawData(_sBuf, _bufLen); } void EventCreated::mapEncode(::qpid::types::Variant::Map& map) const { using namespace ::qpid::types; map["jrnlId"] = ::qpid::types::Variant(jrnlId); map["fileSize"] = ::qpid::types::Variant(fileSize); map["numFiles"] = ::qpid::types::Variant(numFiles); } qpid-cpp-store-debian-0.16/lib/gen/qmf/com/redhat/rhm/store/Store.cpp0000644000176300017630000004162511466365520024424 0ustar cajuscajus // // Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. // // This source file was created by a code generator. // Please do not edit. #include "qpid/management/Manageable.h" #include "qpid/management/Buffer.h" #include "qpid/types/Variant.h" #include "qpid/amqp_0_10/Codecs.h" #include "qpid/management/ManagementAgent.h" #include "Store.h" #include #include using namespace qmf::com::redhat::rhm::store; using qpid::management::ManagementAgent; using qpid::management::Manageable; using qpid::management::ManagementObject; using qpid::management::Args; using qpid::management::Mutex; using std::string; string Store::packageName = string ("com.redhat.rhm.store"); string Store::className = string ("store"); uint8_t Store::md5Sum[MD5_LEN] = {0x18,0xd,0xd4,0x15,0xd3,0x9a,0xf,0xbe,0x3a,0x40,0xe1,0x1b,0x9e,0x5b,0x7e,0x86}; Store::Store (ManagementAgent*, Manageable* _core, ::qpid::management::Manageable* _parent) : ManagementObject(_core) { brokerRef = _parent->GetManagementObject ()->getObjectId (); location = ""; defaultInitialFileCount = 0; defaultDataFileSize = 0; tplIsInitialized = 0; tplDirectory = ""; tplWritePageSize = 0; tplWritePages = 0; tplInitialFileCount = 0; tplDataFileSize = 0; tplCurrentFileCount = 0; tplTransactionDepth = 0; tplTransactionDepthHigh = 0; tplTransactionDepthLow = 0; tplOutstandingAIOs = 0; tplOutstandingAIOsHigh = 0; tplOutstandingAIOsLow = 0; perThreadStatsArray = new struct PerThreadStats*[maxThreads]; for (int idx = 0; idx < maxThreads; idx++) perThreadStatsArray[idx] = 0; } Store::~Store () { for (int idx = 0; idx < maxThreads; idx++) if (perThreadStatsArray[idx] != 0) delete perThreadStatsArray[idx]; delete[] perThreadStatsArray; } namespace { const string NAME("name"); const string TYPE("type"); const string ACCESS("access"); const string IS_INDEX("index"); const string IS_OPTIONAL("optional"); const string UNIT("unit"); const string MIN("min"); const string MAX("max"); const string MAXLEN("maxlen"); const string DESC("desc"); const string ARGCOUNT("argCount"); const string ARGS("args"); const string DIR("dir"); const string DEFAULT("default"); } void Store::registerSelf(ManagementAgent* agent) { agent->registerClass(packageName, className, md5Sum, writeSchema); } void Store::writeSchema (std::string& schema) { const int _bufSize=65536; char _msgChars[_bufSize]; ::qpid::management::Buffer buf(_msgChars, _bufSize); ::qpid::types::Variant::Map ft; // Schema class header: buf.putOctet (CLASS_KIND_TABLE); buf.putShortString (packageName); // Package Name buf.putShortString (className); // Class Name buf.putBin128 (md5Sum); // Schema Hash buf.putShort (11); // Config Element Count buf.putShort (9); // Inst Element Count buf.putShort (0); // Method Count // Properties ft.clear(); ft[NAME] = "brokerRef"; ft[TYPE] = TYPE_REF; ft[ACCESS] = ACCESS_RO; ft[IS_INDEX] = 1; ft[IS_OPTIONAL] = 0; buf.putMap(ft); ft.clear(); ft[NAME] = "location"; ft[TYPE] = TYPE_SSTR; ft[ACCESS] = ACCESS_RO; ft[IS_INDEX] = 0; ft[IS_OPTIONAL] = 0; ft[DESC] = "Logical directory on disk"; buf.putMap(ft); ft.clear(); ft[NAME] = "defaultInitialFileCount"; ft[TYPE] = TYPE_U16; ft[ACCESS] = ACCESS_RO; ft[IS_INDEX] = 0; ft[IS_OPTIONAL] = 0; ft[UNIT] = "file"; ft[DESC] = "Default number of files initially allocated to each journal"; buf.putMap(ft); ft.clear(); ft[NAME] = "defaultDataFileSize"; ft[TYPE] = TYPE_U32; ft[ACCESS] = ACCESS_RO; ft[IS_INDEX] = 0; ft[IS_OPTIONAL] = 0; ft[UNIT] = "RdPg"; ft[DESC] = "Default size of each journal data file"; buf.putMap(ft); ft.clear(); ft[NAME] = "tplIsInitialized"; ft[TYPE] = TYPE_BOOL; ft[ACCESS] = ACCESS_RO; ft[IS_INDEX] = 0; ft[IS_OPTIONAL] = 0; ft[DESC] = "Transaction prepared list has been initialized by a transactional prepare"; buf.putMap(ft); ft.clear(); ft[NAME] = "tplDirectory"; ft[TYPE] = TYPE_SSTR; ft[ACCESS] = ACCESS_RO; ft[IS_INDEX] = 0; ft[IS_OPTIONAL] = 0; ft[DESC] = "Transaction prepared list directory"; buf.putMap(ft); ft.clear(); ft[NAME] = "tplWritePageSize"; ft[TYPE] = TYPE_U32; ft[ACCESS] = ACCESS_RO; ft[IS_INDEX] = 0; ft[IS_OPTIONAL] = 0; ft[UNIT] = "byte"; ft[DESC] = "Page size in transaction prepared list write-page-cache"; buf.putMap(ft); ft.clear(); ft[NAME] = "tplWritePages"; ft[TYPE] = TYPE_U32; ft[ACCESS] = ACCESS_RO; ft[IS_INDEX] = 0; ft[IS_OPTIONAL] = 0; ft[UNIT] = "wpage"; ft[DESC] = "Number of pages in transaction prepared list write-page-cache"; buf.putMap(ft); ft.clear(); ft[NAME] = "tplInitialFileCount"; ft[TYPE] = TYPE_U16; ft[ACCESS] = ACCESS_RO; ft[IS_INDEX] = 0; ft[IS_OPTIONAL] = 0; ft[UNIT] = "file"; ft[DESC] = "Number of files initially allocated to transaction prepared list journal"; buf.putMap(ft); ft.clear(); ft[NAME] = "tplDataFileSize"; ft[TYPE] = TYPE_U32; ft[ACCESS] = ACCESS_RO; ft[IS_INDEX] = 0; ft[IS_OPTIONAL] = 0; ft[UNIT] = "byte"; ft[DESC] = "Size of each journal data file in transaction prepared list journal"; buf.putMap(ft); ft.clear(); ft[NAME] = "tplCurrentFileCount"; ft[TYPE] = TYPE_U32; ft[ACCESS] = ACCESS_RO; ft[IS_INDEX] = 0; ft[IS_OPTIONAL] = 0; ft[UNIT] = "file"; ft[DESC] = "Number of files currently allocated to transaction prepared list journal"; buf.putMap(ft); // Statistics ft.clear(); ft[NAME] = "tplTransactionDepth"; ft[TYPE] = TYPE_U32; ft[UNIT] = "txn"; ft[DESC] = "Number of currently enqueued prepared transactions"; buf.putMap(ft); ft.clear(); ft[NAME] = "tplTransactionDepthHigh"; ft[TYPE] = TYPE_U32; ft[UNIT] = "txn"; ft[DESC] = "Number of currently enqueued prepared transactions (High)"; buf.putMap(ft); ft.clear(); ft[NAME] = "tplTransactionDepthLow"; ft[TYPE] = TYPE_U32; ft[UNIT] = "txn"; ft[DESC] = "Number of currently enqueued prepared transactions (Low)"; buf.putMap(ft); ft.clear(); ft[NAME] = "tplTxnPrepares"; ft[TYPE] = TYPE_U64; ft[UNIT] = "record"; ft[DESC] = "Total transaction prepares on transaction prepared list"; buf.putMap(ft); ft.clear(); ft[NAME] = "tplTxnCommits"; ft[TYPE] = TYPE_U64; ft[UNIT] = "record"; ft[DESC] = "Total transaction commits on transaction prepared list"; buf.putMap(ft); ft.clear(); ft[NAME] = "tplTxnAborts"; ft[TYPE] = TYPE_U64; ft[UNIT] = "record"; ft[DESC] = "Total transaction aborts on transaction prepared list"; buf.putMap(ft); ft.clear(); ft[NAME] = "tplOutstandingAIOs"; ft[TYPE] = TYPE_U32; ft[UNIT] = "aio_op"; ft[DESC] = "Number of currently outstanding AIO requests in Async IO system"; buf.putMap(ft); ft.clear(); ft[NAME] = "tplOutstandingAIOsHigh"; ft[TYPE] = TYPE_U32; ft[UNIT] = "aio_op"; ft[DESC] = "Number of currently outstanding AIO requests in Async IO system (High)"; buf.putMap(ft); ft.clear(); ft[NAME] = "tplOutstandingAIOsLow"; ft[TYPE] = TYPE_U32; ft[UNIT] = "aio_op"; ft[DESC] = "Number of currently outstanding AIO requests in Async IO system (Low)"; buf.putMap(ft); // Methods { uint32_t _len = buf.getPosition(); buf.reset(); buf.getRawData(schema, _len); } } void Store::aggregatePerThreadStats(struct PerThreadStats* totals) const { totals->tplTxnPrepares = 0; totals->tplTxnCommits = 0; totals->tplTxnAborts = 0; for (int idx = 0; idx < maxThreads; idx++) { struct PerThreadStats* threadStats = perThreadStatsArray[idx]; if (threadStats != 0) { totals->tplTxnPrepares += threadStats->tplTxnPrepares; totals->tplTxnCommits += threadStats->tplTxnCommits; totals->tplTxnAborts += threadStats->tplTxnAborts; } } } uint32_t Store::writePropertiesSize() const { uint32_t size = writeTimestampsSize(); size += 16; // brokerRef size += (1 + location.length()); // location size += 2; // defaultInitialFileCount size += 4; // defaultDataFileSize size += 1; // tplIsInitialized size += (1 + tplDirectory.length()); // tplDirectory size += 4; // tplWritePageSize size += 4; // tplWritePages size += 2; // tplInitialFileCount size += 4; // tplDataFileSize size += 4; // tplCurrentFileCount return size; } void Store::readProperties (const std::string& _sBuf) { char *_tmpBuf = new char[_sBuf.length()]; memcpy(_tmpBuf, _sBuf.data(), _sBuf.length()); ::qpid::management::Buffer buf(_tmpBuf, _sBuf.length()); Mutex::ScopedLock mutex(accessLock); { std::string _tbuf; buf.getRawData(_tbuf, writeTimestampsSize()); readTimestamps(_tbuf); } {std::string _s; buf.getRawData(_s, brokerRef.encodedSize()); brokerRef.decode(_s);}; buf.getShortString(location); defaultInitialFileCount = buf.getShort(); defaultDataFileSize = buf.getLong(); tplIsInitialized = buf.getOctet()==1; buf.getShortString(tplDirectory); tplWritePageSize = buf.getLong(); tplWritePages = buf.getLong(); tplInitialFileCount = buf.getShort(); tplDataFileSize = buf.getLong(); tplCurrentFileCount = buf.getLong(); delete [] _tmpBuf; } void Store::writeProperties (std::string& _sBuf) const { const int _bufSize=65536; char _msgChars[_bufSize]; ::qpid::management::Buffer buf(_msgChars, _bufSize); Mutex::ScopedLock mutex(accessLock); configChanged = false; { std::string _tbuf; writeTimestamps(_tbuf); buf.putRawData(_tbuf); } {std::string _s; brokerRef.encode(_s); buf.putRawData(_s);}; buf.putShortString(location); buf.putShort(defaultInitialFileCount); buf.putLong(defaultDataFileSize); buf.putOctet(tplIsInitialized?1:0); buf.putShortString(tplDirectory); buf.putLong(tplWritePageSize); buf.putLong(tplWritePages); buf.putShort(tplInitialFileCount); buf.putLong(tplDataFileSize); buf.putLong(tplCurrentFileCount); uint32_t _bufLen = buf.getPosition(); buf.reset(); buf.getRawData(_sBuf, _bufLen); } void Store::writeStatistics (std::string& _sBuf, bool skipHeaders) { const int _bufSize=65536; char _msgChars[_bufSize]; ::qpid::management::Buffer buf(_msgChars, _bufSize); Mutex::ScopedLock mutex(accessLock); instChanged = false; struct PerThreadStats totals; aggregatePerThreadStats(&totals); if (!skipHeaders) { std::string _tbuf; writeTimestamps (_tbuf); buf.putRawData(_tbuf); } buf.putLong(tplTransactionDepth); buf.putLong(tplTransactionDepthHigh); buf.putLong(tplTransactionDepthLow); buf.putLongLong(totals.tplTxnPrepares); buf.putLongLong(totals.tplTxnCommits); buf.putLongLong(totals.tplTxnAborts); buf.putLong(tplOutstandingAIOs); buf.putLong(tplOutstandingAIOsHigh); buf.putLong(tplOutstandingAIOsLow); // Maintenance of hi-lo statistics tplTransactionDepthHigh = tplTransactionDepth; tplTransactionDepthLow = tplTransactionDepth; tplOutstandingAIOsHigh = tplOutstandingAIOs; tplOutstandingAIOsLow = tplOutstandingAIOs; uint32_t _bufLen = buf.getPosition(); buf.reset(); buf.getRawData(_sBuf, _bufLen); } void Store::doMethod (string&, const string&, string& outStr, const string&) { Manageable::status_t status = Manageable::STATUS_UNKNOWN_METHOD; std::string text; bool _matched = false; const int _bufSize=65536; char _msgChars[_bufSize]; ::qpid::management::Buffer outBuf(_msgChars, _bufSize); if (!_matched) { outBuf.putLong(status); outBuf.putShortString(Manageable::StatusText(status, text)); } uint32_t _bufLen = outBuf.getPosition(); outBuf.reset(); outBuf.getRawData(outStr, _bufLen); } std::string Store::getKey() const { std::stringstream key; key << brokerRef.getV2Key(); return key.str(); } void Store::mapEncodeValues (::qpid::types::Variant::Map& _map, bool includeProperties, bool includeStatistics) { using namespace ::qpid::types; Mutex::ScopedLock mutex(accessLock); if (includeProperties) { configChanged = false; _map["brokerRef"] = ::qpid::types::Variant(brokerRef); _map["location"] = ::qpid::types::Variant(location); _map["defaultInitialFileCount"] = ::qpid::types::Variant(defaultInitialFileCount); _map["defaultDataFileSize"] = ::qpid::types::Variant(defaultDataFileSize); _map["tplIsInitialized"] = ::qpid::types::Variant(tplIsInitialized); _map["tplDirectory"] = ::qpid::types::Variant(tplDirectory); _map["tplWritePageSize"] = ::qpid::types::Variant(tplWritePageSize); _map["tplWritePages"] = ::qpid::types::Variant(tplWritePages); _map["tplInitialFileCount"] = ::qpid::types::Variant(tplInitialFileCount); _map["tplDataFileSize"] = ::qpid::types::Variant(tplDataFileSize); _map["tplCurrentFileCount"] = ::qpid::types::Variant(tplCurrentFileCount); } if (includeStatistics) { instChanged = false; struct PerThreadStats totals; aggregatePerThreadStats(&totals); _map["tplTransactionDepth"] = ::qpid::types::Variant(tplTransactionDepth); _map["tplTransactionDepthHigh"] = ::qpid::types::Variant(tplTransactionDepthHigh); _map["tplTransactionDepthLow"] = ::qpid::types::Variant(tplTransactionDepthLow); _map["tplTxnPrepares"] = ::qpid::types::Variant(totals.tplTxnPrepares); _map["tplTxnCommits"] = ::qpid::types::Variant(totals.tplTxnCommits); _map["tplTxnAborts"] = ::qpid::types::Variant(totals.tplTxnAborts); _map["tplOutstandingAIOs"] = ::qpid::types::Variant(tplOutstandingAIOs); _map["tplOutstandingAIOsHigh"] = ::qpid::types::Variant(tplOutstandingAIOsHigh); _map["tplOutstandingAIOsLow"] = ::qpid::types::Variant(tplOutstandingAIOsLow); // Maintenance of hi-lo statistics tplTransactionDepthHigh = tplTransactionDepth; tplTransactionDepthLow = tplTransactionDepth; tplOutstandingAIOsHigh = tplOutstandingAIOs; tplOutstandingAIOsLow = tplOutstandingAIOs; } } void Store::mapDecodeValues (const ::qpid::types::Variant::Map& _map) { ::qpid::types::Variant::Map::const_iterator _i; Mutex::ScopedLock mutex(accessLock); if ((_i = _map.find("brokerRef")) != _map.end()) { brokerRef = _i->second; } if ((_i = _map.find("location")) != _map.end()) { location = (_i->second).getString(); } if ((_i = _map.find("defaultInitialFileCount")) != _map.end()) { defaultInitialFileCount = _i->second; } if ((_i = _map.find("defaultDataFileSize")) != _map.end()) { defaultDataFileSize = _i->second; } if ((_i = _map.find("tplIsInitialized")) != _map.end()) { tplIsInitialized = _i->second; } if ((_i = _map.find("tplDirectory")) != _map.end()) { tplDirectory = (_i->second).getString(); } if ((_i = _map.find("tplWritePageSize")) != _map.end()) { tplWritePageSize = _i->second; } if ((_i = _map.find("tplWritePages")) != _map.end()) { tplWritePages = _i->second; } if ((_i = _map.find("tplInitialFileCount")) != _map.end()) { tplInitialFileCount = _i->second; } if ((_i = _map.find("tplDataFileSize")) != _map.end()) { tplDataFileSize = _i->second; } if ((_i = _map.find("tplCurrentFileCount")) != _map.end()) { tplCurrentFileCount = _i->second; } } void Store::doMethod (string&, const ::qpid::types::Variant::Map&, ::qpid::types::Variant::Map& outMap, const string&) { Manageable::status_t status = Manageable::STATUS_UNKNOWN_METHOD; std::string text; outMap["_status_code"] = (uint32_t) status; outMap["_status_text"] = Manageable::StatusText(status, text); } qpid-cpp-store-debian-0.16/lib/gen/qmf/com/redhat/rhm/store/EventRecovered.h0000644000176300017630000000443211354735417025712 0ustar cajuscajus #ifndef _MANAGEMENT_RECOVERED_ #define _MANAGEMENT_RECOVERED_ // // Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. // // This source file was created by a code generator. // Please do not edit. #include "qpid/management/ManagementEvent.h" namespace qmf { namespace com { namespace redhat { namespace rhm { namespace store { class EventRecovered : public ::qpid::management::ManagementEvent { private: static void writeSchema (std::string& schema); static std::string packageName; static std::string eventName; static uint8_t md5Sum[MD5_LEN]; const std::string& jrnlId; const uint32_t fileSize; const uint16_t numFiles; const uint32_t numEnq; const uint32_t numTxn; const uint32_t numTxnEnq; const uint32_t numTxnDeq; public: writeSchemaCall_t getWriteSchemaCall(void) { return writeSchema; } EventRecovered(const std::string& _jrnlId, const uint32_t _fileSize, const uint16_t _numFiles, const uint32_t _numEnq, const uint32_t _numTxn, const uint32_t _numTxnEnq, const uint32_t _numTxnDeq); ~EventRecovered() {}; static void registerSelf(::qpid::management::ManagementAgent* agent); std::string& getPackageName() const { return packageName; } std::string& getEventName() const { return eventName; } uint8_t* getMd5Sum() const { return md5Sum; } uint8_t getSeverity() const { return 5; } void encode(std::string& buffer) const; void mapEncode(::qpid::types::Variant::Map& map) const; }; }}}}} #endif /*!_MANAGEMENT_RECOVERED_*/ qpid-cpp-store-debian-0.16/lib/gen/qmf/com/redhat/rhm/store/EventEnqThresholdExceeded.h0000644000176300017630000000404511354735417030023 0ustar cajuscajus #ifndef _MANAGEMENT_ENQTHRESHOLDEXCEEDED_ #define _MANAGEMENT_ENQTHRESHOLDEXCEEDED_ // // Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. // // This source file was created by a code generator. // Please do not edit. #include "qpid/management/ManagementEvent.h" namespace qmf { namespace com { namespace redhat { namespace rhm { namespace store { class EventEnqThresholdExceeded : public ::qpid::management::ManagementEvent { private: static void writeSchema (std::string& schema); static std::string packageName; static std::string eventName; static uint8_t md5Sum[MD5_LEN]; const std::string& jrnlId; const std::string& what; public: writeSchemaCall_t getWriteSchemaCall(void) { return writeSchema; } EventEnqThresholdExceeded(const std::string& _jrnlId, const std::string& _what); ~EventEnqThresholdExceeded() {}; static void registerSelf(::qpid::management::ManagementAgent* agent); std::string& getPackageName() const { return packageName; } std::string& getEventName() const { return eventName; } uint8_t* getMd5Sum() const { return md5Sum; } uint8_t getSeverity() const { return 4; } void encode(std::string& buffer) const; void mapEncode(::qpid::types::Variant::Map& map) const; }; }}}}} #endif /*!_MANAGEMENT_ENQTHRESHOLDEXCEEDED_*/ qpid-cpp-store-debian-0.16/lib/gen/qmf/com/redhat/rhm/store/Package.h0000644000176300017630000000243011202031611024272 0ustar cajuscajus #ifndef _MANAGEMENT_PACKAGE_COM_REDHAT_RHM_STORE_ #define _MANAGEMENT_PACKAGE_COM_REDHAT_RHM_STORE_ // // Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. // // This source file was created by a code generator. // Please do not edit. #include "qpid/management/ManagementAgent.h" namespace qmf { namespace com { namespace redhat { namespace rhm { namespace store { class Package { public: Package (::qpid::management::ManagementAgent* agent); ~Package () {} }; }}}}} #endif /*!_MANAGEMENT_PACKAGE_COM_REDHAT_RHM_STORE_*/ qpid-cpp-store-debian-0.16/lib/Cursor.h0000644000176300017630000000312611142067437016736 0ustar cajuscajus/* Copyright (c) 2007, 2008 Red Hat, Inc. This file is part of the Qpid async store library msgstore.so. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA The GNU Lesser General Public License is available in the file COPYING. */ #ifndef _Cursor_ #define _Cursor_ #include #include "db-inc.h" namespace mrg{ namespace msgstore{ class Cursor { Dbc* cursor; public: typedef boost::shared_ptr db_ptr; Cursor() : cursor(0) {} virtual ~Cursor() { if(cursor) cursor->close(); } void open(db_ptr db, DbTxn* txn, u_int32_t flags = 0) { db->cursor(txn, &cursor, flags); } void close() { if(cursor) cursor->close(); cursor = 0; } Dbc* get() { return cursor; } Dbc* operator->() { return cursor; } bool next(Dbt& key, Dbt& value) { return cursor->get(&key, &value, DB_NEXT) == 0; } bool current(Dbt& key, Dbt& value) { return cursor->get(&key, &value, DB_CURRENT) == 0; } }; }} #endif qpid-cpp-store-debian-0.16/lib/IdSequence.cpp0000644000176300017630000000237711305530250020035 0ustar cajuscajus/* Copyright (c) 2007, 2008, 2009 Red Hat, Inc. This file is part of the Qpid async store library msgstore.so. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA The GNU Lesser General Public License is available in the file COPYING. */ #include "IdSequence.h" using namespace mrg::msgstore; using qpid::sys::Mutex; IdSequence::IdSequence() : id(1) {} u_int64_t IdSequence::next() { Mutex::ScopedLock guard(lock); if (!id) id++; // avoid 0 when folding around return id++; } void IdSequence::reset(uint64_t value) { //deliberately not threadsafe, used only on recovery id = value; } qpid-cpp-store-debian-0.16/lib/qmf-schema.xml0000644000176300017630000002024011161720772020046 0ustar cajuscajus qpid-cpp-store-debian-0.16/lib/StoreException.h0000644000176300017630000000414211142067437020433 0ustar cajuscajus/* Copyright (c) 2007, 2008 Red Hat, Inc. This file is part of the Qpid async store library msgstore.so. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA The GNU Lesser General Public License is available in the file COPYING. */ #ifndef _StoreException_ #define _StoreException_ #include "IdDbt.h" #include namespace mrg{ namespace msgstore{ class StoreException : public std::exception { std::string text; public: StoreException(const std::string& _text) : text(_text) {} StoreException(const std::string& _text, const DbException& cause) : text(_text + ": " + cause.what()) {} virtual ~StoreException() throw() {} virtual const char* what() const throw() { return text.c_str(); } }; class StoreFullException : public StoreException { public: StoreFullException(const std::string& _text) : StoreException(_text) {} StoreFullException(const std::string& _text, const DbException& cause) : StoreException(_text, cause) {} virtual ~StoreFullException() throw() {} }; #define THROW_STORE_EXCEPTION(MESSAGE) throw StoreException(boost::str(boost::format("%s (%s:%d)") % (MESSAGE) % __FILE__ % __LINE__)) #define THROW_STORE_EXCEPTION_2(MESSAGE, EXCEPTION) throw StoreException(boost::str(boost::format("%s (%s:%d)") % (MESSAGE) % __FILE__ % __LINE__), EXCEPTION) #define THROW_STORE_FULL_EXCEPTION(MESSAGE) throw StoreFullException(boost::str(boost::format("%s (%s:%d)") % (MESSAGE) % __FILE__ % __LINE__)) }} #endif qpid-cpp-store-debian-0.16/lib/MessageStoreImpl.h0000644000176300017630000003464311701102677020711 0ustar cajuscajus/* Copyright (c) 2007, 2008, 2009 Red Hat, Inc. This file is part of the Qpid async store library msgstore.so. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA The GNU Lesser General Public License is available in the file COPYING. */ #ifndef _MessageStoreImpl_ #define _MessageStoreImpl_ #include #include "db-inc.h" #include "Cursor.h" #include "IdDbt.h" #include "IdSequence.h" #include "JournalImpl.h" #include "jrnl/jcfg.hpp" #include "PreparedTransaction.h" #include "qpid/broker/Broker.h" #include "qpid/broker/MessageStore.h" #include "qpid/management/Manageable.h" #include "qmf/com/redhat/rhm/store/Store.h" #include "TxnCtxt.h" // Assume DB_VERSION_MAJOR == 4 #if (DB_VERSION_MINOR == 2) #include #define DB_BUFFER_SMALL ENOMEM #endif namespace qpid { namespace sys { class Timer; }} namespace mrg { namespace msgstore { /** * An implementation of the MessageStore interface based on Berkeley DB */ class MessageStoreImpl : public qpid::broker::MessageStore, public qpid::management::Manageable { public: typedef boost::shared_ptr db_ptr; typedef boost::shared_ptr dbEnv_ptr; struct StoreOptions : public qpid::Options { StoreOptions(const std::string& name="Store Options"); std::string clusterName; std::string storeDir; u_int16_t numJrnlFiles; bool autoJrnlExpand; u_int16_t autoJrnlExpandMaxFiles; u_int32_t jrnlFsizePgs; bool truncateFlag; u_int32_t wCachePageSizeKib; u_int16_t tplNumJrnlFiles; u_int32_t tplJrnlFsizePgs; u_int32_t tplWCachePageSizeKib; }; protected: typedef std::map queue_index; typedef std::map exchange_index; typedef std::map message_index; typedef LockedMappings::map txn_lock_map; typedef boost::ptr_list txn_list; // Structs for Transaction Recover List (TPL) recover state struct TplRecoverStruct { u_int64_t rid; // rid of TPL record bool deq_flag; bool commit_flag; bool tpc_flag; TplRecoverStruct(const u_int64_t _rid, const bool _deq_flag, const bool _commit_flag, const bool _tpc_flag); }; typedef TplRecoverStruct TplRecover; typedef std::pair TplRecoverMapPair; typedef std::map TplRecoverMap; typedef TplRecoverMap::const_iterator TplRecoverMapCitr; typedef std::map JournalListMap; typedef JournalListMap::iterator JournalListMapItr; // Default store settings static const u_int16_t defNumJrnlFiles = 8; static const u_int32_t defJrnlFileSizePgs = 24; static const bool defTruncateFlag = false; static const u_int32_t defWCachePageSize = JRNL_WMGR_DEF_PAGE_SIZE * JRNL_DBLK_SIZE * JRNL_SBLK_SIZE / 1024; static const u_int16_t defTplNumJrnlFiles = 8; static const u_int32_t defTplJrnlFileSizePgs = 24; static const u_int32_t defTplWCachePageSize = defWCachePageSize / 8; // TODO: set defAutoJrnlExpand to true and defAutoJrnlExpandMaxFiles to 16 when auto-expand comes on-line static const bool defAutoJrnlExpand = false; static const u_int16_t defAutoJrnlExpandMaxFiles = 0; static const std::string storeTopLevelDir; static qpid::sys::Duration defJournalGetEventsTimeout; static qpid::sys::Duration defJournalFlushTimeout; std::list dbs; dbEnv_ptr dbenv; db_ptr queueDb; db_ptr configDb; db_ptr exchangeDb; db_ptr mappingDb; db_ptr bindingDb; db_ptr generalDb; // Pointer to Transaction Prepared List (TPL) journal instance boost::shared_ptr tplStorePtr; TplRecoverMap tplRecoverMap; qpid::sys::Mutex tplInitLock; JournalListMap journalList; qpid::sys::Mutex journalListLock; qpid::sys::Mutex bdbLock; IdSequence queueIdSequence; IdSequence exchangeIdSequence; IdSequence generalIdSequence; IdSequence messageIdSequence; std::string storeDir; u_int16_t numJrnlFiles; bool autoJrnlExpand; u_int16_t autoJrnlExpandMaxFiles; u_int32_t jrnlFsizeSblks; bool truncateFlag; u_int32_t wCachePgSizeSblks; u_int16_t wCacheNumPages; u_int16_t tplNumJrnlFiles; u_int32_t tplJrnlFsizeSblks; u_int32_t tplWCachePgSizeSblks; u_int16_t tplWCacheNumPages; u_int64_t highestRid; bool isInit; const char* envPath; qpid::sys::Timer& timer; qmf::com::redhat::rhm::store::Store* mgmtObject; qpid::management::ManagementAgent* agent; // Parameter validation and calculation static u_int16_t chkJrnlNumFilesParam(const u_int16_t param, const std::string paramName); static u_int32_t chkJrnlFileSizeParam(const u_int32_t param, const std::string paramName, const u_int32_t wCachePgSizeSblks = 0); static u_int32_t chkJrnlWrPageCacheSize(const u_int32_t param, const std::string paramName, const u_int16_t jrnlFsizePgs); static u_int16_t getJrnlWrNumPages(const u_int32_t wrPageSizeKib); void chkJrnlAutoExpandOptions(const MessageStoreImpl::StoreOptions* opts, bool& autoJrnlExpand, u_int16_t& autoJrnlExpandMaxFiles, const std::string& autoJrnlExpandMaxFilesParamName, const u_int16_t numJrnlFiles, const std::string& numJrnlFilesParamName); void init(); void recoverQueues(TxnCtxt& txn, qpid::broker::RecoveryManager& recovery, queue_index& index, txn_list& locked, message_index& messages); void recoverMessages(TxnCtxt& txn, qpid::broker::RecoveryManager& recovery, queue_index& index, txn_list& locked, message_index& prepared); void recoverMessages(TxnCtxt& txn, qpid::broker::RecoveryManager& recovery, qpid::broker::RecoverableQueue::shared_ptr& queue, txn_list& locked, message_index& prepared, long& rcnt, long& idcnt); qpid::broker::RecoverableMessage::shared_ptr getExternMessage(qpid::broker::RecoveryManager& recovery, uint64_t mId, unsigned& headerSize); void recoverExchanges(TxnCtxt& txn, qpid::broker::RecoveryManager& recovery, exchange_index& index); void recoverBindings(TxnCtxt& txn, exchange_index& exchanges, queue_index& queues); void recoverGeneral(TxnCtxt& txn, qpid::broker::RecoveryManager& recovery); int enqueueMessage(TxnCtxt& txn, IdDbt& msgId, qpid::broker::RecoverableMessage::shared_ptr& msg, queue_index& index, txn_list& locked, message_index& prepared); void readTplStore(); void recoverTplStore(); void recoverLockedMappings(txn_list& txns); TxnCtxt* check(qpid::broker::TransactionContext* ctxt); u_int64_t msgEncode(std::vector& buff, const boost::intrusive_ptr& message); void store(const qpid::broker::PersistableQueue* queue, TxnCtxt* txn, const boost::intrusive_ptr& message, bool newId); void async_dequeue(qpid::broker::TransactionContext* ctxt, const boost::intrusive_ptr& msg, const qpid::broker::PersistableQueue& queue); void destroy(db_ptr db, const qpid::broker::Persistable& p); bool create(db_ptr db, IdSequence& seq, const qpid::broker::Persistable& p); void completed(TxnCtxt& txn, bool commit); void deleteBindingsForQueue(const qpid::broker::PersistableQueue& queue); void deleteBinding(const qpid::broker::PersistableExchange& exchange, const qpid::broker::PersistableQueue& queue, const std::string& key); void put(db_ptr db, DbTxn* txn, Dbt& key, Dbt& value); void open(db_ptr db, DbTxn* txn, const char* file, bool dupKey); void closeDbs(); // journal functions void createJrnlQueue(const qpid::broker::PersistableQueue& queue); u_int32_t bHash(const std::string str); std::string getJrnlDir(const qpid::broker::PersistableQueue& queue); //for exmaple /var/rhm/ + queueDir/ std::string getJrnlHashDir(const std::string& queueName); std::string getJrnlBaseDir(); std::string getBdbBaseDir(); std::string getTplBaseDir(); inline void checkInit() { // TODO: change the default dir to ~/.qpidd if (!isInit) { init("/tmp"); isInit = true; } } void chkTplStoreInit(); // debug aid for printing XIDs that may contain non-printable chars static std::string xid2str(const std::string xid) { std::ostringstream oss; oss << std::hex << std::setfill('0'); for (unsigned i=0; i shared_ptr; MessageStoreImpl(qpid::sys::Timer& timer, const char* envpath = 0); virtual ~MessageStoreImpl(); bool init(const qpid::Options* options); bool init(const std::string& dir, u_int16_t jfiles = defNumJrnlFiles, u_int32_t jfileSizePgs = defJrnlFileSizePgs, const bool truncateFlag = false, u_int32_t wCachePageSize = defWCachePageSize, u_int16_t tplJfiles = defTplNumJrnlFiles, u_int32_t tplJfileSizePgs = defTplJrnlFileSizePgs, u_int32_t tplWCachePageSize = defTplWCachePageSize, bool autoJExpand = defAutoJrnlExpand, u_int16_t autoJExpandMaxFiles = defAutoJrnlExpandMaxFiles); void truncateInit(const bool saveStoreContent = false); void initManagement (qpid::broker::Broker* broker); void finalize(); void create(qpid::broker::PersistableQueue& queue, const qpid::framing::FieldTable& args); void destroy(qpid::broker::PersistableQueue& queue); void create(const qpid::broker::PersistableExchange& queue, const qpid::framing::FieldTable& args); void destroy(const qpid::broker::PersistableExchange& queue); void bind(const qpid::broker::PersistableExchange& exchange, const qpid::broker::PersistableQueue& queue, const std::string& key, const qpid::framing::FieldTable& args); void unbind(const qpid::broker::PersistableExchange& exchange, const qpid::broker::PersistableQueue& queue, const std::string& key, const qpid::framing::FieldTable& args); void create(const qpid::broker::PersistableConfig& config); void destroy(const qpid::broker::PersistableConfig& config); void recover(qpid::broker::RecoveryManager& queues); void stage(const boost::intrusive_ptr& msg); void destroy(qpid::broker::PersistableMessage& msg); void appendContent(const boost::intrusive_ptr& msg, const std::string& data); void loadContent(const qpid::broker::PersistableQueue& queue, const boost::intrusive_ptr& msg, std::string& data, uint64_t offset, uint32_t length); void enqueue(qpid::broker::TransactionContext* ctxt, const boost::intrusive_ptr& msg, const qpid::broker::PersistableQueue& queue); void dequeue(qpid::broker::TransactionContext* ctxt, const boost::intrusive_ptr& msg, const qpid::broker::PersistableQueue& queue); void flush(const qpid::broker::PersistableQueue& queue); u_int32_t outstandingQueueAIO(const qpid::broker::PersistableQueue& queue); void collectPreparedXids(std::set& xids); std::auto_ptr begin(); std::auto_ptr begin(const std::string& xid); void prepare(qpid::broker::TPCTransactionContext& ctxt); void localPrepare(TxnCtxt* ctxt); void commit(qpid::broker::TransactionContext& ctxt); void abort(qpid::broker::TransactionContext& ctxt); qpid::management::ManagementObject* GetManagementObject (void) const { return mgmtObject; } inline qpid::management::Manageable::status_t ManagementMethod (u_int32_t, qpid::management::Args&) { return qpid::management::Manageable::STATUS_OK; } std::string getStoreDir() const; private: void journalDeleted(JournalImpl&); }; // class MessageStoreImpl } // namespace msgstore } // namespace mrg #endif qpid-cpp-store-debian-0.16/lib/JournalImpl.h0000644000176300017630000002426211701124140017703 0ustar cajuscajus/* Copyright (c) 2007, 2008, 2009 Red Hat, Inc. This file is part of the Qpid async store library msgstore.so. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA The GNU Lesser General Public License is available in the file COPYING. */ #ifndef _JournalImpl_ #define _JournalImpl_ #include #include "jrnl/enums.hpp" #include "jrnl/jcntl.hpp" #include "DataTokenImpl.h" #include "PreparedTransaction.h" #include #include #include #include #include #include "qpid/management/Manageable.h" #include "qmf/com/redhat/rhm/store/Journal.h" namespace qpid { namespace sys { class Timer; }} namespace mrg { namespace msgstore { class JournalImpl; class InactivityFireEvent : public qpid::sys::TimerTask { JournalImpl* _parent; qpid::sys::Mutex _ife_lock; public: InactivityFireEvent(JournalImpl* p, const qpid::sys::Duration timeout); virtual ~InactivityFireEvent() {} void fire(); inline void cancel() { qpid::sys::Mutex::ScopedLock sl(_ife_lock); _parent = 0; } }; class GetEventsFireEvent : public qpid::sys::TimerTask { JournalImpl* _parent; qpid::sys::Mutex _gefe_lock; public: GetEventsFireEvent(JournalImpl* p, const qpid::sys::Duration timeout); virtual ~GetEventsFireEvent() {} void fire(); inline void cancel() { qpid::sys::Mutex::ScopedLock sl(_gefe_lock); _parent = 0; } }; class JournalImpl : public qpid::broker::ExternalQueueStore, public mrg::journal::jcntl, public mrg::journal::aio_callback { public: typedef boost::function DeleteCallback; private: // static qpid::sys::Mutex _static_lock; // static u_int32_t cnt; qpid::sys::Timer& timer; bool getEventsTimerSetFlag; boost::intrusive_ptr getEventsFireEventsPtr; qpid::sys::Mutex _getf_lock; qpid::sys::Mutex _read_lock; u_int64_t lastReadRid; // rid of last read msg for loadMsgContent() - detects out-of-order read requests std::vector oooRidList; // list of out-of-order rids (greater than current rid) encountered during read sequence bool writeActivityFlag; bool flushTriggeredFlag; boost::intrusive_ptr inactivityFireEventPtr; // temp local vars for loadMsgContent below void* _xidp; void* _datap; size_t _dlen; mrg::journal::data_tok _dtok; bool _external; qpid::management::ManagementAgent* _agent; qmf::com::redhat::rhm::store::Journal* _mgmtObject; DeleteCallback deleteCallback; public: JournalImpl(qpid::sys::Timer& timer, const std::string& journalId, const std::string& journalDirectory, const std::string& journalBaseFilename, const qpid::sys::Duration getEventsTimeout, const qpid::sys::Duration flushTimeout, qpid::management::ManagementAgent* agent, DeleteCallback deleteCallback=DeleteCallback() ); virtual ~JournalImpl(); void initManagement(qpid::management::ManagementAgent* agent); void initialize(const u_int16_t num_jfiles, const bool auto_expand, const u_int16_t ae_max_jfiles, const u_int32_t jfsize_sblks, const u_int16_t wcache_num_pages, const u_int32_t wcache_pgsize_sblks, mrg::journal::aio_callback* const cbp); inline void initialize(const u_int16_t num_jfiles, const bool auto_expand, const u_int16_t ae_max_jfiles, const u_int32_t jfsize_sblks, const u_int16_t wcache_num_pages, const u_int32_t wcache_pgsize_sblks) { initialize(num_jfiles, auto_expand, ae_max_jfiles, jfsize_sblks, wcache_num_pages, wcache_pgsize_sblks, this); } void recover(const u_int16_t num_jfiles, const bool auto_expand, const u_int16_t ae_max_jfiles, const u_int32_t jfsize_sblks, const u_int16_t wcache_num_pages, const u_int32_t wcache_pgsize_sblks, mrg::journal::aio_callback* const cbp, boost::ptr_list* prep_tx_list_ptr, u_int64_t& highest_rid, u_int64_t queue_id); inline void recover(const u_int16_t num_jfiles, const bool auto_expand, const u_int16_t ae_max_jfiles, const u_int32_t jfsize_sblks, const u_int16_t wcache_num_pages, const u_int32_t wcache_pgsize_sblks, boost::ptr_list* prep_tx_list_ptr, u_int64_t& highest_rid, u_int64_t queue_id) { recover(num_jfiles, auto_expand, ae_max_jfiles, jfsize_sblks, wcache_num_pages, wcache_pgsize_sblks, this, prep_tx_list_ptr, highest_rid, queue_id); } void recover_complete(); // Temporary fn to read and save last msg read from journal so it can be assigned // in chunks. To be replaced when coding to do this direct from the journal is ready. // Returns true if the record is extern, false if local. bool loadMsgContent(u_int64_t rid, std::string& data, size_t length, size_t offset = 0); // Overrides for write inactivity timer void enqueue_data_record(const void* const data_buff, const size_t tot_data_len, const size_t this_data_len, mrg::journal::data_tok* dtokp, const bool transient = false); void enqueue_extern_data_record(const size_t tot_data_len, mrg::journal::data_tok* dtokp, const bool transient = false); void enqueue_txn_data_record(const void* const data_buff, const size_t tot_data_len, const size_t this_data_len, mrg::journal::data_tok* dtokp, const std::string& xid, const bool transient = false); void enqueue_extern_txn_data_record(const size_t tot_data_len, mrg::journal::data_tok* dtokp, const std::string& xid, const bool transient = false); void dequeue_data_record(mrg::journal::data_tok* const dtokp, const bool txn_coml_commit = false); void dequeue_txn_data_record(mrg::journal::data_tok* const dtokp, const std::string& xid, const bool txn_coml_commit = false); void txn_abort(mrg::journal::data_tok* const dtokp, const std::string& xid); void txn_commit(mrg::journal::data_tok* const dtokp, const std::string& xid); void stop(bool block_till_aio_cmpl = false); // Logging void log(mrg::journal::log_level level, const std::string& log_stmt) const; void log(mrg::journal::log_level level, const char* const log_stmt) const; // Overrides for get_events timer mrg::journal::iores flush(const bool block_till_aio_cmpl = false); // TimerTask callback void getEventsFire(); void flushFire(); // AIO callbacks virtual void wr_aio_cb(std::vector& dtokl); virtual void rd_aio_cb(std::vector& pil); qpid::management::ManagementObject* GetManagementObject (void) const { return _mgmtObject; } qpid::management::Manageable::status_t ManagementMethod (uint32_t, qpid::management::Args&, std::string&); void resetDeleteCallback() { deleteCallback = DeleteCallback(); } private: void free_read_buffers(); inline void setGetEventTimer() { getEventsFireEventsPtr->setupNextFire(); timer.add(getEventsFireEventsPtr); getEventsTimerSetFlag = true; } void handleIoResult(const mrg::journal::iores r); // Management instrumentation callbacks overridden from jcntl inline void instr_incr_outstanding_aio_cnt() { if (_mgmtObject != 0) _mgmtObject->inc_outstandingAIOs(); } inline void instr_decr_outstanding_aio_cnt() { if (_mgmtObject != 0) _mgmtObject->dec_outstandingAIOs(); } }; // class JournalImpl class TplJournalImpl : public JournalImpl { public: TplJournalImpl(qpid::sys::Timer& timer, const std::string& journalId, const std::string& journalDirectory, const std::string& journalBaseFilename, const qpid::sys::Duration getEventsTimeout, const qpid::sys::Duration flushTimeout, qpid::management::ManagementAgent* agent) : JournalImpl(timer, journalId, journalDirectory, journalBaseFilename, getEventsTimeout, flushTimeout, agent) {} virtual ~TplJournalImpl() {} // Special version of read_data_record that ignores transactions - needed when reading the TPL inline mrg::journal::iores read_data_record(void** const datapp, std::size_t& dsize, void** const xidpp, std::size_t& xidsize, bool& transient, bool& external, mrg::journal::data_tok* const dtokp) { return JournalImpl::read_data_record(datapp, dsize, xidpp, xidsize, transient, external, dtokp, true); } inline void read_reset() { _rmgr.invalidate(); } }; // class TplJournalImpl } // namespace msgstore } // namespace mrg #endif qpid-cpp-store-debian-0.16/lib/BindingDbt.cpp0000644000176300017630000000332411514124615020013 0ustar cajuscajus/* Copyright (c) 2007, 2008 Red Hat, Inc. This file is part of the Qpid async store library msgstore.so. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA The GNU Lesser General Public License is available in the file COPYING. */ #include "BindingDbt.h" namespace mrg { namespace msgstore { BindingDbt::BindingDbt(const qpid::broker::PersistableExchange& e, const qpid::broker::PersistableQueue& q, const std::string& k, const qpid::framing::FieldTable& a) : data(new char[encodedSize(e, q, k, a)]), buffer(data, encodedSize(e, q, k, a)) { buffer.putLongLong(q.getPersistenceId()); buffer.putShortString(q.getName()); buffer.putShortString(k); buffer.put(a); set_data(data); set_size(encodedSize(e, q, k, a)); } BindingDbt::~BindingDbt() { delete [] data; } uint32_t BindingDbt::encodedSize(const qpid::broker::PersistableExchange& /*not used*/, const qpid::broker::PersistableQueue& q, const std::string& k, const qpid::framing::FieldTable& a) { return 8 /*queue id*/ + q.getName().size() + 1 + k.size() + 1 + a.encodedSize(); } }} qpid-cpp-store-debian-0.16/lib/PreparedTransaction.cpp0000644000176300017630000000537311142067437021772 0ustar cajuscajus/* Copyright (c) 2007, 2008 Red Hat, Inc. This file is part of the Qpid async store library msgstore.so. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA The GNU Lesser General Public License is available in the file COPYING. */ #include "PreparedTransaction.h" #include using namespace mrg::msgstore; using std::string; void LockedMappings::add(queue_id queue, message_id message) { locked.push_back(std::make_pair(queue, message)); } bool LockedMappings::isLocked(queue_id queue, message_id message) { idpair op( std::make_pair(queue, message) ); return find(locked.begin(), locked.end(), op) != locked.end(); } void LockedMappings::add(LockedMappings::map& map, std::string& key, queue_id queue, message_id message) { LockedMappings::map::iterator i = map.find(key); if (i == map.end()) { LockedMappings::shared_ptr ptr(new LockedMappings()); i = map.insert(std::make_pair(key, ptr)).first; } i->second->add(queue, message); } bool PreparedTransaction::isLocked(queue_id queue, message_id message) { return (enqueues.get() && enqueues->isLocked(queue, message)) || (dequeues.get() && dequeues->isLocked(queue, message)); } bool PreparedTransaction::isLocked(PreparedTransaction::list& txns, queue_id queue, message_id message) { for (PreparedTransaction::list::iterator i = txns.begin(); i != txns.end(); i++) { if (i->isLocked(queue, message)) { return true; } } return false; } PreparedTransaction::list::iterator PreparedTransaction::getLockedPreparedTransaction(PreparedTransaction::list& txns, queue_id queue, message_id message) { for (PreparedTransaction::list::iterator i = txns.begin(); i != txns.end(); i++) { if (i->isLocked(queue, message)) { return i; } } return txns.end(); } PreparedTransaction::PreparedTransaction(const std::string& _xid, LockedMappings::shared_ptr _enqueues, LockedMappings::shared_ptr _dequeues) : xid(_xid), enqueues(_enqueues), dequeues(_dequeues) {} qpid-cpp-store-debian-0.16/lib/IdDbt.cpp0000644000176300017630000000224511142067437017003 0ustar cajuscajus/* Copyright (c) 2007, 2008 Red Hat, Inc. This file is part of the Qpid async store library msgstore.so. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA The GNU Lesser General Public License is available in the file COPYING. */ #include "IdDbt.h" using namespace mrg::msgstore; IdDbt::IdDbt() : id(0) { init(); } IdDbt::IdDbt(u_int64_t _id) : id(_id) { init(); } void IdDbt::init() { set_data(&id); set_size(sizeof(u_int64_t)); set_ulen(sizeof(u_int64_t)); set_flags(DB_DBT_USERMEM); } qpid-cpp-store-debian-0.16/lib/BindingDbt.h0000644000176300017630000000335411142067437017470 0ustar cajuscajus/* Copyright (c) 2007, 2008 Red Hat, Inc. This file is part of the Qpid async store library msgstore.so. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA The GNU Lesser General Public License is available in the file COPYING. */ #ifndef _BindingDbt_ #define _BindingDbt_ #include "db-inc.h" #include #include #include #include namespace mrg{ namespace msgstore{ class BindingDbt : public Dbt { char* data; qpid::framing::Buffer buffer; static uint32_t encodedSize(const qpid::broker::PersistableExchange& e, const qpid::broker::PersistableQueue& q, const std::string& k, const qpid::framing::FieldTable& a); public: BindingDbt(const qpid::broker::PersistableExchange& e, const qpid::broker::PersistableQueue& q, const std::string& k, const qpid::framing::FieldTable& a); virtual ~BindingDbt(); }; }} #endif qpid-cpp-store-debian-0.16/lib/BufferValue.h0000644000176300017630000000243511142067437017671 0ustar cajuscajus/* Copyright (c) 2007 Red Hat, Inc. This file is part of the Qpid async store library msgstore.so. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA The GNU Lesser General Public License is available in the file COPYING. */ #ifndef _BufferValue_ #define _BufferValue_ #include "db-inc.h" #include #include namespace mrg{ namespace msgstore{ class BufferValue : public Dbt { char* data; public: qpid::framing::Buffer buffer; BufferValue(u_int32_t size, u_int64_t offset); BufferValue(const qpid::broker::Persistable& p); virtual ~BufferValue(); }; }} #endif qpid-cpp-store-debian-0.16/lib/jrnl/0000755000176300017630000000000011761423514016252 5ustar cajuscajusqpid-cpp-store-debian-0.16/lib/jrnl/jinf.cpp0000644000176300017630000004445011623467711017717 0ustar cajuscajus/** * \file jinf.cpp * * Qpid asynchronous store plugin library * * This file contains the code for the mrg::journal::jinf class. * * See jinf.hpp comments for details of this class. * * \author Kim van der Riet * * Copyright (c) 2007, 2008, 2009 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #include "jrnl/jinf.hpp" #include #include #include #include #include "jrnl/file_hdr.hpp" #include "jrnl/jcntl.hpp" #include "jrnl/jerrno.hpp" #include "jrnl/lp_map.hpp" #include #include namespace mrg { namespace journal { jinf::jinf(const std::string& jinf_filename, bool validate_flag): _jver(0), _filename(jinf_filename), _num_jfiles(0), _ae(false), _ae_max_jfiles(0), _jfsize_sblks(0), _sblk_size_dblks(0), _dblk_size(0), _wcache_pgsize_sblks(0), _wcache_num_pages(0), _rcache_pgsize_sblks(0), _rcache_num_pages(0), _tm_ptr(0), _valid_flag(false), _analyzed_flag(false), _initial_owi(false), _frot(false) { read(_filename); if (validate_flag) validate(); } jinf::jinf(const std::string& jid, const std::string& jdir, const std::string& base_filename, const u_int16_t num_jfiles, const bool auto_expand, const u_int16_t ae_max_jfiles, const u_int32_t jfsize_sblks, const u_int32_t wcache_pgsize_sblks, const u_int16_t wcache_num_pages, const timespec& ts): _jver(RHM_JDAT_VERSION), _jid(jid), _jdir(jdir), _base_filename(base_filename), _ts(ts), _num_jfiles(num_jfiles), _ae(auto_expand), _ae_max_jfiles(ae_max_jfiles), _jfsize_sblks(jfsize_sblks), _sblk_size_dblks(JRNL_SBLK_SIZE), _dblk_size(JRNL_DBLK_SIZE), _wcache_pgsize_sblks(wcache_pgsize_sblks), _wcache_num_pages(wcache_num_pages), _rcache_pgsize_sblks(JRNL_RMGR_PAGE_SIZE), _rcache_num_pages(JRNL_RMGR_PAGES), _tm_ptr(std::localtime(&ts.tv_sec)), _valid_flag(false), _analyzed_flag(false), _initial_owi(false) { set_filename(); } jinf::~jinf() {} void jinf::validate() { bool err = false; std::ostringstream oss; if (_jver != RHM_JDAT_VERSION) { oss << "File \"" << _filename << "\": "; oss << "RHM_JDAT_VERSION mismatch: found=" << (int)_jver; oss << "; required=" << RHM_JDAT_VERSION << std::endl; err = true; } if (_num_jfiles < JRNL_MIN_NUM_FILES) { oss << "File \"" << _filename << "\": "; oss << "Number of journal files too small: found=" << _num_jfiles; oss << "; minimum=" << JRNL_MIN_NUM_FILES << std::endl; err = true; } if (_num_jfiles > JRNL_MAX_NUM_FILES) { oss << "File \"" << _filename << "\": "; oss << "Number of journal files too large: found=" << _num_jfiles; oss << "; maximum=" << JRNL_MAX_NUM_FILES << std::endl; err = true; } if (_ae) { if (_ae_max_jfiles < _num_jfiles) { oss << "File \"" << _filename << "\": "; oss << "Number of journal files exceeds auto-expansion limit: found=" << _num_jfiles; oss << "; maximum=" << _ae_max_jfiles; err = true; } if (_ae_max_jfiles > JRNL_MAX_NUM_FILES) { oss << "File \"" << _filename << "\": "; oss << "Auto-expansion file limit too large: found=" << _ae_max_jfiles; oss << "; maximum=" << JRNL_MAX_NUM_FILES; err = true; } } if (_jfsize_sblks < JRNL_MIN_FILE_SIZE) { oss << "File \"" << _filename << "\": "; oss << "Journal file size too small: found=" << _jfsize_sblks; oss << "; minimum=" << JRNL_MIN_FILE_SIZE << " (sblks)" << std::endl; err = true; } if (_sblk_size_dblks != JRNL_SBLK_SIZE) { oss << "File \"" << _filename << "\": "; oss << "JRNL_SBLK_SIZE mismatch: found=" << _sblk_size_dblks; oss << "; required=" << JRNL_SBLK_SIZE << std::endl; err = true; } if (_dblk_size != JRNL_DBLK_SIZE) { oss << "File \"" << _filename << "\": "; oss << "JRNL_DBLK_SIZE mismatch: found=" << _dblk_size; oss << "; required=" << JRNL_DBLK_SIZE << std::endl; err = true; } if (err) throw jexception(jerrno::JERR_JINF_CVALIDFAIL, oss.str(), "jinf", "validate"); _valid_flag = true; } void jinf::analyze() { lp_map early_map; // map for all owi flags same as pfid 0 lp_map late_map; // map for all owi flags opposite to pfid 0 bool late_latch = false; // latch for owi switchover if (!_valid_flag) validate(); bool done = false; for (u_int16_t pfid=0; pfid<_num_jfiles && !done; pfid++) { std::ostringstream oss; if (_jdir.at(_jdir.size() - 1) == '/') oss << _jdir << _base_filename << "."; else oss << _jdir << "/" << _base_filename << "."; oss << std::setw(4) << std::setfill('0') << std::hex << pfid; oss << "." << JRNL_DATA_EXTENSION; // Check size of each file is consistent and expected u_int32_t fsize = get_filesize(oss.str()); if (fsize != (_jfsize_sblks + 1) * _sblk_size_dblks * _dblk_size) { std::ostringstream oss1; oss1 << "File \"" << oss.str() << "\": size=" << fsize << "; expected=" << ((_jfsize_sblks + 1) * _sblk_size_dblks * _dblk_size); throw jexception(jerrno::JERR_JINF_BADFILESIZE, oss1.str(), "jinf", "analyze"); } std::ifstream jifs(oss.str().c_str()); if (!jifs.good()) throw jexception(jerrno::JERR__FILEIO, oss.str(), "jinf", "analyze"); file_hdr fhdr; jifs.read((char*)&fhdr, sizeof(fhdr)); if (fhdr._magic != RHM_JDAT_FILE_MAGIC) // No file header { if (fhdr._magic != 0) throw jexception(jerrno::JERR_JINF_INVALIDFHDR, oss.str(), "jinf", "analyze"); if (!pfid) // pfid 0 == lid 0 cannot be empty throw jexception(jerrno::JERR_JINF_JDATEMPTY, oss.str(), "jinf", "analyze"); _frot = true; done = true; } else { assert(pfid == fhdr._pfid); if (pfid == 0) { _initial_owi = fhdr.get_owi(); early_map.insert(fhdr._lfid, pfid); } else { if (_initial_owi == fhdr.get_owi()) { early_map.insert(fhdr._lfid, pfid); if (late_latch && (!_ae || _num_jfiles == JRNL_MIN_NUM_FILES)) throw jexception(jerrno::JERR_JINF_OWIBAD, oss.str(), "jinf", "analyze"); } else { late_map.insert(fhdr._lfid, pfid); late_latch = true; } } } jifs.close(); } // for (pfid) // If this is not the first rotation, all files should be in either early or late maps if (!_frot) assert(early_map.size() + late_map.size() == _num_jfiles); _pfid_list.clear(); late_map.get_pfid_list(_pfid_list); early_map.get_pfid_list(_pfid_list); // Check OWI consistency // for (u_int16_t lfid=0; lfid<_num_jfiles && !done; lfid++) // { // throw jexception(jerrno::JERR_JINF_OWIBAD, oss.str(), "jinf", "analyze"); // } _analyzed_flag = true; } void jinf::write() { std::ostringstream oss; oss << _jdir << "/" << _base_filename << "." << JRNL_INFO_EXTENSION; std::ofstream of(oss.str().c_str(), std::ofstream::out | std::ofstream::trunc); if (!of.good()) throw jexception(jerrno::JERR__FILEIO, oss.str(), "jinf", "write"); of << xml_str(); of.close(); } u_int16_t jinf::incr_num_jfiles() { if (_num_jfiles >= JRNL_MAX_NUM_FILES) throw jexception(jerrno::JERR_JINF_TOOMANYFILES, "jinf", "incr_num_jfiles"); return ++_num_jfiles; } u_int16_t jinf::get_first_pfid() { if (!_analyzed_flag) analyze(); return *_pfid_list.begin(); } u_int16_t jinf::get_last_pfid() { if (!_analyzed_flag) analyze(); return *_pfid_list.rbegin(); } jinf::pfid_list& jinf::get_pfid_list() { if (!_analyzed_flag) analyze(); return _pfid_list; } void jinf::get_normalized_pfid_list(pfid_list& pfid_list) { if (!_analyzed_flag) analyze(); pfid_list.clear(); u_int16_t s = _pfid_list.size(); u_int16_t iz = 0; // index of 0 value while (_pfid_list[iz] && iz < s) iz++; assert(_pfid_list[iz] == 0); for (u_int16_t i = iz; i < iz + s; i++) pfid_list.push_back(_pfid_list[i % s]); assert(pfid_list[0] == 0); assert(pfid_list.size() == s); } bool jinf::get_initial_owi() { if (!_analyzed_flag) analyze(); return _initial_owi; } bool jinf::get_frot() { if (!_analyzed_flag) analyze(); return _frot; } std::string jinf::to_string() const { std::ostringstream oss; oss << std::setfill('0'); oss << "Journal ID \"" << _jid << "\" initialized " << (_tm_ptr->tm_year + 1900) << "/"; oss << std::setw(2) << (_tm_ptr->tm_mon + 1) << "/" << std::setw(2) << _tm_ptr->tm_mday << " "; oss << std::setw(2) << _tm_ptr->tm_hour << ":" << std::setw(2) << _tm_ptr->tm_min << ":"; oss << std::setw(2) << _tm_ptr->tm_sec << "." << std::setw(9) << _ts.tv_nsec << ":" << std::endl; oss << " Journal directory: \"" << _jdir << "\"" << std::endl; oss << " Journal base filename: \"" << _base_filename << "\"" << std::endl; oss << " Journal version: " << (unsigned)_jver << std::endl; oss << " Number of journal files: " << _num_jfiles << std::endl; // TODO: Uncomment these lines when auto-expand is enabled. // oss << " Auto-expand mode: " << (_ae ? "enabled" : "disabled") << std::endl; // if (_ae) oss << " Max. number of journal files (in auto-expand mode): " << _ae_max_jfiles << std::endl; oss << " Journal file size: " << _jfsize_sblks << " sblks" << std::endl; oss << " Softblock size (JRNL_SBLK_SIZE): " << _sblk_size_dblks << " dblks" << std::endl; oss << " Datablock size (JRNL_DBLK_SIZE): " << _dblk_size << " bytes" << std::endl; oss << " Write page size: " << _wcache_pgsize_sblks << " sblks" << std::endl; oss << " Number of write pages: " << _wcache_num_pages << std::endl; oss << " Read page size (JRNL_RMGR_PAGE_SIZE): " << _rcache_pgsize_sblks << " sblks" << std::endl; oss << " Number of read pages (JRNL_RMGR_PAGES): " << _rcache_num_pages << std::endl; return oss.str(); } std::string jinf::xml_str() const { // TODO: This is *not* an XML writer, rather for simplicity, it uses literals. I'm sure a more elegant way can be // found to do this using the real thing... std::ostringstream oss; oss << std::setfill('0'); oss << "" << std::endl; oss << "" << std::endl; oss << " " << std::endl; oss << " " << std::endl; oss << " " << std::endl; oss << " " << std::endl; oss << " " << std::endl; oss << " " << std::endl; oss << " " << std::endl; oss << " " << std::endl; oss << " " << std::endl; oss << " tm_year + 1900) << "/"; oss << std::setw(2) << (_tm_ptr->tm_mon + 1) << "/" << std::setw(2) << _tm_ptr->tm_mday << " "; oss << std::setw(2) << _tm_ptr->tm_hour << ":" << std::setw(2) << _tm_ptr->tm_min << ":"; oss << std::setw(2) << _tm_ptr->tm_sec << "." << std::setw(9) << _ts.tv_nsec; oss << "\" />" << std::endl; oss << " " << std::endl; oss << " " << std::endl; oss << " " << std::endl; oss << " " << std::endl; if (_ae) oss << " " << std::endl; oss << " " << std::endl; oss << " " << std::endl; oss << " " << std::endl; oss << " " << std::endl; oss << " " << std::endl; oss << " " << std::endl; oss << " " << std::endl; oss << " " << std::endl; oss << " " << std::endl; oss << " " << std::endl; oss << "" << std::endl; return oss.str(); } void jinf::set_filename() { std::ostringstream oss; oss << _jdir << "/" << _base_filename << "." << JRNL_INFO_EXTENSION; _filename = oss.str().c_str(); } void jinf::read(const std::string& jinf_filename) { // TODO: This is *not* an XML reader, rather for simplicity, it is a brute-force line reader which relies on string // recognition. It relies on the format of xml_str() above; it will not handle a XML restructuring. // *** Can it be replaced cheaply by a real XML reader? Should it be, or is this sufficient? *** char buff[1024]; // limit of line input length std::ifstream jinfs(jinf_filename.c_str()); if (!jinfs.good()) throw jexception(jerrno::JERR__FILEIO, jinf_filename.c_str(), "jinf", "read"); u_int32_t charcnt = 0; while (jinfs.good()) { jinfs.getline(buff, 1023); charcnt += std::strlen(buff); if (std::strstr(buff, "journal_version")) _jver = u_int16_value(buff); else if(std::strstr(buff, "id_string")) string_value(_jid, buff); else if(std::strstr(buff, "directory")) string_value(_jdir, buff); else if(std::strstr(buff, "base_filename")) string_value(_base_filename, buff); else if(std::strstr(buff, "number_jrnl_files")) _num_jfiles = u_int16_value(buff); else if(std::strstr(buff, "auto_expand_max_jrnl_files")) _ae_max_jfiles = u_int16_value(buff); else if(std::strstr(buff, "auto_expand")) _ae = bool_value(buff); else if(std::strstr(buff, "jrnl_file_size_sblks")) _jfsize_sblks = u_int32_value(buff); else if(std::strstr(buff, "JRNL_SBLK_SIZE")) _sblk_size_dblks = u_int16_value(buff); else if(std::strstr(buff, "JRNL_DBLK_SIZE")) _dblk_size = u_int32_value(buff); else if(std::strstr(buff, "wcache_pgsize_sblks")) _wcache_pgsize_sblks = u_int32_value(buff); else if(std::strstr(buff, "wcache_num_pages")) _wcache_num_pages = u_int32_value(buff); else if(std::strstr(buff, "JRNL_RMGR_PAGE_SIZE")) _rcache_pgsize_sblks = u_int32_value(buff); else if(std::strstr(buff, "JRNL_RMGR_PAGES")) _rcache_num_pages = u_int32_value(buff); else if(std::strstr(buff, "nanoseconds")) _ts.tv_nsec = u_int32_value(buff); else if(std::strstr(buff, "seconds")) { _ts.tv_sec = u_int32_value(buff); _tm_ptr = std::localtime(&_ts.tv_sec); } } jinfs.close(); if (charcnt == 0) throw jexception(jerrno::JERR_JINF_ZEROLENFILE, jinf_filename.c_str(), "jinf", "read"); } bool jinf::bool_value(char* line) const { return std::strcmp(find_value(line), "true") == 0; } u_int16_t jinf::u_int16_value(char* line) const { return std::atoi(find_value(line)); } u_int32_t jinf::u_int32_value(char* line) const { return std::atol(find_value(line)); } std::string& jinf::string_value(std::string& str, char* line) const { str.assign(find_value(line)); return str; } char* jinf::find_value(char* line) const { const char* target1_str = "value=\""; int target2_char = '\"'; char* t1 = std::strstr(line, target1_str); if (t1 == 0) { std::ostringstream oss; oss << "File \"" << _filename << "\": line=" << line; throw jexception(jerrno::JERR_JINF_NOVALUESTR, oss.str(), "jinf", "find_value"); } t1 += std::strlen(target1_str); char* t2 = std::strchr(t1, target2_char); if (t2 == 0) { std::ostringstream oss; oss << "File \"" << _filename << "\": line=" << line; throw jexception(jerrno::JERR_JINF_BADVALUESTR, oss.str(), "jinf", "find_value"); } *t2 = '\0'; return t1; } u_int32_t jinf::get_filesize(const std::string& file_name) const { struct stat s; if (::stat(file_name.c_str(), &s)) { std::ostringstream oss; oss << "stat: file=\"" << file_name << "\"" << FORMAT_SYSERR(errno); throw jexception(jerrno::JERR_JINF_STAT, oss.str(), "jinf", "get_filesize"); } if (!S_ISREG(s.st_mode)) // not a regular file, { std::ostringstream oss; oss << "File \"" << file_name << "\" is not a regular file: mode=0x" << std::hex << s.st_mode; throw jexception(jerrno::JERR_JINF_NOTREGFILE, oss.str(), "jinf", "get_filesize"); } return u_int32_t(s.st_size); } } // namespace journal } // namespace mrg qpid-cpp-store-debian-0.16/lib/jrnl/enums.hpp0000644000176300017630000000743611142067437020125 0ustar cajuscajus/** * \file enums.hpp * * Qpid asynchronous store plugin library * * File containing definitions for namespace mrg::journal enums. * * \author Kim van der Riet * * Copyright (c) 2007, 2008 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #ifndef mrg_journal_enums_hpp #define mrg_journal_enums_hpp namespace mrg { namespace journal { // TODO: Change this to flags, as multiple of these conditions may exist simultaneously /** * \brief Enumeration of possilbe return states from journal read and write operations. */ enum _iores { RHM_IORES_SUCCESS = 0, ///< Success: IO operation completed noramlly. RHM_IORES_PAGE_AIOWAIT, ///< IO operation suspended - next page is waiting for AIO. RHM_IORES_FILE_AIOWAIT, ///< IO operation suspended - next file is waiting for AIO. RHM_IORES_EMPTY, ///< During read operations, nothing further is available to read. RHM_IORES_RCINVALID, ///< Read page cache is invalid (ie obsolete or uninitialized) RHM_IORES_ENQCAPTHRESH, ///< Enqueue capacity threshold (limit) reached. RHM_IORES_FULL, ///< During write operations, the journal files are full. RHM_IORES_BUSY, ///< Another blocking operation is in progress. RHM_IORES_TXPENDING, ///< Operation blocked by pending transaction. RHM_IORES_NOTIMPL ///< Function is not yet implemented. }; typedef _iores iores; static inline const char* iores_str(iores res) { switch (res) { case RHM_IORES_SUCCESS: return "RHM_IORES_SUCCESS"; case RHM_IORES_PAGE_AIOWAIT: return "RHM_IORES_PAGE_AIOWAIT"; case RHM_IORES_FILE_AIOWAIT: return "RHM_IORES_FILE_AIOWAIT"; case RHM_IORES_EMPTY: return "RHM_IORES_EMPTY"; case RHM_IORES_RCINVALID: return "RHM_IORES_RCINVALID"; case RHM_IORES_ENQCAPTHRESH: return "RHM_IORES_ENQCAPTHRESH"; case RHM_IORES_FULL: return "RHM_IORES_FULL"; case RHM_IORES_BUSY: return "RHM_IORES_BUSY"; case RHM_IORES_TXPENDING: return "RHM_IORES_TXPENDING"; case RHM_IORES_NOTIMPL: return "RHM_IORES_NOTIMPL"; } return ""; } enum _log_level { LOG_TRACE = 0, LOG_DEBUG, LOG_INFO, LOG_NOTICE, LOG_WARN, LOG_ERROR, LOG_CRITICAL }; typedef _log_level log_level; static inline const char* log_level_str(log_level ll) { switch (ll) { case LOG_TRACE: return "TRACE"; case LOG_DEBUG: return "DEBUG"; case LOG_INFO: return "INFO"; case LOG_NOTICE: return "NOTICE"; case LOG_WARN: return "WARN"; case LOG_ERROR: return "ERROR"; case LOG_CRITICAL: return "CRITICAL"; } return ""; } } // namespace journal } // namespace mrg #endif // ifndef mrg_journal_enums_hpp qpid-cpp-store-debian-0.16/lib/jrnl/data_tok.cpp0000644000176300017630000001130311463612347020545 0ustar cajuscajus/** * \file data_tok.cpp * * Qpid asynchronous store plugin library * * File containing code for class mrg::journal::data_tok (data block token). * See comments in file data_tok.hpp for details. * * \author Kim van der Riet * * Copyright (c) 2007, 2008 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #include "jrnl/data_tok.hpp" #include #include "jrnl/jerrno.hpp" #include "jrnl/jexception.hpp" #include "jrnl/slock.hpp" #include namespace mrg { namespace journal { // Static members u_int64_t data_tok::_cnt = 0; smutex data_tok::_mutex; data_tok::data_tok(): _wstate(NONE), _rstate(UNREAD), _dsize(0), _dblks_written(0), _dblks_read(0), _pg_cnt(0), _fid(0), _rid(0), _xid(), _dequeue_rid(0), _external_rid(false) { slock s(_mutex); _icnt = _cnt++; } data_tok::~data_tok() {} const char* data_tok::wstate_str() const { return wstate_str(_wstate); } const char* data_tok::wstate_str(write_state wstate) { switch (wstate) { case NONE: return "NONE"; case ENQ_CACHED: return "ENQ_CACHED"; case ENQ_PART: return "ENQ_PART"; case ENQ_SUBM: return "ENQ_SUBM"; case ENQ: return "ENQ"; case DEQ_CACHED: return "DEQ_CACHED"; case DEQ_PART: return "DEQ_PART"; case DEQ_SUBM: return "DEQ_SUBM"; case DEQ: return "DEQ"; case ABORT_CACHED: return "ABORT_CACHED"; case ABORT_PART: return "ABORT_PART"; case ABORT_SUBM: return "ABORT_SUBM"; case ABORTED: return "ABORTED"; case COMMIT_CACHED: return "COMMIT_CACHED"; case COMMIT_PART: return "COMMIT_PART"; case COMMIT_SUBM: return "COMMIT_SUBM"; case COMMITTED: return "COMMITTED"; } // Not using default: forces compiler to ensure all cases are covered. return ""; } const char* data_tok::rstate_str() const { return rstate_str(_rstate); } const char* data_tok::rstate_str(read_state rstate) { switch (rstate) { case NONE: return "NONE"; case READ_PART: return "READ_PART"; case SKIP_PART: return "SKIP_PART"; case READ: return "READ"; // Not using default: forces compiler to ensure all cases are covered. } return ""; } void data_tok::set_rstate(const read_state rstate) { if (_wstate != ENQ && rstate != UNREAD) { std::ostringstream oss; oss << "Attempted to change read state to " << rstate_str(rstate); oss << " while write state is not enqueued (wstate ENQ); wstate=" << wstate_str() << "."; throw jexception(jerrno::JERR_DTOK_ILLEGALSTATE, oss.str(), "data_tok", "set_rstate"); } _rstate = rstate; } void data_tok::reset() { _wstate = NONE; _rstate = UNREAD; _dsize = 0; _dblks_written = 0; _dblks_read = 0; _pg_cnt = 0; _fid = 0; _rid = 0; _xid.clear(); } // debug aid std::string data_tok::status_str() const { std::ostringstream oss; oss << std::hex << std::setfill('0'); oss << "dtok id=0x" << _icnt << "; ws=" << wstate_str() << "; rs=" << rstate_str(); oss << "; fid=0x" << _fid << "; rid=0x" << _rid << "; xid="; for (unsigned i=0; i<_xid.size(); i++) { if (isprint(_xid[i])) oss << _xid[i]; else oss << "/" << std::setw(2) << (int)((char)_xid[i]); } oss << "; drid=0x" << _dequeue_rid << " extrid=" << (_external_rid?"T":"F"); oss << "; ds=0x" << _dsize << "; dw=0x" << _dblks_written << "; dr=0x" << _dblks_read; oss << " pc=0x" << _pg_cnt; return oss.str(); } } // namespace journal } // namespace mrg qpid-cpp-store-debian-0.16/lib/jrnl/cvar.cpp0000644000176300017630000000224511142067437017715 0ustar cajuscajus/** * \file cvar.cpp * * Qpid asynchronous store plugin library * * File containing code for class mrg::journal::cvar (condition variable). See * comments in file cvar.hpp for details. * * \author Kim van der Riet * * Copyright (c) 2007, 2008 Red Hat Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #include "jrnl/cvar.hpp" qpid-cpp-store-debian-0.16/lib/jrnl/wmgr.cpp0000644000176300017630000011236711672661757017762 0ustar cajuscajus/** * \file wmgr.cpp * * Qpid asynchronous store plugin library * * File containing code for class mrg::journal::wmgr (read manager). See * comments in file wmgr.hpp for details. * * \author Kim van der Riet * * Copyright (c) 2007, 2008, 2009, 2010 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #include "jrnl/wmgr.hpp" #include #include #include #include #include "jrnl/file_hdr.hpp" #include "jrnl/jcntl.hpp" #include "jrnl/jerrno.hpp" #include namespace mrg { namespace journal { wmgr::wmgr(jcntl* jc, enq_map& emap, txn_map& tmap, wrfc& wrfc): pmgr(jc, emap, tmap), _wrfc(wrfc), _max_dtokpp(0), _max_io_wait_us(0), _fhdr_base_ptr(0), _fhdr_ptr_arr(0), _fhdr_aio_cb_arr(0), _cached_offset_dblks(0), _jfsize_dblks(0), _jfsize_pgs(0), _num_jfiles(0), _enq_busy(false), _deq_busy(false), _abort_busy(false), _commit_busy(false), _txn_pending_set() {} wmgr::wmgr(jcntl* jc, enq_map& emap, txn_map& tmap, wrfc& wrfc, const u_int32_t max_dtokpp, const u_int32_t max_iowait_us): pmgr(jc, emap, tmap /* , dtoklp */), _wrfc(wrfc), _max_dtokpp(max_dtokpp), _max_io_wait_us(max_iowait_us), _fhdr_base_ptr(0), _fhdr_ptr_arr(0), _fhdr_aio_cb_arr(0), _cached_offset_dblks(0), _jfsize_dblks(0), _jfsize_pgs(0), _num_jfiles(0), _enq_busy(false), _deq_busy(false), _abort_busy(false), _commit_busy(false), _txn_pending_set() {} wmgr::~wmgr() { wmgr::clean(); } void wmgr::initialize(aio_callback* const cbp, const u_int32_t wcache_pgsize_sblks, const u_int16_t wcache_num_pages, const u_int32_t max_dtokpp, const u_int32_t max_iowait_us, std::size_t eo) { _enq_busy = false; _deq_busy = false; _abort_busy = false; _commit_busy = false; _max_dtokpp = max_dtokpp; _max_io_wait_us = max_iowait_us; initialize(cbp, wcache_pgsize_sblks, wcache_num_pages); _jfsize_dblks = _jc->jfsize_sblks() * JRNL_SBLK_SIZE; _jfsize_pgs = _jc->jfsize_sblks() / _cache_pgsize_sblks; assert(_jc->jfsize_sblks() % JRNL_RMGR_PAGE_SIZE == 0); if (eo) { const u_int32_t wr_pg_size_dblks = _cache_pgsize_sblks * JRNL_SBLK_SIZE; u_int32_t data_dblks = (eo / JRNL_DBLK_SIZE) - 4; // 4 dblks for file hdr _pg_cntr = data_dblks / wr_pg_size_dblks; _pg_offset_dblks = data_dblks - (_pg_cntr * wr_pg_size_dblks); } } iores wmgr::enqueue(const void* const data_buff, const std::size_t tot_data_len, const std::size_t this_data_len, data_tok* dtokp, const void* const xid_ptr, const std::size_t xid_len, const bool transient, const bool external) { if (xid_len) assert(xid_ptr != 0); if (_deq_busy || _abort_busy || _commit_busy) return RHM_IORES_BUSY; if (this_data_len != tot_data_len && !external) return RHM_IORES_NOTIMPL; iores res = pre_write_check(WMGR_ENQUEUE, dtokp, xid_len, tot_data_len, external); if (res != RHM_IORES_SUCCESS) return res; bool cont = false; if (_enq_busy) // If enqueue() exited last time with RHM_IORES_FULL or RHM_IORES_PAGE_AIOWAIT { if (dtokp->wstate() == data_tok::ENQ_PART) cont = true; else { std::ostringstream oss; oss << "This data_tok: id=" << dtokp->id() << " state=" << dtokp->wstate_str(); throw jexception(jerrno::JERR_WMGR_ENQDISCONT, oss.str(), "wmgr", "enqueue"); } } u_int64_t rid = (dtokp->external_rid() | cont) ? dtokp->rid() : _wrfc.get_incr_rid(); _enq_rec.reset(rid, data_buff, tot_data_len, xid_ptr, xid_len, _wrfc.owi(), transient, external); if (!cont) { dtokp->set_rid(rid); dtokp->set_dequeue_rid(0); if (xid_len) dtokp->set_xid(xid_ptr, xid_len); else dtokp->clear_xid(); _enq_busy = true; } bool done = false; while (!done) { assert(_pg_offset_dblks < _cache_pgsize_sblks * JRNL_SBLK_SIZE); void* wptr = (void*)((char*)_page_ptr_arr[_pg_index] + _pg_offset_dblks * JRNL_DBLK_SIZE); u_int32_t data_offs_dblks = dtokp->dblocks_written(); u_int32_t ret = _enq_rec.encode(wptr, data_offs_dblks, (_cache_pgsize_sblks * JRNL_SBLK_SIZE) - _pg_offset_dblks); // Remember fid which contains the record header in case record is split over several files if (data_offs_dblks == 0) dtokp->set_fid(_wrfc.index()); _pg_offset_dblks += ret; _cached_offset_dblks += ret; dtokp->incr_dblocks_written(ret); dtokp->incr_pg_cnt(); _page_cb_arr[_pg_index]._pdtokl->push_back(dtokp); // Is the encoding of this record complete? if (dtokp->dblocks_written() >= _enq_rec.rec_size_dblks()) { // TODO: Incorrect - must set state to ENQ_CACHED; ENQ_SUBM is set when AIO returns. dtokp->set_wstate(data_tok::ENQ_SUBM); dtokp->set_dsize(tot_data_len); // Only add this data token to page token list when submit is complete, this way // long multi-page messages have their token on the page containing the END of the // message. AIO callbacks will then only process this token when entire message is // enqueued. _wrfc.incr_enqcnt(dtokp->fid()); if (xid_len) // If part of transaction, add to transaction map { std::string xid((char*)xid_ptr, xid_len); _tmap.insert_txn_data(xid, txn_data(rid, 0, dtokp->fid(), true)); } else { if (_emap.insert_pfid(rid, dtokp->fid()) < enq_map::EMAP_OK) // fail { // The only error code emap::insert_pfid() returns is enq_map::EMAP_DUP_RID. std::ostringstream oss; oss << std::hex << "rid=0x" << rid << " _pfid=0x" << dtokp->fid(); throw jexception(jerrno::JERR_MAP_DUPLICATE, oss.str(), "wmgr", "enqueue"); } } done = true; } else dtokp->set_wstate(data_tok::ENQ_PART); file_header_check(rid, cont, _enq_rec.rec_size_dblks() - data_offs_dblks); flush_check(res, cont, done); } if (dtokp->wstate() >= data_tok::ENQ_SUBM) _enq_busy = false; return res; } iores wmgr::dequeue(data_tok* dtokp, const void* const xid_ptr, const std::size_t xid_len, const bool txn_coml_commit) { if (xid_len) assert(xid_ptr != 0); if (_enq_busy || _abort_busy || _commit_busy) return RHM_IORES_BUSY; iores res = pre_write_check(WMGR_DEQUEUE, dtokp); if (res != RHM_IORES_SUCCESS) return res; bool cont = false; if (_deq_busy) // If dequeue() exited last time with RHM_IORES_FULL or RHM_IORES_PAGE_AIOWAIT { if (dtokp->wstate() == data_tok::DEQ_PART) cont = true; else { std::ostringstream oss; oss << "This data_tok: id=" << dtokp->id() << " state=" << dtokp->wstate_str(); throw jexception(jerrno::JERR_WMGR_DEQDISCONT, oss.str(), "wmgr", "dequeue"); } } const bool ext_rid = dtokp->external_rid(); u_int64_t rid = (ext_rid | cont) ? dtokp->rid() : _wrfc.get_incr_rid(); u_int64_t dequeue_rid = (ext_rid | cont) ? dtokp->dequeue_rid() : dtokp->rid(); _deq_rec.reset(rid, dequeue_rid, xid_ptr, xid_len, _wrfc.owi(), txn_coml_commit); if (!cont) { if (!ext_rid) { dtokp->set_rid(rid); dtokp->set_dequeue_rid(dequeue_rid); } if (xid_len) dtokp->set_xid(xid_ptr, xid_len); else dtokp->clear_xid(); dequeue_check(dtokp->xid(), dequeue_rid); dtokp->set_dblocks_written(0); // Reset dblks_written from previous op _deq_busy = true; } bool done = false; while (!done) { assert(_pg_offset_dblks < _cache_pgsize_sblks * JRNL_SBLK_SIZE); void* wptr = (void*)((char*)_page_ptr_arr[_pg_index] + _pg_offset_dblks * JRNL_DBLK_SIZE); u_int32_t data_offs_dblks = dtokp->dblocks_written(); u_int32_t ret = _deq_rec.encode(wptr, data_offs_dblks, (_cache_pgsize_sblks * JRNL_SBLK_SIZE) - _pg_offset_dblks); // Remember fid which contains the record header in case record is split over several files if (data_offs_dblks == 0) dtokp->set_fid(_wrfc.index()); _pg_offset_dblks += ret; _cached_offset_dblks += ret; dtokp->incr_dblocks_written(ret); dtokp->incr_pg_cnt(); _page_cb_arr[_pg_index]._pdtokl->push_back(dtokp); // Is the encoding of this record complete? if (dtokp->dblocks_written() >= _deq_rec.rec_size_dblks()) { // TODO: Incorrect - must set state to ENQ_CACHED; ENQ_SUBM is set when AIO returns. dtokp->set_wstate(data_tok::DEQ_SUBM); if (xid_len) // If part of transaction, add to transaction map { // If the enqueue is part of a pending txn, it will not yet be in emap _emap.lock(dequeue_rid); // ignore rid not found error std::string xid((char*)xid_ptr, xid_len); _tmap.insert_txn_data(xid, txn_data(rid, dequeue_rid, dtokp->fid(), false)); } else { int16_t fid = _emap.get_remove_pfid(dtokp->dequeue_rid()); if (fid < enq_map::EMAP_OK) // fail { if (fid == enq_map::EMAP_RID_NOT_FOUND) { std::ostringstream oss; oss << std::hex << "rid=0x" << rid; throw jexception(jerrno::JERR_MAP_NOTFOUND, oss.str(), "wmgr", "dequeue"); } if (fid == enq_map::EMAP_LOCKED) { std::ostringstream oss; oss << std::hex << "rid=0x" << rid; throw jexception(jerrno::JERR_MAP_LOCKED, oss.str(), "wmgr", "dequeue"); } } _wrfc.decr_enqcnt(fid); } done = true; } else dtokp->set_wstate(data_tok::DEQ_PART); file_header_check(rid, cont, _deq_rec.rec_size_dblks() - data_offs_dblks); flush_check(res, cont, done); } if (dtokp->wstate() >= data_tok::DEQ_SUBM) _deq_busy = false; return res; } iores wmgr::abort(data_tok* dtokp, const void* const xid_ptr, const std::size_t xid_len) { // commit and abort MUST have a valid xid assert(xid_ptr != 0 && xid_len > 0); if (_enq_busy || _deq_busy || _commit_busy) return RHM_IORES_BUSY; iores res = pre_write_check(WMGR_ABORT, dtokp); if (res != RHM_IORES_SUCCESS) return res; bool cont = false; if (_abort_busy) // If abort() exited last time with RHM_IORES_FULL or RHM_IORES_PAGE_AIOWAIT { if (dtokp->wstate() == data_tok::ABORT_PART) cont = true; else { std::ostringstream oss; oss << "This data_tok: id=" << dtokp->id() << " state=" << dtokp->wstate_str(); throw jexception(jerrno::JERR_WMGR_DEQDISCONT, oss.str(), "wmgr", "abort"); } } u_int64_t rid = (dtokp->external_rid() | cont) ? dtokp->rid() : _wrfc.get_incr_rid(); _txn_rec.reset(RHM_JDAT_TXA_MAGIC, rid, xid_ptr, xid_len, _wrfc.owi()); if (!cont) { dtokp->set_rid(rid); dtokp->set_dequeue_rid(0); dtokp->set_xid(xid_ptr, xid_len); dtokp->set_dblocks_written(0); // Reset dblks_written from previous op _abort_busy = true; } bool done = false; while (!done) { assert(_pg_offset_dblks < _cache_pgsize_sblks * JRNL_SBLK_SIZE); void* wptr = (void*)((char*)_page_ptr_arr[_pg_index] + _pg_offset_dblks * JRNL_DBLK_SIZE); u_int32_t data_offs_dblks = dtokp->dblocks_written(); u_int32_t ret = _txn_rec.encode(wptr, data_offs_dblks, (_cache_pgsize_sblks * JRNL_SBLK_SIZE) - _pg_offset_dblks); // Remember fid which contains the record header in case record is split over several files if (data_offs_dblks == 0) dtokp->set_fid(_wrfc.index()); _pg_offset_dblks += ret; _cached_offset_dblks += ret; dtokp->incr_dblocks_written(ret); dtokp->incr_pg_cnt(); _page_cb_arr[_pg_index]._pdtokl->push_back(dtokp); // Is the encoding of this record complete? if (dtokp->dblocks_written() >= _txn_rec.rec_size_dblks()) { dtokp->set_wstate(data_tok::ABORT_SUBM); // Delete this txn from tmap, unlock any locked records in emap std::string xid((char*)xid_ptr, xid_len); txn_data_list tdl = _tmap.get_remove_tdata_list(xid); // tdl will be empty if xid not found for (tdl_itr itr = tdl.begin(); itr != tdl.end(); itr++) { if (!itr->_enq_flag) _emap.unlock(itr->_drid); // ignore rid not found error if (itr->_enq_flag) _wrfc.decr_enqcnt(itr->_pfid); } std::pair::iterator, bool> res = _txn_pending_set.insert(xid); if (!res.second) { std::ostringstream oss; oss << std::hex << "_txn_pending_set: xid=\"" << xid << "\""; throw jexception(jerrno::JERR_MAP_DUPLICATE, oss.str(), "wmgr", "abort"); } done = true; } else dtokp->set_wstate(data_tok::ABORT_PART); file_header_check(rid, cont, _txn_rec.rec_size_dblks() - data_offs_dblks); flush_check(res, cont, done); } if (dtokp->wstate() >= data_tok::ABORT_SUBM) _abort_busy = false; return res; } iores wmgr::commit(data_tok* dtokp, const void* const xid_ptr, const std::size_t xid_len) { // commit and abort MUST have a valid xid assert(xid_ptr != 0 && xid_len > 0); if (_enq_busy || _deq_busy || _abort_busy) return RHM_IORES_BUSY; iores res = pre_write_check(WMGR_COMMIT, dtokp); if (res != RHM_IORES_SUCCESS) return res; bool cont = false; if (_commit_busy) // If commit() exited last time with RHM_IORES_FULL or RHM_IORES_PAGE_AIOWAIT { if (dtokp->wstate() == data_tok::COMMIT_PART) cont = true; else { std::ostringstream oss; oss << "This data_tok: id=" << dtokp->id() << " state=" << dtokp->wstate_str(); throw jexception(jerrno::JERR_WMGR_DEQDISCONT, oss.str(), "wmgr", "commit"); } } u_int64_t rid = (dtokp->external_rid() | cont) ? dtokp->rid() : _wrfc.get_incr_rid(); _txn_rec.reset(RHM_JDAT_TXC_MAGIC, rid, xid_ptr, xid_len, _wrfc.owi()); if (!cont) { dtokp->set_rid(rid); dtokp->set_dequeue_rid(0); dtokp->set_xid(xid_ptr, xid_len); dtokp->set_dblocks_written(0); // Reset dblks_written from previous op _commit_busy = true; } bool done = false; while (!done) { assert(_pg_offset_dblks < _cache_pgsize_sblks * JRNL_SBLK_SIZE); void* wptr = (void*)((char*)_page_ptr_arr[_pg_index] + _pg_offset_dblks * JRNL_DBLK_SIZE); u_int32_t data_offs_dblks = dtokp->dblocks_written(); u_int32_t ret = _txn_rec.encode(wptr, data_offs_dblks, (_cache_pgsize_sblks * JRNL_SBLK_SIZE) - _pg_offset_dblks); // Remember fid which contains the record header in case record is split over several files if (data_offs_dblks == 0) dtokp->set_fid(_wrfc.index()); _pg_offset_dblks += ret; _cached_offset_dblks += ret; dtokp->incr_dblocks_written(ret); dtokp->incr_pg_cnt(); _page_cb_arr[_pg_index]._pdtokl->push_back(dtokp); // Is the encoding of this record complete? if (dtokp->dblocks_written() >= _txn_rec.rec_size_dblks()) { dtokp->set_wstate(data_tok::COMMIT_SUBM); // Delete this txn from tmap, process records into emap std::string xid((char*)xid_ptr, xid_len); txn_data_list tdl = _tmap.get_remove_tdata_list(xid); // tdl will be empty if xid not found for (tdl_itr itr = tdl.begin(); itr != tdl.end(); itr++) { if (itr->_enq_flag) // txn enqueue { if (_emap.insert_pfid(itr->_rid, itr->_pfid) < enq_map::EMAP_OK) // fail { // The only error code emap::insert_pfid() returns is enq_map::EMAP_DUP_RID. std::ostringstream oss; oss << std::hex << "rid=0x" << itr->_rid << " _pfid=0x" << itr->_pfid; throw jexception(jerrno::JERR_MAP_DUPLICATE, oss.str(), "wmgr", "commit"); } } else // txn dequeue { int16_t fid = _emap.get_remove_pfid(itr->_drid, true); if (fid < enq_map::EMAP_OK) // fail { if (fid == enq_map::EMAP_RID_NOT_FOUND) { std::ostringstream oss; oss << std::hex << "rid=0x" << rid; throw jexception(jerrno::JERR_MAP_NOTFOUND, oss.str(), "wmgr", "dequeue"); } if (fid == enq_map::EMAP_LOCKED) { std::ostringstream oss; oss << std::hex << "rid=0x" << rid; throw jexception(jerrno::JERR_MAP_LOCKED, oss.str(), "wmgr", "dequeue"); } } _wrfc.decr_enqcnt(fid); } } std::pair::iterator, bool> res = _txn_pending_set.insert(xid); if (!res.second) { std::ostringstream oss; oss << std::hex << "_txn_pending_set: xid=\"" << xid << "\""; throw jexception(jerrno::JERR_MAP_DUPLICATE, oss.str(), "wmgr", "commit"); } done = true; } else dtokp->set_wstate(data_tok::COMMIT_PART); file_header_check(rid, cont, _txn_rec.rec_size_dblks() - data_offs_dblks); flush_check(res, cont, done); } if (dtokp->wstate() >= data_tok::COMMIT_SUBM) _commit_busy = false; return res; } void wmgr::file_header_check(const u_int64_t rid, const bool cont, const u_int32_t rec_dblks_rem) { // Has the file header been written (i.e. write pointers still at 0)? if (_wrfc.is_void()) { bool file_fit = rec_dblks_rem <= _jfsize_dblks; bool file_full = rec_dblks_rem == _jfsize_dblks; std::size_t fro = 0; if (cont) { if (file_fit && !file_full) fro = (rec_dblks_rem + JRNL_SBLK_SIZE) * JRNL_DBLK_SIZE; } else fro = JRNL_SBLK_SIZE * JRNL_DBLK_SIZE; write_fhdr(rid, _wrfc.index(), _wrfc.index(), fro); } } void wmgr::flush_check(iores& res, bool& cont, bool& done) { // Is page is full, flush if (_pg_offset_dblks >= _cache_pgsize_sblks * JRNL_SBLK_SIZE) { res = write_flush(); assert(res == RHM_IORES_SUCCESS); if (_page_cb_arr[_pg_index]._state == AIO_PENDING && !done) { res = RHM_IORES_PAGE_AIOWAIT; done = true; } // If file is full, rotate to next file if (_pg_cntr >= _jfsize_pgs) { iores rfres = rotate_file(); if (rfres != RHM_IORES_SUCCESS) res = rfres; if (!done) { if (rfres == RHM_IORES_SUCCESS) cont = true; else done = true; } } } } iores wmgr::flush() { iores res = write_flush(); if (_pg_cntr >= _jfsize_pgs) { iores rfres = rotate_file(); if (rfres != RHM_IORES_SUCCESS) res = rfres; } return res; } iores wmgr::write_flush() { iores res = RHM_IORES_SUCCESS; // Don't bother flushing an empty page or one that is still in state AIO_PENDING if (_cached_offset_dblks) { if (_page_cb_arr[_pg_index]._state == AIO_PENDING) res = RHM_IORES_PAGE_AIOWAIT; else { if (_page_cb_arr[_pg_index]._state != IN_USE) { std::ostringstream oss; oss << "pg_index=" << _pg_index << " state=" << _page_cb_arr[_pg_index].state_str(); throw jexception(jerrno::JERR_WMGR_BADPGSTATE, oss.str(), "wmgr", "write_flush"); } // Send current page using AIO // In manual flushes, dblks may not coincide with sblks, add filler records ("RHMx") // if necessary. dblk_roundup(); std::size_t pg_offs = (_pg_offset_dblks - _cached_offset_dblks) * JRNL_DBLK_SIZE; aio_cb* aiocbp = &_aio_cb_arr[_pg_index]; aio::prep_pwrite_2(aiocbp, _wrfc.fh(), (char*)_page_ptr_arr[_pg_index] + pg_offs, _cached_offset_dblks * JRNL_DBLK_SIZE, _wrfc.subm_offs()); page_cb* pcbp = (page_cb*)(aiocbp->data); // This page control block (pcb) pcbp->_wdblks = _cached_offset_dblks; pcbp->_wfh = _wrfc.file_controller(); if (aio::submit(_ioctx, 1, &aiocbp) < 0) throw jexception(jerrno::JERR__AIO, "wmgr", "write_flush"); _wrfc.add_subm_cnt_dblks(_cached_offset_dblks); _wrfc.incr_aio_cnt(); _aio_evt_rem++; _cached_offset_dblks = 0; _jc->instr_incr_outstanding_aio_cnt(); rotate_page(); // increments _pg_index, resets _pg_offset_dblks if req'd if (_page_cb_arr[_pg_index]._state == UNUSED) _page_cb_arr[_pg_index]._state = IN_USE; } } get_events(UNUSED, 0); if (_page_cb_arr[_pg_index]._state == UNUSED) _page_cb_arr[_pg_index]._state = IN_USE; return res; } iores wmgr::rotate_file() { _pg_cntr = 0; iores res = _wrfc.rotate(); _jc->chk_wr_frot(); return res; } int32_t wmgr::get_events(page_state state, timespec* const timeout, bool flush) { if (_aio_evt_rem == 0) // no events to get return 0; int ret = 0; if ((ret = aio::getevents(_ioctx, flush ? _aio_evt_rem : 1, _aio_evt_rem/*_cache_num_pages + _jc->num_jfiles()*/, _aio_event_arr, timeout)) < 0) { if (ret == -EINTR) // Interrupted by signal return 0; std::ostringstream oss; oss << "io_getevents() failed: " << std::strerror(-ret) << " (" << ret << ")"; throw jexception(jerrno::JERR__AIO, oss.str(), "wmgr", "get_events"); } if (ret == 0 && timeout) return jerrno::AIO_TIMEOUT; int32_t tot_data_toks = 0; for (int i=0; idata); // This page control block (pcb) long aioret = (long)_aio_event_arr[i].res; if (aioret < 0) { std::ostringstream oss; oss << "AIO write operation failed: " << std::strerror(-aioret) << " (" << aioret << ") ["; if (pcbp) oss << "pg=" << pcbp->_index; else { file_hdr* fhp = (file_hdr*)aiocbp->u.c.buf; oss << "fid=" << fhp->_pfid; } oss << " size=" << aiocbp->u.c.nbytes; oss << " offset=" << aiocbp->u.c.offset << " fh=" << aiocbp->aio_fildes << "]"; throw jexception(jerrno::JERR__AIO, oss.str(), "wmgr", "get_events"); } if (pcbp) // Page writes have pcb { u_int32_t s = pcbp->_pdtokl->size(); std::vector dtokl; dtokl.reserve(s); for (u_int32_t k=0; k_pdtokl->at(k); if (dtokp->decr_pg_cnt() == 0) { std::set::iterator it; switch (dtokp->wstate()) { case data_tok::ENQ_SUBM: dtokl.push_back(dtokp); tot_data_toks++; dtokp->set_wstate(data_tok::ENQ); if (dtokp->has_xid()) // Ignoring return value here. A non-zero return can signify that the transaction // has committed or aborted, and which was completed prior to the aio returning. _tmap.set_aio_compl(dtokp->xid(), dtokp->rid()); break; case data_tok::DEQ_SUBM: dtokl.push_back(dtokp); tot_data_toks++; dtokp->set_wstate(data_tok::DEQ); if (dtokp->has_xid()) // Ignoring return value - see note above. _tmap.set_aio_compl(dtokp->xid(), dtokp->rid()); break; case data_tok::ABORT_SUBM: dtokl.push_back(dtokp); tot_data_toks++; dtokp->set_wstate(data_tok::ABORTED); it = _txn_pending_set.find(dtokp->xid()); if (it == _txn_pending_set.end()) { std::ostringstream oss; oss << std::hex << "_txn_pending_set: abort xid=\""; oss << dtokp->xid() << "\""; throw jexception(jerrno::JERR_MAP_NOTFOUND, oss.str(), "wmgr", "get_events"); } _txn_pending_set.erase(it); break; case data_tok::COMMIT_SUBM: dtokl.push_back(dtokp); tot_data_toks++; dtokp->set_wstate(data_tok::COMMITTED); it = _txn_pending_set.find(dtokp->xid()); if (it == _txn_pending_set.end()) { std::ostringstream oss; oss << std::hex << "_txn_pending_set: commit xid=\""; oss << dtokp->xid() << "\""; throw jexception(jerrno::JERR_MAP_NOTFOUND, oss.str(), "wmgr", "get_events"); } _txn_pending_set.erase(it); break; case data_tok::ENQ_PART: case data_tok::DEQ_PART: case data_tok::ABORT_PART: case data_tok::COMMIT_PART: // ignore these break; default: // throw for anything else std::ostringstream oss; oss << "dtok_id=" << dtokp->id() << " dtok_state=" << dtokp->wstate_str(); throw jexception(jerrno::JERR_WMGR_BADDTOKSTATE, oss.str(), "wmgr", "get_events"); } // switch } // if } // for // Increment the completed write offset // NOTE: We cannot use _wrfc here, as it may have rotated since submitting count. // Use stored pointer to fcntl in the pcb instead. pcbp->_wfh->add_wr_cmpl_cnt_dblks(pcbp->_wdblks); pcbp->_wfh->decr_aio_cnt(); _jc->instr_decr_outstanding_aio_cnt(); // Clean up this pcb's data_tok list pcbp->_pdtokl->clear(); pcbp->_state = state; // Perform AIO return callback if (_cbp && tot_data_toks) _cbp->wr_aio_cb(dtokl); } else // File header writes have no pcb { // get lfid from original file header record, update info for that lfid file_hdr* fhp = (file_hdr*)aiocbp->u.c.buf; u_int32_t lfid = fhp->_lfid; fcntl* fcntlp = _jc->get_fcntlp(lfid); fcntlp->add_wr_cmpl_cnt_dblks(JRNL_SBLK_SIZE); fcntlp->decr_aio_cnt(); fcntlp->set_wr_fhdr_aio_outstanding(false); } } return tot_data_toks; } bool wmgr::is_txn_synced(const std::string& xid) { // Ignore xid not found error here if (_tmap.is_txn_synced(xid) == txn_map::TMAP_NOT_SYNCED) return false; // Check for outstanding commit/aborts std::set::iterator it = _txn_pending_set.find(xid); return it == _txn_pending_set.end(); } void wmgr::initialize(aio_callback* const cbp, const u_int32_t wcache_pgsize_sblks, const u_int16_t wcache_num_pages) { pmgr::initialize(cbp, wcache_pgsize_sblks, wcache_num_pages); wmgr::clean(); _num_jfiles = _jc->num_jfiles(); if (::posix_memalign(&_fhdr_base_ptr, _sblksize, _sblksize * _num_jfiles)) { wmgr::clean(); std::ostringstream oss; oss << "posix_memalign(): blksize=" << _sblksize << " size=" << _sblksize; oss << FORMAT_SYSERR(errno); throw jexception(jerrno::JERR__MALLOC, oss.str(), "wmgr", "initialize"); } _fhdr_ptr_arr = (void**)std::malloc(_num_jfiles * sizeof(void*)); MALLOC_CHK(_fhdr_ptr_arr, "_fhdr_ptr_arr", "wmgr", "initialize"); _fhdr_aio_cb_arr = (aio_cb**)std::malloc(sizeof(aio_cb*) * _num_jfiles); MALLOC_CHK(_fhdr_aio_cb_arr, "_fhdr_aio_cb_arr", "wmgr", "initialize"); std::memset(_fhdr_aio_cb_arr, 0, sizeof(aio_cb*) * _num_jfiles); for (u_int16_t i=0; i<_num_jfiles; i++) { _fhdr_ptr_arr[i] = (void*)((char*)_fhdr_base_ptr + _sblksize * i); _fhdr_aio_cb_arr[i] = new aio_cb; } _page_cb_arr[0]._state = IN_USE; _ddtokl.clear(); _cached_offset_dblks = 0; _enq_busy = false; } iores wmgr::pre_write_check(const _op_type op, const data_tok* const dtokp, const std::size_t xidsize, const std::size_t dsize, const bool external ) const { // Check status of current file if (!_wrfc.is_wr_reset()) { if (!_wrfc.wr_reset()) return RHM_IORES_FULL; } // Check status of current page is ok for writing if (_page_cb_arr[_pg_index]._state != IN_USE) { if (_page_cb_arr[_pg_index]._state == UNUSED) _page_cb_arr[_pg_index]._state = IN_USE; else if (_page_cb_arr[_pg_index]._state == AIO_PENDING) return RHM_IORES_PAGE_AIOWAIT; else { std::ostringstream oss; oss << "jrnl=" << _jc->id() << " op=" << _op_str[op]; oss << " index=" << _pg_index << " pg_state=" << _page_cb_arr[_pg_index].state_str(); throw jexception(jerrno::JERR_WMGR_BADPGSTATE, oss.str(), "wmgr", "pre_write_check"); } } // operation-specific checks switch (op) { case WMGR_ENQUEUE: { // Check for enqueue reaching cutoff threshold u_int32_t size_dblks = jrec::size_dblks(enq_rec::rec_size(xidsize, dsize, external)); if (!_enq_busy && _wrfc.enq_threshold(_cached_offset_dblks + size_dblks)) return RHM_IORES_ENQCAPTHRESH; if (!dtokp->is_writable()) { std::ostringstream oss; oss << "jrnl=" << _jc->id() << " op=" << _op_str[op]; oss << " dtok_id=" << dtokp->id() << " dtok_state=" << dtokp->wstate_str(); throw jexception(jerrno::JERR_WMGR_BADDTOKSTATE, oss.str(), "wmgr", "pre_write_check"); } } break; case WMGR_DEQUEUE: if (!dtokp->is_dequeueable()) { std::ostringstream oss; oss << "jrnl=" << _jc->id() << " op=" << _op_str[op]; oss << " dtok_id=" << dtokp->id() << " dtok_state=" << dtokp->wstate_str(); throw jexception(jerrno::JERR_WMGR_BADDTOKSTATE, oss.str(), "wmgr", "pre_write_check"); } break; case WMGR_ABORT: break; case WMGR_COMMIT: break; } return RHM_IORES_SUCCESS; } void wmgr::dequeue_check(const std::string& xid, const u_int64_t drid) { // First check emap bool found = false; int16_t fid = _emap.get_pfid(drid); if (fid < enq_map::EMAP_OK) // fail { if (fid == enq_map::EMAP_RID_NOT_FOUND) { if (xid.size()) found = _tmap.data_exists(xid, drid); } else if (fid == enq_map::EMAP_LOCKED) { std::ostringstream oss; oss << std::hex << "drid=0x" << drid; throw jexception(jerrno::JERR_MAP_LOCKED, oss.str(), "wmgr", "dequeue_check"); } } else found = true; if (!found) { std::ostringstream oss; oss << "jrnl=" << _jc->id() << " drid=0x" << std::hex << drid; throw jexception(jerrno::JERR_WMGR_DEQRIDNOTENQ, oss.str(), "wmgr", "dequeue_check"); } } void wmgr::dblk_roundup() { const u_int32_t xmagic = RHM_JDAT_EMPTY_MAGIC; u_int32_t wdblks = jrec::size_blks(_cached_offset_dblks, JRNL_SBLK_SIZE) * JRNL_SBLK_SIZE; while (_cached_offset_dblks < wdblks) { void* wptr = (void*)((char*)_page_ptr_arr[_pg_index] + _pg_offset_dblks * JRNL_DBLK_SIZE); std::memcpy(wptr, (void*)&xmagic, sizeof(xmagic)); #ifdef RHM_CLEAN std::memset((char*)wptr + sizeof(xmagic), RHM_CLEAN_CHAR, JRNL_DBLK_SIZE - sizeof(xmagic)); #endif _pg_offset_dblks++; _cached_offset_dblks++; } } void wmgr::write_fhdr(u_int64_t rid, u_int16_t fid, u_int16_t lid, std::size_t fro) { file_hdr fhdr(RHM_JDAT_FILE_MAGIC, RHM_JDAT_VERSION, rid, fid, lid, fro, _wrfc.owi(), true); std::memcpy(_fhdr_ptr_arr[fid], &fhdr, sizeof(fhdr)); #ifdef RHM_CLEAN std::memset((char*)_fhdr_ptr_arr[fid] + sizeof(fhdr), RHM_CLEAN_CHAR, _sblksize - sizeof(fhdr)); #endif aio_cb* aiocbp = _fhdr_aio_cb_arr[fid]; aio::prep_pwrite(aiocbp, _wrfc.fh(), _fhdr_ptr_arr[fid], _sblksize, 0); if (aio::submit(_ioctx, 1, &aiocbp) < 0) throw jexception(jerrno::JERR__AIO, "wmgr", "write_fhdr"); _aio_evt_rem++; _wrfc.add_subm_cnt_dblks(JRNL_SBLK_SIZE); _wrfc.incr_aio_cnt(); _wrfc.file_controller()->set_wr_fhdr_aio_outstanding(true); } void wmgr::rotate_page() { _page_cb_arr[_pg_index]._state = AIO_PENDING; if (_pg_offset_dblks >= _cache_pgsize_sblks * JRNL_SBLK_SIZE) { _pg_offset_dblks = 0; _pg_cntr++; } if (++_pg_index >= _cache_num_pages) _pg_index = 0; } void wmgr::clean() { std::free(_fhdr_base_ptr); _fhdr_base_ptr = 0; std::free(_fhdr_ptr_arr); _fhdr_ptr_arr = 0; if (_fhdr_aio_cb_arr) { for (u_int32_t i=0; i<_num_jfiles; i++) delete _fhdr_aio_cb_arr[i]; std::free(_fhdr_aio_cb_arr); _fhdr_aio_cb_arr = 0; } } const std::string wmgr::status_str() const { std::ostringstream oss; oss << "wmgr: pi=" << _pg_index << " pc=" << _pg_cntr; oss << " po=" << _pg_offset_dblks << " aer=" << _aio_evt_rem; oss << " edac:" << (_enq_busy?"T":"F") << (_deq_busy?"T":"F"); oss << (_abort_busy?"T":"F") << (_commit_busy?"T":"F"); oss << " ps=["; for (int i=0; i<_cache_num_pages; i++) { switch (_page_cb_arr[i]._state) { case UNUSED: oss << "-"; break; case IN_USE: oss << "U"; break; case AIO_PENDING: oss << "A"; break; case AIO_COMPLETE: oss << "*"; break; default: oss << _page_cb_arr[i]._state; } } oss << "] " << _wrfc.status_str(); return oss.str(); } // static const char* wmgr::_op_str[] = {"enqueue", "dequeue", "abort", "commit"}; } // namespace journal } // namespace mrg qpid-cpp-store-debian-0.16/lib/jrnl/jcntl.cpp0000644000176300017630000010117611733102340020064 0ustar cajuscajus/** * \file jcntl.cpp * * Qpid asynchronous store plugin library * * Messaging journal top-level control and interface class * mrg::journal::jcntl. See comments in file jcntl.hpp for details. * * \author Kim van der Riet * * Copyright (c) 2007, 2008, 2009, 2010 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #include "jrnl/jcntl.hpp" #include #include #include #include #include #include #include #include #include "jrnl/file_hdr.hpp" #include "jrnl/jerrno.hpp" #include "jrnl/jinf.hpp" #include #include #include namespace mrg { namespace journal { #define AIO_CMPL_TIMEOUT_SEC 5 #define AIO_CMPL_TIMEOUT_NSEC 0 #define FINAL_AIO_CMPL_TIMEOUT_SEC 15 #define FINAL_AIO_CMPL_TIMEOUT_NSEC 0 // Static timespec jcntl::_aio_cmpl_timeout; ///< Timeout for blocking libaio returns timespec jcntl::_final_aio_cmpl_timeout; ///< Timeout for blocking libaio returns when stopping or finalizing bool jcntl::_init = init_statics(); bool jcntl::init_statics() { _aio_cmpl_timeout.tv_sec = AIO_CMPL_TIMEOUT_SEC; _aio_cmpl_timeout.tv_nsec = AIO_CMPL_TIMEOUT_NSEC; _final_aio_cmpl_timeout.tv_sec = FINAL_AIO_CMPL_TIMEOUT_SEC; _final_aio_cmpl_timeout.tv_nsec = FINAL_AIO_CMPL_TIMEOUT_NSEC; return true; } // Functions jcntl::jcntl(const std::string& jid, const std::string& jdir, const std::string& base_filename): _jid(jid), _jdir(jdir, base_filename), _base_filename(base_filename), _init_flag(false), _stop_flag(false), _readonly_flag(false), _autostop(true), _jfsize_sblks(0), _lpmgr(), _emap(), _tmap(), _rrfc(&_lpmgr), _wrfc(&_lpmgr), _rmgr(this, _emap, _tmap, _rrfc), _wmgr(this, _emap, _tmap, _wrfc), _rcvdat() {} jcntl::~jcntl() { if (_init_flag && !_stop_flag) try { stop(true); } catch (const jexception& e) { std::cerr << e << std::endl; } _lpmgr.finalize(); } void jcntl::initialize(const u_int16_t num_jfiles, const bool ae, const u_int16_t ae_max_jfiles, const u_int32_t jfsize_sblks, const u_int16_t wcache_num_pages, const u_int32_t wcache_pgsize_sblks, aio_callback* const cbp) { _init_flag = false; _stop_flag = false; _readonly_flag = false; _emap.clear(); _tmap.clear(); _lpmgr.finalize(); // Set new file geometry parameters assert(num_jfiles >= JRNL_MIN_NUM_FILES); assert(num_jfiles <= JRNL_MAX_NUM_FILES); _emap.set_num_jfiles(num_jfiles); _tmap.set_num_jfiles(num_jfiles); assert(jfsize_sblks >= JRNL_MIN_FILE_SIZE); assert(jfsize_sblks <= JRNL_MAX_FILE_SIZE); _jfsize_sblks = jfsize_sblks; // Clear any existing journal files _jdir.clear_dir(); _lpmgr.initialize(num_jfiles, ae, ae_max_jfiles, this, &new_fcntl); _wrfc.initialize(_jfsize_sblks); _rrfc.initialize(); _rrfc.set_findex(0); _rmgr.initialize(cbp); _wmgr.initialize(cbp, wcache_pgsize_sblks, wcache_num_pages, JRNL_WMGR_MAXDTOKPP, JRNL_WMGR_MAXWAITUS); // Write info file (.jinf) to disk write_infofile(); _init_flag = true; } void jcntl::recover(const u_int16_t num_jfiles, const bool ae, const u_int16_t ae_max_jfiles, const u_int32_t jfsize_sblks, const u_int16_t wcache_num_pages, const u_int32_t wcache_pgsize_sblks, // const rd_aio_cb rd_cb, const wr_aio_cb wr_cb, const std::vector* prep_txn_list_ptr, aio_callback* const cbp, const std::vector* prep_txn_list_ptr, u_int64_t& highest_rid) { _init_flag = false; _stop_flag = false; _readonly_flag = false; _emap.clear(); _tmap.clear(); _lpmgr.finalize(); assert(num_jfiles >= JRNL_MIN_NUM_FILES); assert(num_jfiles <= JRNL_MAX_NUM_FILES); assert(jfsize_sblks >= JRNL_MIN_FILE_SIZE); assert(jfsize_sblks <= JRNL_MAX_FILE_SIZE); _jfsize_sblks = jfsize_sblks; // Verify journal dir and journal files _jdir.verify_dir(); _rcvdat.reset(num_jfiles, ae, ae_max_jfiles); rcvr_janalyze(_rcvdat, prep_txn_list_ptr); highest_rid = _rcvdat._h_rid; if (_rcvdat._jfull) throw jexception(jerrno::JERR_JCNTL_RECOVERJFULL, "jcntl", "recover"); this->log(LOG_DEBUG, _rcvdat.to_log(_jid)); _lpmgr.recover(_rcvdat, this, &new_fcntl); _wrfc.initialize(_jfsize_sblks, &_rcvdat); _rrfc.initialize(); _rrfc.set_findex(_rcvdat.ffid()); _rmgr.initialize(cbp); _wmgr.initialize(cbp, wcache_pgsize_sblks, wcache_num_pages, JRNL_WMGR_MAXDTOKPP, JRNL_WMGR_MAXWAITUS, (_rcvdat._lffull ? 0 : _rcvdat._eo)); _readonly_flag = true; _init_flag = true; } void jcntl::recover_complete() { if (!_readonly_flag) throw jexception(jerrno::JERR_JCNTL_NOTRECOVERED, "jcntl", "recover_complete"); for (u_int16_t i=0; i<_lpmgr.num_jfiles(); i++) _lpmgr.get_fcntlp(i)->reset(&_rcvdat); _wrfc.initialize(_jfsize_sblks, &_rcvdat); _rrfc.initialize(); _rrfc.set_findex(_rcvdat.ffid()); _rmgr.recover_complete(); _readonly_flag = false; } void jcntl::delete_jrnl_files() { stop(true); // wait for AIO to complete _jdir.delete_dir(); } iores jcntl::enqueue_data_record(const void* const data_buff, const std::size_t tot_data_len, const std::size_t this_data_len, data_tok* dtokp, const bool transient) { iores r; check_wstatus("enqueue_data_record"); { slock s(_wr_mutex); while (handle_aio_wait(_wmgr.enqueue(data_buff, tot_data_len, this_data_len, dtokp, 0, 0, transient, false), r, dtokp)) ; } return r; } iores jcntl::enqueue_extern_data_record(const std::size_t tot_data_len, data_tok* dtokp, const bool transient) { iores r; check_wstatus("enqueue_extern_data_record"); { slock s(_wr_mutex); while (handle_aio_wait(_wmgr.enqueue(0, tot_data_len, 0, dtokp, 0, 0, transient, true), r, dtokp)) ; } return r; } iores jcntl::enqueue_txn_data_record(const void* const data_buff, const std::size_t tot_data_len, const std::size_t this_data_len, data_tok* dtokp, const std::string& xid, const bool transient) { iores r; check_wstatus("enqueue_tx_data_record"); { slock s(_wr_mutex); while (handle_aio_wait(_wmgr.enqueue(data_buff, tot_data_len, this_data_len, dtokp, xid.data(), xid.size(), transient, false), r, dtokp)) ; } return r; } iores jcntl::enqueue_extern_txn_data_record(const std::size_t tot_data_len, data_tok* dtokp, const std::string& xid, const bool transient) { iores r; check_wstatus("enqueue_extern_txn_data_record"); { slock s(_wr_mutex); while (handle_aio_wait(_wmgr.enqueue(0, tot_data_len, 0, dtokp, xid.data(), xid.size(), transient, true), r, dtokp)) ; } return r; } /* TODO iores jcntl::get_data_record(const u_int64_t& rid, const std::size_t& dsize, const std::size_t& dsize_avail, const void** const data, bool auto_discard) { check_rstatus("get_data_record"); return _rmgr.get(rid, dsize, dsize_avail, data, auto_discard); } */ /* TODO iores jcntl::discard_data_record(data_tok* const dtokp) { check_rstatus("discard_data_record"); return _rmgr.discard(dtokp); } */ iores jcntl::read_data_record(void** const datapp, std::size_t& dsize, void** const xidpp, std::size_t& xidsize, bool& transient, bool& external, data_tok* const dtokp, bool ignore_pending_txns) { check_rstatus("read_data"); iores res = _rmgr.read(datapp, dsize, xidpp, xidsize, transient, external, dtokp, ignore_pending_txns); if (res == RHM_IORES_RCINVALID) { get_wr_events(0); // check for outstanding write events iores sres = _rmgr.synchronize(); // flushes all outstanding read events if (sres != RHM_IORES_SUCCESS) return sres; _rmgr.wait_for_validity(&_aio_cmpl_timeout, true); // throw if timeout occurs res = _rmgr.read(datapp, dsize, xidpp, xidsize, transient, external, dtokp, ignore_pending_txns); } return res; } iores jcntl::dequeue_data_record(data_tok* const dtokp, const bool txn_coml_commit) { iores r; check_wstatus("dequeue_data"); { slock s(_wr_mutex); while (handle_aio_wait(_wmgr.dequeue(dtokp, 0, 0, txn_coml_commit), r, dtokp)) ; } return r; } iores jcntl::dequeue_txn_data_record(data_tok* const dtokp, const std::string& xid, const bool txn_coml_commit) { iores r; check_wstatus("dequeue_data"); { slock s(_wr_mutex); while (handle_aio_wait(_wmgr.dequeue(dtokp, xid.data(), xid.size(), txn_coml_commit), r, dtokp)) ; } return r; } iores jcntl::txn_abort(data_tok* const dtokp, const std::string& xid) { iores r; check_wstatus("txn_abort"); { slock s(_wr_mutex); while (handle_aio_wait(_wmgr.abort(dtokp, xid.data(), xid.size()), r, dtokp)) ; } return r; } iores jcntl::txn_commit(data_tok* const dtokp, const std::string& xid) { iores r; check_wstatus("txn_commit"); { slock s(_wr_mutex); while (handle_aio_wait(_wmgr.commit(dtokp, xid.data(), xid.size()), r, dtokp)) ; } return r; } bool jcntl::is_txn_synced(const std::string& xid) { slock s(_wr_mutex); bool res = _wmgr.is_txn_synced(xid); return res; } int32_t jcntl::get_wr_events(timespec* const timeout) { stlock t(_wr_mutex); if (!t.locked()) return jerrno::LOCK_TAKEN; int32_t res = _wmgr.get_events(pmgr::UNUSED, timeout); return res; } int32_t jcntl::get_rd_events(timespec* const timeout) { return _rmgr.get_events(pmgr::AIO_COMPLETE, timeout); } void jcntl::stop(const bool block_till_aio_cmpl) { if (_readonly_flag) check_rstatus("stop"); else check_wstatus("stop"); _stop_flag = true; if (!_readonly_flag) flush(block_till_aio_cmpl); _rrfc.finalize(); _lpmgr.finalize(); } u_int16_t jcntl::get_earliest_fid() { u_int16_t ffid = _wrfc.earliest_index(); u_int16_t fid = _wrfc.index(); while ( _emap.get_enq_cnt(ffid) == 0 && _tmap.get_txn_pfid_cnt(ffid) == 0 && ffid != fid) { if (++ffid >= _lpmgr.num_jfiles()) ffid = 0; } if (!_rrfc.is_active()) _rrfc.set_findex(ffid); return ffid; } iores jcntl::flush(const bool block_till_aio_cmpl) { if (!_init_flag) return RHM_IORES_SUCCESS; if (_readonly_flag) throw jexception(jerrno::JERR_JCNTL_READONLY, "jcntl", "flush"); iores res; { slock s(_wr_mutex); res = _wmgr.flush(); } if (block_till_aio_cmpl) aio_cmpl_wait(); return res; } void jcntl::log(log_level ll, const std::string& log_stmt) const { log(ll, log_stmt.c_str()); } void jcntl::log(log_level ll, const char* const log_stmt) const { if (ll > LOG_INFO) { std::cout << log_level_str(ll) << ": Journal \"" << _jid << "\": " << log_stmt << std::endl; } } void jcntl::chk_wr_frot() { if (_wrfc.index() == _rrfc.index()) _rmgr.invalidate(); } void jcntl::fhdr_wr_sync(const u_int16_t lid) { fcntl* fcntlp = _lpmgr.get_fcntlp(lid); while (fcntlp->wr_fhdr_aio_outstanding()) { if (get_wr_events(&_aio_cmpl_timeout) == jerrno::AIO_TIMEOUT) throw jexception(jerrno::JERR_JCNTL_AIOCMPLWAIT, "jcntl", "fhdr_wr_sync"); } } fcntl* jcntl::new_fcntl(jcntl* const jcp, const u_int16_t lid, const u_int16_t fid, const rcvdat* const rdp) { if (!jcp) return 0; std::ostringstream oss; oss << jcp->jrnl_dir() << "/" << jcp->base_filename(); return new fcntl(oss.str(), fid, lid, jcp->jfsize_sblks(), rdp); } // Protected/Private functions void jcntl::check_wstatus(const char* fn_name) const { if (!_init_flag) throw jexception(jerrno::JERR__NINIT, "jcntl", fn_name); if (_readonly_flag) throw jexception(jerrno::JERR_JCNTL_READONLY, "jcntl", fn_name); if (_stop_flag) throw jexception(jerrno::JERR_JCNTL_STOPPED, "jcntl", fn_name); } void jcntl::check_rstatus(const char* fn_name) const { if (!_init_flag) throw jexception(jerrno::JERR__NINIT, "jcntl", fn_name); if (_stop_flag) throw jexception(jerrno::JERR_JCNTL_STOPPED, "jcntl", fn_name); } void jcntl::write_infofile() const { timespec ts; if (::clock_gettime(CLOCK_REALTIME, &ts)) { std::ostringstream oss; oss << FORMAT_SYSERR(errno); throw jexception(jerrno::JERR__RTCLOCK, oss.str(), "jcntl", "write_infofile"); } jinf ji(_jid, _jdir.dirname(), _base_filename, _lpmgr.num_jfiles(), _lpmgr.is_ae(), _lpmgr.ae_max_jfiles(), _jfsize_sblks, _wmgr.cache_pgsize_sblks(), _wmgr.cache_num_pages(), ts); ji.write(); } void jcntl::aio_cmpl_wait() { //while (_wmgr.get_aio_evt_rem()) while (true) { u_int32_t aer; { slock s(_wr_mutex); aer = _wmgr.get_aio_evt_rem(); } if (aer == 0) break; // no events left if (get_wr_events(&_aio_cmpl_timeout) == jerrno::AIO_TIMEOUT) throw jexception(jerrno::JERR_JCNTL_AIOCMPLWAIT, "jcntl", "aio_cmpl_wait"); } } bool jcntl::handle_aio_wait(const iores res, iores& resout, const data_tok* dtp) { resout = res; if (res == RHM_IORES_PAGE_AIOWAIT) { while (_wmgr.curr_pg_blocked()) { if (_wmgr.get_events(pmgr::UNUSED, &_aio_cmpl_timeout) == jerrno::AIO_TIMEOUT) { std::ostringstream oss; oss << "get_events() returned JERR_JCNTL_AIOCMPLWAIT; wmgr_status: " << _wmgr.status_str(); this->log(LOG_CRITICAL, oss.str()); throw jexception(jerrno::JERR_JCNTL_AIOCMPLWAIT, "jcntl", "handle_aio_wait"); } } return true; } else if (res == RHM_IORES_FILE_AIOWAIT) { while (_wmgr.curr_file_blocked()) { if (_wmgr.get_events(pmgr::UNUSED, &_aio_cmpl_timeout) == jerrno::AIO_TIMEOUT) { std::ostringstream oss; oss << "get_events() returned JERR_JCNTL_AIOCMPLWAIT; wmgr_status: " << _wmgr.status_str(); this->log(LOG_CRITICAL, oss.str()); throw jexception(jerrno::JERR_JCNTL_AIOCMPLWAIT, "jcntl", "handle_aio_wait"); } } _wrfc.wr_reset(); resout = RHM_IORES_SUCCESS; data_tok::write_state ws = dtp->wstate(); return ws == data_tok::ENQ_PART || ws == data_tok::DEQ_PART || ws == data_tok::ABORT_PART || ws == data_tok::COMMIT_PART; } return false; } void jcntl::rcvr_janalyze(rcvdat& rd, const std::vector* prep_txn_list_ptr) { jinf ji(_jdir.dirname() + "/" + _base_filename + "." + JRNL_INFO_EXTENSION, true); // If the number of files does not tie up with the jinf file from the journal being recovered, // use the jinf data. if (rd._njf != ji.num_jfiles()) { std::ostringstream oss; oss << "Recovery found " << ji.num_jfiles() << " files (different from --num-jfiles value of " << rd._njf << ")."; this->log(LOG_WARN, oss.str()); rd._njf = ji.num_jfiles(); _rcvdat._enq_cnt_list.resize(rd._njf); } _emap.set_num_jfiles(rd._njf); _tmap.set_num_jfiles(rd._njf); if (_jfsize_sblks != ji.jfsize_sblks()) { std::ostringstream oss; oss << "Recovery found file size = " << (ji.jfsize_sblks() / JRNL_RMGR_PAGE_SIZE) << " (different from --jfile-size-pgs value of " << (_jfsize_sblks / JRNL_RMGR_PAGE_SIZE) << ")."; this->log(LOG_WARN, oss.str()); _jfsize_sblks = ji.jfsize_sblks(); } if (_jdir.dirname().compare(ji.jdir())) { std::ostringstream oss; oss << "Journal file location change: original = \"" << ji.jdir() << "\"; current = \"" << _jdir.dirname() << "\""; this->log(LOG_WARN, oss.str()); ji.set_jdir(_jdir.dirname()); } try { rd._ffid = ji.get_first_pfid(); rd._lfid = ji.get_last_pfid(); rd._owi = ji.get_initial_owi(); rd._frot = ji.get_frot(); rd._jempty = false; ji.get_normalized_pfid_list(rd._fid_list); // _pfid_list } catch (const jexception& e) { if (e.err_code() != jerrno::JERR_JINF_JDATEMPTY) throw; } // Restore all read and write pointers and transactions if (!rd._jempty) { u_int16_t fid = rd._ffid; std::ifstream ifs; bool lowi = rd._owi; // local copy of owi to be used during analysis while (rcvr_get_next_record(fid, &ifs, lowi, rd)) ; if (ifs.is_open()) ifs.close(); // Remove all txns from tmap that are not in the prepared list if (prep_txn_list_ptr) { std::vector xid_list; _tmap.xid_list(xid_list); for (std::vector::iterator itr = xid_list.begin(); itr != xid_list.end(); itr++) { std::vector::const_iterator pitr = std::find(prep_txn_list_ptr->begin(), prep_txn_list_ptr->end(), *itr); if (pitr == prep_txn_list_ptr->end()) // not found in prepared list { txn_data_list tdl = _tmap.get_remove_tdata_list(*itr); // tdl will be empty if xid not found // Unlock any affected enqueues in emap for (tdl_itr i=tdl.begin(); i_enq_flag) // enq op - decrement enqueue count rd._enq_cnt_list[i->_pfid]--; else if (_emap.is_enqueued(i->_drid, true)) // deq op - unlock enq record { int16_t ret = _emap.unlock(i->_drid); if (ret < enq_map::EMAP_OK) // fail { // enq_map::unlock()'s only error is enq_map::EMAP_RID_NOT_FOUND std::ostringstream oss; oss << std::hex << "_emap.unlock(): drid=0x\"" << i->_drid; throw jexception(jerrno::JERR_MAP_NOTFOUND, oss.str(), "jcntl", "rcvr_janalyze"); } } } } } } // Check for file full condition - add one to _jfsize_sblks to account for file header rd._lffull = rd._eo == (1 + _jfsize_sblks) * JRNL_SBLK_SIZE * JRNL_DBLK_SIZE; // Check for journal full condition u_int16_t next_wr_fid = (rd._lfid + 1) % rd._njf; rd._jfull = rd._ffid == next_wr_fid && rd._enq_cnt_list[next_wr_fid] && rd._lffull; } } bool jcntl::rcvr_get_next_record(u_int16_t& fid, std::ifstream* ifsp, bool& lowi, rcvdat& rd) { std::size_t cum_size_read = 0; void* xidp = 0; rec_hdr h; bool hdr_ok = false; std::streampos file_pos; while (!hdr_ok) { if (!ifsp->is_open()) { if (!jfile_cycle(fid, ifsp, lowi, rd, true)) return false; } file_pos = ifsp->tellg(); ifsp->read((char*)&h, sizeof(rec_hdr)); if (ifsp->gcount() == sizeof(rec_hdr)) hdr_ok = true; else { if (!jfile_cycle(fid, ifsp, lowi, rd, true)) return false; } } switch(h._magic) { case RHM_JDAT_ENQ_MAGIC: { enq_rec er; u_int16_t start_fid = fid; // fid may increment in decode() if record folds over file boundary if (!decode(er, fid, ifsp, cum_size_read, h, lowi, rd, file_pos)) return false; if (!er.is_transient()) // Ignore transient msgs { rd._enq_cnt_list[start_fid]++; if (er.xid_size()) { er.get_xid(&xidp); assert(xidp != 0); std::string xid((char*)xidp, er.xid_size()); _tmap.insert_txn_data(xid, txn_data(h._rid, 0, start_fid, true)); if (_tmap.set_aio_compl(xid, h._rid) < txn_map::TMAP_OK) // fail - xid or rid not found { std::ostringstream oss; oss << std::hex << "_tmap.set_aio_compl: txn_enq xid=\"" << xid << "\" rid=0x" << h._rid; throw jexception(jerrno::JERR_MAP_NOTFOUND, oss.str(), "jcntl", "rcvr_get_next_record"); } std::free(xidp); } else { if (_emap.insert_pfid(h._rid, start_fid) < enq_map::EMAP_OK) // fail { // The only error code emap::insert_pfid() returns is enq_map::EMAP_DUP_RID. std::ostringstream oss; oss << std::hex << "rid=0x" << h._rid << " _pfid=0x" << start_fid; throw jexception(jerrno::JERR_MAP_DUPLICATE, oss.str(), "jcntl", "rcvr_get_next_record"); } } } } break; case RHM_JDAT_DEQ_MAGIC: { deq_rec dr; u_int16_t start_fid = fid; // fid may increment in decode() if record folds over file boundary if (!decode(dr, fid, ifsp, cum_size_read, h, lowi, rd, file_pos)) return false; if (dr.xid_size()) { // If the enqueue is part of a pending txn, it will not yet be in emap _emap.lock(dr.deq_rid()); // ignore not found error dr.get_xid(&xidp); assert(xidp != 0); std::string xid((char*)xidp, dr.xid_size()); _tmap.insert_txn_data(xid, txn_data(dr.rid(), dr.deq_rid(), start_fid, false, dr.is_txn_coml_commit())); if (_tmap.set_aio_compl(xid, dr.rid()) < txn_map::TMAP_OK) // fail - xid or rid not found { std::ostringstream oss; oss << std::hex << "_tmap.set_aio_compl: txn_deq xid=\"" << xid << "\" rid=0x" << dr.rid(); throw jexception(jerrno::JERR_MAP_NOTFOUND, oss.str(), "jcntl", "rcvr_get_next_record"); } std::free(xidp); } else { int16_t enq_fid = _emap.get_remove_pfid(dr.deq_rid(), true); if (enq_fid >= enq_map::EMAP_OK) // ignore not found error rd._enq_cnt_list[enq_fid]--; } } break; case RHM_JDAT_TXA_MAGIC: { txn_rec ar; if (!decode(ar, fid, ifsp, cum_size_read, h, lowi, rd, file_pos)) return false; // Delete this txn from tmap, unlock any locked records in emap ar.get_xid(&xidp); assert(xidp != 0); std::string xid((char*)xidp, ar.xid_size()); txn_data_list tdl = _tmap.get_remove_tdata_list(xid); // tdl will be empty if xid not found for (tdl_itr itr = tdl.begin(); itr != tdl.end(); itr++) { if (itr->_enq_flag) rd._enq_cnt_list[itr->_pfid]--; else _emap.unlock(itr->_drid); // ignore not found error } std::free(xidp); } break; case RHM_JDAT_TXC_MAGIC: { txn_rec cr; if (!decode(cr, fid, ifsp, cum_size_read, h, lowi, rd, file_pos)) return false; // Delete this txn from tmap, process records into emap cr.get_xid(&xidp); assert(xidp != 0); std::string xid((char*)xidp, cr.xid_size()); txn_data_list tdl = _tmap.get_remove_tdata_list(xid); // tdl will be empty if xid not found for (tdl_itr itr = tdl.begin(); itr != tdl.end(); itr++) { if (itr->_enq_flag) // txn enqueue { if (_emap.insert_pfid(itr->_rid, itr->_pfid) < enq_map::EMAP_OK) // fail { // The only error code emap::insert_pfid() returns is enq_map::EMAP_DUP_RID. std::ostringstream oss; oss << std::hex << "rid=0x" << itr->_rid << " _pfid=0x" << itr->_pfid; throw jexception(jerrno::JERR_MAP_DUPLICATE, oss.str(), "jcntl", "rcvr_get_next_record"); } } else // txn dequeue { int16_t enq_fid = _emap.get_remove_pfid(itr->_drid, true); if (enq_fid >= enq_map::EMAP_OK) rd._enq_cnt_list[enq_fid]--; } } std::free(xidp); } break; case RHM_JDAT_EMPTY_MAGIC: { u_int32_t rec_dblks = jrec::size_dblks(sizeof(rec_hdr)); ifsp->ignore(rec_dblks * JRNL_DBLK_SIZE - sizeof(rec_hdr)); assert(!ifsp->fail() && !ifsp->bad()); if (!jfile_cycle(fid, ifsp, lowi, rd, false)) return false; } break; case 0: check_journal_alignment(fid, file_pos, rd); return false; default: // Stop as this is the overwrite boundary. check_journal_alignment(fid, file_pos, rd); return false; } return true; } bool jcntl::decode(jrec& rec, u_int16_t& fid, std::ifstream* ifsp, std::size_t& cum_size_read, rec_hdr& h, bool& lowi, rcvdat& rd, std::streampos& file_offs) { u_int16_t start_fid = fid; std::streampos start_file_offs = file_offs; if (!check_owi(fid, h, lowi, rd, file_offs)) return false; bool done = false; while (!done) { try { done = rec.rcv_decode(h, ifsp, cum_size_read); } catch (const jexception& e) { // TODO - review this logic and tidy up how rd._lfid is assigned. See new jinf.get_end_file() fn. // Original // if (e.err_code() != jerrno::JERR_JREC_BADRECTAIL || // fid != (rd._ffid ? rd._ffid - 1 : _num_jfiles - 1)) throw; // Tried this, but did not work // if (e.err_code() != jerrno::JERR_JREC_BADRECTAIL || h._magic != 0) throw; check_journal_alignment(start_fid, start_file_offs, rd); // rd._lfid = start_fid; return false; } if (!done && !jfile_cycle(fid, ifsp, lowi, rd, false)) { check_journal_alignment(start_fid, start_file_offs, rd); return false; } } return true; } bool jcntl::jfile_cycle(u_int16_t& fid, std::ifstream* ifsp, bool& lowi, rcvdat& rd, const bool jump_fro) { if (ifsp->is_open()) { if (ifsp->eof() || !ifsp->good()) { ifsp->clear(); rd._eo = ifsp->tellg(); // remember file offset before closing assert(rd._eo != std::numeric_limits::max()); // Check for error code -1 ifsp->close(); if (++fid >= rd._njf) { fid = 0; lowi = !lowi; // Flip local owi } if (fid == rd._ffid) // used up all journal files return false; } } if (!ifsp->is_open()) { std::ostringstream oss; oss << _jdir.dirname() << "/" << _base_filename << "."; oss << std::hex << std::setfill('0') << std::setw(4) << fid << "." << JRNL_DATA_EXTENSION; ifsp->clear(); // clear eof flag, req'd for older versions of c++ ifsp->open(oss.str().c_str(), std::ios_base::in | std::ios_base::binary); if (!ifsp->good()) throw jexception(jerrno::JERR__FILEIO, oss.str(), "jcntl", "jfile_cycle"); // Read file header file_hdr fhdr; ifsp->read((char*)&fhdr, sizeof(fhdr)); assert(ifsp->good()); if (fhdr._magic == RHM_JDAT_FILE_MAGIC) { assert(fhdr._lfid == fid); if (!rd._fro) rd._fro = fhdr._fro; std::streamoff foffs = jump_fro ? fhdr._fro : JRNL_DBLK_SIZE * JRNL_SBLK_SIZE; ifsp->seekg(foffs); } else { ifsp->close(); return false; } } return true; } bool jcntl::check_owi(const u_int16_t fid, rec_hdr& h, bool& lowi, rcvdat& rd, std::streampos& file_pos) { if (rd._ffid ? h.get_owi() == lowi : h.get_owi() != lowi) // Overwrite indicator changed { u_int16_t expected_fid = rd._ffid ? rd._ffid - 1 : rd._njf - 1; if (fid == expected_fid) { check_journal_alignment(fid, file_pos, rd); return false; } std::ostringstream oss; oss << std::hex << std::setfill('0') << "Magic=0x" << std::setw(8) << h._magic; oss << " fid=0x" << std::setw(4) << fid << " rid=0x" << std::setw(8) << h._rid; oss << " foffs=0x" << std::setw(8) << file_pos; oss << " expected_fid=0x" << std::setw(4) << expected_fid; throw jexception(jerrno::JERR_JCNTL_OWIMISMATCH, oss.str(), "jcntl", "check_owi"); } if (rd._h_rid == 0) rd._h_rid = h._rid; else if (h._rid - rd._h_rid < 0x8000000000000000ULL) // RFC 1982 comparison for unsigned 64-bit rd._h_rid = h._rid; return true; } void jcntl::check_journal_alignment(const u_int16_t fid, std::streampos& file_pos, rcvdat& rd) { unsigned sblk_offs = file_pos % (JRNL_DBLK_SIZE * JRNL_SBLK_SIZE); if (sblk_offs) { { std::ostringstream oss; oss << std::hex << "Bad record alignment found at fid=0x" << fid; oss << " offs=0x" << file_pos << " (likely journal overwrite boundary); " << std::dec; oss << (JRNL_SBLK_SIZE - (sblk_offs/JRNL_DBLK_SIZE)) << " filler record(s) required."; this->log(LOG_WARN, oss.str()); } const u_int32_t xmagic = RHM_JDAT_EMPTY_MAGIC; std::ostringstream oss; oss << _jdir.dirname() << "/" << _base_filename << "."; oss << std::hex << std::setfill('0') << std::setw(4) << fid << "." << JRNL_DATA_EXTENSION; std::ofstream ofsp(oss.str().c_str(), std::ios_base::in | std::ios_base::out | std::ios_base::binary); if (!ofsp.good()) throw jexception(jerrno::JERR__FILEIO, oss.str(), "jcntl", "check_journal_alignment"); ofsp.seekp(file_pos); void* buff = std::malloc(JRNL_DBLK_SIZE); assert(buff != 0); std::memcpy(buff, (void*)&xmagic, sizeof(xmagic)); // Normally, RHM_CLEAN must be set before these fills are done, but this is a recover // situation (i.e. performance is not an issue), and it makes the location of the write // clear should inspection of the file be required. std::memset((char*)buff + sizeof(xmagic), RHM_CLEAN_CHAR, JRNL_DBLK_SIZE - sizeof(xmagic)); while (file_pos % (JRNL_DBLK_SIZE * JRNL_SBLK_SIZE)) { ofsp.write((const char*)buff, JRNL_DBLK_SIZE); assert(!ofsp.fail()); std::ostringstream oss; oss << std::hex << "Recover phase write: Wrote filler record: fid=0x" << fid << " offs=0x" << file_pos; this->log(LOG_NOTICE, oss.str()); file_pos = ofsp.tellp(); } ofsp.close(); std::free(buff); rd._lfid = fid; if (!rd._frot) rd._ffid = (fid + 1) % rd._njf; this->log(LOG_INFO, "Bad record alignment fixed."); } rd._eo = file_pos; } } // namespace journal } // namespace mrg qpid-cpp-store-debian-0.16/lib/jrnl/wrfc.cpp0000644000176300017630000001067411142067437017730 0ustar cajuscajus/** * \file wrfc.cpp * * Qpid asynchronous store plugin library * * File containing code for class mrg::journal::rrfc (rotating * file controller). See comments in file rrfc.hpp for details. * * \author Kim van der Riet * * Copyright (c) 2007, 2008, 2009 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #include "jrnl/wrfc.hpp" #include #include "jrnl/jerrno.hpp" #include "jrnl/jexception.hpp" namespace mrg { namespace journal { wrfc::wrfc(const lpmgr* lpmp): rfc(lpmp), _fsize_sblks(0), _fsize_dblks(0), _enq_cap_offs_dblks(0), _rid(0), _reset_ok(false), _owi(false), _frot(true) {} wrfc::~wrfc() {} void wrfc::initialize(const u_int32_t fsize_sblks, rcvdat* rdp) { if (rdp) { _fc_index = rdp->_lfid; _curr_fc = _lpmp->get_fcntlp(_fc_index); _curr_fc->wr_reset(rdp); _rid = rdp->_h_rid + 1; _reset_ok = true; _owi = rdp->_owi; _frot = rdp->_frot; if (rdp->_lffull) rotate(); } else { rfc::initialize(); rfc::set_findex(0); _rid = 0ULL; _reset_ok = false; } _fsize_sblks = fsize_sblks; _fsize_dblks = fsize_sblks * JRNL_SBLK_SIZE; _enq_cap_offs_dblks = (u_int32_t)std::ceil(_fsize_dblks * _lpmp->num_jfiles() * (100.0 - JRNL_ENQ_THRESHOLD) / 100); // Check the offset is at least one file; if not, make it so if (_enq_cap_offs_dblks < _fsize_dblks) _enq_cap_offs_dblks = _fsize_dblks; } iores wrfc::rotate() { if (!_lpmp->num_jfiles()) throw jexception(jerrno::JERR__NINIT, "wrfc", "rotate"); _fc_index++; if (_fc_index == _lpmp->num_jfiles()) { _fc_index = 0; _owi = !_owi; _frot = false; } _curr_fc = _lpmp->get_fcntlp(_fc_index); if (_curr_fc->aio_cnt()) return RHM_IORES_FILE_AIOWAIT; if (!wr_reset()) //Checks if file is still in use (ie not fully dequeued yet) return RHM_IORES_FULL; return RHM_IORES_SUCCESS; } u_int16_t wrfc::earliest_index() const { if (_frot) return 0; u_int16_t next_index = _fc_index + 1; if (next_index >= _lpmp->num_jfiles()) next_index = 0; return next_index; } bool wrfc::enq_threshold(const u_int32_t enq_dsize_dblks) const { u_int32_t subm_dblks = subm_cnt_dblks(); // includes file hdr if > 0 // This compensates for new files which don't have their file headers written yet, // as file header space cannot be included in this calculation. if (subm_dblks != 0) subm_dblks -= 4; u_int32_t fwd_dblks = subm_dblks + enq_dsize_dblks + _enq_cap_offs_dblks; u_int16_t findex = _fc_index; fcntl* fcp = _curr_fc; bool in_use = false; while (fwd_dblks && !(findex != _fc_index && fcp->enqcnt())) { fwd_dblks -= fwd_dblks > _fsize_dblks ? _fsize_dblks : fwd_dblks; if (fwd_dblks) { if (++findex == _lpmp->num_jfiles()) findex = 0; fcp = _lpmp->get_fcntlp(findex); } in_use |= fcp->enqcnt() > 0; } // Return true if threshold exceeded return findex != _fc_index && in_use; } bool wrfc::wr_reset() { _reset_ok = _curr_fc->reset(); // returns false if full (ie file still contains enqueued recs) return _reset_ok; } // TODO: update this to reflect all status data std::string wrfc::status_str() const { std::ostringstream oss; oss << "wrfc: " << rfc::status_str(); if (is_active()) oss << " fcntl[" << _fc_index << "]: " << _curr_fc->status_str(); return oss.str(); } } // namespace journal } // namespace mrg qpid-cpp-store-debian-0.16/lib/jrnl/deq_rec.cpp0000644000176300017630000003637511142067437020377 0ustar cajuscajus/** * \file deq_rec.cpp * * Qpid asynchronous store plugin library * * This file contains the code for the mrg::journal::deq_rec (journal dequeue * record) class. See comments in file deq_rec.hpp for details. * * \author Kim van der Riet * * Copyright (c) 2007, 2008 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #include "jrnl/deq_rec.hpp" #include #include #include #include #include #include "jrnl/jerrno.hpp" #include "jrnl/jexception.hpp" #include namespace mrg { namespace journal { deq_rec::deq_rec(): _deq_hdr(RHM_JDAT_DEQ_MAGIC, RHM_JDAT_VERSION, 0, 0, 0, false), _xidp(0), _buff(0), _deq_tail(_deq_hdr) {} deq_rec::deq_rec(const u_int64_t rid, const u_int64_t drid, const void* const xidp, const std::size_t xidlen, const bool owi, const bool txn_coml_commit): _deq_hdr(RHM_JDAT_DEQ_MAGIC, RHM_JDAT_VERSION, rid, drid, xidlen, owi, txn_coml_commit), _xidp(xidp), _buff(0), _deq_tail(_deq_hdr) {} deq_rec::~deq_rec() { clean(); } void deq_rec::reset() { _deq_hdr._rid = 0; _deq_hdr.set_owi(false); _deq_hdr.set_txn_coml_commit(false); _deq_hdr._deq_rid = 0; _deq_hdr._xidsize = 0; _deq_tail._rid = 0; _xidp = 0; _buff = 0; } void deq_rec::reset(const u_int64_t rid, const u_int64_t drid, const void* const xidp, const std::size_t xidlen, const bool owi, const bool txn_coml_commit) { _deq_hdr._rid = rid; _deq_hdr.set_owi(owi); _deq_hdr.set_txn_coml_commit(txn_coml_commit); _deq_hdr._deq_rid = drid; _deq_hdr._xidsize = xidlen; _deq_tail._rid = rid; _xidp = xidp; _buff = 0; } u_int32_t deq_rec::encode(void* wptr, u_int32_t rec_offs_dblks, u_int32_t max_size_dblks) { assert(wptr != 0); assert(max_size_dblks > 0); if (_xidp == 0) assert(_deq_hdr._xidsize == 0); std::size_t rec_offs = rec_offs_dblks * JRNL_DBLK_SIZE; std::size_t rem = max_size_dblks * JRNL_DBLK_SIZE; std::size_t wr_cnt = 0; if (rec_offs_dblks) // Continuation of split dequeue record (over 2 or more pages) { if (size_dblks(rec_size()) - rec_offs_dblks > max_size_dblks) // Further split required { rec_offs -= sizeof(_deq_hdr); std::size_t wsize = _deq_hdr._xidsize > rec_offs ? _deq_hdr._xidsize - rec_offs : 0; std::size_t wsize2 = wsize; if (wsize) { if (wsize > rem) wsize = rem; std::memcpy(wptr, (char*)_xidp + rec_offs, wsize); wr_cnt += wsize; rem -= wsize; } rec_offs -= _deq_hdr._xidsize - wsize2; if (rem) { wsize = sizeof(_deq_tail) > rec_offs ? sizeof(_deq_tail) - rec_offs : 0; wsize2 = wsize; if (wsize) { if (wsize > rem) wsize = rem; std::memcpy((char*)wptr + wr_cnt, (char*)&_deq_tail + rec_offs, wsize); wr_cnt += wsize; rem -= wsize; } rec_offs -= sizeof(_deq_tail) - wsize2; } assert(rem == 0); assert(rec_offs == 0); } else // No further split required { rec_offs -= sizeof(_deq_hdr); std::size_t wsize = _deq_hdr._xidsize > rec_offs ? _deq_hdr._xidsize - rec_offs : 0; if (wsize) { std::memcpy(wptr, (char*)_xidp + rec_offs, wsize); wr_cnt += wsize; } rec_offs -= _deq_hdr._xidsize - wsize; wsize = sizeof(_deq_tail) > rec_offs ? sizeof(_deq_tail) - rec_offs : 0; if (wsize) { std::memcpy((char*)wptr + wr_cnt, (char*)&_deq_tail + rec_offs, wsize); wr_cnt += wsize; #ifdef RHM_CLEAN std::size_t rec_offs = rec_offs_dblks * JRNL_DBLK_SIZE; std::size_t dblk_rec_size = size_dblks(rec_size() - rec_offs) * JRNL_DBLK_SIZE; std::memset((char*)wptr + wr_cnt, RHM_CLEAN_CHAR, dblk_rec_size - wr_cnt); #endif } rec_offs -= sizeof(_deq_tail) - wsize; assert(rec_offs == 0); } } else // Start at beginning of data record { // Assumption: the header will always fit into the first dblk std::memcpy(wptr, (void*)&_deq_hdr, sizeof(_deq_hdr)); wr_cnt = sizeof(_deq_hdr); if (size_dblks(rec_size()) > max_size_dblks) // Split required - can only occur with xid { std::size_t wsize; rem -= sizeof(_deq_hdr); if (rem) { wsize = rem >= _deq_hdr._xidsize ? _deq_hdr._xidsize : rem; std::memcpy((char*)wptr + wr_cnt, _xidp, wsize); wr_cnt += wsize; rem -= wsize; } if (rem) { wsize = rem >= sizeof(_deq_tail) ? sizeof(_deq_tail) : rem; std::memcpy((char*)wptr + wr_cnt, (void*)&_deq_tail, wsize); wr_cnt += wsize; rem -= wsize; } assert(rem == 0); } else // No split required { if (_deq_hdr._xidsize) { std::memcpy((char*)wptr + wr_cnt, _xidp, _deq_hdr._xidsize); wr_cnt += _deq_hdr._xidsize; std::memcpy((char*)wptr + wr_cnt, (void*)&_deq_tail, sizeof(_deq_tail)); wr_cnt += sizeof(_deq_tail); } #ifdef RHM_CLEAN std::size_t dblk_rec_size = size_dblks(rec_size()) * JRNL_DBLK_SIZE; std::memset((char*)wptr + wr_cnt, RHM_CLEAN_CHAR, dblk_rec_size - wr_cnt); #endif } } return size_dblks(wr_cnt); } u_int32_t deq_rec::decode(rec_hdr& h, void* rptr, u_int32_t rec_offs_dblks, u_int32_t max_size_dblks) { assert(rptr != 0); assert(max_size_dblks > 0); std::size_t rd_cnt = 0; if (rec_offs_dblks) // Continuation of record on new page { const u_int32_t hdr_xid_dblks = size_dblks(deq_hdr::size() + _deq_hdr._xidsize); const u_int32_t hdr_xid_tail_dblks = size_dblks(deq_hdr::size() + _deq_hdr._xidsize + rec_tail::size()); const std::size_t rec_offs = rec_offs_dblks * JRNL_DBLK_SIZE; if (hdr_xid_tail_dblks - rec_offs_dblks <= max_size_dblks) { // Remainder of xid fits within this page if (rec_offs - deq_hdr::size() < _deq_hdr._xidsize) { // Part of xid still outstanding, copy remainder of xid and tail const std::size_t xid_offs = rec_offs - deq_hdr::size(); const std::size_t xid_rem = _deq_hdr._xidsize - xid_offs; std::memcpy((char*)_buff + xid_offs, rptr, xid_rem); rd_cnt = xid_rem; std::memcpy((void*)&_deq_tail, ((char*)rptr + rd_cnt), sizeof(_deq_tail)); chk_tail(); rd_cnt += sizeof(_deq_tail); } else { // Tail or part of tail only outstanding, complete tail const std::size_t tail_offs = rec_offs - deq_hdr::size() - _deq_hdr._xidsize; const std::size_t tail_rem = rec_tail::size() - tail_offs; std::memcpy((char*)&_deq_tail + tail_offs, rptr, tail_rem); chk_tail(); rd_cnt = tail_rem; } } else if (hdr_xid_dblks - rec_offs_dblks <= max_size_dblks) { // Remainder of xid fits within this page, tail split const std::size_t xid_offs = rec_offs - deq_hdr::size(); const std::size_t xid_rem = _deq_hdr._xidsize - xid_offs; std::memcpy((char*)_buff + xid_offs, rptr, xid_rem); rd_cnt += xid_rem; const std::size_t tail_rem = (max_size_dblks * JRNL_DBLK_SIZE) - rd_cnt; if (tail_rem) { std::memcpy((void*)&_deq_tail, ((char*)rptr + xid_rem), tail_rem); rd_cnt += tail_rem; } } else { // Remainder of xid split const std::size_t xid_cp_size = (max_size_dblks * JRNL_DBLK_SIZE); std::memcpy((char*)_buff + rec_offs - deq_hdr::size(), rptr, xid_cp_size); rd_cnt += xid_cp_size; } } else // Start of record { // Get and check header _deq_hdr.hdr_copy(h); rd_cnt = sizeof(rec_hdr); _deq_hdr._deq_rid = *(u_int64_t*)((char*)rptr + rd_cnt); rd_cnt += sizeof(u_int64_t); #if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT) rd_cnt += sizeof(u_int32_t); // Filler 0 #endif _deq_hdr._xidsize = *(std::size_t*)((char*)rptr + rd_cnt); rd_cnt = _deq_hdr.size(); chk_hdr(); if (_deq_hdr._xidsize) { _buff = std::malloc(_deq_hdr._xidsize); MALLOC_CHK(_buff, "_buff", "deq_rec", "decode"); const u_int32_t hdr_xid_dblks = size_dblks(deq_hdr::size() + _deq_hdr._xidsize); const u_int32_t hdr_xid_tail_dblks = size_dblks(deq_hdr::size() + _deq_hdr._xidsize + rec_tail::size()); // Check if record (header + xid + tail) fits within this page, we can check the // tail before the expense of copying data to memory if (hdr_xid_tail_dblks <= max_size_dblks) { // Entire header, xid and tail fits within this page std::memcpy(_buff, (char*)rptr + rd_cnt, _deq_hdr._xidsize); rd_cnt += _deq_hdr._xidsize; std::memcpy((void*)&_deq_tail, (char*)rptr + rd_cnt, sizeof(_deq_tail)); rd_cnt += sizeof(_deq_tail); chk_tail(); } else if (hdr_xid_dblks <= max_size_dblks) { // Entire header and xid fit within this page, tail split std::memcpy(_buff, (char*)rptr + rd_cnt, _deq_hdr._xidsize); rd_cnt += _deq_hdr._xidsize; const std::size_t tail_rem = (max_size_dblks * JRNL_DBLK_SIZE) - rd_cnt; if (tail_rem) { std::memcpy((void*)&_deq_tail, (char*)rptr + rd_cnt, tail_rem); rd_cnt += tail_rem; } } else { // Header fits within this page, xid split const std::size_t xid_cp_size = (max_size_dblks * JRNL_DBLK_SIZE) - rd_cnt; std::memcpy(_buff, (char*)rptr + rd_cnt, xid_cp_size); rd_cnt += xid_cp_size; } } } return size_dblks(rd_cnt); } bool deq_rec::rcv_decode(rec_hdr h, std::ifstream* ifsp, std::size_t& rec_offs) { if (rec_offs == 0) { _deq_hdr.hdr_copy(h); ifsp->read((char*)&_deq_hdr._deq_rid, sizeof(u_int64_t)); #if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT) ifsp->ignore(sizeof(u_int32_t)); // _filler0 #endif ifsp->read((char*)&_deq_hdr._xidsize, sizeof(std::size_t)); #if defined(JRNL_LITTLE_ENDIAN) && defined(JRNL_32_BIT) ifsp->ignore(sizeof(u_int32_t)); // _filler0 #endif rec_offs = sizeof(_deq_hdr); // Read header, allocate (if req'd) for xid if (_deq_hdr._xidsize) { _buff = std::malloc(_deq_hdr._xidsize); MALLOC_CHK(_buff, "_buff", "enq_rec", "rcv_decode"); } } if (rec_offs < sizeof(_deq_hdr) + _deq_hdr._xidsize) { // Read xid (or continue reading xid) std::size_t offs = rec_offs - sizeof(_deq_hdr); ifsp->read((char*)_buff + offs, _deq_hdr._xidsize - offs); std::size_t size_read = ifsp->gcount(); rec_offs += size_read; if (size_read < _deq_hdr._xidsize - offs) { assert(ifsp->eof()); // As we may have read past eof, turn off fail bit ifsp->clear(ifsp->rdstate()&(~std::ifstream::failbit)); assert(!ifsp->fail() && !ifsp->bad()); return false; } } if (rec_offs < sizeof(_deq_hdr) + (_deq_hdr._xidsize ? _deq_hdr._xidsize + sizeof(rec_tail) : 0)) { // Read tail (or continue reading tail) std::size_t offs = rec_offs - sizeof(_deq_hdr) - _deq_hdr._xidsize; ifsp->read((char*)&_deq_tail + offs, sizeof(rec_tail) - offs); std::size_t size_read = ifsp->gcount(); rec_offs += size_read; if (size_read < sizeof(rec_tail) - offs) { assert(ifsp->eof()); // As we may have read past eof, turn off fail bit ifsp->clear(ifsp->rdstate()&(~std::ifstream::failbit)); assert(!ifsp->fail() && !ifsp->bad()); return false; } } ifsp->ignore(rec_size_dblks() * JRNL_DBLK_SIZE - rec_size()); if (_deq_hdr._xidsize) chk_tail(); // Throws if tail invalid or record incomplete assert(!ifsp->fail() && !ifsp->bad()); return true; } std::size_t deq_rec::get_xid(void** const xidpp) { if (!_buff) { *xidpp = 0; return 0; } *xidpp = _buff; return _deq_hdr._xidsize; } std::string& deq_rec::str(std::string& str) const { std::ostringstream oss; oss << "deq_rec: m=" << _deq_hdr._magic; oss << " v=" << (int)_deq_hdr._version; oss << " rid=" << _deq_hdr._rid; oss << " drid=" << _deq_hdr._deq_rid; if (_xidp) oss << " xid=\"" << _xidp << "\""; str.append(oss.str()); return str; } std::size_t deq_rec::xid_size() const { return _deq_hdr._xidsize; } std::size_t deq_rec::rec_size() const { return deq_hdr::size() + (_deq_hdr._xidsize ? _deq_hdr._xidsize + rec_tail::size() : 0); } void deq_rec::chk_hdr() const { jrec::chk_hdr(_deq_hdr); if (_deq_hdr._magic != RHM_JDAT_DEQ_MAGIC) { std::ostringstream oss; oss << std::hex << std::setfill('0'); oss << "deq magic: rid=0x" << std::setw(16) << _deq_hdr._rid; oss << ": expected=0x" << std::setw(8) << RHM_JDAT_DEQ_MAGIC; oss << " read=0x" << std::setw(2) << (int)_deq_hdr._magic; throw jexception(jerrno::JERR_JREC_BADRECHDR, oss.str(), "deq_rec", "chk_hdr"); } } void deq_rec::chk_hdr(u_int64_t rid) const { chk_hdr(); jrec::chk_rid(_deq_hdr, rid); } void deq_rec::chk_tail() const { jrec::chk_tail(_deq_tail, _deq_hdr); } void deq_rec::clean() { // clean up allocated memory here } } // namespace journal } // namespace mrg qpid-cpp-store-debian-0.16/lib/jrnl/enq_map.cpp0000644000176300017630000001053211433263011020365 0ustar cajuscajus/** * \file enq_map.cpp * * Qpid asynchronous store plugin library * * File containing code for class mrg::journal::enq_map (enqueue map). See * comments in file enq_map.hpp for details. * * \author Kim van der Riet * * Copyright (c) 2007, 2008, 2009, 2010 Red Hat Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #include "jrnl/enq_map.hpp" #include #include "jrnl/jerrno.hpp" #include "jrnl/slock.hpp" #include namespace mrg { namespace journal { // static return/error codes int16_t enq_map::EMAP_DUP_RID = -3; int16_t enq_map::EMAP_LOCKED = -2; int16_t enq_map::EMAP_RID_NOT_FOUND = -1; int16_t enq_map::EMAP_OK = 0; int16_t enq_map::EMAP_FALSE = 0; int16_t enq_map::EMAP_TRUE = 1; enq_map::enq_map(): _map(), _pfid_enq_cnt() {} enq_map::~enq_map() {} void enq_map::set_num_jfiles(const u_int16_t num_jfiles) { _pfid_enq_cnt.resize(num_jfiles, 0); } int16_t enq_map::insert_pfid(const u_int64_t rid, const u_int16_t pfid) { return insert_pfid(rid, pfid, false); } int16_t enq_map::insert_pfid(const u_int64_t rid, const u_int16_t pfid, const bool locked) { std::pair ret; emap_data_struct rec(pfid, locked); { slock s(_mutex); ret = _map.insert(emap_param(rid, rec)); } if (ret.second == false) return EMAP_DUP_RID; _pfid_enq_cnt.at(pfid)++; return EMAP_OK; } int16_t enq_map::get_pfid(const u_int64_t rid) { slock s(_mutex); emap_itr itr = _map.find(rid); if (itr == _map.end()) // not found in map return EMAP_RID_NOT_FOUND; if (itr->second._lock) return EMAP_LOCKED; return itr->second._pfid; } int16_t enq_map::get_remove_pfid(const u_int64_t rid, const bool txn_flag) { slock s(_mutex); emap_itr itr = _map.find(rid); if (itr == _map.end()) // not found in map return EMAP_RID_NOT_FOUND; if (itr->second._lock && !txn_flag) // locked, but not a commit/abort return EMAP_LOCKED; u_int16_t pfid = itr->second._pfid; _map.erase(itr); _pfid_enq_cnt.at(pfid)--; return pfid; } bool enq_map::is_enqueued(const u_int64_t rid, bool ignore_lock) { slock s(_mutex); emap_itr itr = _map.find(rid); if (itr == _map.end()) // not found in map return false; if (!ignore_lock && itr->second._lock) // locked return false; return true; } int16_t enq_map::lock(const u_int64_t rid) { slock s(_mutex); emap_itr itr = _map.find(rid); if (itr == _map.end()) // not found in map return EMAP_RID_NOT_FOUND; itr->second._lock = true; return EMAP_OK; } int16_t enq_map::unlock(const u_int64_t rid) { slock s(_mutex); emap_itr itr = _map.find(rid); if (itr == _map.end()) // not found in map return EMAP_RID_NOT_FOUND; itr->second._lock = false; return EMAP_OK; } int16_t enq_map::is_locked(const u_int64_t rid) { slock s(_mutex); emap_itr itr = _map.find(rid); if (itr == _map.end()) // not found in map return EMAP_RID_NOT_FOUND; return itr->second._lock ? EMAP_TRUE : EMAP_FALSE; } void enq_map::rid_list(std::vector& rv) { rv.clear(); { slock s(_mutex); for (emap_itr itr = _map.begin(); itr != _map.end(); itr++) rv.push_back(itr->first); } } void enq_map::pfid_list(std::vector& fv) { fv.clear(); { slock s(_mutex); for (emap_itr itr = _map.begin(); itr != _map.end(); itr++) fv.push_back(itr->second._pfid); } } } // namespace journal } // namespace mrg qpid-cpp-store-debian-0.16/lib/jrnl/smutex.hpp0000644000176300017630000000356311372046135020315 0ustar cajuscajus/** * \file smutex.hpp * * Qpid asynchronous store plugin library * * Messaging journal scoped mutex class mrg::journal::smutex. * * \author Kim van der Riet * * Copyright (c) 2010 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #ifndef mrg_journal_smutex_hpp #define mrg_journal_smutex_hpp #include "jrnl/jexception.hpp" #include namespace mrg { namespace journal { // Ultra-simple scoped mutex class that allows a posix mutex to be initialized and destroyed with error checks class smutex { protected: mutable pthread_mutex_t _m; public: inline smutex() { PTHREAD_CHK(::pthread_mutex_init(&_m, 0), "::pthread_mutex_init", "smutex", "smutex"); } inline virtual ~smutex() { PTHREAD_CHK(::pthread_mutex_destroy(&_m), "::pthread_mutex_destroy", "smutex", "~smutex"); } inline pthread_mutex_t* get() const { return &_m; } }; } // namespace journal } // namespace mrg #endif // ifndef mrg_journal_smutex_hpp qpid-cpp-store-debian-0.16/lib/jrnl/time_ns.cpp0000644000176300017630000000300611670231232020404 0ustar cajuscajus/** * \file time_ns.cpp * * Qpid asynchronous store plugin library * * Messaging journal time struct mrg::journal::time_ns, derived from * the timespec struct and provided with helper functions. * * \author Kim van der Riet * * Copyright (c) 2008 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #include "time_ns.hpp" #include namespace mrg { namespace journal { const std::string time_ns::str(int precision) const { const double t = tv_sec + (tv_nsec/1e9); std::ostringstream oss; oss.setf(std::ios::fixed, std::ios::floatfield); oss.precision(precision); oss << t; return oss.str(); } } // namespace journal } // namespace mrg qpid-cpp-store-debian-0.16/lib/jrnl/rfc.hpp0000644000176300017630000001704211142067437017542 0ustar cajuscajus/** * \file rfc.hpp * * Qpid asynchronous store plugin library * * File containing code for class mrg::journal::rfc (rotating * file controller). See class documentation for details. * * \author Kim van der Riet * * Copyright (c) 2008, 2009 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #ifndef mrg_journal_rfc_hpp #define mrg_journal_rfc_hpp namespace mrg { namespace journal { class rfc; } } #include "jrnl/lpmgr.hpp" #include "jrnl/enums.hpp" namespace mrg { namespace journal { /** * \class rfc * \brief Rotating File Controller (rfc) - Class to handle the manangement of an array of file controllers (fcntl) * objects for use in a circular disk buffer (journal). Each fcntl object corresponds to a file in the journal. * * The following states exist in this class: * *
    *                                                                   is_init()  is_active()
    *                  +===+                    _lpmp.is_init() == false
    *      +---------->|   |     Uninitialized: _curr_fc == 0               F           F
    *      |       +-->+===+ --+
    *      |       |           |
    *      |       |           |
    *      |   finalize()   initialize()
    *      |       |           |
    *      |       |           |
    *      |       +-- +===+<--+                _lpmp.is_init() == true
    *  finalize()      |   |     Inactive:      _curr_fc == 0               T           F
    *      |       +-->+===+ --+
    *      |       |           |
    *      |       |           |
    *      | unset_findex() set_findex()
    *      |       |           |
    *      |       |           |
    *      |       +-- +===+<--+                _lpmp.is_init() == true
    *      +---------- |   |     Active:        _curr_fc != 0               T           T
    *                  +===+
    * 
* * The Uninitialized state is where the class starts after construction. Once the number of files is known and * the array of file controllers allocated, then initialize() is called to set these, causing the state to move * to Inactive. * * The Inactive state has the file controllers allocated and pointing to their respective journal files, but no * current file controller has been selected. The pointer to the current file controller _curr_fc is null. Once the * index of the active file is known, then calling set_findex() will set the index and internal pointer * to the currently active file controller. This moves the state to Active. * * Note TODO: Comment on sync issues between change in num files in _lpmp and _fc_index/_curr_fc. */ class rfc { protected: const lpmgr* _lpmp; ///< Pointer to jcntl's lpmgr instance containing lfid/pfid map and fcntl objects u_int16_t _fc_index; ///< Index of current file controller fcntl* _curr_fc; ///< Pointer to current file controller public: rfc(const lpmgr* lpmp); virtual ~rfc(); /** * \brief Initialize the controller, moving from state Uninitialized to Inactive. The main function of * initialize() is to set the number of files and the pointer to the fcntl array. */ virtual inline void initialize() {} /** * \brief Reset the controller to Uninitialized state, usually called when the journal is stopped. Once called, * initialize() must be called to reuse an instance. */ virtual void finalize(); /** * \brief Check initialization state: true = Not Uninitialized, ie Initialized or Active; false = Uninitialized. */ virtual inline bool is_init() const { return _lpmp->is_init(); } /** * \brief Check active state: true = Initialized and _curr_fc not null; false otherwise. */ virtual inline bool is_active() const { return _lpmp->is_init() && _curr_fc != 0; } /** * \brief Sets the current file index and active fcntl object. Moves to state Active. */ virtual void set_findex(const u_int16_t fc_index); /** * \brief Nulls the current file index and active fcntl pointer, moves to state Inactive. */ virtual void unset_findex(); /** * \brief Rotate active file controller to next file in rotating file group. * \exception jerrno::JERR__NINIT if called before calling initialize(). */ virtual iores rotate() = 0; /** * \brief Returns the index of the currently active file within the rotating file group. */ inline u_int16_t index() const { return _fc_index; } /** * \brief Returns the currently active journal file controller within the rotating file group. */ inline fcntl* file_controller() const { return _curr_fc; } /** * \brief Returns the currently active physical file id (pfid) */ inline u_int16_t pfid() const { return _curr_fc->pfid(); } // Convenience access methods to current file controller // Note: Do not call when not in active state inline u_int32_t enqcnt() const { return _curr_fc->enqcnt(); } inline u_int32_t incr_enqcnt() { return _curr_fc->incr_enqcnt(); } inline u_int32_t incr_enqcnt(const u_int16_t fid) { return _lpmp->get_fcntlp(fid)->incr_enqcnt(); } inline u_int32_t add_enqcnt(const u_int32_t a) { return _curr_fc->add_enqcnt(a); } inline u_int32_t add_enqcnt(const u_int16_t fid, const u_int32_t a) { return _lpmp->get_fcntlp(fid)->add_enqcnt(a); } inline u_int32_t decr_enqcnt(const u_int16_t fid) { return _lpmp->get_fcntlp(fid)->decr_enqcnt(); } inline u_int32_t subtr_enqcnt(const u_int16_t fid, const u_int32_t s) { return _lpmp->get_fcntlp(fid)->subtr_enqcnt(s); } virtual inline u_int32_t subm_cnt_dblks() const = 0; virtual inline std::size_t subm_offs() const = 0; virtual inline u_int32_t add_subm_cnt_dblks(u_int32_t a) = 0; virtual inline u_int32_t cmpl_cnt_dblks() const = 0; virtual inline std::size_t cmpl_offs() const = 0; virtual inline u_int32_t add_cmpl_cnt_dblks(u_int32_t a) = 0; virtual inline bool is_void() const = 0; virtual inline bool is_empty() const = 0; virtual inline u_int32_t remaining_dblks() const = 0; virtual inline bool is_full() const = 0; virtual inline bool is_compl() const = 0; virtual inline u_int32_t aio_outstanding_dblks() const = 0; virtual inline bool file_rotate() const = 0; // Debug aid virtual std::string status_str() const; }; // class rfc } // namespace journal } // namespace mrg #endif // ifndef mrg_journal_rfc_hpp qpid-cpp-store-debian-0.16/lib/jrnl/jcfg.hpp0000644000176300017630000001015411667166757017717 0ustar cajuscajus/** * \file jcfg.hpp * * Qpid asynchronous store plugin library * * This file contains \#defines that control the implementation details of * the journal. * * \author Kim van der Riet * * Copyright (c) 2007, 2008 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #ifndef mrg_journal_jcfg_hpp #define mrg_journal_jcfg_hpp #if defined(__i386__) /* little endian, 32 bits */ #define JRNL_LITTLE_ENDIAN #define JRNL_32_BIT #elif defined(__PPC__) || defined(__s390__) /* big endian, 32 bits */ #define JRNL_BIG_ENDIAN #define JRNL_32_BIT #elif defined(__ia64__) || defined(__x86_64__) || defined(__alpha__) /* little endian, 64 bits */ #define JRNL_LITTLE_ENDIAN #define JRNL_64_BIT #elif defined(__powerpc64__) || defined(__s390x__) /* big endian, 64 bits */ #define JRNL_BIG_ENDIAN #define JRNL_64_BIT #else #error endian? #endif /** * Rule: Data block size (JRNL_DBLK_SIZE) MUST be a power of 2 such that *
* JRNL_DBLK_SIZE * JRNL_SBLK_SIZE == n * 512 (n = 1,2,3...)
* 
* (The disk softblock size is 512 for Linux kernels >= 2.6) */ #define JRNL_DBLK_SIZE 128 ///< Data block size in bytes (CANNOT BE LESS THAN 32!) #define JRNL_SBLK_SIZE 4 ///< Disk softblock size in multiples of JRNL_DBLK_SIZE #define JRNL_MIN_FILE_SIZE 128 ///< Min. jrnl file size in sblks (excl. file_hdr) #define JRNL_MAX_FILE_SIZE 4194176 ///< Max. jrnl file size in sblks (excl. file_hdr) #define JRNL_MIN_NUM_FILES 4 ///< Min. number of journal files #define JRNL_MAX_NUM_FILES 64 ///< Max. number of journal files #define JRNL_ENQ_THRESHOLD 80 ///< Percent full when enqueue connection will be closed #define JRNL_RMGR_PAGE_SIZE 128 ///< Journal page size in softblocks #define JRNL_RMGR_PAGES 16 ///< Number of pages to use in wmgr #define JRNL_WMGR_DEF_PAGE_SIZE 64 ///< Journal write page size in softblocks (default) #define JRNL_WMGR_DEF_PAGES 32 ///< Number of pages to use in wmgr (default) #define JRNL_WMGR_MAXDTOKPP 1024 ///< Max. dtoks (data blocks) per page in wmgr #define JRNL_WMGR_MAXWAITUS 100 ///< Max. wait time (us) before submitting AIO #define JRNL_INFO_EXTENSION "jinf" ///< Extension for journal info files #define JRNL_DATA_EXTENSION "jdat" ///< Extension for journal data files #define RHM_JDAT_TXA_MAGIC 0x614d4852 ///< ("RHMa" in little endian) Magic for dtx abort hdrs #define RHM_JDAT_TXC_MAGIC 0x634d4852 ///< ("RHMc" in little endian) Magic for dtx commit hdrs #define RHM_JDAT_DEQ_MAGIC 0x644d4852 ///< ("RHMd" in little endian) Magic for deq rec hdrs #define RHM_JDAT_ENQ_MAGIC 0x654d4852 ///< ("RHMe" in little endian) Magic for enq rec hdrs #define RHM_JDAT_FILE_MAGIC 0x664d4852 ///< ("RHMf" in little endian) Magic for file hdrs #define RHM_JDAT_EMPTY_MAGIC 0x784d4852 ///< ("RHMx" in little endian) Magic for empty dblk #define RHM_JDAT_VERSION 0x01 ///< Version (of file layout) #define RHM_CLEAN_CHAR 0xff ///< Char used to clear empty space on disk #define RHM_LENDIAN_FLAG 0 ///< Value of little endian flag on disk #define RHM_BENDIAN_FLAG 1 ///< Value of big endian flag on disk #endif // ifndef mrg_journal_jcfg_hpp qpid-cpp-store-debian-0.16/lib/jrnl/txn_rec.hpp0000644000176300017630000000655011422075452020431 0ustar cajuscajus/** * \file txn_rec.hpp * * Qpid asynchronous store plugin library * * This file contains the code for the mrg::journal::txn_rec (journal data * record) class. See class documentation for details. * * \author Kim van der Riet * * Copyright (c) 2007, 2008 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #ifndef mrg_journal_txn_rec_hpp #define mrg_journal_txn_rec_hpp namespace mrg { namespace journal { class txn_rec; } } #include #include "jrnl/jrec.hpp" #include "jrnl/txn_hdr.hpp" namespace mrg { namespace journal { /** * \class txn_rec * \brief Class to handle a single journal DTX commit or abort record. */ class txn_rec : public jrec { private: txn_hdr _txn_hdr; ///< transaction header const void* _xidp; ///< xid pointer for encoding (writing to disk) void* _buff; ///< Pointer to buffer to receive data read from disk rec_tail _txn_tail; ///< Record tail public: // constructor used for read operations and xid must have memory allocated txn_rec(); // constructor used for write operations, where xid already exists txn_rec(const u_int32_t magic, const u_int64_t rid, const void* const xidp, const std::size_t xidlen, const bool owi); virtual ~txn_rec(); // Prepare instance for use in reading data from journal void reset(const u_int32_t magic); // Prepare instance for use in writing data to journal void reset(const u_int32_t magic, const u_int64_t rid, const void* const xidp, const std::size_t xidlen, const bool owi); u_int32_t encode(void* wptr, u_int32_t rec_offs_dblks, u_int32_t max_size_dblks); u_int32_t decode(rec_hdr& h, void* rptr, u_int32_t rec_offs_dblks, u_int32_t max_size_dblks); // Decode used for recover bool rcv_decode(rec_hdr h, std::ifstream* ifsp, std::size_t& rec_offs); std::size_t get_xid(void** const xidpp); std::string& str(std::string& str) const; inline std::size_t data_size() const { return 0; } // This record never carries data std::size_t xid_size() const; std::size_t rec_size() const; inline u_int64_t rid() const { return _txn_hdr._rid; } private: void chk_hdr() const; void chk_hdr(u_int64_t rid) const; void chk_tail() const; virtual void clean(); }; // class txn_rec } // namespace journal } // namespace mrg #endif // ifndef mrg_journal_txn_rec_hpp qpid-cpp-store-debian-0.16/lib/jrnl/slock.hpp0000644000176300017630000000512511372046135020077 0ustar cajuscajus/** * \file slock.hpp * * Qpid asynchronous store plugin library * * Messaging journal scoped lock class mrg::journal::slock and scoped try-lock * class mrg::journal::stlock. * * \author Kim van der Riet * * Copyright (c) 2007, 2008 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #ifndef mrg_journal_slock_hpp #define mrg_journal_slock_hpp #include "jrnl/jexception.hpp" #include "jrnl/smutex.hpp" #include namespace mrg { namespace journal { // Ultra-simple scoped lock class, auto-releases mutex when it goes out-of-scope class slock { protected: const smutex& _sm; public: inline slock(const smutex& sm) : _sm(sm) { PTHREAD_CHK(::pthread_mutex_lock(_sm.get()), "::pthread_mutex_lock", "slock", "slock"); } inline ~slock() { PTHREAD_CHK(::pthread_mutex_unlock(_sm.get()), "::pthread_mutex_unlock", "slock", "~slock"); } }; // Ultra-simple scoped try-lock class, auto-releases mutex when it goes out-of-scope class stlock { protected: const smutex& _sm; bool _locked; public: inline stlock(const smutex& sm) : _sm(sm), _locked(false) { int ret = ::pthread_mutex_trylock(_sm.get()); _locked = (ret == 0); // check if lock obtained if (!_locked && ret != EBUSY) PTHREAD_CHK(ret, "::pthread_mutex_trylock", "stlock", "stlock"); } inline ~stlock() { if (_locked) PTHREAD_CHK(::pthread_mutex_unlock(_sm.get()), "::pthread_mutex_unlock", "stlock", "~stlock"); } inline bool locked() const { return _locked; } }; } // namespace journal } // namespace mrg #endif // ifndef mrg_journal_slock_hpp qpid-cpp-store-debian-0.16/lib/jrnl/aio_callback.hpp0000644000176300017630000000311311142067437021346 0ustar cajuscajus/** * \file aio_callback.hpp * * Qpid asynchronous store plugin library * * This file contains the definition for the AIO callback function * pointer. * * \author Kim van der Riet * * Copyright (c) 2007, 2008 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #ifndef mrg_journal_aio_callback_hpp #define mrg_journal_aio_callback_hpp #include #include namespace mrg { namespace journal { class data_tok; class aio_callback { public: virtual ~aio_callback() {} virtual void wr_aio_cb(std::vector& dtokl) = 0; virtual void rd_aio_cb(std::vector& pil) = 0; }; } // namespace journal } // namespace mrg #endif // ifndef mrg_journal_aio_callback_hpp qpid-cpp-store-debian-0.16/lib/jrnl/jerrno.cpp0000644000176300017630000003100111623467711020254 0ustar cajuscajus/** * \file jerrno.cpp * * Qpid asynchronous store plugin library * * File containing code for class mrg::journal::jerrno (journal error * codes). See comments in file jerrno.hpp for details. * * See file jerrno.hpp for class details. * * \author Kim van der Riet * * Copyright (c) 2007, 2008, 2009 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #include "jrnl/jerrno.hpp" namespace mrg { namespace journal { std::map jerrno::_err_map; std::map::iterator jerrno::_err_map_itr; bool jerrno::_initialized = jerrno::__init(); // generic errors const u_int32_t jerrno::JERR__MALLOC = 0x0100; const u_int32_t jerrno::JERR__UNDERFLOW = 0x0101; const u_int32_t jerrno::JERR__NINIT = 0x0102; const u_int32_t jerrno::JERR__AIO = 0x0103; const u_int32_t jerrno::JERR__FILEIO = 0x0104; const u_int32_t jerrno::JERR__RTCLOCK = 0x0105; const u_int32_t jerrno::JERR__PTHREAD = 0x0106; const u_int32_t jerrno::JERR__TIMEOUT = 0x0107; const u_int32_t jerrno::JERR__UNEXPRESPONSE = 0x0108; const u_int32_t jerrno::JERR__RECNFOUND = 0x0109; const u_int32_t jerrno::JERR__NOTIMPL = 0x010a; // class jcntl const u_int32_t jerrno::JERR_JCNTL_STOPPED = 0x0200; const u_int32_t jerrno::JERR_JCNTL_READONLY = 0x0201; const u_int32_t jerrno::JERR_JCNTL_AIOCMPLWAIT = 0x0202; const u_int32_t jerrno::JERR_JCNTL_UNKNOWNMAGIC = 0x0203; const u_int32_t jerrno::JERR_JCNTL_NOTRECOVERED = 0x0204; const u_int32_t jerrno::JERR_JCNTL_RECOVERJFULL = 0x0205; const u_int32_t jerrno::JERR_JCNTL_OWIMISMATCH = 0x0206; // class jdir const u_int32_t jerrno::JERR_JDIR_NOTDIR = 0x0300; const u_int32_t jerrno::JERR_JDIR_MKDIR = 0x0301; const u_int32_t jerrno::JERR_JDIR_OPENDIR = 0x0302; const u_int32_t jerrno::JERR_JDIR_READDIR = 0x0303; const u_int32_t jerrno::JERR_JDIR_CLOSEDIR = 0x0304; const u_int32_t jerrno::JERR_JDIR_RMDIR = 0x0305; const u_int32_t jerrno::JERR_JDIR_NOSUCHFILE = 0x0306; const u_int32_t jerrno::JERR_JDIR_FMOVE = 0x0307; const u_int32_t jerrno::JERR_JDIR_STAT = 0x0308; const u_int32_t jerrno::JERR_JDIR_UNLINK = 0x0309; const u_int32_t jerrno::JERR_JDIR_BADFTYPE = 0x030a; // class fcntl const u_int32_t jerrno::JERR_FCNTL_OPENWR = 0x0400; const u_int32_t jerrno::JERR_FCNTL_WRITE = 0x0401; const u_int32_t jerrno::JERR_FCNTL_CLOSE = 0x0402; const u_int32_t jerrno::JERR_FCNTL_FILEOFFSOVFL = 0x0403; const u_int32_t jerrno::JERR_FCNTL_CMPLOFFSOVFL = 0x0404; const u_int32_t jerrno::JERR_FCNTL_RDOFFSOVFL = 0x0405; // class lfmgr const u_int32_t jerrno::JERR_LFMGR_BADAEFNUMLIM = 0x0500; const u_int32_t jerrno::JERR_LFMGR_AEFNUMLIMIT = 0x0501; const u_int32_t jerrno::JERR_LFMGR_AEDISABLED = 0x0502; // class rrfc const u_int32_t jerrno::JERR_RRFC_OPENRD = 0x0600; // class jrec, enq_rec, deq_rec, txn_rec const u_int32_t jerrno::JERR_JREC_BADRECHDR = 0x0700; const u_int32_t jerrno::JERR_JREC_BADRECTAIL = 0x0701; // class wmgr const u_int32_t jerrno::JERR_WMGR_BADPGSTATE = 0x0801; const u_int32_t jerrno::JERR_WMGR_BADDTOKSTATE = 0x0802; const u_int32_t jerrno::JERR_WMGR_ENQDISCONT = 0x0803; const u_int32_t jerrno::JERR_WMGR_DEQDISCONT = 0x0804; const u_int32_t jerrno::JERR_WMGR_DEQRIDNOTENQ = 0x0805; // class rmgr const u_int32_t jerrno::JERR_RMGR_UNKNOWNMAGIC = 0x0900; const u_int32_t jerrno::JERR_RMGR_RIDMISMATCH = 0x0901; //const u_int32_t jerrno::JERR_RMGR_FIDMISMATCH = 0x0902; const u_int32_t jerrno::JERR_RMGR_ENQSTATE = 0x0903; const u_int32_t jerrno::JERR_RMGR_BADRECTYPE = 0x0904; // class data_tok const u_int32_t jerrno::JERR_DTOK_ILLEGALSTATE = 0x0a00; // const u_int32_t jerrno::JERR_DTOK_RIDNOTSET = 0x0a01; // class enq_map, txn_map const u_int32_t jerrno::JERR_MAP_DUPLICATE = 0x0b00; const u_int32_t jerrno::JERR_MAP_NOTFOUND = 0x0b01; const u_int32_t jerrno::JERR_MAP_LOCKED = 0x0b02; // class jinf const u_int32_t jerrno::JERR_JINF_CVALIDFAIL = 0x0c00; const u_int32_t jerrno::JERR_JINF_NOVALUESTR = 0x0c01; const u_int32_t jerrno::JERR_JINF_BADVALUESTR = 0x0c02; const u_int32_t jerrno::JERR_JINF_JDATEMPTY = 0x0c03; const u_int32_t jerrno::JERR_JINF_TOOMANYFILES = 0x0c04; const u_int32_t jerrno::JERR_JINF_INVALIDFHDR = 0x0c05; const u_int32_t jerrno::JERR_JINF_STAT = 0x0c06; const u_int32_t jerrno::JERR_JINF_NOTREGFILE = 0x0c07; const u_int32_t jerrno::JERR_JINF_BADFILESIZE = 0x0c08; const u_int32_t jerrno::JERR_JINF_OWIBAD = 0x0c09; const u_int32_t jerrno::JERR_JINF_ZEROLENFILE = 0x0c0a; // Negative returns for some functions const int32_t jerrno::AIO_TIMEOUT = -1; const int32_t jerrno::LOCK_TAKEN = -2; // static initialization fn bool jerrno::__init() { // generic errors _err_map[JERR__MALLOC] = "JERR__MALLOC: Buffer memory allocation failed."; _err_map[JERR__UNDERFLOW] = "JERR__UNDERFLOW: Underflow error"; _err_map[JERR__NINIT] = "JERR__NINIT: Operation on uninitialized class."; _err_map[JERR__AIO] = "JERR__AIO: AIO error."; _err_map[JERR__FILEIO] = "JERR__FILEIO: File read or write failure."; _err_map[JERR__RTCLOCK] = "JERR__RTCLOCK: Reading real-time clock failed."; _err_map[JERR__PTHREAD] = "JERR__PTHREAD: pthread failure."; _err_map[JERR__TIMEOUT] = "JERR__TIMEOUT: Timeout waiting for event."; _err_map[JERR__UNEXPRESPONSE] = "JERR__UNEXPRESPONSE: Unexpected response to call or event."; _err_map[JERR__RECNFOUND] = "JERR__RECNFOUND: Record not found."; _err_map[JERR__NOTIMPL] = "JERR__NOTIMPL: Not implemented"; // class jcntl _err_map[JERR_JCNTL_STOPPED] = "JERR_JCNTL_STOPPED: Operation on stopped journal."; _err_map[JERR_JCNTL_READONLY] = "JERR_JCNTL_READONLY: Write operation on read-only journal (during recovery)."; _err_map[JERR_JCNTL_AIOCMPLWAIT] = "JERR_JCNTL_AIOCMPLWAIT: Timeout waiting for AIOs to complete."; _err_map[JERR_JCNTL_UNKNOWNMAGIC] = "JERR_JCNTL_UNKNOWNMAGIC: Found record with unknown magic."; _err_map[JERR_JCNTL_NOTRECOVERED] = "JERR_JCNTL_NOTRECOVERED: Operation requires recover() to be run first."; _err_map[JERR_JCNTL_RECOVERJFULL] = "JERR_JCNTL_RECOVERJFULL: Journal data files full, cannot write."; _err_map[JERR_JCNTL_OWIMISMATCH] = "JERR_JCNTL_OWIMISMATCH: Overwrite Indicator (OWI) change found in unexpected location."; // class jdir _err_map[JERR_JDIR_NOTDIR] = "JERR_JDIR_NOTDIR: Directory name exists but is not a directory."; _err_map[JERR_JDIR_MKDIR] = "JERR_JDIR_MKDIR: Directory creation failed."; _err_map[JERR_JDIR_OPENDIR] = "JERR_JDIR_OPENDIR: Directory open failed."; _err_map[JERR_JDIR_READDIR] = "JERR_JDIR_READDIR: Directory read failed."; _err_map[JERR_JDIR_CLOSEDIR] = "JERR_JDIR_CLOSEDIR: Directory close failed."; _err_map[JERR_JDIR_RMDIR] = "JERR_JDIR_RMDIR: Directory delete failed."; _err_map[JERR_JDIR_NOSUCHFILE] = "JERR_JDIR_NOSUCHFILE: File does not exist."; _err_map[JERR_JDIR_FMOVE] = "JERR_JDIR_FMOVE: File move failed."; _err_map[JERR_JDIR_STAT] = "JERR_JDIR_STAT: File stat failed."; _err_map[JERR_JDIR_UNLINK] = "JERR_JDIR_UNLINK: File delete failed."; _err_map[JERR_JDIR_BADFTYPE] = "JERR_JDIR_BADFTYPE: Bad or unknown file type (stat mode)."; // class fcntl _err_map[JERR_FCNTL_OPENWR] = "JERR_FCNTL_OPENWR: Unable to open file for write."; _err_map[JERR_FCNTL_WRITE] = "JERR_FCNTL_WRITE: Unable to write to file."; _err_map[JERR_FCNTL_CLOSE] = "JERR_FCNTL_CLOSE: File close failed."; _err_map[JERR_FCNTL_FILEOFFSOVFL] = "JERR_FCNTL_FILEOFFSOVFL: Attempted increase file offset past file size."; _err_map[JERR_FCNTL_CMPLOFFSOVFL] = "JERR_FCNTL_CMPLOFFSOVFL: Attempted increase completed file offset past submitted offset."; _err_map[JERR_FCNTL_RDOFFSOVFL] = "JERR_FCNTL_RDOFFSOVFL: Attempted increase read offset past write offset."; // class lfmgr _err_map[JERR_LFMGR_BADAEFNUMLIM] = "JERR_LFMGR_BADAEFNUMLIM: Auto-expand file number limit lower than initial number of journal files."; _err_map[JERR_LFMGR_AEFNUMLIMIT] = "JERR_LFMGR_AEFNUMLIMIT: Exceeded auto-expand file number limit."; _err_map[JERR_LFMGR_AEDISABLED] = "JERR_LFMGR_AEDISABLED: Attempted to expand with auto-expand disabled."; // class rrfc _err_map[JERR_RRFC_OPENRD] = "JERR_RRFC_OPENRD: Unable to open file for read."; // class jrec, enq_rec, deq_rec, txn_rec _err_map[JERR_JREC_BADRECHDR] = "JERR_JREC_BADRECHDR: Invalid data record header."; _err_map[JERR_JREC_BADRECTAIL] = "JERR_JREC_BADRECTAIL: Invalid data record tail."; // class wmgr _err_map[JERR_WMGR_BADPGSTATE] = "JERR_WMGR_BADPGSTATE: Page buffer in illegal state for operation."; _err_map[JERR_WMGR_BADDTOKSTATE] = "JERR_WMGR_BADDTOKSTATE: Data token in illegal state for operation."; _err_map[JERR_WMGR_ENQDISCONT] = "JERR_WMGR_ENQDISCONT: Enqueued new dtok when previous enqueue returned partly completed (state ENQ_PART)."; _err_map[JERR_WMGR_DEQDISCONT] = "JERR_WMGR_DEQDISCONT: Dequeued new dtok when previous dequeue returned partly completed (state DEQ_PART)."; _err_map[JERR_WMGR_DEQRIDNOTENQ] = "JERR_WMGR_DEQRIDNOTENQ: Dequeue rid is not enqueued."; // class rmgr _err_map[JERR_RMGR_UNKNOWNMAGIC] = "JERR_RMGR_UNKNOWNMAGIC: Found record with unknown magic."; _err_map[JERR_RMGR_RIDMISMATCH] = "JERR_RMGR_RIDMISMATCH: RID mismatch between current record and dtok RID"; //_err_map[JERR_RMGR_FIDMISMATCH] = "JERR_RMGR_FIDMISMATCH: FID mismatch between emap and rrfc"; _err_map[JERR_RMGR_ENQSTATE] = "JERR_RMGR_ENQSTATE: Attempted read when data token wstate was not ENQ"; _err_map[JERR_RMGR_BADRECTYPE] = "JERR_RMGR_BADRECTYPE: Attempted operation on inappropriate record type"; // class data_tok _err_map[JERR_DTOK_ILLEGALSTATE] = "JERR_MTOK_ILLEGALSTATE: Attempted to change to illegal state."; //_err_map[JERR_DTOK_RIDNOTSET] = "JERR_DTOK_RIDNOTSET: Record ID not set."; // class enq_map, txn_map _err_map[JERR_MAP_DUPLICATE] = "JERR_MAP_DUPLICATE: Attempted to insert record into map using duplicate key."; _err_map[JERR_MAP_NOTFOUND] = "JERR_MAP_NOTFOUND: Key not found in map."; _err_map[JERR_MAP_LOCKED] = "JERR_MAP_LOCKED: Record ID locked by a pending transaction."; // class jinf _err_map[JERR_JINF_CVALIDFAIL] = "JERR_JINF_CVALIDFAIL: Journal compatibility validation failure."; _err_map[JERR_JINF_NOVALUESTR] = "JERR_JINF_NOVALUESTR: No value attribute found in jinf file."; _err_map[JERR_JINF_BADVALUESTR] = "JERR_JINF_BADVALUESTR: Bad format for value attribute in jinf file"; _err_map[JERR_JINF_JDATEMPTY] = "JERR_JINF_JDATEMPTY: Journal data files empty."; _err_map[JERR_JINF_TOOMANYFILES] = "JERR_JINF_TOOMANYFILES: Too many journal data files."; _err_map[JERR_JINF_INVALIDFHDR] = "JERR_JINF_INVALIDFHDR: Invalid journal data file header"; _err_map[JERR_JINF_STAT] = "JERR_JINF_STAT: Error while trying to stat a journal data file"; _err_map[JERR_JINF_NOTREGFILE] = "JERR_JINF_NOTREGFILE: Target journal data file is not a regular file"; _err_map[JERR_JINF_BADFILESIZE] = "JERR_JINF_BADFILESIZE: Journal data file is of incorrect or unexpected size"; _err_map[JERR_JINF_OWIBAD] = "JERR_JINF_OWIBAD: Journal data files have inconsistent OWI flags; >1 transition found in non-auto-expand or min-size journal"; _err_map[JERR_JINF_ZEROLENFILE] = "JERR_JINF_ZEROLENFILE: Journal info file zero length"; //_err_map[] = ""; return true; } const char* jerrno::err_msg(const u_int32_t err_no) throw () { _err_map_itr = _err_map.find(err_no); if (_err_map_itr == _err_map.end()) return ""; return _err_map_itr->second; } } // namespace journal } // namespace mrg qpid-cpp-store-debian-0.16/lib/jrnl/rrfc.hpp0000644000176300017630000001570411142067437017727 0ustar cajuscajus/** * \file rrfc.hpp * * Qpid asynchronous store plugin library * * File containing code for class mrg::journal::rrfc (rotating * file controller). See class documentation for details. * * \author Kim van der Riet * * Copyright (c) 2007, 2008, 2009 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #ifndef mrg_journal_rrfc_hpp #define mrg_journal_rrfc_hpp namespace mrg { namespace journal { class rrfc; } } #include "jrnl/fcntl.hpp" #include "jrnl/rfc.hpp" namespace mrg { namespace journal { /** * \class rrfc * \brief Read Rotating File Controller (rrfc) - Subclassed from pure virtual class rfc. Used to control the read * pipeline in a rotating file buffer or journal. See class rfc for further details. * * The states that exist in this class are identical to class rfc from which it inherits, but in addition, the value * of the read file handle _fh is also considered. The calls to set_findex also opens the file handle _fh to the * active file for reading. Similarly, unset_findex() closes this file handle. * *
    *                                                                   is_init()  is_active()
    *                  +===+                    _lpmp.is_init() == false
    *      +---------->|   |     Uninitialized: _curr_fc == 0               F           F
    *      |       +-->+===+ --+                _fh == -1
    *      |       |           |
    *      |       |           |
    *      |   finalize()   initialize()
    *      |       |           |
    *      |       |           |
    *      |       +-- +===+<--+                _lpmp.is_init() == true
    *  finalize()      |   |     Inactive:      _curr_fc == 0               T           F
    *      |       +-->+===+ --+                _fh == -1
    *      |       |           |
    *      |       |           |
    *      | unset_findex() set_findex()
    *      |       |           |
    *      |       |           |
    *      |       +-- +===+<--+                _lpmp.is_init() == true
    *      +---------- |   |     Active:        _curr_fc != 0               T           T
    *                  +===+                    _fh >= 0
    * 
* * In adition to the states above, class rrfc contains a validity flag. This is operated indepenedently of the state * machine. This flag (_valid) indicates when the read buffers are valid for reading. This is not strictly a state, * but simply a flag used to keep track of the status, and is set/unset with calls to set_valid() and set_invalid() * respectively. */ class rrfc : public rfc { protected: int _fh; ///< Read file handle bool _valid; ///< Flag is true when read pages contain vailid data public: rrfc(const lpmgr* lpmp); virtual ~rrfc(); /** * \brief Initialize the controller, moving from state Uninitialized to Initialized. The main function of * initialize() is to set the number of files and the pointer to the fcntl array. */ inline void initialize() { rfc::initialize(); _valid = false; } /** * \brief Reset the controller to Uninitialized state, usually called when the journal is stopped. Once called, * initialize() must be called to reuse an instance. */ void finalize(); /** * \brief Opens the file handle for reading a particular fid. Moves to state open. */ void set_findex(const u_int16_t fc_index); /** * \brief Closes the read file handle and nulls the active fcntl pointer. Moves to state closed. */ void unset_findex(); /** * \brief Check the state: true = open; false = closed. */ inline bool is_active() const { return _curr_fc != 0 && _fh >= 0; } /** * \brief Sets the validity flag which indicates that the read buffers contain valid data for reading. */ inline void set_invalid() { _valid = false; } /** * \brief Resets the validity flag wich indicates that the read buffers are no longer synchronized and cannot * be read whithout resynchronization. */ inline void set_valid() { _valid = true; } /** * \brief Checks the read buffer validity status: true = valid, can be read; false = invalid, synchronization * required. */ inline bool is_valid() const { return _valid; } /** * \brief Rotate active file controller to next file in rotating file group. * \exception jerrno::JERR__NINIT if called before calling initialize(). */ iores rotate(); inline int fh() const { return _fh; } inline u_int32_t subm_cnt_dblks() const { return _curr_fc->rd_subm_cnt_dblks(); } inline std::size_t subm_offs() const { return _curr_fc->rd_subm_offs(); } inline u_int32_t add_subm_cnt_dblks(u_int32_t a) { return _curr_fc->add_rd_subm_cnt_dblks(a); } inline u_int32_t cmpl_cnt_dblks() const { return _curr_fc->rd_cmpl_cnt_dblks(); } inline std::size_t cmpl_offs() const { return _curr_fc->rd_cmpl_offs(); } inline u_int32_t add_cmpl_cnt_dblks(u_int32_t a) { return _curr_fc->add_rd_cmpl_cnt_dblks(a); } inline bool is_void() const { return _curr_fc->rd_void(); } inline bool is_empty() const { return _curr_fc->rd_empty(); } inline u_int32_t remaining_dblks() const { return _curr_fc->rd_remaining_dblks(); } inline bool is_full() const { return _curr_fc->is_rd_full(); } inline bool is_compl() const { return _curr_fc->is_rd_compl(); } inline u_int32_t aio_outstanding_dblks() const { return _curr_fc->rd_aio_outstanding_dblks(); } inline bool file_rotate() const { return _curr_fc->rd_file_rotate(); } inline bool is_wr_aio_outstanding() const { return _curr_fc->wr_aio_outstanding_dblks() > 0; } // Debug aid std::string status_str() const; protected: void open_fh(const std::string& fn); void close_fh(); }; // class rrfc } // namespace journal } // namespace mrg #endif // ifndef mrg_journal_rrfc_hpp qpid-cpp-store-debian-0.16/lib/jrnl/jdir.cpp0000644000176300017630000003360511717442050017712 0ustar cajuscajus/** * \file jdir.cpp * * Qpid asynchronous store plugin library * * File containing code for class mrg::journal::jdir (journal data * directory), used for controlling and manipulating journal data * direcories and files. See comments in file jdir.hpp for details. * * \author Kim van der Riet * * Copyright (c) 2007, 2008 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #include "jrnl/jdir.hpp" #include #include #include #include #include "jrnl/jcfg.hpp" #include "jrnl/jerrno.hpp" #include "jrnl/jexception.hpp" #include #include #include namespace mrg { namespace journal { jdir::jdir(const std::string& dirname, const std::string& _base_filename): _dirname(dirname), _base_filename(_base_filename) {} jdir::~jdir() {} // === create_dir === void jdir::create_dir() { create_dir(_dirname); } void jdir::create_dir(const char* dirname) { create_dir(std::string(dirname)); } void jdir::create_dir(const std::string& dirname) { std::size_t fdp = dirname.find_last_of('/'); if (fdp != std::string::npos) { std::string parent_dir = dirname.substr(0, fdp); if (!exists(parent_dir)) create_dir(parent_dir); } if (::mkdir(dirname.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH)) { if (errno != EEXIST) // Dir exists, ignore { std::ostringstream oss; oss << "dir=\"" << dirname << "\"" << FORMAT_SYSERR(errno); throw jexception(jerrno::JERR_JDIR_MKDIR, oss.str(), "jdir", "create_dir"); } } } // === clear_dir === void jdir::clear_dir(const bool create_flag) { clear_dir(_dirname, _base_filename, create_flag); } void jdir::clear_dir(const char* dirname, const char* base_filename, const bool create_flag) { clear_dir(std::string(dirname), std::string(base_filename), create_flag); } void jdir::clear_dir(const std::string& dirname, const std::string& #ifndef RHM_JOWRITE base_filename #endif , const bool create_flag) { DIR* dir = ::opendir(dirname.c_str()); if (!dir) { if (errno == 2 && create_flag) // ENOENT (No such file or dir) { create_dir(dirname); return; } std::ostringstream oss; oss << "dir=\"" << dirname << "\"" << FORMAT_SYSERR(errno); throw jexception(jerrno::JERR_JDIR_OPENDIR, oss.str(), "jdir", "clear_dir"); } #ifndef RHM_JOWRITE struct dirent* entry; bool found = false; std::string bak_dir; while ((entry = ::readdir(dir)) != 0) { // Ignore . and .. if (std::strcmp(entry->d_name, ".") != 0 && std::strcmp(entry->d_name, "..") != 0) { if (std::strlen(entry->d_name) > base_filename.size()) { if (std::strncmp(entry->d_name, base_filename.c_str(), base_filename.size()) == 0) { if (!found) { bak_dir = create_bak_dir(dirname, base_filename); found = true; } std::ostringstream oldname; oldname << dirname << "/" << entry->d_name; std::ostringstream newname; newname << bak_dir << "/" << entry->d_name; if (::rename(oldname.str().c_str(), newname.str().c_str())) { ::closedir(dir); std::ostringstream oss; oss << "file=\"" << oldname.str() << "\" dest=\"" << newname.str() << "\"" << FORMAT_SYSERR(errno); throw jexception(jerrno::JERR_JDIR_FMOVE, oss.str(), "jdir", "clear_dir"); } } } } } // FIXME: Find out why this fails with false alarms/errors from time to time... // While commented out, there is no error capture from reading dir entries. // check_err(errno, dir, dirname, "clear_dir"); #endif close_dir(dir, dirname, "clear_dir"); } // === push_down === std::string jdir::push_down(const std::string& dirname, const std::string& target_dir, const std::string& bak_dir_base) { std::string bak_dir_name = create_bak_dir(dirname, bak_dir_base); DIR* dir = ::opendir(dirname.c_str()); if (!dir) { std::ostringstream oss; oss << "dir=\"" << dirname << "\"" << FORMAT_SYSERR(errno); throw jexception(jerrno::JERR_JDIR_OPENDIR, oss.str(), "jdir", "push_down"); } // Copy contents of targetDirName into bak dir struct dirent* entry; while ((entry = ::readdir(dir)) != 0) { // Search for targetDirName in storeDirName if (std::strcmp(entry->d_name, target_dir.c_str()) == 0) { std::ostringstream oldname; oldname << dirname << "/" << target_dir; std::ostringstream newname; newname << bak_dir_name << "/" << target_dir; if (::rename(oldname.str().c_str(), newname.str().c_str())) { ::closedir(dir); std::ostringstream oss; oss << "file=\"" << oldname.str() << "\" dest=\"" << newname.str() << "\"" << FORMAT_SYSERR(errno); throw jexception(jerrno::JERR_JDIR_FMOVE, oss.str(), "jdir", "push_down"); } break; } } close_dir(dir, dirname, "push_down"); return bak_dir_name; } // === verify_dir === void jdir::verify_dir() { verify_dir(_dirname, _base_filename); } void jdir::verify_dir(const char* dirname, const char* base_filename) { verify_dir(std::string(dirname), std::string(base_filename)); } void jdir::verify_dir(const std::string& dirname, const std::string& base_filename) { if (!is_dir(dirname)) { std::ostringstream oss; oss << "dir=\"" << dirname << "\""; throw jexception(jerrno::JERR_JDIR_NOTDIR, oss.str(), "jdir", "verify_dir"); } // Read jinf file, then verify all journal files are present jinf ji(dirname + "/" + base_filename + "." + JRNL_INFO_EXTENSION, true); for (u_int16_t fnum=0; fnum < ji.num_jfiles(); fnum++) { std::ostringstream oss; oss << dirname << "/" << base_filename << "."; oss << std::setw(4) << std::setfill('0') << std::hex << fnum; oss << "." << JRNL_DATA_EXTENSION; if (!exists(oss.str())) throw jexception(jerrno::JERR_JDIR_NOSUCHFILE, oss.str(), "jdir", "verify_dir"); } } // === delete_dir === void jdir::delete_dir(bool children_only) { delete_dir(_dirname, children_only); } void jdir::delete_dir(const char* dirname, bool children_only) { delete_dir(std::string(dirname), children_only); } void jdir::delete_dir(const std::string& dirname, bool children_only) { struct dirent* entry; struct stat s; DIR* dir = ::opendir(dirname.c_str()); if (!dir) { if (errno == ENOENT) // dir does not exist. return; std::ostringstream oss; oss << "dir=\"" << dirname << "\"" << FORMAT_SYSERR(errno); throw jexception(jerrno::JERR_JDIR_OPENDIR, oss.str(), "jdir", "delete_dir"); } else { while ((entry = ::readdir(dir)) != 0) { // Ignore . and .. if (std::strcmp(entry->d_name, ".") != 0 && std::strcmp(entry->d_name, "..") != 0) { std::string full_name(dirname + "/" + entry->d_name); if (::lstat(full_name.c_str(), &s)) { ::closedir(dir); std::ostringstream oss; oss << "stat: file=\"" << full_name << "\"" << FORMAT_SYSERR(errno); throw jexception(jerrno::JERR_JDIR_STAT, oss.str(), "jdir", "delete_dir"); } if (S_ISREG(s.st_mode) || S_ISLNK(s.st_mode)) // This is a file or slink { if(::unlink(full_name.c_str())) { ::closedir(dir); std::ostringstream oss; oss << "unlink: file=\"" << entry->d_name << "\"" << FORMAT_SYSERR(errno); throw jexception(jerrno::JERR_JDIR_UNLINK, oss.str(), "jdir", "delete_dir"); } } else if (S_ISDIR(s.st_mode)) // This is a dir { delete_dir(full_name); } else // all other types, throw up! { ::closedir(dir); std::ostringstream oss; oss << "file=\"" << entry->d_name << "\" is not a dir, file or slink."; oss << " (mode=0x" << std::hex << s.st_mode << std::dec << ")"; throw jexception(jerrno::JERR_JDIR_BADFTYPE, oss.str(), "jdir", "delete_dir"); } } } // FIXME: Find out why this fails with false alarms/errors from time to time... // While commented out, there is no error capture from reading dir entries. // check_err(errno, dir, dirname, "delete_dir"); } // Now dir is empty, close and delete it close_dir(dir, dirname, "delete_dir"); if (!children_only) if (::rmdir(dirname.c_str())) { std::ostringstream oss; oss << "dir=\"" << dirname << "\"" << FORMAT_SYSERR(errno); throw jexception(jerrno::JERR_JDIR_RMDIR, oss.str(), "jdir", "delete_dir"); } } std::string jdir::create_bak_dir(const std::string& dirname, const std::string& base_filename) { DIR* dir = ::opendir(dirname.c_str()); long dir_num = 0L; if (!dir) { std::ostringstream oss; oss << "dir=\"" << dirname << "\"" << FORMAT_SYSERR(errno); throw jexception(jerrno::JERR_JDIR_OPENDIR, oss.str(), "jdir", "create_bak_dir"); } struct dirent* entry; while ((entry = ::readdir(dir)) != 0) { // Ignore . and .. if (std::strcmp(entry->d_name, ".") != 0 && std::strcmp(entry->d_name, "..") != 0) { if (std::strlen(entry->d_name) == base_filename.size() + 10) // Format: basename.bak.XXXX { std::ostringstream oss; oss << "_" << base_filename << ".bak."; if (std::strncmp(entry->d_name, oss.str().c_str(), base_filename.size() + 6) == 0) { long this_dir_num = std::strtol(entry->d_name + base_filename.size() + 6, 0, 16); if (this_dir_num > dir_num) dir_num = this_dir_num; } } } } // FIXME: Find out why this fails with false alarms/errors from time to time... // While commented out, there is no error capture from reading dir entries. // check_err(errno, dir, dirname, "create_bak_dir"); close_dir(dir, dirname, "create_bak_dir"); std::ostringstream dn; dn << dirname << "/_" << base_filename << ".bak." << std::hex << std::setw(4) << std::setfill('0') << ++dir_num; if (::mkdir(dn.str().c_str(), S_IRWXU | S_IRWXG | S_IROTH)) { std::ostringstream oss; oss << "dir=\"" << dn.str() << "\"" << FORMAT_SYSERR(errno); throw jexception(jerrno::JERR_JDIR_MKDIR, oss.str(), "jdir", "create_bak_dir"); } return std::string(dn.str()); } bool jdir::is_dir(const char* name) { struct stat s; if (::stat(name, &s)) { std::ostringstream oss; oss << "file=\"" << name << "\"" << FORMAT_SYSERR(errno); throw jexception(jerrno::JERR_JDIR_STAT, oss.str(), "jdir", "is_dir"); } return S_ISDIR(s.st_mode); } bool jdir::is_dir(const std::string& name) { return is_dir(name.c_str()); } bool jdir::exists(const char* name) { struct stat s; if (::stat(name, &s)) { if (errno == ENOENT) // No such dir or file return false; // Throw for any other condition std::ostringstream oss; oss << "file=\"" << name << "\"" << FORMAT_SYSERR(errno); throw jexception(jerrno::JERR_JDIR_STAT, oss.str(), "jdir", "exists"); } return true; } bool jdir::exists(const std::string& name) { return exists(name.c_str()); } void jdir::check_err(const int err_num, DIR* dir, const std::string& dir_name, const std::string& fn_name) { if (err_num) { std::ostringstream oss; oss << "dir=\"" << dir_name << "\"" << FORMAT_SYSERR(err_num); ::closedir(dir); // Try to close, it makes no sense to trap errors here... throw jexception(jerrno::JERR_JDIR_READDIR, oss.str(), "jdir", fn_name); } } void jdir::close_dir(DIR* dir, const std::string& dir_name, const std::string& fn_name) { if (::closedir(dir)) { std::ostringstream oss; oss << "dir=\"" << dir_name << "\"" << FORMAT_SYSERR(errno); throw jexception(jerrno::JERR_JDIR_CLOSEDIR, oss.str(), "jdir", fn_name); } } std::ostream& operator<<(std::ostream& os, const jdir& jdir) { os << jdir._dirname; return os; } std::ostream& operator<<(std::ostream& os, const jdir* jdirPtr) { os << jdirPtr->_dirname; return os; } } // namespace journal } // namespace mrg qpid-cpp-store-debian-0.16/lib/jrnl/lpmgr.hpp0000644000176300017630000003527011446152364020115 0ustar cajuscajus/** * \file lpmgr.hpp * * Qpid asynchronous store plugin library * * Class mrg::journal::lpmgr. See class documentation for details. * * \author Kim van der Riet * * Copyright (c) 2008, 2009 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #ifndef mrg_journal_lpmgr_hpp #define mrg_journal_lpmgr_hpp namespace mrg { namespace journal { class jcntl; class lpmgr; } } #include "jrnl/fcntl.hpp" #include namespace mrg { namespace journal { /** * \brief LFID-PFID manager. This class maps the logical file id (lfid) to the physical file id (pfid) so that files * may be inserted into the file ring buffer in (nearly) arbitrary logical locations while the physical ids continue * to be appended. NOTE: NOT THREAD SAFE. * * The entire functionality of the LFID-PFID manager is to maintain an array of pointers to fcntl objects which have * a one-to-one relationship to the physical %journal files. The logical file id (lfid) is used as an index to the * array to read the mapped physical file id (pfid). By altering the order of these pointers within the array, the * mapping of logical to physical files may be altered. This can be used to allow for the logical insertion of * %journal files into a ring buffer, even though the physical file ids must be appended to those that preceded them. * * Since the insert() operation uses after-lfid as its position parameter, it is not possible to insert before lfid * 0 - i.e. It is only possible to insert after an existing lfid. Consequently, lfid 0 and pfid 0 are always * coincident in a %journal. Note, however, that inserting before lfid 0 is logically equivilent to inserting after * the last lfid. * * When one or more files are inserted after a particular lfid, the lfids of the following files are incremented. The * pfids of the inserted files follow those of all existing files, thus leading to a lfid-pfid discreppancy (ie no * longer a one-to-one mapping): * * Example: Before insertion, %journal file headers would look as follows: *
    *          Logical view (sorted by lfid):               Physical view (sorted by pfid):
    *          +---+---+---+---+---+---+                    +---+---+---+---+---+---+
    * pfid --> | 0 | 1 | 2 | 3 | 4 | 5 |           pfid --> | 0 | 1 | 2 | 3 | 4 | 5 |
    * lfid --> | 0 | 1 | 2 | 3 | 4 | 5 |           lfid --> | 0 | 1 | 2 | 3 | 4 | 5 |
    *          +---+---+---+---+---+---+                    +---+---+---+---+---+---+
    * 
* * After insertion of 2 files after lid 2 (marked with *s): *
    *          Logical view (sorted by lfid):               Physical view (sorted by pfid):
    *          +---+---+---+---+---+---+---+---+            +---+---+---+---+---+---+---+---+
    * pfid --> | 0 | 1 | 2 |*6*|*7*| 3 | 4 | 5 |   pfid --> | 0 | 1 | 2 | 3 | 4 | 5 |*6*|*7*|
    * lfid --> | 0 | 1 | 2 |*3*|*4*| 5 | 6 | 7 |   lfid --> | 0 | 1 | 2 | 5 | 6 | 7 |*3*|*4*|
    *          +---+---+---+---+---+---+---+---+            +---+---+---+---+---+---+---+---+
    * 
* * The insert() function updates the internal map immediately, but the physical files (which have both the pfid and * lfid written into the file header) are only updated as they are overwritten in the normal course of enqueueing * and dequeueing messages. If the %journal should fail after insertion but before the files following those inserted * are overwritten, then duplicate lfids will be present (though no duplicate pfids are possible). The overwrite * indicator (owi) flag and the pfid numbers may be used to resolve the ambiguity and determine the logically earlier * lfid in this case. * * Example: Before insertion, the current active write file being lfid/pfid 2 as determined by the owi flag, %journal * file headers would look as follows: *
    *          Logical view (sorted by lfid):               Physical view (sorted by pfid):
    *          +---+---+---+---+---+---+                    +---+---+---+---+---+---+
    * pfid --> | 0 | 1 | 2 | 3 | 4 | 5 |           pfid --> | 0 | 1 | 2 | 3 | 4 | 5 |
    * lfid --> | 0 | 1 | 2 | 3 | 4 | 5 |           lfid --> | 0 | 1 | 2 | 3 | 4 | 5 |
    *  owi --> | t | t | t | f | f | f |            owi --> | t | t | t | f | f | f |
    *          +---+---+---+---+---+---+                    +---+---+---+---+---+---+
    * 
* * After inserting 2 files after lfid 2 and then 3 (the newly inserted file) - marked with *s: *
    *          Logical view (sorted by lfid):               Physical view (sorted by pfid):
    *          +---+---+---+---+---+---+---+---+            +---+---+---+---+---+---+---+---+
    * pfid --> | 0 | 1 | 2 |*6*|*7*| 3 | 4 | 5 |   pfid --> | 0 | 1 | 2 | 3 | 4 | 5 |*3*|*4*|
    * lfid --> | 0 | 1 | 2 |*3*|*4*| 3 | 4 | 5 |   lfid --> | 0 | 1 | 2 | 3 | 4 | 5 |*3*|*4*|
    *  owi --> | t | t | t | t | t | f | f | f |    owi --> | t | t | t | f | f | f | t | t |
    *          +---+---+---+---+---+---+---+---+            +---+---+---+---+---+---+---+---+
    * 
* * If a broker failure occurs at this point, then there are two independent tests that may be made to resolve * duplicate lfids during recovery in such cases: *
    *
  1. The correct lfid has owi flag that matches that of pfid/lfid 0
  2. *
  3. The most recently inserted (hence correct) lfid has pfids that are higher than the duplicate that was not * overwritten
  4. *
* * NOTE: NOT THREAD SAFE. Provide external thread protection if used in multi-threaded environments. */ class lpmgr { public: /** * \brief Function pointer to function that will create a new fcntl object and return its pointer. * * \param jcp Pointer to jcntl instance from which journal file details will be obtained. * \param lfid Logical file ID for new fcntl instance. * \param pfid Physical file ID for file associated with new fcntl instance. * \param rdp Pointer to rcvdat instance which conatins recovery information for new fcntl instance when * recovering an existing file, or null if a new file is to be created. */ typedef fcntl* (new_obj_fn_ptr)(jcntl* const jcp, const u_int16_t lfid, const u_int16_t pfid, const rcvdat* const rdp); private: bool _ae; ///< Auto-expand mode u_int16_t _ae_max_jfiles; ///< Max file count for auto-expansion; 0 = no limit std::vector _fcntl_arr; ///< Array of pointers to fcntl objects public: lpmgr(); virtual ~lpmgr(); /** * \brief Initialize from scratch for a known number of %journal files. All lfid values are identical to pfid * values (which is normal before any inserts have occurred). * * \param num_jfiles Number of files to be created, and consequently the number of fcntl objects in array * _fcntl_arr. * \param ae If true, allows auto-expansion; if false, disables auto-expansion. * \param ae_max_jfiles The maximum number of files allowed for auto-expansion. Cannot be lower than the current * number of files. However, a zero value disables the limit checks, and allows unlimited * expansion. * \param jcp Pointer to jcntl instance. This is used to find the file path and base filename so that * new files may be created. * \param fp Pointer to function which creates and returns a pointer to a new fcntl object (and hence * causes a new %journal file to be created). */ void initialize(const u_int16_t num_jfiles, const bool ae, const u_int16_t ae_max_jfiles, jcntl* const jcp, new_obj_fn_ptr fp); /** * \brief Initialize from a known lfid-pfid map pfid_list (within rcvdat param rd), which is usually obtained * from a recover. The index of pfid_list is the logical file id (lfid); the value contained in the vector is * the physical file id (pfid). * * \param rd Ref to rcvdat struct which contains recovery data and the pfid_list. * \param jcp Pointer to jcntl instance. This is used to find the file path and base filename so that * new files may be created. * \param fp Pointer to function which creates and returns a pointer to a new fcntl object (and hence * causes a new %journal file to be created). */ void recover(const rcvdat& rd, jcntl* const jcp, new_obj_fn_ptr fp); /** * \brief Insert num_jfiles files after lfid index after_lfid. This causes all lfids after after_lfid to be * increased by num_jfiles. * * Note that it is not possible to insert before lfid 0, and thus lfid 0 should always point to pfid 0. * Inserting before lfid 0 is logically equivilent to inserting after the last lfid in a circular buffer. * * \param after_lfid Lid index after which to insert file(s). * \param jcp Pointer to jcntl instance. This is used to find the file path and base filename so that * new files may be created. * \param fp Pointer to function which creates and returns a pointer to a new fcntl object (and hence * causes a new %journal file to be created). * \param num_jfiles The number of files by which to increase. */ void insert(const u_int16_t after_lfid, jcntl* const jcp, new_obj_fn_ptr fp, const u_int16_t num_jfiles = 1); /** * \brief Clears _fcntl_arr and deletes all fcntl instances. */ void finalize(); /** * \brief Returns true if initialized; false otherwise. After construction, will return false until initialize() * is called; thereafter true until finalize() is called, whereupon it will return false again. * * \return True if initialized; false otherwise. */ inline bool is_init() const { return _fcntl_arr.size() > 0; } /** * \brief Returns true if auto-expand mode is enabled; false if not. * * \return True if auto-expand mode is enabled; false if not. */ inline bool is_ae() const { return _ae; } /** * \brief Sets the auto-expand mode to enabled if ae is true, to disabled otherwise. The value of _ae_max_jfiles * must be valid to succeed (i.e. _ae_max_jfiles must be greater than the current number of files or be zero). * * \param ae If true will enable auto-expand mode; if false will disable it. */ void set_ae(const bool ae); /** * \brief Returns the number of %journal files, including any that were appended or inserted since * initialization. * * \return Number of %journal files if initialized; 0 otherwise. */ inline u_int16_t num_jfiles() const { return static_cast(_fcntl_arr.size()); } /** * \brief Returns the maximum number of files allowed for auto-expansion. * * \return Maximum number of files allowed for auto-expansion. A zero value represents a disabled limit * - i.e. unlimited expansion. */ inline u_int16_t ae_max_jfiles() const { return _ae_max_jfiles; } /** * \brief Sets the maximum number of files allowed for auto-expansion. A zero value disables the limit. * * \param ae_max_jfiles The maximum number of files allowed for auto-expansion. Cannot be lower than the current * number of files. However, a zero value disables the limit checks, and allows unlimited * expansion. */ void set_ae_max_jfiles(const u_int16_t ae_max_jfiles); /** * \brief Calculates the number of future files available for auto-expansion. * * \return The number of future files available for auto-expansion. */ u_int16_t ae_jfiles_rem() const; /** * \brief Get a pointer to fcntl instance for a given lfid. * * \return Pointer to fcntl object corresponding to logical file id lfid, or 0 if lfid is out of range * (greater than number of files in use). */ inline fcntl* get_fcntlp(const u_int16_t lfid) const { if (lfid >= _fcntl_arr.size()) return 0; return _fcntl_arr[lfid]; } // Testing functions void get_pfid_list(std::vector& pfid_list) const; void get_lfid_list(std::vector& lfid_list) const; protected: /** * \brief Append num_jfiles files to the end of the logical and file id sequence. This is similar to extending * the from-scratch initialization. * * \param jcp Pointer to jcntl instance. This is used to find the file path and base filename so that * new files may be created. * \param fp Pointer to function which creates and returns a pointer to a new fcntl object (and hence * causes a new %journal file to be created). * \param num_jfiles The number of files by which to increase. */ void append(jcntl* const jcp, new_obj_fn_ptr fp, const u_int16_t num_jfiles = 1); }; } // namespace journal } // namespace mrg #endif // ifndef mrg_journal_lpmgr_hpp qpid-cpp-store-debian-0.16/lib/jrnl/lpmgr.cpp0000644000176300017630000001611511234102152020066 0ustar cajuscajus/** * \file lpmgr.cpp * * Qpid asynchronous store plugin library * * File containing code for class mrg::journal::lpmgr (non-logging file * handle), used for controlling journal log files. See comments in file * lpmgr.hpp for details. * * \author Kim van der Riet * * Copyright (c) 2008, 2009 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #include "jrnl/lpmgr.hpp" #include #include #include namespace mrg { namespace journal { lpmgr::lpmgr() : _ae(false), _ae_max_jfiles(0) {} lpmgr::~lpmgr() { finalize(); } void lpmgr::initialize(const u_int16_t num_jfiles, const bool ae, const u_int16_t ae_max_jfiles, jcntl* const jcp, new_obj_fn_ptr fp) { assert(jcp != 0); finalize(); // Validate params if (ae && ae_max_jfiles > 0 && ae_max_jfiles <= num_jfiles) { std::ostringstream oss; oss << "ae_max_jfiles (" << ae_max_jfiles << ") <= num_jfiles (" << num_jfiles << ")"; throw jexception(jerrno::JERR_LFMGR_BADAEFNUMLIM, oss.str(), "lpmgr", "initialize"); } _ae = ae; _ae_max_jfiles = ae_max_jfiles; const std::size_t num_res_files = ae ? (ae_max_jfiles ? ae_max_jfiles : JRNL_MAX_NUM_FILES) : num_jfiles; _fcntl_arr.reserve(num_res_files); append(jcp, fp, num_jfiles); } void lpmgr::recover(const rcvdat& rd, jcntl* const jcp, new_obj_fn_ptr fp) { assert(jcp != 0); finalize(); // Validate rd params if (rd._aemjf > 0 && rd._aemjf <= rd._njf) { std::ostringstream oss; oss << "ae_max_jfiles (" << rd._aemjf << ") <= num_jfiles (" << rd._njf << ")"; throw jexception(jerrno::JERR_LFMGR_BADAEFNUMLIM, oss.str(), "lpmgr", "recover"); } _ae = rd._ae; _ae_max_jfiles = rd._aemjf; const std::size_t num_res_files = rd._ae ? (rd._aemjf ? rd._aemjf : JRNL_MAX_NUM_FILES) : rd._njf; _fcntl_arr.reserve(num_res_files); _fcntl_arr.assign(rd._njf, 0); std::vector lfid_list(rd._fid_list.size(), 0); for (std::size_t lid = 0; lid < rd._fid_list.size(); lid++) lfid_list[rd._fid_list[lid]] = lid; // NOTE: rd._fid_list may be smaller than rd._njf (journal may be empty or not yet file-cycled) for (std::size_t pfid = 0; pfid < rd._njf; pfid++) if (pfid < rd._fid_list.size()) _fcntl_arr[lfid_list[pfid]] = fp(jcp, lfid_list[pfid], pfid, &rd); else _fcntl_arr[pfid] = fp(jcp, pfid, pfid, &rd); } void lpmgr::insert(const u_int16_t after_lfid, jcntl* const jcp, new_obj_fn_ptr fp, const u_int16_t num_jfiles) { assert(jcp != 0); assert(after_lfid < _fcntl_arr.size()); if (!_ae) throw jexception(jerrno::JERR_LFMGR_AEDISABLED, "lpmgr", "insert"); if (num_jfiles == 0) return; std::size_t pfid = _fcntl_arr.size(); const u_int16_t eff_ae_max_jfiles = _ae_max_jfiles ? _ae_max_jfiles : JRNL_MAX_NUM_FILES; if (pfid + num_jfiles > eff_ae_max_jfiles) { std::ostringstream oss; oss << "num_files=" << pfid << " incr=" << num_jfiles << " limit=" << _ae_max_jfiles; throw jexception(jerrno::JERR_LFMGR_AEFNUMLIMIT, oss.str(), "lpmgr", "insert"); } for (std::size_t lid = after_lfid + 1; lid <= after_lfid + num_jfiles; lid++, pfid++) _fcntl_arr.insert(_fcntl_arr.begin() + lid, fp(jcp, lid, pfid, 0)); for (std::size_t lid = after_lfid + num_jfiles + 1; lid < _fcntl_arr.size(); lid++) { fcntl* p = _fcntl_arr[lid]; assert(p != 0); p->set_lfid(p->lfid() + num_jfiles); } } void lpmgr::finalize() { for (u_int32_t i = 0; i < _fcntl_arr.size(); i++) delete _fcntl_arr[i]; _fcntl_arr.clear(); _ae = false; _ae_max_jfiles = 0; } void lpmgr::set_ae(const bool ae) { if (ae && _ae_max_jfiles > 0 && _ae_max_jfiles <= _fcntl_arr.size()) { std::ostringstream oss; oss << "ae_max_jfiles (" << _ae_max_jfiles << ") <= _fcntl_arr.size (" << _fcntl_arr.size() << ")"; throw jexception(jerrno::JERR_LFMGR_BADAEFNUMLIM, oss.str(), "lpmgr", "set_ae"); } if (ae && _fcntl_arr.max_size() < _ae_max_jfiles) _fcntl_arr.reserve(_ae_max_jfiles ? _ae_max_jfiles : JRNL_MAX_NUM_FILES); _ae = ae; } void lpmgr::set_ae_max_jfiles(const u_int16_t ae_max_jfiles) { if (_ae && ae_max_jfiles > 0 && ae_max_jfiles <= _fcntl_arr.size()) { std::ostringstream oss; oss << "ae_max_jfiles (" << _ae_max_jfiles << ") <= _fcntl_arr.size() (" << _fcntl_arr.size() << ")"; throw jexception(jerrno::JERR_LFMGR_BADAEFNUMLIM, oss.str(), "lpmgr", "set_ae_max_jfiles"); } if (_ae && _fcntl_arr.max_size() < ae_max_jfiles) _fcntl_arr.reserve(ae_max_jfiles ? ae_max_jfiles : JRNL_MAX_NUM_FILES); _ae_max_jfiles = ae_max_jfiles; } u_int16_t lpmgr::ae_jfiles_rem() const { if (_ae_max_jfiles > _fcntl_arr.size()) return _ae_max_jfiles - _fcntl_arr.size(); if (_ae_max_jfiles == 0) return JRNL_MAX_NUM_FILES - _fcntl_arr.size(); return 0; } // Testing functions void lpmgr::get_pfid_list(std::vector& pfid_list) const { pfid_list.clear(); for (std::size_t i = 0; i < _fcntl_arr.size(); i++) pfid_list.push_back(_fcntl_arr[i]->pfid()); } void lpmgr::get_lfid_list(std::vector& lfid_list) const { lfid_list.clear(); lfid_list.assign(_fcntl_arr.size(), 0); for (std::size_t i = 0; i < _fcntl_arr.size(); i++) lfid_list[_fcntl_arr[i]->pfid()] = i; } // === protected fns === void lpmgr::append(jcntl* const jcp, new_obj_fn_ptr fp, const u_int16_t num_jfiles) { std::size_t s = _fcntl_arr.size(); if (_ae_max_jfiles && s + num_jfiles > _ae_max_jfiles) { std::ostringstream oss; oss << "num_files=" << s << " incr=" << num_jfiles << " limit=" << _ae_max_jfiles; throw jexception(jerrno::JERR_LFMGR_AEFNUMLIMIT, oss.str(), "lpmgr", "append"); } for (std::size_t i = s; i < s + num_jfiles; i++) _fcntl_arr.push_back(fp(jcp, i, i, 0)); } } // namespace journal } // namespace mrg qpid-cpp-store-debian-0.16/lib/jrnl/data_tok.hpp0000644000176300017630000001545511372046135020561 0ustar cajuscajus/** * \file data_tok.hpp * * Qpid asynchronous store plugin library * * File containing code for class mrg::journal::data_tok (data block token). * See class documentation for details. * * \author Kim van der Riet * * Copyright (c) 2007, 2008 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #ifndef mrg_journal_data_tok_hpp #define mrg_journal_data_tok_hpp namespace mrg { namespace journal { class data_tok; } } #include #include #include "jrnl/smutex.hpp" #include #include #include namespace mrg { namespace journal { /** * \class data_tok * \brief Data block token (data_tok) used to track wstate of a data block through asynchronous * I/O process */ class data_tok { public: // TODO: Fix this, separate write state from operation // ie: wstate = NONE, CACHED, PART, SUBM, COMPL // op = ENQUEUE, DEQUEUE, ABORT, COMMIT enum write_state { NONE, ///< Data block not sent to journal ENQ_CACHED, ///< Data block enqueue written to page cache ENQ_PART, ///< Data block part-submitted to AIO, waiting for page buffer to free up ENQ_SUBM, ///< Data block enqueue submitted to AIO ENQ, ///< Data block enqueue AIO write complete (enqueue complete) DEQ_CACHED, ///< Data block dequeue written to page cache DEQ_PART, ///< Data block part-submitted to AIO, waiting for page buffer to free up DEQ_SUBM, ///< Data block dequeue submitted to AIO DEQ, ///< Data block dequeue AIO write complete (dequeue complete) ABORT_CACHED, ABORT_PART, ABORT_SUBM, ABORTED, COMMIT_CACHED, COMMIT_PART, COMMIT_SUBM, COMMITTED }; enum read_state { UNREAD, ///< Data block not read READ_PART, ///< Data block is part-read; waiting for page buffer to fill SKIP_PART, ///< Prev. dequeued dblock is part-skipped; waiting for page buffer to fill READ ///< Data block is fully read }; protected: static smutex _mutex; static u_int64_t _cnt; u_int64_t _icnt; write_state _wstate; ///< Enqueued / dequeued state of data read_state _rstate; ///< Read state of data std::size_t _dsize; ///< Data size in bytes u_int32_t _dblks_written; ///< Data blocks read/written u_int32_t _dblks_read; ///< Data blocks read/written u_int32_t _pg_cnt; ///< Page counter - incr for each page containing part of data u_int16_t _fid; ///< FID containing header of enqueue record u_int64_t _rid; ///< RID of data set by enqueue operation std::string _xid; ///< XID set by enqueue operation u_int64_t _dequeue_rid; ///< RID of data set by dequeue operation bool _external_rid; ///< Flag to indicate external setting of rid public: data_tok(); virtual ~data_tok(); inline u_int64_t id() const { return _icnt; } inline write_state wstate() const { return _wstate; } const char* wstate_str() const; static const char* wstate_str(write_state wstate); inline read_state rstate() const { return _rstate; } const char* rstate_str() const; static const char* rstate_str(read_state rstate); inline bool is_writable() const { return _wstate == NONE || _wstate == ENQ_PART; } inline bool is_enqueued() const { return _wstate == ENQ; } inline bool is_readable() const { return _wstate == ENQ; } inline bool is_read() const { return _rstate == READ; } inline bool is_dequeueable() const { return _wstate == ENQ || _wstate == DEQ_PART; } inline void set_wstate(const write_state wstate) { _wstate = wstate; } void set_rstate(const read_state rstate); inline std::size_t dsize() const { return _dsize; } inline void set_dsize(std::size_t dsize) { _dsize = dsize; } inline u_int32_t dblocks_written() const { return _dblks_written; } inline void incr_dblocks_written(u_int32_t dblks_written) { _dblks_written += dblks_written; } inline void set_dblocks_written(u_int32_t dblks_written) { _dblks_written = dblks_written; } inline u_int32_t dblocks_read() const { return _dblks_read; } inline void incr_dblocks_read(u_int32_t dblks_read) { _dblks_read += dblks_read; } inline void set_dblocks_read(u_int32_t dblks_read) { _dblks_read = dblks_read; } inline u_int32_t pg_cnt() const { return _pg_cnt; } inline u_int32_t incr_pg_cnt() { return ++_pg_cnt; } inline u_int32_t decr_pg_cnt() { assert(_pg_cnt != 0); return --_pg_cnt; } inline u_int16_t fid() const { return _fid; } inline void set_fid(const u_int16_t fid) { _fid = fid; } inline u_int64_t rid() const { return _rid; } inline void set_rid(const u_int64_t rid) { _rid = rid; } inline u_int64_t dequeue_rid() const {return _dequeue_rid; } inline void set_dequeue_rid(const u_int64_t rid) { _dequeue_rid = rid; } inline bool external_rid() const { return _external_rid; } inline void set_external_rid(const bool external_rid) { _external_rid = external_rid; } inline bool has_xid() const { return !_xid.empty(); } inline const std::string& xid() const { return _xid; } inline void clear_xid() { _xid.clear(); } inline void set_xid(const std::string& xid) { _xid.assign(xid); } inline void set_xid(const void* xidp, const std::size_t xid_len) { _xid.assign((const char*)xidp, xid_len); } void reset(); // debug aid std::string status_str() const; }; } // namespace journal } // namespace mrg #endif // ifndef mrg_journal_data_tok_hpp qpid-cpp-store-debian-0.16/lib/jrnl/wrfc.hpp0000644000176300017630000001434011451122067017721 0ustar cajuscajus/** * \file wrfc.hpp * * Qpid asynchronous store plugin library * * File containing code for class mrg::journal::wrfc (write rotating * file controller). See class documentation for details. * * \author Kim van der Riet * * Copyright (c) 2007, 2008, 2009 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #ifndef mrg_journal_wrfc_hpp #define mrg_journal_wrfc_hpp namespace mrg { namespace journal { class wrfc; } } #include #include "jrnl/enums.hpp" #include "jrnl/rrfc.hpp" namespace mrg { namespace journal { /** * \class wrfc * \brief Class to handle write management of a journal rotating file controller. */ class wrfc : public rfc { private: u_int32_t _fsize_sblks; ///< Size of journal files in sblks u_int32_t _fsize_dblks; ///< Size of journal files in dblks u_int32_t _enq_cap_offs_dblks; ///< Enqueue capacity offset u_int64_t _rid; ///< Master counter for record ID (rid) bool _reset_ok; ///< Flag set when reset succeeds bool _owi; ///< Overwrite indicator bool _frot; ///< Flag is true for first rotation, false otherwise public: wrfc(const lpmgr* lpmp); virtual ~wrfc(); /** * \brief Initialize the controller. * \param fsize_sblks Size of each journal file in sblks. * \param rdp Struct carrying restore information. Optional for non-restore use, defaults to 0 (NULL). */ void initialize(const u_int32_t fsize_sblks, rcvdat* rdp = 0); /** * \brief Rotate active file controller to next file in rotating file group. * \exception jerrno::JERR__NINIT if called before calling initialize(). */ iores rotate(); /** * \brief Returns the index of the earliest complete file within the rotating * file group. Unwritten files are excluded. The currently active file is * excluded unless it is the only written file. */ u_int16_t earliest_index() const; /** * \brief Determines if a proposed write would cause the enqueue threshold to be exceeded. * * The following routine finds whether the next write will take the write pointer to beyond the * enqueue limit threshold. The following illustrates how this is achieved. *
        * Current file index: 4                         +---+----------+
        * X's mark still-enqueued records               |msg| 1-thresh |
        * msg = current msg size + unwritten cache      +---+----------+
        * thresh = JRNL_ENQ_THRESHOLD as a fraction     ^              V
        *            +-------+-------+-------+-------+--+----+-------+-+-----+-------+
        * file num ->|   0   |   1   |   2   |   3   |   4   |   5   |   6   |   7   |
        * enq recs ->| X  XX |XX XXX |XX XXXX|XXXXXXX|XX     |       |       |     X |
        *            +-------+-------+-------+-------+--+----+-------+-+-----+-------+
        *                                               ^        ^       ^
        *                                  subm_dblks --+        |       |
        *                                                      These files must be free of enqueues
        *                                                      If not, return true.
        * 
* \param enq_dsize_dblks Proposed size of write in dblocks */ bool enq_threshold(const u_int32_t enq_dsize_dblks) const; inline u_int64_t rid() const { return _rid; } inline u_int64_t get_incr_rid() { return _rid++; } bool wr_reset(); inline bool is_wr_reset() const { return _reset_ok; } inline bool owi() const { return _owi; } inline bool frot() const { return _frot; } // Convenience access methods to current file controller inline int fh() const { return _curr_fc->wr_fh(); } inline u_int32_t subm_cnt_dblks() const { return _curr_fc->wr_subm_cnt_dblks(); } inline std::size_t subm_offs() const { return _curr_fc->wr_subm_offs(); } inline u_int32_t add_subm_cnt_dblks(u_int32_t a) { return _curr_fc->add_wr_subm_cnt_dblks(a); } inline u_int32_t cmpl_cnt_dblks() const { return _curr_fc->wr_cmpl_cnt_dblks(); } inline std::size_t cmpl_offs() const { return _curr_fc->wr_cmpl_offs(); } inline u_int32_t add_cmpl_cnt_dblks(u_int32_t a) { return _curr_fc->add_wr_cmpl_cnt_dblks(a); } inline u_int16_t aio_cnt() const { return _curr_fc->aio_cnt(); } inline u_int16_t incr_aio_cnt() { return _curr_fc->incr_aio_cnt(); } inline u_int16_t decr_aio_cnt() { return _curr_fc->decr_aio_cnt(); } inline bool is_void() const { return _curr_fc->wr_void(); } inline bool is_empty() const { return _curr_fc->wr_empty(); } inline u_int32_t remaining_dblks() const { return _curr_fc->wr_remaining_dblks(); } inline bool is_full() const { return _curr_fc->is_wr_full(); }; inline bool is_compl() const { return _curr_fc->is_wr_compl(); }; inline u_int32_t aio_outstanding_dblks() const { return _curr_fc->wr_aio_outstanding_dblks(); } inline bool file_rotate() const { return _curr_fc->wr_file_rotate(); } // Debug aid std::string status_str() const; }; } // namespace journal } // namespace mrg #endif // ifndef mrg_journal_wrfc_hpp qpid-cpp-store-debian-0.16/lib/jrnl/pmgr.cpp0000644000176300017630000001370511142067437017732 0ustar cajuscajus/** * \file pmgr.cpp * * Qpid asynchronous store plugin library * * File containing code for class mrg::journal::pmgr (page manager). See * comments in file pmgr.hpp for details. * * \author Kim van der Riet * * Copyright (c) 2007, 2008, 2009 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #include "jrnl/pmgr.hpp" #include #include #include #include "jrnl/jcfg.hpp" #include "jrnl/jcntl.hpp" #include "jrnl/jerrno.hpp" #include namespace mrg { namespace journal { pmgr::page_cb::page_cb(u_int16_t index): _index(index), _state(UNUSED), _wdblks(0), _rdblks(0), _pdtokl(0), _wfh(0), _rfh(0), _pbuff(0) {} const char* pmgr::page_cb::state_str() const { switch(_state) { case UNUSED: return "UNUSED"; case IN_USE: return "IN_USE"; case AIO_PENDING: return "AIO_PENDING"; case AIO_COMPLETE: return "AIO_COMPLETE"; } return ""; } const u_int32_t pmgr::_sblksize = JRNL_SBLK_SIZE * JRNL_DBLK_SIZE; pmgr::pmgr(jcntl* jc, enq_map& emap, txn_map& tmap): _cache_pgsize_sblks(0), _cache_num_pages(0), _jc(jc), _emap(emap), _tmap(tmap), _page_base_ptr(0), _page_ptr_arr(0), _page_cb_arr(0), _aio_cb_arr(0), _aio_event_arr(0), _ioctx(0), _pg_index(0), _pg_cntr(0), _pg_offset_dblks(0), _aio_evt_rem(0), _cbp(0), _enq_rec(), _deq_rec(), _txn_rec() {} pmgr::~pmgr() { pmgr::clean(); } void pmgr::initialize(aio_callback* const cbp, const u_int32_t cache_pgsize_sblks, const u_int16_t cache_num_pages) { // As static use of this class keeps old values around, clean up first... pmgr::clean(); _pg_index = 0; _pg_cntr = 0; _pg_offset_dblks = 0; _aio_evt_rem = 0; _cache_pgsize_sblks = cache_pgsize_sblks; _cache_num_pages = cache_num_pages; _cbp = cbp; // 1. Allocate page memory (as a single block) std::size_t cache_pgsize = _cache_num_pages * _cache_pgsize_sblks * _sblksize; if (::posix_memalign(&_page_base_ptr, _sblksize, cache_pgsize)) { clean(); std::ostringstream oss; oss << "posix_memalign(): blksize=" << _sblksize << " size=" << cache_pgsize; oss << FORMAT_SYSERR(errno); throw jexception(jerrno::JERR__MALLOC, oss.str(), "pmgr", "initialize"); } // 2. Allocate array of page pointers _page_ptr_arr = (void**)std::malloc(_cache_num_pages * sizeof(void*)); MALLOC_CHK(_page_ptr_arr, "_page_ptr_arr", "pmgr", "initialize"); // 3. Allocate and initilaize page control block (page_cb) array _page_cb_arr = (page_cb*)std::malloc(_cache_num_pages * sizeof(page_cb)); MALLOC_CHK(_page_cb_arr, "_page_cb_arr", "pmgr", "initialize"); std::memset(_page_cb_arr, 0, _cache_num_pages * sizeof(page_cb)); // 5. Allocate IO control block (iocb) array _aio_cb_arr = (aio_cb*)std::malloc(_cache_num_pages * sizeof(aio_cb)); MALLOC_CHK(_aio_cb_arr, "_aio_cb_arr", "pmgr", "initialize"); // 6. Set page pointers in _page_ptr_arr, _page_cb_arr and iocbs to pages within page block for (u_int16_t i=0; i<_cache_num_pages; i++) { _page_ptr_arr[i] = (void*)((char*)_page_base_ptr + _cache_pgsize_sblks * _sblksize * i); _page_cb_arr[i]._index = i; _page_cb_arr[i]._state = UNUSED; _page_cb_arr[i]._pbuff = _page_ptr_arr[i]; _page_cb_arr[i]._pdtokl = new std::deque; _page_cb_arr[i]._pdtokl->clear(); _aio_cb_arr[i].data = (void*)&_page_cb_arr[i]; } // 7. Allocate io_event array, max one event per cache page plus one for each file const u_int16_t max_aio_evts = _cache_num_pages + _jc->num_jfiles(); _aio_event_arr = (aio_event*)std::malloc(max_aio_evts * sizeof(aio_event)); MALLOC_CHK(_aio_event_arr, "_aio_event_arr", "pmgr", "initialize"); // 8. Initialize AIO context if (int ret = aio::queue_init(max_aio_evts, &_ioctx)) { std::ostringstream oss; oss << "io_queue_init() failed: " << FORMAT_SYSERR(-ret); throw jexception(jerrno::JERR__AIO, oss.str(), "pmgr", "initialize"); } } void pmgr::clean() { // clean up allocated memory here if (_ioctx) aio::queue_release(_ioctx); std::free(_page_base_ptr); _page_base_ptr = 0; if (_page_cb_arr) { for (int i=0; i<_cache_num_pages; i++) delete _page_cb_arr[i]._pdtokl; std::free(_page_ptr_arr); _page_ptr_arr = 0; } std::free(_page_cb_arr); _page_cb_arr = 0; std::free(_aio_cb_arr); _aio_cb_arr = 0; std::free(_aio_event_arr); _aio_event_arr = 0; } const char* pmgr::page_state_str(page_state ps) { switch (ps) { case UNUSED: return "UNUSED"; case IN_USE: return "IN_USE"; case AIO_PENDING: return "AIO_PENDING"; case AIO_COMPLETE: return "AIO_COMPLETE"; } return ""; } } // namespace journal } // namespace mrg qpid-cpp-store-debian-0.16/lib/jrnl/lp_map.cpp0000644000176300017630000000426711142067437020240 0ustar cajuscajus/** * \file lp_map.cpp * * Qpid asynchronous store plugin library * * File containing code for class mrg::journal::lp_map (logical file map). See * comments in file lp_map.hpp for details. * * \author Kim van der Riet * * Copyright (c) 2008, 2009 Red Hat Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #include "jrnl/lp_map.hpp" #include "jrnl/jerrno.hpp" #include "jrnl/jexception.hpp" #include namespace mrg { namespace journal { lp_map::lp_map() : _map() {} lp_map::~lp_map() {} void lp_map::insert(u_int16_t lfid, u_int16_t pfid) { lfpair ip = lfpair(lfid, pfid); lfret ret = _map.insert(ip); if (ret.second == false) { std::ostringstream oss; oss << std::hex << "lfid=0x" << lfid << " pfid=0x" << pfid; throw jexception(jerrno::JERR_MAP_DUPLICATE, oss.str(), "lp_map", "insert"); } } void lp_map::get_pfid_list(std::vector& pfid_list) { for (lp_map_citr_t i = _map.begin(); i != _map.end(); i++) pfid_list.push_back(i->second); } // debug aid std::string lp_map::to_string() { std::ostringstream oss; oss << "{lfid:pfid "; for (lp_map_citr_t i=_map.begin(); i!=_map.end(); i++) { if (i != _map.begin()) oss << ", "; oss << (*i).first << ":" << (*i).second; } oss << "}"; return oss.str(); } } // namespace journal } // namespace mrg qpid-cpp-store-debian-0.16/lib/jrnl/rec_tail.hpp0000644000176300017630000000632411273576464020565 0ustar cajuscajus/** * \file rec_tail.hpp * * Qpid asynchronous store plugin library * * File containing code for class mrg::journal::rec_tail (record tail), used to * finalize a persistent record. The presence of a valid tail at the expected * position in the journal file indicates that the record write was completed. * * \author Kim van der Riet * * Copyright (c) 2007, 2008 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #ifndef mrg_journal_rec_tail_hpp #define mrg_journal_rec_tail_hpp #include #include "jrnl/jcfg.hpp" namespace mrg { namespace journal { #pragma pack(1) /** * \brief Struct for data common to the tail of all records. The magic number * used here is the binary inverse (1's complement) of the magic used in the * record header; this minimizes possible confusion with other headers that may * be present during recovery. The tail is used with all records that have either * XIDs or data - ie any size-variable content. Currently the only records that * do NOT use the tail are non-transactional dequeues and filler records. * * Record header info in binary format (12 bytes): *
    *   0                           7
    * +---+---+---+---+---+---+---+---+
    * |   ~(magic)    |      rid      |
    * +---+---+---+---+---+---+---+---+
    * |  rid (con't)  |
    * +---+---+---+---+
    * 
*/ struct rec_tail { u_int32_t _xmagic; ///< Binary inverse (1's complement) of hdr magic number u_int64_t _rid; ///< ID (rotating 64-bit counter) /** * \brief Default constructor, which sets all values to 0. */ inline rec_tail(): _xmagic(0xffffffff), _rid(0) {} /** * \brief Convenience constructor which initializes values during construction from * existing enq_hdr instance. */ inline rec_tail(const rec_hdr& h): _xmagic(~h._magic), _rid(h._rid) {} /** * \brief Convenience constructor which initializes values during construction. */ inline rec_tail(const u_int32_t xmagic, const u_int64_t rid): _xmagic(xmagic), _rid(rid) {} /** * \brief Returns the size of the header in bytes. */ inline static std::size_t size() { return sizeof(rec_tail); } }; #pragma pack() } // namespace journal } // namespace mrg #endif // ifndef mrg_journal_rec_tail_hpp qpid-cpp-store-debian-0.16/lib/jrnl/jexception.hpp0000644000176300017630000001130611372046135021132 0ustar cajuscajus/** * \file jexception.hpp * * Qpid asynchronous store plugin library * * Generic journal exception class mrg::journal::jexception (derived * from class std::exception). Intended to serve as a common exception * class for all more speicalized exceptions in the message journal. See * class documentation for details. * * \author Kim van der Riet * * Copyright (c) 2007, 2008 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #ifndef mrg_journal_jrnl_exception_hpp #define mrg_journal_jrnl_exception_hpp namespace mrg { namespace journal { class jexception; } } #include #include #include #include #include #include "jrnl/jerrno.hpp" #include #include #include // Macro for formatting commom system errors #define FORMAT_SYSERR(errno) " errno=" << errno << " (" << std::strerror(errno) << ")" #define MALLOC_CHK(ptr, var, cls, fn) if(ptr == 0) { \ clean(); \ std::ostringstream oss; \ oss << var << ": malloc() failed: " << FORMAT_SYSERR(errno); \ throw jexception(jerrno::JERR__MALLOC, oss.str(), cls, fn); \ } // TODO: The following is a temporary bug-tracking aid which forces a core. // Replace with the commented out version below when BZ484048 is resolved. #define PTHREAD_CHK(err, pfn, cls, fn) if(err != 0) { \ std::ostringstream oss; \ oss << cls << "::" << fn << "(): " << pfn; \ errno = err; \ ::perror(oss.str().c_str()); \ ::abort(); \ } /* #define PTHREAD_CHK(err, pfn, cls, fn) if(err != 0) { \ std::ostringstream oss; \ oss << pfn << " failed: " << FORMAT_SYSERR(err); \ throw jexception(jerrno::JERR__PTHREAD, oss.str(), cls, fn); \ } */ #define ASSERT(cond, msg) if(cond == 0) { \ std::cerr << msg << std::endl; \ ::abort(); \ } namespace mrg { namespace journal { /** * \class jexception * \brief Generic journal exception class */ class jexception : public std::exception { private: u_int32_t _err_code; std::string _additional_info; std::string _throwing_class; std::string _throwing_fn; std::string _what; void format(); public: jexception() throw (); jexception(const u_int32_t err_code) throw (); jexception(const char* additional_info) throw (); jexception(const std::string& additional_info) throw (); jexception(const u_int32_t err_code, const char* additional_info) throw (); jexception(const u_int32_t err_code, const std::string& additional_info) throw (); jexception(const u_int32_t err_code, const char* throwing_class, const char* throwing_fn) throw (); jexception(const u_int32_t err_code, const std::string& throwing_class, const std::string& throwing_fn) throw (); jexception(const u_int32_t err_code, const char* additional_info, const char* throwing_class, const char* throwing_fn) throw (); jexception(const u_int32_t err_code, const std::string& additional_info, const std::string& throwing_class, const std::string& throwing_fn) throw (); virtual ~jexception() throw (); virtual const char* what() const throw (); // override std::exception::what() inline u_int32_t err_code() const throw () { return _err_code; } inline const std::string additional_info() const throw () { return _additional_info; } inline const std::string throwing_class() const throw () { return _throwing_class; } inline const std::string throwing_fn() const throw () { return _throwing_fn; } friend std::ostream& operator<<(std::ostream& os, const jexception& je); friend std::ostream& operator<<(std::ostream& os, const jexception* jePtr); }; // class jexception } // namespace journal } // namespace mrg #endif // ifndef mrg_journal_jrnl_exception_hpp qpid-cpp-store-debian-0.16/lib/jrnl/aio.hpp0000644000176300017630000001321611423363204017530 0ustar cajuscajus/** * \file aio.hpp * * Qpid asynchronous store plugin library * * This file contains an encapsulation of the libaio interface used * by the journal. * * \author Kim van der Riet * * Copyright (c) 2007, 2008, 2009 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #ifndef mrg_journal_aio_hpp #define mrg_journal_aio_hpp #include #include #include #include namespace mrg { namespace journal { typedef iocb aio_cb; typedef io_event aio_event; /** * \brief This class is a C++ wrapper class for the libaio functions used by the journal. Note that only those * functions used by the journal are included here. This is not a complete implementation of all libaio functions. */ class aio { public: static inline int queue_init(int maxevents, io_context_t* ctxp) { return ::io_queue_init(maxevents, ctxp); } static inline int queue_release(io_context_t ctx) { return ::io_queue_release(ctx); } static inline int submit(io_context_t ctx, long nr, aio_cb* aios[]) { return ::io_submit(ctx, nr, aios); } static inline int getevents(io_context_t ctx, long min_nr, long nr, aio_event* events, timespec* const timeout) { return ::io_getevents(ctx, min_nr, nr, events, timeout); } /** * \brief This function allows iocbs to be initialized with a pointer that can be re-used. This prepares an * aio_cb struct for read use. (This is a wrapper for libaio's ::io_prep_pread() function.) * * \param aiocbp Pointer to the aio_cb struct to be prepared. * \param fd File descriptor to be used for read. * \param buf Pointer to buffer in which read data is to be placed. * \param count Number of bytes to read - buffer must be large enough. * \param offset Offset within file from which data will be read. */ static inline void prep_pread(aio_cb* aiocbp, int fd, void* buf, std::size_t count, int64_t offset) { ::io_prep_pread(aiocbp, fd, buf, count, offset); } /** * \brief Special version of libaio's io_prep_pread() which preserves the value of the data pointer. This allows * iocbs to be initialized with a pointer that can be re-used. This prepares a aio_cb struct for read use. * * \param aiocbp Pointer to the aio_cb struct to be prepared. * \param fd File descriptor to be used for read. * \param buf Pointer to buffer in which read data is to be placed. * \param count Number of bytes to read - buffer must be large enough. * \param offset Offset within file from which data will be read. */ static inline void prep_pread_2(aio_cb* aiocbp, int fd, void* buf, std::size_t count, int64_t offset) { std::memset((void*) ((char*) aiocbp + sizeof(void*)), 0, sizeof(aio_cb) - sizeof(void*)); aiocbp->aio_fildes = fd; aiocbp->aio_lio_opcode = IO_CMD_PREAD; aiocbp->aio_reqprio = 0; aiocbp->u.c.buf = buf; aiocbp->u.c.nbytes = count; aiocbp->u.c.offset = offset; } /** * \brief This function allows iocbs to be initialized with a pointer that can be re-used. This function prepares * an aio_cb struct for write use. (This is a wrapper for libaio's ::io_prep_pwrite() function.) * * \param aiocbp Pointer to the aio_cb struct to be prepared. * \param fd File descriptor to be used for write. * \param buf Pointer to buffer in which data to be written is located. * \param count Number of bytes to write. * \param offset Offset within file to which data will be written. */ static inline void prep_pwrite(aio_cb* aiocbp, int fd, void* buf, std::size_t count, int64_t offset) { ::io_prep_pwrite(aiocbp, fd, buf, count, offset); } /** * \brief Special version of libaio's io_prep_pwrite() which preserves the value of the data pointer. This allows * iocbs to be initialized with a pointer that can be re-used. This function prepares an aio_cb struct for write * use. * * \param aiocbp Pointer to the aio_cb struct to be prepared. * \param fd File descriptor to be used for write. * \param buf Pointer to buffer in which data to be written is located. * \param count Number of bytes to write. * \param offset Offset within file to which data will be written. */ static inline void prep_pwrite_2(aio_cb* aiocbp, int fd, void* buf, std::size_t count, int64_t offset) { std::memset((void*) ((char*) aiocbp + sizeof(void*)), 0, sizeof(aio_cb) - sizeof(void*)); aiocbp->aio_fildes = fd; aiocbp->aio_lio_opcode = IO_CMD_PWRITE; aiocbp->aio_reqprio = 0; aiocbp->u.c.buf = buf; aiocbp->u.c.nbytes = count; aiocbp->u.c.offset = offset; } }; } // namespace journal } // namespace mrg #endif // ifndef mrg_journal_aio_hpp qpid-cpp-store-debian-0.16/lib/jrnl/jerrno.hpp0000644000176300017630000002123511623467711020271 0ustar cajuscajus/** * \file jerrno.hpp * * Qpid asynchronous store plugin library * * File containing code for class mrg::journal::jerrno (journal error * codes). See class documentation for details. * * \author Kim van der Riet * * Copyright (c) 2007, 2008, 2009 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #ifndef mrg_journal_jerrno_hpp #define mrg_journal_jerrno_hpp namespace mrg { namespace journal { class jerrno; } } #include #include #include namespace mrg { namespace journal { /** * \class jerrno * \brief Class containing static error definitions and static map for error messages. */ class jerrno { static std::map _err_map; ///< Map of error messages static std::map::iterator _err_map_itr; ///< Iterator static bool _initialized; ///< Dummy flag, used to initialise map. public: // generic errors static const u_int32_t JERR__MALLOC; ///< Buffer memory allocation failed static const u_int32_t JERR__UNDERFLOW; ///< Underflow error static const u_int32_t JERR__NINIT; ///< Operation on uninitialized class static const u_int32_t JERR__AIO; ///< AIO failure static const u_int32_t JERR__FILEIO; ///< File read or write failure static const u_int32_t JERR__RTCLOCK; ///< Reading real-time clock failed static const u_int32_t JERR__PTHREAD; ///< pthread failure static const u_int32_t JERR__TIMEOUT; ///< Timeout waiting for an event static const u_int32_t JERR__UNEXPRESPONSE; ///< Unexpected response to call or event static const u_int32_t JERR__RECNFOUND; ///< Record not found static const u_int32_t JERR__NOTIMPL; ///< Not implemented // class jcntl static const u_int32_t JERR_JCNTL_STOPPED; ///< Operation on stopped journal static const u_int32_t JERR_JCNTL_READONLY; ///< Write operation on read-only journal static const u_int32_t JERR_JCNTL_AIOCMPLWAIT; ///< Timeout waiting for AIOs to complete static const u_int32_t JERR_JCNTL_UNKNOWNMAGIC; ///< Found record with unknown magic static const u_int32_t JERR_JCNTL_NOTRECOVERED; ///< Req' recover() to be called first static const u_int32_t JERR_JCNTL_RECOVERJFULL; ///< Journal data files full, cannot write static const u_int32_t JERR_JCNTL_OWIMISMATCH; ///< OWI change found in unexpected location // class jdir static const u_int32_t JERR_JDIR_NOTDIR; ///< Exists but is not a directory static const u_int32_t JERR_JDIR_MKDIR; ///< Directory creation failed static const u_int32_t JERR_JDIR_OPENDIR; ///< Directory open failed static const u_int32_t JERR_JDIR_READDIR; ///< Directory read failed static const u_int32_t JERR_JDIR_CLOSEDIR; ///< Directory close failed static const u_int32_t JERR_JDIR_RMDIR; ///< Directory delete failed static const u_int32_t JERR_JDIR_NOSUCHFILE; ///< File does not exist static const u_int32_t JERR_JDIR_FMOVE; ///< File move failed static const u_int32_t JERR_JDIR_STAT; ///< File stat failed static const u_int32_t JERR_JDIR_UNLINK; ///< File delete failed static const u_int32_t JERR_JDIR_BADFTYPE; ///< Bad or unknown file type (stat mode) // class fcntl static const u_int32_t JERR_FCNTL_OPENWR; ///< Unable to open file for write static const u_int32_t JERR_FCNTL_WRITE; ///< Unable to write to file static const u_int32_t JERR_FCNTL_CLOSE; ///< File close failed static const u_int32_t JERR_FCNTL_FILEOFFSOVFL; ///< Increased offset past file size static const u_int32_t JERR_FCNTL_CMPLOFFSOVFL; ///< Increased cmpl offs past subm offs static const u_int32_t JERR_FCNTL_RDOFFSOVFL; ///< Increased read offs past write offs // class lfmgr static const u_int32_t JERR_LFMGR_BADAEFNUMLIM; ///< Bad auto-expand file number limit static const u_int32_t JERR_LFMGR_AEFNUMLIMIT; ///< Exceeded auto-expand file number limit static const u_int32_t JERR_LFMGR_AEDISABLED; ///< Attempted to expand with auto-expand disabled // class rrfc static const u_int32_t JERR_RRFC_OPENRD; ///< Unable to open file for read // class jrec, enq_rec, deq_rec, txn_rec static const u_int32_t JERR_JREC_BADRECHDR; ///< Invalid data record header static const u_int32_t JERR_JREC_BADRECTAIL; ///< Invalid data record tail // class wmgr static const u_int32_t JERR_WMGR_BADPGSTATE; ///< Page buffer in illegal state. static const u_int32_t JERR_WMGR_BADDTOKSTATE; ///< Data token in illegal state. static const u_int32_t JERR_WMGR_ENQDISCONT; ///< Enq. new dtok when previous part compl. static const u_int32_t JERR_WMGR_DEQDISCONT; ///< Deq. new dtok when previous part compl. static const u_int32_t JERR_WMGR_DEQRIDNOTENQ; ///< Deq. rid not enqueued // class rmgr static const u_int32_t JERR_RMGR_UNKNOWNMAGIC; ///< Found record with unknown magic static const u_int32_t JERR_RMGR_RIDMISMATCH; ///< RID mismatch between rec and dtok //static const u_int32_t JERR_RMGR_FIDMISMATCH; ///< FID mismatch between emap and rrfc static const u_int32_t JERR_RMGR_ENQSTATE; ///< Attempted read when wstate not ENQ static const u_int32_t JERR_RMGR_BADRECTYPE; ///< Attempted op on incorrect rec type // class data_tok static const u_int32_t JERR_DTOK_ILLEGALSTATE; ///< Attempted to change to illegal state // static const u_int32_t JERR_DTOK_RIDNOTSET; ///< Record ID not set // class enq_map, txn_map static const u_int32_t JERR_MAP_DUPLICATE; ///< Attempted to insert using duplicate key static const u_int32_t JERR_MAP_NOTFOUND; ///< Key not found in map static const u_int32_t JERR_MAP_LOCKED; ///< rid locked by pending txn // class jinf static const u_int32_t JERR_JINF_CVALIDFAIL; ///< Compatibility validation failure static const u_int32_t JERR_JINF_NOVALUESTR; ///< No value attr found in jinf file static const u_int32_t JERR_JINF_BADVALUESTR; ///< Bad format for value attr in jinf file static const u_int32_t JERR_JINF_JDATEMPTY; ///< Journal data files empty static const u_int32_t JERR_JINF_TOOMANYFILES; ///< Too many journal data files static const u_int32_t JERR_JINF_INVALIDFHDR; ///< Invalid file header static const u_int32_t JERR_JINF_STAT; ///< Error while trying to stat a file static const u_int32_t JERR_JINF_NOTREGFILE; ///< Target file is not a regular file static const u_int32_t JERR_JINF_BADFILESIZE; ///< File is of incorrect or unexpected size static const u_int32_t JERR_JINF_OWIBAD; ///< OWI inconsistent (>1 transition in non-ae journal) static const u_int32_t JERR_JINF_ZEROLENFILE; ///< Journal info file is zero length (empty). // Negative returns for some functions static const int32_t AIO_TIMEOUT; ///< Timeout waiting for AIO return static const int32_t LOCK_TAKEN; ///< Attempted to take lock, but it was taken by another thread /** * \brief Method to access error message from known error number. */ static const char* err_msg(const u_int32_t err_no) throw (); private: /** * \brief Static function to initialize map. */ static bool __init(); }; } // namespace journal } // namespace mrg #endif // ifndef mrg_journal_jerrno_hpp qpid-cpp-store-debian-0.16/lib/jrnl/enq_hdr.hpp0000644000176300017630000001275111142067437020412 0ustar cajuscajus/** * \file enq_hdr.hpp * * Qpid asynchronous store plugin library * * File containing code for class mrg::journal::enq_hdr (enueue header), * used to start an enqueue record in the journal. * * \author Kim van der Riet * * Copyright (c) 2007, 2008 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #ifndef mrg_journal_enq_hdr_hpp #define mrg_journal_enq_hdr_hpp #include #include "jrnl/rec_hdr.hpp" namespace mrg { namespace journal { #pragma pack(1) /** * \brief Struct for enqueue record. * * Struct for enqueue record. In addition to the common data, this header includes both the * xid and data blob sizes. * * This header precedes all enqueue data in journal files. * * Record header info in binary format (32 bytes): *
    *   0                           7
    * +---+---+---+---+---+---+---+---+  -+
    * |     magic     | v | e | flags |   |
    * +---+---+---+---+---+---+---+---+   | struct hdr
    * |              rid              |   |
    * +---+---+---+---+---+---+---+---+  -+
    * |            xidsize            |
    * +---+---+---+---+---+---+---+---+
    * |             dsize             |
    * +---+---+---+---+---+---+---+---+
    * v = file version (If the format or encoding of this file changes, then this
    *     number should be incremented)
    * e = endian flag, false (0x00) for little endian, true (0x01) for big endian
    * 
* * Note that journal files should be transferable between 32- and 64-bit * hardware of the same endianness, but not between hardware of opposite * entianness without some sort of binary conversion utility. Thus buffering * will be needed for types that change size between 32- and 64-bit compiles. */ struct enq_hdr : rec_hdr { #if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT) u_int32_t _filler0; ///< Big-endian filler for 32-bit size_t #endif std::size_t _xidsize; ///< XID size #if defined(JRNL_LITTLE_ENDIAN) && defined(JRNL_32_BIT) u_int32_t _filler0; ///< Little-endian filler for 32-bit size_t #endif #if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT) u_int32_t _filler1; ///< Big-endian filler for 32-bit size_t #endif std::size_t _dsize; ///< Record data size #if defined(JRNL_LITTLE_ENDIAN) && defined(JRNL_32_BIT) u_int32_t _filler1; ///< Little-endian filler for 32-bit size_t #endif static const u_int16_t ENQ_HDR_TRANSIENT_MASK = 0x10; static const u_int16_t ENQ_HDR_EXTERNAL_MASK = 0x20; /** * \brief Default constructor, which sets all values to 0. */ inline enq_hdr(): rec_hdr(), #if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT) _filler0(0), #endif _xidsize(0), #if defined(JRNL_LITTLE_ENDIAN) && defined(JRNL_32_BIT) _filler0(0), #endif #if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT) _filler1(0), #endif _dsize(0) #if defined(JRNL_LITTLE_ENDIAN) && defined(JRNL_32_BIT) , _filler1(0) #endif {} /** * \brief Convenience constructor which initializes values during construction. */ inline enq_hdr(const u_int32_t magic, const u_int8_t version, const u_int64_t rid, const std::size_t xidsize, const std::size_t dsize, const bool owi, const bool transient = false): rec_hdr(magic, version, rid, owi), #if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT) _filler0(0), #endif _xidsize(xidsize), #if defined(JRNL_LITTLE_ENDIAN) && defined(JRNL_32_BIT) _filler0(0), #endif #if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT) _filler1(0), #endif _dsize(dsize) #if defined(JRNL_LITTLE_ENDIAN) && defined(JRNL_32_BIT) , _filler1(0) #endif { set_transient(transient); } inline bool is_transient() const { return _uflag & ENQ_HDR_TRANSIENT_MASK; } inline void set_transient(const bool transient) { _uflag = transient ? _uflag | ENQ_HDR_TRANSIENT_MASK : _uflag & (~ENQ_HDR_TRANSIENT_MASK); } inline bool is_external() const { return _uflag & ENQ_HDR_EXTERNAL_MASK; } inline void set_external(const bool external) { _uflag = external ? _uflag | ENQ_HDR_EXTERNAL_MASK : _uflag & (~ENQ_HDR_EXTERNAL_MASK); } /** * \brief Returns the size of the header in bytes. */ inline static std::size_t size() { return sizeof(enq_hdr); } }; #pragma pack() } // namespace journal } // namespace mrg #endif // ifndef mrg_journal_enq_hdr_hpp qpid-cpp-store-debian-0.16/lib/jrnl/enq_rec.hpp0000644000176300017630000000770611142067437020412 0ustar cajuscajus/** * \file enq_rec.hpp * * Qpid asynchronous store plugin library * * This file contains the code for the mrg::journal::enq_rec (journal enqueue * record) class. See class documentation for details. * * Copyright (c) 2007, 2008 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #ifndef mrg_journal_enq_rec_hpp #define mrg_journal_enq_rec_hpp namespace mrg { namespace journal { class enq_rec; } } #include #include "jrnl/enq_hdr.hpp" #include "jrnl/jrec.hpp" namespace mrg { namespace journal { /** * \class enq_rec * \brief Class to handle a single journal enqueue record. */ class enq_rec : public jrec { private: enq_hdr _enq_hdr; const void* _xidp; ///< xid pointer for encoding (for writing to disk) const void* _data; ///< Pointer to data to be written to disk void* _buff; ///< Pointer to buffer to receive data read from disk rec_tail _enq_tail; public: /** * \brief Constructor used for read operations. */ enq_rec(); /** * \brief Constructor used for write operations, where mbuf contains data to be written. */ enq_rec(const u_int64_t rid, const void* const dbuf, const std::size_t dlen, const void* const xidp, const std::size_t xidlen, const bool owi, const bool transient); /** * \brief Destructor */ virtual ~enq_rec(); // Prepare instance for use in reading data from journal, xid and data will be allocated void reset(); // Prepare instance for use in writing data to journal void reset(const u_int64_t rid, const void* const dbuf, const std::size_t dlen, const void* const xidp, const std::size_t xidlen, const bool owi, const bool transient, const bool external); u_int32_t encode(void* wptr, u_int32_t rec_offs_dblks, u_int32_t max_size_dblks); u_int32_t decode(rec_hdr& h, void* rptr, u_int32_t rec_offs_dblks, u_int32_t max_size_dblks); // Decode used for recover bool rcv_decode(rec_hdr h, std::ifstream* ifsp, std::size_t& rec_offs); std::size_t get_xid(void** const xidpp); std::size_t get_data(void** const datapp); inline bool is_transient() const { return _enq_hdr.is_transient(); } inline bool is_external() const { return _enq_hdr.is_external(); } std::string& str(std::string& str) const; inline std::size_t data_size() const { return _enq_hdr._dsize; } inline std::size_t xid_size() const { return _enq_hdr._xidsize; } std::size_t rec_size() const; static std::size_t rec_size(const std::size_t xidsize, const std::size_t dsize, const bool external); inline u_int64_t rid() const { return _enq_hdr._rid; } void set_rid(const u_int64_t rid); private: void chk_hdr() const; void chk_hdr(u_int64_t rid) const; void chk_tail() const; virtual void clean(); }; // class enq_rec } // namespace journal } // namespace mrg #endif // ifndef mrg_journal_enq_rec_hpp qpid-cpp-store-debian-0.16/lib/jrnl/jexception.cpp0000644000176300017630000001101211142067437021122 0ustar cajuscajus/** * \file jexception.cpp * * Qpid asynchronous store plugin library * * Generic journal exception class mrg::journal::jexception. See comments * in file jexception.hpp for details. * * \author Kim van der Riet * * Copyright (c) 2007, 2008 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #include "jrnl/jexception.hpp" #include #include #include "jrnl/jerrno.hpp" #define CATLEN(p) MAX_MSG_SIZE - std::strlen(p) - 1 namespace mrg { namespace journal { jexception::jexception() throw (): std::exception(), _err_code(0) { format(); } jexception::jexception(const u_int32_t err_code) throw (): std::exception(), _err_code(err_code) { format(); } jexception::jexception(const char* additional_info) throw (): std::exception(), _err_code(0), _additional_info(additional_info) { format(); } jexception::jexception(const std::string& additional_info) throw (): std::exception(), _err_code(0), _additional_info(additional_info) { format(); } jexception::jexception(const u_int32_t err_code, const char* additional_info) throw (): std::exception(), _err_code(err_code), _additional_info(additional_info) { format(); } jexception::jexception(const u_int32_t err_code, const std::string& additional_info) throw (): std::exception(), _err_code(err_code), _additional_info(additional_info) { format(); } jexception::jexception(const u_int32_t err_code, const char* throwing_class, const char* throwing_fn) throw (): std::exception(), _err_code(err_code), _throwing_class(throwing_class), _throwing_fn(throwing_fn) { format(); } jexception::jexception(const u_int32_t err_code, const std::string& throwing_class, const std::string& throwing_fn) throw (): std::exception(), _err_code(err_code), _throwing_class(throwing_class), _throwing_fn(throwing_fn) { format(); } jexception::jexception(const u_int32_t err_code, const char* additional_info, const char* throwing_class, const char* throwing_fn) throw (): std::exception(), _err_code(err_code), _additional_info(additional_info), _throwing_class(throwing_class), _throwing_fn(throwing_fn) { format(); } jexception::jexception(const u_int32_t err_code, const std::string& additional_info, const std::string& throwing_class, const std::string& throwing_fn) throw (): std::exception(), _err_code(err_code), _additional_info(additional_info), _throwing_class(throwing_class), _throwing_fn(throwing_fn) { format(); } jexception::~jexception() throw () {} void jexception::format() { const bool ai = !_additional_info.empty(); const bool tc = !_throwing_class.empty(); const bool tf = !_throwing_fn.empty(); std::ostringstream oss; oss << "jexception 0x" << std::hex << std::setfill('0') << std::setw(4) << _err_code << " "; if (tc) { oss << _throwing_class; if (tf) oss << "::"; else oss << " "; } if (tf) oss << _throwing_fn << "() "; if (tc || tf) oss << "threw " << jerrno::err_msg(_err_code); if (ai) oss << " (" << _additional_info << ")"; _what.assign(oss.str()); } const char* jexception::what() const throw () { return _what.c_str(); } std::ostream& operator<<(std::ostream& os, const jexception& je) { os << je.what(); return os; } std::ostream& operator<<(std::ostream& os, const jexception* jePtr) { os << jePtr->what(); return os; } } // namespace journal } // namespace mrg qpid-cpp-store-debian-0.16/lib/jrnl/txn_rec.cpp0000644000176300017630000003517211142067437020431 0ustar cajuscajus/** * \file txn_rec.cpp * * Qpid asynchronous store plugin library * * This file contains the code for the mrg::journal::txn_rec (journal dequeue * record) class. See comments in file txn_rec.hpp for details. * * \author Kim van der Riet * * Copyright (c) 2007, 2008 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #include "jrnl/txn_rec.hpp" #include #include #include #include #include #include "jrnl/jerrno.hpp" #include "jrnl/jexception.hpp" #include namespace mrg { namespace journal { txn_rec::txn_rec(): _txn_hdr(), _xidp(0), _buff(0), _txn_tail() { _txn_hdr._version = RHM_JDAT_VERSION; } txn_rec::txn_rec(const u_int32_t magic, const u_int64_t rid, const void* const xidp, const std::size_t xidlen, const bool owi): _txn_hdr(magic, RHM_JDAT_VERSION, rid, xidlen, owi), _xidp(xidp), _buff(0), _txn_tail(_txn_hdr) {} txn_rec::~txn_rec() { clean(); } void txn_rec::reset(const u_int32_t magic) { _txn_hdr._magic = magic; _txn_hdr._rid = 0; _txn_hdr._xidsize = 0; _xidp = 0; _buff = 0; _txn_tail._xmagic = ~magic; _txn_tail._rid = 0; } void txn_rec::reset(const u_int32_t magic, const u_int64_t rid, const void* const xidp, const std::size_t xidlen, const bool owi) { _txn_hdr._magic = magic; _txn_hdr._rid = rid; _txn_hdr.set_owi(owi); _txn_hdr._xidsize = xidlen; _xidp = xidp; _buff = 0; _txn_tail._xmagic = ~magic; _txn_tail._rid = rid; } u_int32_t txn_rec::encode(void* wptr, u_int32_t rec_offs_dblks, u_int32_t max_size_dblks) { assert(wptr != 0); assert(max_size_dblks > 0); assert(_xidp != 0 && _txn_hdr._xidsize > 0); std::size_t rec_offs = rec_offs_dblks * JRNL_DBLK_SIZE; std::size_t rem = max_size_dblks * JRNL_DBLK_SIZE; std::size_t wr_cnt = 0; if (rec_offs_dblks) // Continuation of split dequeue record (over 2 or more pages) { if (size_dblks(rec_size()) - rec_offs_dblks > max_size_dblks) // Further split required { rec_offs -= sizeof(_txn_hdr); std::size_t wsize = _txn_hdr._xidsize > rec_offs ? _txn_hdr._xidsize - rec_offs : 0; std::size_t wsize2 = wsize; if (wsize) { if (wsize > rem) wsize = rem; std::memcpy(wptr, (char*)_xidp + rec_offs, wsize); wr_cnt += wsize; rem -= wsize; } rec_offs -= _txn_hdr._xidsize - wsize2; if (rem) { wsize = sizeof(_txn_tail) > rec_offs ? sizeof(_txn_tail) - rec_offs : 0; wsize2 = wsize; if (wsize) { if (wsize > rem) wsize = rem; std::memcpy((char*)wptr + wr_cnt, (char*)&_txn_tail + rec_offs, wsize); wr_cnt += wsize; rem -= wsize; } rec_offs -= sizeof(_txn_tail) - wsize2; } assert(rem == 0); assert(rec_offs == 0); } else // No further split required { rec_offs -= sizeof(_txn_hdr); std::size_t wsize = _txn_hdr._xidsize > rec_offs ? _txn_hdr._xidsize - rec_offs : 0; if (wsize) { std::memcpy(wptr, (char*)_xidp + rec_offs, wsize); wr_cnt += wsize; } rec_offs -= _txn_hdr._xidsize - wsize; wsize = sizeof(_txn_tail) > rec_offs ? sizeof(_txn_tail) - rec_offs : 0; if (wsize) { std::memcpy((char*)wptr + wr_cnt, (char*)&_txn_tail + rec_offs, wsize); wr_cnt += wsize; #ifdef RHM_CLEAN std::size_t rec_offs = rec_offs_dblks * JRNL_DBLK_SIZE; std::size_t dblk_rec_size = size_dblks(rec_size() - rec_offs) * JRNL_DBLK_SIZE; std::memset((char*)wptr + wr_cnt, RHM_CLEAN_CHAR, dblk_rec_size - wr_cnt); #endif } rec_offs -= sizeof(_txn_tail) - wsize; assert(rec_offs == 0); } } else // Start at beginning of data record { // Assumption: the header will always fit into the first dblk std::memcpy(wptr, (void*)&_txn_hdr, sizeof(_txn_hdr)); wr_cnt = sizeof(_txn_hdr); if (size_dblks(rec_size()) > max_size_dblks) // Split required { std::size_t wsize; rem -= sizeof(_txn_hdr); if (rem) { wsize = rem >= _txn_hdr._xidsize ? _txn_hdr._xidsize : rem; std::memcpy((char*)wptr + wr_cnt, _xidp, wsize); wr_cnt += wsize; rem -= wsize; } if (rem) { wsize = rem >= sizeof(_txn_tail) ? sizeof(_txn_tail) : rem; std::memcpy((char*)wptr + wr_cnt, (void*)&_txn_tail, wsize); wr_cnt += wsize; rem -= wsize; } assert(rem == 0); } else // No split required { std::memcpy((char*)wptr + wr_cnt, _xidp, _txn_hdr._xidsize); wr_cnt += _txn_hdr._xidsize; std::memcpy((char*)wptr + wr_cnt, (void*)&_txn_tail, sizeof(_txn_tail)); wr_cnt += sizeof(_txn_tail); #ifdef RHM_CLEAN std::size_t dblk_rec_size = size_dblks(rec_size()) * JRNL_DBLK_SIZE; std::memset((char*)wptr + wr_cnt, RHM_CLEAN_CHAR, dblk_rec_size - wr_cnt); #endif } } return size_dblks(wr_cnt); } u_int32_t txn_rec::decode(rec_hdr& h, void* rptr, u_int32_t rec_offs_dblks, u_int32_t max_size_dblks) { assert(rptr != 0); assert(max_size_dblks > 0); std::size_t rd_cnt = 0; if (rec_offs_dblks) // Continuation of record on new page { const u_int32_t hdr_xid_dblks = size_dblks(txn_hdr::size() + _txn_hdr._xidsize); const u_int32_t hdr_xid_tail_dblks = size_dblks(txn_hdr::size() + _txn_hdr._xidsize + rec_tail::size()); const std::size_t rec_offs = rec_offs_dblks * JRNL_DBLK_SIZE; if (hdr_xid_tail_dblks - rec_offs_dblks <= max_size_dblks) { // Remainder of xid fits within this page if (rec_offs - txn_hdr::size() < _txn_hdr._xidsize) { // Part of xid still outstanding, copy remainder of xid and tail const std::size_t xid_offs = rec_offs - txn_hdr::size(); const std::size_t xid_rem = _txn_hdr._xidsize - xid_offs; std::memcpy((char*)_buff + xid_offs, rptr, xid_rem); rd_cnt = xid_rem; std::memcpy((void*)&_txn_tail, ((char*)rptr + rd_cnt), sizeof(_txn_tail)); chk_tail(); rd_cnt += sizeof(_txn_tail); } else { // Tail or part of tail only outstanding, complete tail const std::size_t tail_offs = rec_offs - txn_hdr::size() - _txn_hdr._xidsize; const std::size_t tail_rem = rec_tail::size() - tail_offs; std::memcpy((char*)&_txn_tail + tail_offs, rptr, tail_rem); chk_tail(); rd_cnt = tail_rem; } } else if (hdr_xid_dblks - rec_offs_dblks <= max_size_dblks) { // Remainder of xid fits within this page, tail split const std::size_t xid_offs = rec_offs - txn_hdr::size(); const std::size_t xid_rem = _txn_hdr._xidsize - xid_offs; std::memcpy((char*)_buff + xid_offs, rptr, xid_rem); rd_cnt += xid_rem; const std::size_t tail_rem = (max_size_dblks * JRNL_DBLK_SIZE) - rd_cnt; if (tail_rem) { std::memcpy((void*)&_txn_tail, ((char*)rptr + xid_rem), tail_rem); rd_cnt += tail_rem; } } else { // Remainder of xid split const std::size_t xid_cp_size = (max_size_dblks * JRNL_DBLK_SIZE); std::memcpy((char*)_buff + rec_offs - txn_hdr::size(), rptr, xid_cp_size); rd_cnt += xid_cp_size; } } else // Start of record { // Get and check header _txn_hdr.hdr_copy(h); rd_cnt = sizeof(rec_hdr); #if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT) rd_cnt += sizeof(u_int32_t); // Filler 0 #endif _txn_hdr._xidsize = *(std::size_t*)((char*)rptr + rd_cnt); rd_cnt = _txn_hdr.size(); chk_hdr(); _buff = std::malloc(_txn_hdr._xidsize); MALLOC_CHK(_buff, "_buff", "txn_rec", "decode"); const u_int32_t hdr_xid_dblks = size_dblks(txn_hdr::size() + _txn_hdr._xidsize); const u_int32_t hdr_xid_tail_dblks = size_dblks(txn_hdr::size() + _txn_hdr._xidsize + rec_tail::size()); // Check if record (header + xid + tail) fits within this page, we can check the // tail before the expense of copying data to memory if (hdr_xid_tail_dblks <= max_size_dblks) { // Entire header, xid and tail fits within this page std::memcpy(_buff, (char*)rptr + rd_cnt, _txn_hdr._xidsize); rd_cnt += _txn_hdr._xidsize; std::memcpy((void*)&_txn_tail, (char*)rptr + rd_cnt, sizeof(_txn_tail)); rd_cnt += sizeof(_txn_tail); chk_tail(); } else if (hdr_xid_dblks <= max_size_dblks) { // Entire header and xid fit within this page, tail split std::memcpy(_buff, (char*)rptr + rd_cnt, _txn_hdr._xidsize); rd_cnt += _txn_hdr._xidsize; const std::size_t tail_rem = (max_size_dblks * JRNL_DBLK_SIZE) - rd_cnt; if (tail_rem) { std::memcpy((void*)&_txn_tail, (char*)rptr + rd_cnt, tail_rem); rd_cnt += tail_rem; } } else { // Header fits within this page, xid split const std::size_t xid_cp_size = (max_size_dblks * JRNL_DBLK_SIZE) - rd_cnt; std::memcpy(_buff, (char*)rptr + rd_cnt, xid_cp_size); rd_cnt += xid_cp_size; } } return size_dblks(rd_cnt); } bool txn_rec::rcv_decode(rec_hdr h, std::ifstream* ifsp, std::size_t& rec_offs) { if (rec_offs == 0) { // Read header, allocate for xid _txn_hdr.hdr_copy(h); #if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT) ifsp->ignore(sizeof(u_int32_t)); // _filler0 #endif ifsp->read((char*)&_txn_hdr._xidsize, sizeof(std::size_t)); #if defined(JRNL_LITTLE_ENDIAN) && defined(JRNL_32_BIT) ifsp->ignore(sizeof(u_int32_t)); // _filler0 #endif rec_offs = sizeof(_txn_hdr); _buff = std::malloc(_txn_hdr._xidsize); MALLOC_CHK(_buff, "_buff", "txn_rec", "rcv_decode"); } if (rec_offs < sizeof(_txn_hdr) + _txn_hdr._xidsize) { // Read xid (or continue reading xid) std::size_t offs = rec_offs - sizeof(_txn_hdr); ifsp->read((char*)_buff + offs, _txn_hdr._xidsize - offs); std::size_t size_read = ifsp->gcount(); rec_offs += size_read; if (size_read < _txn_hdr._xidsize - offs) { assert(ifsp->eof()); // As we may have read past eof, turn off fail bit ifsp->clear(ifsp->rdstate()&(~std::ifstream::failbit)); assert(!ifsp->fail() && !ifsp->bad()); return false; } } if (rec_offs < sizeof(_txn_hdr) + _txn_hdr._xidsize + sizeof(rec_tail)) { // Read tail (or continue reading tail) std::size_t offs = rec_offs - sizeof(_txn_hdr) - _txn_hdr._xidsize; ifsp->read((char*)&_txn_tail + offs, sizeof(rec_tail) - offs); std::size_t size_read = ifsp->gcount(); rec_offs += size_read; if (size_read < sizeof(rec_tail) - offs) { assert(ifsp->eof()); // As we may have read past eof, turn off fail bit ifsp->clear(ifsp->rdstate()&(~std::ifstream::failbit)); assert(!ifsp->fail() && !ifsp->bad()); return false; } } ifsp->ignore(rec_size_dblks() * JRNL_DBLK_SIZE - rec_size()); chk_tail(); // Throws if tail invalid or record incomplete assert(!ifsp->fail() && !ifsp->bad()); return true; } std::size_t txn_rec::get_xid(void** const xidpp) { if (!_buff) { *xidpp = 0; return 0; } *xidpp = _buff; return _txn_hdr._xidsize; } std::string& txn_rec::str(std::string& str) const { std::ostringstream oss; if (_txn_hdr._magic == RHM_JDAT_TXA_MAGIC) oss << "dtxa_rec: m=" << _txn_hdr._magic; else oss << "dtxc_rec: m=" << _txn_hdr._magic; oss << " v=" << (int)_txn_hdr._version; oss << " rid=" << _txn_hdr._rid; oss << " xid=\"" << _xidp << "\""; str.append(oss.str()); return str; } std::size_t txn_rec::xid_size() const { return _txn_hdr._xidsize; } std::size_t txn_rec::rec_size() const { return txn_hdr::size() + _txn_hdr._xidsize + rec_tail::size(); } void txn_rec::chk_hdr() const { jrec::chk_hdr(_txn_hdr); if (_txn_hdr._magic != RHM_JDAT_TXA_MAGIC && _txn_hdr._magic != RHM_JDAT_TXC_MAGIC) { std::ostringstream oss; oss << std::hex << std::setfill('0'); oss << "dtx magic: rid=0x" << std::setw(16) << _txn_hdr._rid; oss << ": expected=(0x" << std::setw(8) << RHM_JDAT_TXA_MAGIC; oss << " or 0x" << RHM_JDAT_TXC_MAGIC; oss << ") read=0x" << std::setw(2) << (int)_txn_hdr._magic; throw jexception(jerrno::JERR_JREC_BADRECHDR, oss.str(), "txn_rec", "chk_hdr"); } } void txn_rec::chk_hdr(u_int64_t rid) const { chk_hdr(); jrec::chk_rid(_txn_hdr, rid); } void txn_rec::chk_tail() const { jrec::chk_tail(_txn_tail, _txn_hdr); } void txn_rec::clean() { // clean up allocated memory here } } // namespace journal } // namespace mrg qpid-cpp-store-debian-0.16/lib/jrnl/rec_hdr.hpp0000644000176300017630000001111211142067437020366 0ustar cajuscajus/** * \file rec_hdr.hpp * * Qpid asynchronous store plugin library * * File containing code for class mrg::journal::rec_hdr (record header), * which is a common initial header used for all journal record structures * except the record tail (rec_tail). * * \author Kim van der Riet * * Copyright (c) 2007, 2008 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #ifndef mrg_journal_rec_hdr_hpp #define mrg_journal_rec_hdr_hpp #include #include "jrnl/jcfg.hpp" #include namespace mrg { namespace journal { #pragma pack(1) /** * \brief Struct for data common to the head of all journal files and records. * This includes identification for the file type, the encoding version, endian * indicator and a record ID. * * File header info in binary format (16 bytes): *
    *   0                           7
    * +---+---+---+---+---+---+---+---+
    * |     magic     | v | e | flags |
    * +---+---+---+---+---+---+---+---+
    * |              rid              |
    * +---+---+---+---+---+---+---+---+
    * v = file version (If the format or encoding of this file changes, then this
    *     number should be incremented)
    * e = endian flag, false (0x00) for little endian, true (0x01) for big endian
    * 
* * Note that journal files should be transferable between 32- and 64-bit * hardware of the same endianness, but not between hardware of opposite * entianness without some sort of binary conversion utility. Thus buffering * will be needed for types that change size between 32- and 64-bit compiles. */ struct rec_hdr { u_int32_t _magic; ///< File type identifier (magic number) u_int8_t _version; ///< File encoding version u_int8_t _eflag; ///< Flag for determining endianness u_int16_t _uflag; ///< User-defined flags u_int64_t _rid; ///< Record ID (rotating 64-bit counter) // Global flags static const u_int16_t HDR_OVERWRITE_INDICATOR_MASK = 0x1; // Convenience constructors and methods /** * \brief Default constructor, which sets all values to 0. */ inline rec_hdr(): _magic(0), _version(0), _eflag(0), _uflag(0), _rid(0) {} /** * \brief Convenience constructor which initializes values during construction. */ inline rec_hdr(const u_int32_t magic, const u_int8_t version, const u_int64_t rid, const bool owi): _magic(magic), _version(version), #if defined(JRNL_BIG_ENDIAN) _eflag(RHM_BENDIAN_FLAG), #else _eflag(RHM_LENDIAN_FLAG), #endif _uflag(owi ? HDR_OVERWRITE_INDICATOR_MASK : 0), _rid(rid) {} /** * \brief Convenience copy method. */ inline void hdr_copy(const rec_hdr& h) { _magic = h._magic; _version = h._version; _eflag = h._eflag; _uflag = h._uflag; _rid =h._rid; } /** * \brief Resets all fields to 0 */ inline void reset() { _magic = 0; _version = 0; _eflag = 0; _uflag = 0; _rid = 0; } inline bool get_owi() const { return _uflag & HDR_OVERWRITE_INDICATOR_MASK; } inline void set_owi(const bool owi) { _uflag = owi ? _uflag | HDR_OVERWRITE_INDICATOR_MASK : _uflag & (~HDR_OVERWRITE_INDICATOR_MASK); } /** * \brief Returns the size of the header in bytes. */ inline static std::size_t size() { return sizeof(rec_hdr); } }; // struct rec_hdr #pragma pack() } // namespace journal } // namespace mrg #endif // ifndef mrg_journal_rec_hdr_hpp qpid-cpp-store-debian-0.16/lib/jrnl/rcvdat.hpp0000644000176300017630000001674411446152364020264 0ustar cajuscajus/** * \file rcvdat.hpp * * Qpid asynchronous store plugin library * * Contains structure for recovery status and offset data. * * \author Kim van der Riet * * Copyright (c) 2007, 2008 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #ifndef mrg_journal_rcvdat_hpp #define mrg_journal_rcvdat_hpp #include #include #include #include "jrnl/jcfg.hpp" #include #include #include namespace mrg { namespace journal { struct rcvdat { u_int16_t _njf; ///< Number of journal files bool _ae; ///< Auto-expand mode u_int16_t _aemjf; ///< Auto-expand mode max journal files bool _owi; ///< Overwrite indicator bool _frot; ///< First rotation flag bool _jempty; ///< Journal data files empty u_int16_t _ffid; ///< First file id std::size_t _fro; ///< First record offset in ffid u_int16_t _lfid; ///< Last file id std::size_t _eo; ///< End offset (first byte past last record) u_int64_t _h_rid; ///< Highest rid found bool _lffull; ///< Last file is full bool _jfull; ///< Journal is full std::vector _fid_list; ///< Fid-lid mapping - list of fids in order of lid std::vector _enq_cnt_list; ///< Number enqueued records found for each file rcvdat(): _njf(0), _ae(false), _aemjf(0), _owi(false), _frot(false), _jempty(true), _ffid(0), _fro(0), _lfid(0), _eo(0), _h_rid(0), _lffull(false), _jfull(false), _fid_list(), _enq_cnt_list() {} void reset(const u_int16_t num_jfiles, const bool auto_expand, const u_int16_t ae_max_jfiles) { _njf = num_jfiles; _ae = auto_expand; _aemjf = ae_max_jfiles; _owi = false; _frot = false; _jempty = true; _ffid = 0; _fro = 0; _lfid = 0; _eo = 0; _h_rid = 0; _lffull = false; _jfull = false; _fid_list.clear(); _enq_cnt_list.clear(); _enq_cnt_list.resize(num_jfiles, 0); } // Find first fid with enqueued records u_int16_t ffid() { u_int16_t index = _ffid; while (index != _lfid && _enq_cnt_list[index] == 0) { if (++index >= _njf) index = 0; } return index; } std::string to_string(const std::string& jid) { std::ostringstream oss; oss << "Recover file analysis (jid=\"" << jid << "\"):" << std::endl; oss << " Number of journal files (_njf) = " << _njf << std::endl; oss << " Auto-expand mode (_ae) = " << (_ae ? "TRUE" : "FALSE") << std::endl; if (_ae) oss << " Auto-expand mode max journal files (_aemjf) = " << _aemjf << std::endl; oss << " Overwrite indicator (_owi) = " << (_owi ? "TRUE" : "FALSE") << std::endl; oss << " First rotation (_frot) = " << (_frot ? "TRUE" : "FALSE") << std::endl; oss << " Journal empty (_jempty) = " << (_jempty ? "TRUE" : "FALSE") << std::endl; oss << " First (earliest) fid (_ffid) = " << _ffid << std::endl; oss << " First record offset in first fid (_fro) = 0x" << std::hex << _fro << std::dec << " (" << (_fro/JRNL_DBLK_SIZE) << " dblks)" << std::endl; oss << " Last (most recent) fid (_lfid) = " << _lfid << std::endl; oss << " End offset (_eo) = 0x" << std::hex << _eo << std::dec << " (" << (_eo/JRNL_DBLK_SIZE) << " dblks)" << std::endl; oss << " Highest rid (_h_rid) = 0x" << std::hex << _h_rid << std::dec << std::endl; oss << " Last file full (_lffull) = " << (_lffull ? "TRUE" : "FALSE") << std::endl; oss << " Journal full (_jfull) = " << (_jfull ? "TRUE" : "FALSE") << std::endl; oss << " Normalized fid list (_fid_list) = ["; for (std::vector::const_iterator i = _fid_list.begin(); i < _fid_list.end(); i++) { if (i != _fid_list.begin()) oss << ", "; oss << *i; } oss << "]" << std::endl; oss << " Enqueued records (txn & non-txn):" << std::endl; for (unsigned i=0; i<_enq_cnt_list.size(); i++) oss << " File " << std::setw(2) << i << ": " << _enq_cnt_list[i] << std::endl; return oss.str(); } std::string to_log(const std::string& jid) { std::ostringstream oss; oss << "Recover file analysis (jid=\"" << jid << "\"):"; oss << " njf=" << _njf; oss << " ae=" << (_owi ? "T" : "F"); oss << " aemjf=" << _aemjf; oss << " owi=" << (_ae ? "T" : "F"); oss << " frot=" << (_frot ? "T" : "F"); oss << " jempty=" << (_jempty ? "T" : "F"); oss << " ffid=" << _ffid; oss << " fro=0x" << std::hex << _fro << std::dec << " (" << (_fro/JRNL_DBLK_SIZE) << " dblks)"; oss << " lfid=" << _lfid; oss << " eo=0x" << std::hex << _eo << std::dec << " (" << (_eo/JRNL_DBLK_SIZE) << " dblks)"; oss << " h_rid=0x" << std::hex << _h_rid << std::dec; oss << " lffull=" << (_lffull ? "T" : "F"); oss << " jfull=" << (_jfull ? "T" : "F"); oss << " Enqueued records (txn & non-txn): [ "; for (unsigned i=0; i<_enq_cnt_list.size(); i++) { if (i) oss << " "; oss << "fid_" << std::setw(2) << std::setfill('0') << i << "=" << _enq_cnt_list[i]; } oss << " ]"; return oss.str(); } }; } // namespace journal } // namespace mrg #endif // ifndef mrg_journal_rcvdat_hpp qpid-cpp-store-debian-0.16/lib/jrnl/rmgr.cpp0000644000176300017630000005610711430554314017732 0ustar cajuscajus/** * \file rmgr.cpp * * Qpid asynchronous store plugin library * * File containing code for class mrg::journal::rmgr (read manager). See * comments in file rmgr.hpp for details. * * \author Kim van der Riet * * Copyright (c) 2007, 2008, 2009, 2010 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #include "jrnl/rmgr.hpp" #include #include #include #include "jrnl/jcntl.hpp" #include "jrnl/jerrno.hpp" #include namespace mrg { namespace journal { rmgr::rmgr(jcntl* jc, enq_map& emap, txn_map& tmap, rrfc& rrfc): pmgr(jc, emap, tmap), _rrfc(rrfc), _hdr(), _fhdr_buffer(0), _fhdr_aio_cb_ptr(0), _fhdr_rd_outstanding(false) {} rmgr::~rmgr() { rmgr::clean(); } void rmgr::initialize(aio_callback* const cbp) { pmgr::initialize(cbp, JRNL_RMGR_PAGE_SIZE, JRNL_RMGR_PAGES); clean(); // Allocate memory for reading file header if (::posix_memalign(&_fhdr_buffer, _sblksize, _sblksize)) { std::ostringstream oss; oss << "posix_memalign(): blksize=" << _sblksize << " size=" << _sblksize; oss << FORMAT_SYSERR(errno); throw jexception(jerrno::JERR__MALLOC, oss.str(), "rmgr", "initialize"); } _fhdr_aio_cb_ptr = new aio_cb; std::memset(_fhdr_aio_cb_ptr, 0, sizeof(aio_cb*)); } void rmgr::clean() { std::free(_fhdr_buffer); _fhdr_buffer = 0; if (_fhdr_aio_cb_ptr) { delete _fhdr_aio_cb_ptr; _fhdr_aio_cb_ptr = 0; } } iores rmgr::read(void** const datapp, std::size_t& dsize, void** const xidpp, std::size_t& xidsize, bool& transient, bool& external, data_tok* dtokp, bool ignore_pending_txns) { iores res = pre_read_check(dtokp); if (res != RHM_IORES_SUCCESS) { set_params_null(datapp, dsize, xidpp, xidsize); return res; } if (dtokp->rstate() == data_tok::SKIP_PART) { if (_page_cb_arr[_pg_index]._state != AIO_COMPLETE) { aio_cycle(); // check if rd AIOs returned; initiate new reads if possible return RHM_IORES_PAGE_AIOWAIT; } const iores res = skip(dtokp); if (res != RHM_IORES_SUCCESS) { set_params_null(datapp, dsize, xidpp, xidsize); return res; } } if (dtokp->rstate() == data_tok::READ_PART) { assert(dtokp->rid() == _hdr._rid); void* rptr = (void*)((char*)_page_ptr_arr[_pg_index] + (_pg_offset_dblks * JRNL_DBLK_SIZE)); const iores res = read_enq(_hdr, rptr, dtokp); dsize = _enq_rec.get_data(datapp); xidsize = _enq_rec.get_xid(xidpp); transient = _enq_rec.is_transient(); external = _enq_rec.is_external(); return res; } set_params_null(datapp, dsize, xidpp, xidsize); _hdr.reset(); // Read header, determine next record type while (true) { if(dblks_rem() == 0 && _rrfc.is_compl() && !_rrfc.is_wr_aio_outstanding()) { aio_cycle(); // check if rd AIOs returned; initiate new reads if possible if(dblks_rem() == 0 && _rrfc.is_compl() && !_rrfc.is_wr_aio_outstanding()) { if (_jc->unflushed_dblks() > 0) _jc->flush(); else if (!_aio_evt_rem) return RHM_IORES_EMPTY; } } if (_page_cb_arr[_pg_index]._state != AIO_COMPLETE) { aio_cycle(); return RHM_IORES_PAGE_AIOWAIT; } void* rptr = (void*)((char*)_page_ptr_arr[_pg_index] + (_pg_offset_dblks * JRNL_DBLK_SIZE)); std::memcpy(&_hdr, rptr, sizeof(rec_hdr)); switch (_hdr._magic) { case RHM_JDAT_ENQ_MAGIC: { _enq_rec.reset(); // sets enqueue rec size // Check if RID of this rec is still enqueued, if so read it, else skip bool is_enq = false; int16_t fid = _emap.get_pfid(_hdr._rid); if (fid < enq_map::EMAP_OK) { bool enforce_txns = !_jc->is_read_only() && !ignore_pending_txns; // Block read for transactionally locked record (only when not recovering) if (fid == enq_map::EMAP_LOCKED && enforce_txns) return RHM_IORES_TXPENDING; // (Recover mode only) Ok, not in emap - now search tmap, if present then read is_enq = _tmap.is_enq(_hdr._rid); if (enforce_txns && is_enq) return RHM_IORES_TXPENDING; } else is_enq = true; if (is_enq) // ok, this record is enqueued, check it, then read it... { if (dtokp->rid()) { if (_hdr._rid != dtokp->rid()) { std::ostringstream oss; oss << std::hex << "rid=0x" << _hdr._rid << "; dtok_rid=0x" << dtokp->rid() << "; dtok_id=0x" << dtokp->id(); throw jexception(jerrno::JERR_RMGR_RIDMISMATCH, oss.str(), "rmgr", "read"); } } else dtokp->set_rid(_hdr._rid); // TODO: Add member _fid to pmgr::page_cb which indicates the fid from which this page was // populated. When this value is set in wmgr::flush() somewehere, then uncomment the following // check: // if (fid != _page_cb_arr[_pg_index]._fid) // { // std::ostringstream oss; // oss << std::hex << std::setfill('0'); // oss << "rid=0x" << std::setw(16) << _hdr._rid; // oss << "; emap_fid=0x" << std::setw(4) << fid; // oss << "; current_fid=" << _rrfc.fid(); // throw jexception(jerrno::JERR_RMGR_FIDMISMATCH, oss.str(), "rmgr", // "read"); // } const iores res = read_enq(_hdr, rptr, dtokp); dsize = _enq_rec.get_data(datapp); xidsize = _enq_rec.get_xid(xidpp); transient = _enq_rec.is_transient(); external = _enq_rec.is_external(); return res; } else // skip this record, it is already dequeued consume_xid_rec(_hdr, rptr, dtokp); break; } case RHM_JDAT_DEQ_MAGIC: consume_xid_rec(_hdr, rptr, dtokp); break; case RHM_JDAT_TXA_MAGIC: consume_xid_rec(_hdr, rptr, dtokp); break; case RHM_JDAT_TXC_MAGIC: consume_xid_rec(_hdr, rptr, dtokp); break; case RHM_JDAT_EMPTY_MAGIC: consume_filler(); break; default: return RHM_IORES_EMPTY; } } } int32_t rmgr::get_events(page_state state, timespec* const timeout, bool flush) { if (_aio_evt_rem == 0) // no events to get return 0; int32_t ret; if ((ret = aio::getevents(_ioctx, flush ? _aio_evt_rem : 1, _aio_evt_rem/*_cache_num_pages + _jc->num_jfiles()*/, _aio_event_arr, timeout)) < 0) { if (ret == -EINTR) // Interrupted by signal return 0; std::ostringstream oss; oss << "io_getevents() failed: " << std::strerror(-ret) << " (" << ret << ")"; throw jexception(jerrno::JERR__AIO, oss.str(), "rmgr", "get_events"); } if (ret == 0 && timeout) return jerrno::AIO_TIMEOUT; std::vector pil; pil.reserve(ret); for (int i=0; idata); // This page control block (pcb) long aioret = (long)_aio_event_arr[i].res; if (aioret < 0) { std::ostringstream oss; oss << "AIO read operation failed: " << std::strerror(-aioret) << " (" << aioret << ")"; oss << " [pg=" << pcbp->_index << " buf=" << aiocbp->u.c.buf; oss << " rsize=0x" << std::hex << aiocbp->u.c.nbytes; oss << " offset=0x" << aiocbp->u.c.offset << std::dec; oss << " fh=" << aiocbp->aio_fildes << "]"; throw jexception(jerrno::JERR__AIO, oss.str(), "rmgr", "get_events"); } if (pcbp) // Page reads have pcb { if (pcbp->_rfh->rd_subm_cnt_dblks() >= JRNL_SBLK_SIZE) // Detects if write reset of this fcntl obj has occurred. { // Increment the completed read offset // NOTE: We cannot use _rrfc here, as it may have rotated since submitting count. // Use stored pointer to fcntl in the pcb instead. pcbp->_rdblks = aiocbp->u.c.nbytes / JRNL_DBLK_SIZE; pcbp->_rfh->add_rd_cmpl_cnt_dblks(pcbp->_rdblks); pcbp->_state = state; pil[i] = pcbp->_index; } } else // File header reads have no pcb { std::memcpy(&_fhdr, _fhdr_buffer, sizeof(file_hdr)); _rrfc.add_cmpl_cnt_dblks(JRNL_SBLK_SIZE); u_int32_t fro_dblks = (_fhdr._fro / JRNL_DBLK_SIZE) - JRNL_SBLK_SIZE; // Check fro_dblks does not exceed the write pointers which can happen in some corrupted journal recoveries if (fro_dblks > _jc->wr_subm_cnt_dblks(_fhdr._pfid) - JRNL_SBLK_SIZE) fro_dblks = _jc->wr_subm_cnt_dblks(_fhdr._pfid) - JRNL_SBLK_SIZE; _pg_cntr = fro_dblks / (JRNL_RMGR_PAGE_SIZE * JRNL_SBLK_SIZE); u_int32_t tot_pg_offs_dblks = _pg_cntr * JRNL_RMGR_PAGE_SIZE * JRNL_SBLK_SIZE; _pg_index = _pg_cntr % JRNL_RMGR_PAGES; _pg_offset_dblks = fro_dblks - tot_pg_offs_dblks; _rrfc.add_subm_cnt_dblks(tot_pg_offs_dblks); _rrfc.add_cmpl_cnt_dblks(tot_pg_offs_dblks); _fhdr_rd_outstanding = false; _rrfc.set_valid(); } } // Perform AIO return callback if (_cbp && ret) _cbp->rd_aio_cb(pil); return ret; } void rmgr::recover_complete() {} void rmgr::invalidate() { if (_rrfc.is_valid()) _rrfc.set_invalid(); } void rmgr::flush(timespec* timeout) { // Wait for any outstanding AIO read operations to complete before synchronizing while (_aio_evt_rem) { if (get_events(AIO_COMPLETE, timeout) == jerrno::AIO_TIMEOUT) // timed out, nothing returned { throw jexception(jerrno::JERR__TIMEOUT, "Timed out waiting for outstanding read aio to return", "rmgr", "init_validation"); } } // Reset all read states and pointers for (int i=0; i<_cache_num_pages; i++) _page_cb_arr[i]._state = UNUSED; _rrfc.unset_findex(); _pg_index = 0; _pg_offset_dblks = 0; } bool rmgr::wait_for_validity(timespec* timeout, const bool throw_on_timeout) { bool timed_out = false; while (!_rrfc.is_valid() && !timed_out) { timed_out = get_events(AIO_COMPLETE, timeout) == jerrno::AIO_TIMEOUT; if (timed_out && throw_on_timeout) throw jexception(jerrno::JERR__TIMEOUT, "Timed out waiting for read validity", "rmgr", "wait_for_validity"); } return _rrfc.is_valid(); } iores rmgr::pre_read_check(data_tok* dtokp) { if (_aio_evt_rem) get_events(AIO_COMPLETE, 0); if (!_rrfc.is_valid()) return RHM_IORES_RCINVALID; // block reads until outstanding file header read completes as fro is needed to read if (_fhdr_rd_outstanding) return RHM_IORES_PAGE_AIOWAIT; if(dblks_rem() == 0 && _rrfc.is_compl() && !_rrfc.is_wr_aio_outstanding()) { aio_cycle(); // check if any AIOs have returned if(dblks_rem() == 0 && _rrfc.is_compl() && !_rrfc.is_wr_aio_outstanding()) { if (_jc->unflushed_dblks() > 0) _jc->flush(); else if (!_aio_evt_rem) return RHM_IORES_EMPTY; } } // Check write state of this token is ENQ - required for read if (dtokp) { if (!dtokp->is_readable()) { std::ostringstream oss; oss << std::hex << std::setfill('0'); oss << "dtok_id=0x" << std::setw(8) << dtokp->id(); oss << "; dtok_rid=0x" << std::setw(16) << dtokp->rid(); oss << "; dtok_wstate=" << dtokp->wstate_str(); throw jexception(jerrno::JERR_RMGR_ENQSTATE, oss.str(), "rmgr", "pre_read_check"); } } return RHM_IORES_SUCCESS; } iores rmgr::read_enq(rec_hdr& h, void* rptr, data_tok* dtokp) { if (_page_cb_arr[_pg_index]._state != AIO_COMPLETE) { aio_cycle(); // check if any AIOs have returned return RHM_IORES_PAGE_AIOWAIT; } // Read data from this page, first block will have header and data size. u_int32_t dblks_rd = _enq_rec.decode(h, rptr, dtokp->dblocks_read(), dblks_rem()); dtokp->incr_dblocks_read(dblks_rd); _pg_offset_dblks += dblks_rd; // If data still incomplete, move to next page and decode again while (dtokp->dblocks_read() < _enq_rec.rec_size_dblks()) { rotate_page(); if (_page_cb_arr[_pg_index]._state != AIO_COMPLETE) { dtokp->set_rstate(data_tok::READ_PART); dtokp->set_dsize(_enq_rec.data_size()); return RHM_IORES_PAGE_AIOWAIT; } rptr = (void*)((char*)_page_ptr_arr[_pg_index]); dblks_rd = _enq_rec.decode(h, rptr, dtokp->dblocks_read(), dblks_rem()); dtokp->incr_dblocks_read(dblks_rd); _pg_offset_dblks += dblks_rd; } // If we have finished with this page, rotate it if (dblks_rem() == 0) rotate_page(); // Set the record size in dtokp dtokp->set_rstate(data_tok::READ); dtokp->set_dsize(_enq_rec.data_size()); return RHM_IORES_SUCCESS; } void rmgr::consume_xid_rec(rec_hdr& h, void* rptr, data_tok* dtokp) { if (h._magic == RHM_JDAT_ENQ_MAGIC) { enq_hdr ehdr; std::memcpy(&ehdr, rptr, sizeof(enq_hdr)); if (ehdr.is_external()) dtokp->set_dsize(ehdr._xidsize + sizeof(enq_hdr) + sizeof(rec_tail)); else dtokp->set_dsize(ehdr._xidsize + ehdr._dsize + sizeof(enq_hdr) + sizeof(rec_tail)); } else if (h._magic == RHM_JDAT_DEQ_MAGIC) { deq_hdr dhdr; std::memcpy(&dhdr, rptr, sizeof(deq_hdr)); if (dhdr._xidsize) dtokp->set_dsize(dhdr._xidsize + sizeof(deq_hdr) + sizeof(rec_tail)); else dtokp->set_dsize(sizeof(deq_hdr)); } else if (h._magic == RHM_JDAT_TXA_MAGIC || h._magic == RHM_JDAT_TXC_MAGIC) { txn_hdr thdr; std::memcpy(&thdr, rptr, sizeof(txn_hdr)); dtokp->set_dsize(thdr._xidsize + sizeof(txn_hdr) + sizeof(rec_tail)); } else { std::ostringstream oss; oss << "Record type found = \"" << (char*)&h._magic << "\""; throw jexception(jerrno::JERR_RMGR_BADRECTYPE, oss.str(), "rmgr", "consume_xid_rec"); } dtokp->set_dblocks_read(0); skip(dtokp); } void rmgr::consume_filler() { // Filler (Magic "RHMx") is one dblk by definition _pg_offset_dblks++; if (dblks_rem() == 0) rotate_page(); } iores rmgr::skip(data_tok* dtokp) { u_int32_t dsize_dblks = jrec::size_dblks(dtokp->dsize()); u_int32_t tot_dblk_cnt = dtokp->dblocks_read(); while (true) { u_int32_t this_dblk_cnt = 0; if (dsize_dblks - tot_dblk_cnt > dblks_rem()) this_dblk_cnt = dblks_rem(); else this_dblk_cnt = dsize_dblks - tot_dblk_cnt; if (this_dblk_cnt) { dtokp->incr_dblocks_read(this_dblk_cnt); _pg_offset_dblks += this_dblk_cnt; tot_dblk_cnt += this_dblk_cnt; } // If skip still incomplete, move to next page and decode again if (tot_dblk_cnt < dsize_dblks) { if (dblks_rem() == 0) rotate_page(); if (_page_cb_arr[_pg_index]._state != AIO_COMPLETE) { dtokp->set_rstate(data_tok::SKIP_PART); return RHM_IORES_PAGE_AIOWAIT; } } else { // Skip complete, put state back to unread dtokp->set_rstate(data_tok::UNREAD); dtokp->set_dsize(0); dtokp->set_dblocks_read(0); // If we have finished with this page, rotate it if (dblks_rem() == 0) rotate_page(); return RHM_IORES_SUCCESS; } } } iores rmgr::aio_cycle() { // Perform validity checks if (_fhdr_rd_outstanding) // read of file header still outstanding in aio return RHM_IORES_SUCCESS; if (!_rrfc.is_valid()) { // Flush and reset all read states and pointers flush(&jcntl::_aio_cmpl_timeout); _jc->get_earliest_fid(); // determine initial file to read; calls _rrfc.set_findex() to set value // If this file has not yet been written to, return RHM_IORES_EMPTY if (_rrfc.is_void() && !_rrfc.is_wr_aio_outstanding()) return RHM_IORES_EMPTY; init_file_header_read(); // send off AIO read request for file header return RHM_IORES_SUCCESS; } int16_t first_uninit = -1; u_int16_t num_uninit = 0; u_int16_t num_compl = 0; bool outstanding = false; // Index must start with current buffer and cycle around so that first // uninitialized buffer is initialized first for (u_int16_t i=_pg_index; i<_pg_index+_cache_num_pages; i++) { int16_t ci = i % _cache_num_pages; switch (_page_cb_arr[ci]._state) { case UNUSED: if (first_uninit < 0) first_uninit = ci; num_uninit++; break; case IN_USE: break; case AIO_PENDING: outstanding = true; break; case AIO_COMPLETE: num_compl++; break; default:; } } iores res = RHM_IORES_SUCCESS; if (num_uninit) res = init_aio_reads(first_uninit, num_uninit); else if (num_compl == _cache_num_pages) // This condition exists after invalidation res = init_aio_reads(0, _cache_num_pages); if (outstanding) get_events(AIO_COMPLETE, 0); return res; } iores rmgr::init_aio_reads(const int16_t first_uninit, const u_int16_t num_uninit) { for (int16_t i=0; i pg_size_dblks ? pg_size_dblks : file_rem_dblks; if (rd_size) { int16_t pi = (i + first_uninit) % _cache_num_pages; // TODO: For perf, combine contiguous pages into single read // 1 or 2 AIOs needed depending on whether read block folds aio_cb* aiocbp = &_aio_cb_arr[pi]; aio::prep_pread_2(aiocbp, _rrfc.fh(), _page_ptr_arr[pi], rd_size * JRNL_DBLK_SIZE, _rrfc.subm_offs()); if (aio::submit(_ioctx, 1, &aiocbp) < 0) throw jexception(jerrno::JERR__AIO, "rmgr", "init_aio_reads"); _rrfc.add_subm_cnt_dblks(rd_size); _aio_evt_rem++; _page_cb_arr[pi]._state = AIO_PENDING; _page_cb_arr[pi]._rfh = _rrfc.file_controller(); } else // If there is nothing to read for this page, neither will there be for the others... break; if (_rrfc.file_rotate()) _rrfc.rotate(); } return RHM_IORES_SUCCESS; } void rmgr::rotate_page() { _page_cb_arr[_pg_index]._rdblks = 0; _page_cb_arr[_pg_index]._state = UNUSED; if (_pg_offset_dblks >= JRNL_RMGR_PAGE_SIZE * JRNL_SBLK_SIZE) { _pg_offset_dblks = 0; _pg_cntr++; } if (++_pg_index >= _cache_num_pages) _pg_index = 0; aio_cycle(); _pg_offset_dblks = 0; // This counter is for bookkeeping only, page rotates are handled directly in init_aio_reads() // FIXME: _pg_cntr should be sync'd with aio ops, not use of page as it is now... // Need to move reset into if (_rrfc.file_rotate()) above. if (_pg_cntr >= (_jc->jfsize_sblks() / JRNL_RMGR_PAGE_SIZE)) _pg_cntr = 0; } u_int32_t rmgr::dblks_rem() const { return _page_cb_arr[_pg_index]._rdblks - _pg_offset_dblks; } void rmgr::set_params_null(void** const datapp, std::size_t& dsize, void** const xidpp, std::size_t& xidsize) { *datapp = 0; dsize = 0; *xidpp = 0; xidsize = 0; } void rmgr::init_file_header_read() { _jc->fhdr_wr_sync(_rrfc.index()); // wait if the file header write is outstanding int rfh = _rrfc.fh(); aio::prep_pread_2(_fhdr_aio_cb_ptr, rfh, _fhdr_buffer, _sblksize, 0); if (aio::submit(_ioctx, 1, &_fhdr_aio_cb_ptr) < 0) throw jexception(jerrno::JERR__AIO, "rmgr", "init_file_header_read"); _aio_evt_rem++; _rrfc.add_subm_cnt_dblks(JRNL_SBLK_SIZE); _fhdr_rd_outstanding = true; } /* TODO (sometime in the future) const iores rmgr::get(const u_int64_t& rid, const std::size_t& dsize, const std::size_t& dsize_avail, const void** const data, bool auto_discard) { return RHM_IORES_SUCCESS; } const iores rmgr::discard(data_tok* dtokp) { return RHM_IORES_SUCCESS; } */ } // namespace journal } // namespace mrg qpid-cpp-store-debian-0.16/lib/jrnl/jrec.cpp0000644000176300017630000000716011142067437017706 0ustar cajuscajus/** * \file jrec.cpp * * Qpid asynchronous store plugin library * * File containing source code for class mrg::journal::jrec (abstract journal * jrecord). See comments in file jrec.hpp for details. * * \author Kim van der Riet * * Copyright (c) 2007, 2008 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #include "jrnl/jrec.hpp" #include #include "jrnl/jerrno.hpp" #include "jrnl/jexception.hpp" #include namespace mrg { namespace journal { jrec::jrec() {} jrec::~jrec() {} void jrec::chk_hdr(const rec_hdr& hdr) { if (hdr._magic == 0) { std::ostringstream oss; oss << std::hex << std::setfill('0'); oss << "enq magic NULL: rid=0x" << hdr._rid; throw jexception(jerrno::JERR_JREC_BADRECHDR, oss.str(), "jrec", "chk_hdr"); } if (hdr._version != RHM_JDAT_VERSION) { std::ostringstream oss; oss << std::hex << std::setfill('0'); oss << "version: rid=0x" << hdr._rid; oss << ": expected=0x" << std::setw(2) << (int)RHM_JDAT_VERSION; oss << " read=0x" << std::setw(2) << (int)hdr._version; throw jexception(jerrno::JERR_JREC_BADRECHDR, oss.str(), "jrec", "chk_hdr"); } #if defined (JRNL_LITTLE_ENDIAN) u_int8_t endian_flag = RHM_LENDIAN_FLAG; #else u_int8_t endian_flag = RHM_BENDIAN_FLAG; #endif if (hdr._eflag != endian_flag) { std::ostringstream oss; oss << std::hex << std::setfill('0'); oss << "endian_flag: rid=" << hdr._rid; oss << ": expected=0x" << std::setw(2) << (int)endian_flag; oss << " read=0x" << std::setw(2) << (int)hdr._eflag; throw jexception(jerrno::JERR_JREC_BADRECHDR, oss.str(), "jrec", "chk_hdr"); } } void jrec::chk_rid(const rec_hdr& hdr, const u_int64_t rid) { if (hdr._rid != rid) { std::ostringstream oss; oss << std::hex << std::setfill('0'); oss << "rid mismatch: expected=0x" << rid; oss << " read=0x" << hdr._rid; throw jexception(jerrno::JERR_JREC_BADRECHDR, oss.str(), "jrec", "chk_hdr"); } } void jrec::chk_tail(const rec_tail& tail, const rec_hdr& hdr) { if (tail._xmagic != ~hdr._magic) { std::ostringstream oss; oss << std::hex << std::setfill('0'); oss << "magic: rid=0x" << hdr._rid; oss << ": expected=0x" << ~hdr._magic; oss << " read=0x" << tail._xmagic; throw jexception(jerrno::JERR_JREC_BADRECTAIL, oss.str(), "jrec", "chk_tail"); } if (tail._rid != hdr._rid) { std::ostringstream oss; oss << std::hex << std::setfill('0'); oss << "rid: rid=0x" << hdr._rid; oss << ": read=0x" << tail._rid; throw jexception(jerrno::JERR_JREC_BADRECTAIL, oss.str(), "jrec", "chk_tail"); } } } // namespace journal } // namespace mrg qpid-cpp-store-debian-0.16/lib/jrnl/jcntl.hpp0000644000176300017630000010657011432543760020107 0ustar cajuscajus/** * \file jcntl.hpp * * Qpid asynchronous store plugin library * * Messaging journal top-level control and interface class * mrg::journal::jcntl. See class documentation for details. * * \author Kim van der Riet * * Copyright (c) 2007, 2008, 2009, 2010 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #ifndef mrg_journal_jcntl_hpp #define mrg_journal_jcntl_hpp namespace mrg { namespace journal { class jcntl; } } #include #include #include "jrnl/jdir.hpp" #include "jrnl/fcntl.hpp" #include "jrnl/lpmgr.hpp" #include "jrnl/rcvdat.hpp" #include "jrnl/slock.hpp" #include "jrnl/smutex.hpp" #include "jrnl/rmgr.hpp" #include "jrnl/wmgr.hpp" #include "jrnl/wrfc.hpp" namespace mrg { namespace journal { /** * \brief Access and control interface for the journal. This is the top-level class for the * journal. * * This is the top-level journal class; one instance of this class controls one instance of the * journal and all its files and associated control structures. Besides this class, the only * other class that needs to be used at a higher level is the data_tok class, one instance of * which is used per data block written to the journal, and is used to track its status through * the AIO enqueue, read and dequeue process. */ class jcntl { protected: /** * \brief Journal ID * * This string uniquely identifies this journal instance. It will most likely be associated * with the identity of the message queue with which it is associated. */ // TODO: This is not included in any files at present, add to file_hdr? std::string _jid; /** * \brief Journal directory * * This string stores the path to the journal directory. It may be absolute or relative, and * should not end in a file separator character. (e.g. "/fastdisk/jdata" is correct, * "/fastdisk/jdata/" is not.) */ jdir _jdir; /** * \brief Base filename * * This string contains the base filename used for the journal files. The filenames will * start with this base, and have various sections added to it to derive the final file names * that will be written to disk. No file separator characters should be included here, but * all other legal filename characters are valid. */ std::string _base_filename; /** * \brief Initialized flag * * This flag starts out set to false, is set to true once this object has been initialized, * either by calling initialize() or recover(). */ bool _init_flag; /** * \brief Stopped flag * * This flag starts out false, and is set to true when stop() is called. At this point, the * journal will no longer accept messages until either initialize() or recover() is called. * There is no way other than through initialization to reset this flag. */ // TODO: It would be helpful to distinguish between states stopping and stopped. If stop(true) is called, // then we are stopping, but must wait for all outstanding aios to return before being finally stopped. During // this period, however, no new enqueue/dequeue/read requests may be accepted. bool _stop_flag; /** * \brief Read-only state flag used during recover. * * When true, this flag prevents journal write operations (enqueue and dequeue), but * allows read to occur. It is used during recovery, and is reset when recovered() is * called. */ bool _readonly_flag; /** * \brief If set, calls stop() if the jouranl write pointer overruns dequeue low water * marker. If not set, then attempts to write will throw exceptions until the journal * file low water marker moves to the next journal file. */ bool _autostop; ///< Autostop flag - stops journal when overrun occurs // Journal control structures u_int32_t _jfsize_sblks; ///< Journal file size in sblks lpmgr _lpmgr; ///< LFID-PFID manager tracks inserted journal files enq_map _emap; ///< Enqueue map for low water mark management txn_map _tmap; ///< Transaction map open transactions rrfc _rrfc; ///< Read journal rotating file controller wrfc _wrfc; ///< Write journal rotating file controller rmgr _rmgr; ///< Read page manager which manages AIO wmgr _wmgr; ///< Write page manager which manages AIO rcvdat _rcvdat; ///< Recovery data used for recovery smutex _wr_mutex; ///< Mutex for journal writes public: static timespec _aio_cmpl_timeout; ///< Timeout for blocking libaio returns static timespec _final_aio_cmpl_timeout; ///< Timeout for blocking libaio returns when stopping or finalizing /** * \brief Journal constructor. * * Constructor which sets the physical file location and base name. * * \param jid A unique identifier for this journal instance. * \param jdir The directory which will contain the journal files. * \param base_filename The string which will be used to start all journal filenames. */ jcntl(const std::string& jid, const std::string& jdir, const std::string& base_filename); /** * \brief Destructor. */ virtual ~jcntl(); inline const std::string& id() const { return _jid; } inline const std::string& jrnl_dir() const { return _jdir.dirname(); } /** * \brief Initialize the journal for storing data. * * Initialize the journal by creating new journal data files and initializing internal * control structures. When complete, the journal will be empty, and ready to store data. * * NOTE: Any existing journal will be ignored by this operation. To use recover * the data from an existing journal, use recover(). * * NOTE: If NULL is passed to the deque pointers, they will be internally created * and deleted. * * NOTE: If NULL is passed to the callbacks, internal default callbacks will be * used. * * \param num_jfiles The number of journal files to be created. * \param auto_expand If true, allows journal file auto-expansion. In this mode, the journal will automatically * add files to the journal if it runs out of space. No more than ae_max_jfiles may be added. If false, then * no files are added and an exception will be thrown if the journal runs out of file space. * \param ae_max_jfiles Upper limit of journal files for auto-expand mode. When auto_expand is true, this is the * maximum total number of files allowed in the journal (original plus those added by auto-expand mode). If * this number of files exist and the journal runs out of space, an exception will be thrown. This number * must be greater than the num_jfiles parameter value but cannot exceed the maximum number of files for a * single journal; if num_jfiles is already at its maximum value, then auto-expand will be disabled. * \param jfsize_sblks The size of each journal file expressed in softblocks. * \param wcache_num_pages The number of write cache pages to create. * \param wcache_pgsize_sblks The size in sblks of each write cache page. * \param cbp Pointer to object containing callback functions for read and write operations. May be 0 (NULL). * * \exception TODO */ void initialize(const u_int16_t num_jfiles, const bool auto_expand, const u_int16_t ae_max_jfiles, const u_int32_t jfsize_sblks, const u_int16_t wcache_num_pages, const u_int32_t wcache_pgsize_sblks, aio_callback* const cbp); /** * /brief Initialize journal by recovering state from previously written journal. * * Initialize journal by recovering state from previously written journal. The journal files * are analyzed, and all records that have not been dequeued and that remain in the journal * will be available for reading. The journal is placed in a read-only state until * recovered() is called; any calls to enqueue or dequeue will fail with an exception * in this state. * * NOTE: If NULL is passed to the deque pointers, they will be internally created * and deleted. * * NOTE: If NULL is passed to the callbacks, internal default callbacks will be * used. * * \param num_jfiles The number of journal files to be created. * \param auto_expand If true, allows journal file auto-expansion. In this mode, the journal will automatically * add files to the journal if it runs out of space. No more than ae_max_jfiles may be added. If false, then * no files are added and an exception will be thrown if the journal runs out of file space. * \param ae_max_jfiles Upper limit of journal files for auto-expand mode. When auto_expand is true, this is the * maximum total number of files allowed in the journal (original plus those added by auto-expand mode). If * this number of files exist and the journal runs out of space, an exception will be thrown. This number * must be greater than the num_jfiles parameter value but cannot exceed the maximum number of files for a * single journal; if num_jfiles is already at its maximum value, then auto-expand will be disabled. * \param jfsize_sblks The size of each journal file expressed in softblocks. * \param wcache_num_pages The number of write cache pages to create. * \param wcache_pgsize_sblks The size in sblks of each write cache page. * \param cbp Pointer to object containing callback functions for read and write operations. May be 0 (NULL). * \param prep_txn_list_ptr * \param highest_rid Returns the highest rid found in the journal during recover * * \exception TODO */ void recover(const u_int16_t num_jfiles, const bool auto_expand, const u_int16_t ae_max_jfiles, const u_int32_t jfsize_sblks, const u_int16_t wcache_num_pages, const u_int32_t wcache_pgsize_sblks, aio_callback* const cbp, const std::vector* prep_txn_list_ptr, u_int64_t& highest_rid); /** * \brief Notification to the journal that recovery is complete and that normal operation * may resume. * * This call notifies the journal that recovery is complete and that normal operation * may resume. The read pointers are reset so that all records read as a part of recover * may be re-read during normal operation. The read-only flag is then reset, allowing * enqueue and dequeue operations to resume. * * \exception TODO */ void recover_complete(); /** * \brief Stops journal and deletes all journal files. * * Clear the journal directory of all journal files matching the base filename. * * \exception TODO */ void delete_jrnl_files(); /** * \brief Enqueue data. * * Enqueue data or part thereof. If a large data block is being written, then it may be * enqueued in parts by setting this_data_len to the size of the data being written in this * call. The total data size must be known in advance, however, as this is written into the * record header on the first record write. The state of the write (i.e. how much has been * written so far) is maintained in the data token dtokp. Partial writes will return in state * ENQ_PART. * * Note that a return value of anything other than RHM_IORES_SUCCESS implies that this write * operation did not complete successfully or was partially completed. The action taken under * these conditions depends on the value of the return. For example, RHM_IORES_AIO_WAIT * implies that all pages in the write page cache are waiting for AIO operations to return, * and that the call should be remade after waiting a bit. * * Example: If a write of 99 kB is divided into three equal parts, then the following states * and returns would characterize a successful operation: *
        *                            dtok.    dtok.   dtok.
        * Pperation         Return   wstate() dsize() written() Comment
        * -----------------+--------+--------+-------+---------+------------------------------------
        *                            NONE     0       0         Value of dtok before op
        * edr(99000, 33000) SUCCESS  ENQ_PART 99000   33000     Enqueue part 1
        * edr(99000, 33000) AIO_WAIT ENQ_PART 99000   50000     Enqueue part 2, not completed
        * edr(99000, 33000) SUCCESS  ENQ_PART 99000   66000     Enqueue part 2 again
        * edr(99000, 33000) SUCCESS  ENQ      99000   99000     Enqueue part 3
        * 
* * \param data_buff Pointer to data to be enqueued for this enqueue operation. * \param tot_data_len Total data length. * \param this_data_len Amount to be written in this enqueue operation. * \param dtokp Pointer to data token which contains the details of the enqueue operation. * \param transient Flag indicating transient persistence (ie, ignored on recover). * * \exception TODO */ iores enqueue_data_record(const void* const data_buff, const std::size_t tot_data_len, const std::size_t this_data_len, data_tok* dtokp, const bool transient = false); iores enqueue_extern_data_record(const std::size_t tot_data_len, data_tok* dtokp, const bool transient = false); /** * \brief Enqueue data. * * \param data_buff Pointer to data to be enqueued for this enqueue operation. * \param tot_data_len Total data length. * \param this_data_len Amount to be written in this enqueue operation. * \param dtokp Pointer to data token which contains the details of the enqueue operation. * \param xid String containing xid. An empty string (i.e. length=0) will be considered * non-transactional. * \param transient Flag indicating transient persistence (ie, ignored on recover). * * \exception TODO */ iores enqueue_txn_data_record(const void* const data_buff, const std::size_t tot_data_len, const std::size_t this_data_len, data_tok* dtokp, const std::string& xid, const bool transient = false); iores enqueue_extern_txn_data_record(const std::size_t tot_data_len, data_tok* dtokp, const std::string& xid, const bool transient = false); /* TODO ** * \brief Retrieve details of next record to be read without consuming the record. * * Retrieve information about current read record. A pointer to the data is returned, along * with the data size and available data size. Data is considered "available" when the AIO * operations to fill page-cache pages from disk have returned, and is ready for consumption. * * If dsize_avail < dsize, then not all of the data is available or part of * the data is in non-contiguous memory, and a subsequent call will update both the pointer * and dsize_avail if more pages have returned from AIO. * * The dsize_avail parameter will return the amount of data from this record that is * available in the page cache as contiguous memory, even if it spans page cache boundaries. * However, if a record spans the end of the page cache and continues at the beginning, even * if both parts are ready for consumption, then this must be divided into at least two * get_data_record() operations, as the data is contained in at least two non-contiguous * segments of the page cache. * * Once all the available data for a record is exposed, it can not be read again using * this function. It must be consumed prior to getting the next record. This can be done by * calling discard_data_record() or read_data_record(). However, if parameter * auto_discard is set to true, then this record will be automatically * consumed when the entire record has become available without having to explicitly call * discard_next_data_record() or read_data_record(). * * If the current record is an open transactional record, then it cannot be read until it is * committed. If it is aborted, it can never be read. Under this condition, get_data_record() * will return RHM_IORES_TXPENDING, the data pointer will be set to NULL and all data * lengths will be set to 0. * * Example: Read a record of 30k. Assume a read page cache of 10 pages of size 10k starting * at address base_ptr (page0 = base_ptr, page1 = page_ptr+10k, etc.). The first 15k of * the record falls at the end of the page cache, the remaining 15k folded to the beginning. * The current page (page 8) containing 5k is available, the remaining pages which contain * this record are pending AIO return: *
        * call       dsize
        * no.  dsize avail data ptr     Return   Comment
        * ----+-----+-----+------------+--------+--------------------------------------------------
        * 1    30k   5k    base_ptr+85k SUCCESS  Initial call, read first 5k
        * 2    30k   0k    base_ptr+90k AIO_WAIT AIO still pending; no further pages avail
        * 3    30k   10k   base_ptr+90k SUCCESS  AIO now returned; now read till end of page cache
        * 4    30k   15k   base_ptr     SUCCESS  data_ptr now pointing to start of page cache
        * 
* * \param rid Reference that returns the record ID (rid) * \param dsize Reference that returns the total data size of the record data . * \param dsize_avail Reference that returns the amount of the data that is available for * consumption. * \param data Pointer to data pointer which will point to the first byte of the next record * data. * \param auto_discard If true, automatically discard the record being read if * the entire record is available (i.e. dsize == dsize_avail). Otherwise * discard_next_data_record() must be explicitly called. * * \exception TODO * // *** NOT YET IMPLEMENTED *** iores get_data_record(const u_int64_t& rid, const std::size_t& dsize, const std::size_t& dsize_avail, const void** const data, bool auto_discard = false); */ /* TODO ** * \brief Discard (skip) next record to be read without reading or retrieving it. * * \exception TODO * // *** NOT YET IMPLEMENTED *** iores discard_data_record(data_tok* const dtokp); */ /** * \brief Reads data from the journal. It is the responsibility of the reader to free * the memory that is allocated through this call - see below for details. * * Reads the next non-dequeued data record from the journal. * * Note that this call allocates memory into which the data and XID are copied. It * is the responsibility of the caller to free this memory. The memory for the data and * XID are allocated in a single call, and the XID precedes the data in the memory space. * Thus, where an XID exists, freeing the XID pointer will free both the XID and data memory. * However, if an XID does not exist for the message, the XID pointer xidpp is set to NULL, * and it is the data pointer datapp that must be freed. Should neither an XID nor data be * present (ie an empty record), then no memory is allocated, and both pointers will be NULL. * In this case, there is no need to free memory. * * TODO: Fix this lousy interface. The caller should NOT be required to clean up these * pointers! Rather use a struct, or better still, let the data token carry the data and * xid pointers and lengths, and have the data token both allocate and delete. * * \param datapp Pointer to pointer that will be set to point to memory allocated and * containing the data. Will be set to NULL if the call fails or there is no data * in the record. * \param dsize Ref that will be set to the size of the data. Will be set to 0 if the call * fails or if there is no data in the record. * \param xidpp Pointer to pointer that will be set to point to memory allocated and * containing the XID. Will be set to NULL if the call fails or there is no XID attached * to this record. * \param xidsize Ref that will be set to the size of the XID. * \param transient Ref that will be set true if record is transient. * \param external Ref that will be set true if record is external. In this case, the data * pointer datapp will be set to NULL, but dsize will contain the size of the data. * NOTE: If there is an xid, then xidpp must be freed. * \param dtokp Pointer to data_tok instance for this data, used to track state of data * through journal. * \param ignore_pending_txns When false (default), if the next record to be read is locked * by a pending transaction, the read fails with RHM_IORES_TXPENDING. However, if set * to true, then locks are ignored. This is required for reading of the Transaction * Prepared List (TPL) which may have its entries locked, but may be read from * time-to-time, and needs all its records (locked and unlocked) to be available. * * \exception TODO */ iores read_data_record(void** const datapp, std::size_t& dsize, void** const xidpp, std::size_t& xidsize, bool& transient, bool& external, data_tok* const dtokp, bool ignore_pending_txns = false); /** * \brief Dequeues (marks as no longer needed) data record in journal. * * Dequeues (marks as no longer needed) data record in journal. Note that it is possible * to use the same data token instance used to enqueue this data; it contains the record ID * needed to correctly mark this data as dequeued in the journal. Otherwise the RID of the * record to be dequeued and the write state of ENQ must be manually set in a new or reset * instance of data_tok. * * \param dtokp Pointer to data_tok instance for this data, used to track state of data * through journal. * \param txn_coml_commit Only used for preparedXID journal. When used for dequeueing * prepared XID list items, sets whether the complete() was called in commit or abort * mode. * * \exception TODO */ iores dequeue_data_record(data_tok* const dtokp, const bool txn_coml_commit = false); /** * \brief Dequeues (marks as no longer needed) data record in journal. * * Dequeues (marks as no longer needed) data record in journal as part of a transaction. * Note that it is possible to use the same data token instance used to enqueue this data; * it contains the RID needed to correctly mark this data as dequeued in the journal. * Otherwise the RID of the record to be dequeued and the write state of ENQ must be * manually set in a new or reset instance of data_tok. * * \param dtokp Pointer to data_tok instance for this data, used to track state of data * through journal. * \param xid String containing xid. An empty string (i.e. length=0) will be considered * non-transactional. * \param txn_coml_commit Only used for preparedXID journal. When used for dequeueing * prepared XID list items, sets whether the complete() was called in commit or abort * mode. * * \exception TODO */ iores dequeue_txn_data_record(data_tok* const dtokp, const std::string& xid, const bool txn_coml_commit = false); /** * \brief Abort the transaction for all records enqueued or dequeued with the matching xid. * * Abort the transaction for all records enqueued with the matching xid. All enqueued records * are effectively deleted from the journal, and can not be read. All dequeued records remain * as though they had never been dequeued. * * \param dtokp Pointer to data_tok instance for this data, used to track state of data * through journal. * \param xid String containing xid. * * \exception TODO */ iores txn_abort(data_tok* const dtokp, const std::string& xid); /** * \brief Commit the transaction for all records enqueued or dequeued with the matching xid. * * Commit the transaction for all records enqueued with the matching xid. All enqueued * records are effectively released for reading and dequeueing. All dequeued records are * removed and can no longer be accessed. * * \param dtokp Pointer to data_tok instance for this data, used to track state of data * through journal. * \param xid String containing xid. * * \exception TODO */ iores txn_commit(data_tok* const dtokp, const std::string& xid); /** * \brief Check whether all the enqueue records for the given xid have reached disk. * * \param xid String containing xid. * * \exception TODO */ bool is_txn_synced(const std::string& xid); /** * \brief Forces a check for returned AIO write events. * * Forces a check for returned AIO write events. This is normally performed by enqueue() and * dequeue() operations, but if these operations cease, then this call needs to be made to * force the processing of any outstanding AIO operations. */ int32_t get_wr_events(timespec* const timeout); /** * \brief Forces a check for returned AIO read events. * * Forces a check for returned AIO read events. This is normally performed by read_data() * operations, but if these operations cease, then this call needs to be made to force the * processing of any outstanding AIO operations. */ int32_t get_rd_events(timespec* const timeout); /** * \brief Stop the journal from accepting any further requests to read or write data. * * This operation is used to stop the journal. This is the normal mechanism for bringing the * journal to an orderly stop. Any outstanding AIO operations or partially written pages in * the write page cache will by flushed and will complete. * * Note: The journal cannot be restarted without either initializing it or restoring * it. * * \param block_till_aio_cmpl If true, will block the thread while waiting for all * outstanding AIO operations to complete. */ void stop(const bool block_till_aio_cmpl = false); /** * \brief Force a flush of the write page cache, creating a single AIO write operation. */ iores flush(const bool block_till_aio_cmpl = false); inline u_int32_t get_enq_cnt() const { return _emap.size(); } inline u_int32_t get_wr_aio_evt_rem() const { slock l(_wr_mutex); return _wmgr.get_aio_evt_rem(); } inline u_int32_t get_rd_aio_evt_rem() const { return _rmgr.get_aio_evt_rem(); } inline u_int32_t get_wr_outstanding_aio_dblks() const { return _wrfc.aio_outstanding_dblks(); } inline u_int32_t get_wr_outstanding_aio_dblks(u_int16_t lfid) const { return _lpmgr.get_fcntlp(lfid)->wr_aio_outstanding_dblks(); } inline u_int32_t get_rd_outstanding_aio_dblks() const { return _rrfc.aio_outstanding_dblks(); } inline u_int32_t get_rd_outstanding_aio_dblks(u_int16_t lfid) const { return _lpmgr.get_fcntlp(lfid)->rd_aio_outstanding_dblks(); } inline u_int16_t get_rd_fid() const { return _rrfc.index(); } inline u_int16_t get_wr_fid() const { return _wrfc.index(); } u_int16_t get_earliest_fid(); /** * \brief Check if a particular rid is enqueued. Note that this function will return * false if the rid is transactionally enqueued and is not committed, or if it is * locked (i.e. transactionally dequeued, but the dequeue has not been committed). */ inline bool is_enqueued(const u_int64_t rid, bool ignore_lock = false) { return _emap.is_enqueued(rid, ignore_lock); } inline bool is_locked(const u_int64_t rid) { if (_emap.is_enqueued(rid, true) < enq_map::EMAP_OK) return false; return _emap.is_locked(rid) == enq_map::EMAP_TRUE; } inline void enq_rid_list(std::vector& rids) { _emap.rid_list(rids); } inline void enq_xid_list(std::vector& xids) { _tmap.xid_list(xids); } inline u_int32_t get_open_txn_cnt() const { return _tmap.size(); } // TODO Make this a const, but txn_map must support const first. inline txn_map& get_txn_map() { return _tmap; } /** * \brief Check if the journal is stopped. * * \return true if the jouranl is stopped; * false otherwise. */ inline bool is_stopped() { return _stop_flag; } /** * \brief Check if the journal is ready to read and write data. * * Checks if the journal is ready to read and write data. This function will return * true if the journal has been either initialized or restored, and the stop() * function has not been called since the initialization. * * Note that the journal may also be stopped if an internal error occurs (such as running out * of data journal file space). * * \return true if the journal is ready to read and write data; * false otherwise. */ inline bool is_ready() const { return _init_flag && !_stop_flag; } inline bool is_read_only() const { return _readonly_flag; } /** * \brief Get the journal directory. * * This returns the journal directory as set during initialization. This is the directory * into which the journal files will be written. */ inline const std::string& dirname() const { return _jdir.dirname(); } /** * \brief Get the journal base filename. * * Get the journal base filename as set during initialization. This is the prefix used in all * journal files of this instance. Note that if more than one instance of the journal shares * the same directory, their base filenames MUST be different or else the instances * will overwrite one another. */ inline const std::string& base_filename() const { return _base_filename; } inline u_int16_t num_jfiles() const { return _lpmgr.num_jfiles(); } inline fcntl* get_fcntlp(const u_int16_t lfid) const { return _lpmgr.get_fcntlp(lfid); } inline u_int32_t jfsize_sblks() const { return _jfsize_sblks; } // Logging virtual void log(log_level level, const std::string& log_stmt) const; virtual void log(log_level level, const char* const log_stmt) const; // FIXME these are _rmgr to _wmgr interactions, remove when _rmgr contains ref to _wmgr: void chk_wr_frot(); inline u_int32_t unflushed_dblks() { return _wmgr.unflushed_dblks(); } void fhdr_wr_sync(const u_int16_t lid); inline u_int32_t wr_subm_cnt_dblks(const u_int16_t lfid) const { return _lpmgr.get_fcntlp(lfid)->wr_subm_cnt_dblks(); } // Management instrumentation callbacks inline virtual void instr_incr_outstanding_aio_cnt() {} inline virtual void instr_decr_outstanding_aio_cnt() {} /** * /brief Static function for creating new fcntl objects for use with obj_arr. */ static fcntl* new_fcntl(jcntl* const jcp, const u_int16_t lid, const u_int16_t fid, const rcvdat* const rdp); protected: static bool _init; static bool init_statics(); /** * \brief Check status of journal before allowing write operations. */ void check_wstatus(const char* fn_name) const; /** * \brief Check status of journal before allowing read operations. */ void check_rstatus(const char* fn_name) const; /** * \brief Write info file <basefilename>.jinf to disk */ void write_infofile() const; /** * \brief Call that blocks while waiting for all outstanding AIOs to complete */ void aio_cmpl_wait(); /** * \brief Call that blocks until at least one message returns; used to wait for * AIO wait conditions to clear. */ bool handle_aio_wait(const iores res, iores& resout, const data_tok* dtp); /** * \brief Analyze journal for recovery. */ void rcvr_janalyze(rcvdat& rd, const std::vector* prep_txn_list_ptr); bool rcvr_get_next_record(u_int16_t& fid, std::ifstream* ifsp, bool& lowi, rcvdat& rd); bool decode(jrec& rec, u_int16_t& fid, std::ifstream* ifsp, std::size_t& cum_size_read, rec_hdr& h, bool& lowi, rcvdat& rd, std::streampos& rec_offset); bool jfile_cycle(u_int16_t& fid, std::ifstream* ifsp, bool& lowi, rcvdat& rd, const bool jump_fro); bool check_owi(const u_int16_t fid, rec_hdr& h, bool& lowi, rcvdat& rd, std::streampos& read_pos); void check_journal_alignment(const u_int16_t fid, std::streampos& rec_offset, rcvdat& rd); }; } // namespace journal } // namespace mrg #endif // ifndef mrg_journal_jcntl_hpp qpid-cpp-store-debian-0.16/lib/jrnl/deq_rec.hpp0000644000176300017630000000714511142067437020375 0ustar cajuscajus/** * \file deq_rec.hpp * * Qpid asynchronous store plugin library * * This file contains the code for the mrg::journal::deq_rec (journal dequeue * record) class. See class documentation for details. * * \author Kim van der Riet * * Copyright (c) 2007, 2008 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #ifndef mrg_journal_deq_rec_hpp #define mrg_journal_deq_rec_hpp namespace mrg { namespace journal { class deq_rec; } } #include #include "jrnl/deq_hdr.hpp" #include "jrnl/jrec.hpp" namespace mrg { namespace journal { /** * \class deq_rec * \brief Class to handle a single journal dequeue record. */ class deq_rec : public jrec { private: deq_hdr _deq_hdr; ///< Dequeue header const void* _xidp; ///< xid pointer for encoding (writing to disk) void* _buff; ///< Pointer to buffer to receive data read from disk rec_tail _deq_tail; ///< Record tail, only encoded if XID is present public: // constructor used for read operations and xid will have memory allocated deq_rec(); // constructor used for write operations, where xid already exists deq_rec(const u_int64_t rid, const u_int64_t drid, const void* const xidp, const std::size_t xidlen, const bool owi, const bool txn_coml_commit); virtual ~deq_rec(); // Prepare instance for use in reading data from journal void reset(); // Prepare instance for use in writing data to journal void reset(const u_int64_t rid, const u_int64_t drid, const void* const xidp, const std::size_t xidlen, const bool owi, const bool txn_coml_commit); u_int32_t encode(void* wptr, u_int32_t rec_offs_dblks, u_int32_t max_size_dblks); u_int32_t decode(rec_hdr& h, void* rptr, u_int32_t rec_offs_dblks, u_int32_t max_size_dblks); // Decode used for recover bool rcv_decode(rec_hdr h, std::ifstream* ifsp, std::size_t& rec_offs); inline bool is_txn_coml_commit() const { return _deq_hdr.is_txn_coml_commit(); } inline u_int64_t rid() const { return _deq_hdr._rid; } inline u_int64_t deq_rid() const { return _deq_hdr._deq_rid; } std::size_t get_xid(void** const xidpp); std::string& str(std::string& str) const; inline std::size_t data_size() const { return 0; } // This record never carries data std::size_t xid_size() const; std::size_t rec_size() const; private: virtual void chk_hdr() const; virtual void chk_hdr(u_int64_t rid) const; virtual void chk_tail() const; virtual void clean(); }; // class deq_rec } // namespace journal } // namespace mrg #endif // ifndef mrg_journal_deq_rec_hpp qpid-cpp-store-debian-0.16/lib/jrnl/wmgr.hpp0000644000176300017630000001511311423363204017732 0ustar cajuscajus/** * \file wmgr.hpp * * Qpid asynchronous store plugin library * * File containing code for class mrg::journal::wmgr (read manager). See * class documentation for details. * * \author Kim van der Riet * * Copyright (c) 2007, 2008 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #ifndef mrg_journal_wmgr_hpp #define mrg_journal_wmgr_hpp namespace mrg { namespace journal { class wmgr; } } #include #include "jrnl/enums.hpp" #include "jrnl/pmgr.hpp" #include "jrnl/wrfc.hpp" #include namespace mrg { namespace journal { /** * \brief Class for managing a write page cache of arbitrary size and number of pages. * * The write page cache works on the principle of caching the write data within a page until * that page is either full or flushed; this initiates a single AIO write operation to store * the data on disk. * * The maximum disk throughput is achieved by keeping the write operations of uniform size. * Waiting for a page cache to fill achieves this; and in high data volume/throughput situations * achieves the optimal disk throughput. Calling flush() forces a write of the current page cache * no matter how full it is, and disrupts the uniformity of the write operations. This should * normally only be done if throughput drops and there is a danger of a page of unwritten data * waiting around for excessive time. * * The usual tradeoff between data storage latency and throughput performance applies. */ class wmgr : public pmgr { private: wrfc& _wrfc; ///< Ref to write rotating file controller u_int32_t _max_dtokpp; ///< Max data writes per page u_int32_t _max_io_wait_us; ///< Max wait in microseconds till submit void* _fhdr_base_ptr; ///< Base pointer to file header memory void** _fhdr_ptr_arr; ///< Array of pointers to file headers memory aio_cb** _fhdr_aio_cb_arr; ///< Array of iocb pointers for file header writes u_int32_t _cached_offset_dblks; ///< Amount of unwritten data in page (dblocks) std::deque _ddtokl; ///< Deferred dequeue data_tok list u_int32_t _jfsize_dblks; ///< Journal file size in dblks (NOT sblks!) u_int32_t _jfsize_pgs; ///< Journal file size in cache pages u_int16_t _num_jfiles; ///< Number of files used in iocb mallocs // TODO: Convert _enq_busy etc into a proper threadsafe lock // TODO: Convert to enum? Are these encodes mutually exclusive? bool _enq_busy; ///< Flag true if enqueue is in progress bool _deq_busy; ///< Flag true if dequeue is in progress bool _abort_busy; ///< Flag true if abort is in progress bool _commit_busy; ///< Flag true if commit is in progress enum _op_type { WMGR_ENQUEUE = 0, WMGR_DEQUEUE, WMGR_ABORT, WMGR_COMMIT }; static const char* _op_str[]; enq_rec _enq_rec; ///< Enqueue record used for encoding/decoding deq_rec _deq_rec; ///< Dequeue record used for encoding/decoding txn_rec _txn_rec; ///< Transaction record used for encoding/decoding std::set _txn_pending_set; ///< Set containing xids of pending commits/aborts public: wmgr(jcntl* jc, enq_map& emap, txn_map& tmap, wrfc& wrfc); wmgr(jcntl* jc, enq_map& emap, txn_map& tmap, wrfc& wrfc, const u_int32_t max_dtokpp, const u_int32_t max_iowait_us); virtual ~wmgr(); void initialize(aio_callback* const cbp, const u_int32_t wcache_pgsize_sblks, const u_int16_t wcache_num_pages, const u_int32_t max_dtokpp, const u_int32_t max_iowait_us, std::size_t eo = 0); iores enqueue(const void* const data_buff, const std::size_t tot_data_len, const std::size_t this_data_len, data_tok* dtokp, const void* const xid_ptr, const std::size_t xid_len, const bool transient, const bool external); iores dequeue(data_tok* dtokp, const void* const xid_ptr, const std::size_t xid_len, const bool txn_coml_commit); iores abort(data_tok* dtokp, const void* const xid_ptr, const std::size_t xid_len); iores commit(data_tok* dtokp, const void* const xid_ptr, const std::size_t xid_len); iores flush(); int32_t get_events(page_state state, timespec* const timeout, bool flush = false); bool is_txn_synced(const std::string& xid); inline bool curr_pg_blocked() const { return _page_cb_arr[_pg_index]._state != UNUSED; } inline bool curr_file_blocked() const { return _wrfc.aio_cnt() > 0; } inline u_int32_t unflushed_dblks() { return _cached_offset_dblks; } // Debug aid const std::string status_str() const; private: void initialize(aio_callback* const cbp, const u_int32_t wcache_pgsize_sblks, const u_int16_t wcache_num_pages); iores pre_write_check(const _op_type op, const data_tok* const dtokp, const std::size_t xidsize = 0, const std::size_t dsize = 0, const bool external = false) const; void dequeue_check(const std::string& xid, const u_int64_t drid); void file_header_check(const u_int64_t rid, const bool cont, const u_int32_t rec_dblks_rem); void flush_check(iores& res, bool& cont, bool& done); iores write_flush(); iores rotate_file(); void dblk_roundup(); void write_fhdr(u_int64_t rid, u_int16_t fid, u_int16_t lid, std::size_t fro); void rotate_page(); void clean(); }; } // namespace journal } // namespace mrg #endif // ifndef mrg_journal_wmgr_hpp qpid-cpp-store-debian-0.16/lib/jrnl/txn_map.hpp0000644000176300017630000001361311433263011020423 0ustar cajuscajus/** * \file txn_map.hpp * * Qpid asynchronous store plugin library * * File containing code for class mrg::journal::txn_map (transaction map). * See class documentation for details. * * \author Kim van der Riet * * Copyright (c) 2007, 2008, 2009, 2010 Red Hat Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #ifndef mrg_journal_txn_map_hpp #define mrg_journal_txn_map_hpp namespace mrg { namespace journal { class txn_map; } } #include "jrnl/smutex.hpp" #include #include #include #include #include namespace mrg { namespace journal { /** * \struct txn_data_struct * \brief Struct encapsulating transaction data necessary for processing a transaction * in the journal once it is closed with either a commit or abort. */ struct txn_data_struct { u_int64_t _rid; ///< Record id for this operation u_int64_t _drid; ///< Dequeue record id for this operation u_int16_t _pfid; ///< Physical file id, to be used when transferring to emap on commit bool _enq_flag; ///< If true, enq op, otherwise deq op bool _commit_flag; ///< (2PC transactions) Records 2PC complete c/a mode bool _aio_compl; ///< Initially false, set to true when record AIO returns txn_data_struct(const u_int64_t rid, const u_int64_t drid, const u_int16_t pfid, const bool enq_flag, const bool commit_flag = false); }; typedef txn_data_struct txn_data; typedef std::vector txn_data_list; typedef txn_data_list::iterator tdl_itr; /** * \class txn_map * \brief Class for storing transaction data for each open (ie not committed or aborted) * xid in the store. If aborted, records are discarded; if committed, they are * transferred to the enqueue map. * * The data is encapsulated by struct txn_data_struct. A vector containing the information * for each operation included as part of the same transaction is mapped against the * xid. * * The aio_compl flag is set true as each AIO write operation for the enqueue or dequeue * returns. Checking that all of these flags are true for a given xid is the mechanism * used to determine if the transaction is syncronized (through method is_txn_synced()). * * On transaction commit, then each operation is handled as follows: * * If an enqueue (_enq_flag is true), then the rid and pfid are transferred to the enq_map. * If a dequeue (_enq_flag is false), then the rid stored in the drid field is used to * remove the corresponding record from the enq_map. * * On transaction abort, then each operation is handled as follows: * * If an enqueue (_enq_flag is true), then the data is simply discarded. * If a dequeue (_enq_flag is false), then the lock for the corresponding enqueue in enq_map * (if not a part of the same transaction) is removed, and the data discarded. * *
    *   key      data
    *
    *   xid1 --- vector< [ rid, drid, pfid, enq_flag, commit_flag, aio_compl ] >
    *   xid2 --- vector< [ rid, drid, pfid, enq_flag, commit_flag, aio_compl ] >
    *   xid3 --- vector< [ rid, drid, pfid, enq_flag, commit_flag, aio_compl ] >
    *   ...
    * 
*/ class txn_map { public: // return/error codes static int16_t TMAP_RID_NOT_FOUND; static int16_t TMAP_XID_NOT_FOUND; static int16_t TMAP_OK; static int16_t TMAP_NOT_SYNCED; static int16_t TMAP_SYNCED; private: typedef std::pair xmap_param; typedef std::map xmap; typedef xmap::iterator xmap_itr; xmap _map; smutex _mutex; std::vector _pfid_txn_cnt; const txn_data_list _empty_data_list; public: txn_map(); virtual ~txn_map(); void set_num_jfiles(const u_int16_t num_jfiles); u_int32_t get_txn_pfid_cnt(const u_int16_t pfid) const; bool insert_txn_data(const std::string& xid, const txn_data& td); const txn_data_list get_tdata_list(const std::string& xid); const txn_data_list get_remove_tdata_list(const std::string& xid); bool in_map(const std::string& xid); u_int32_t enq_cnt(); u_int32_t deq_cnt(); int16_t is_txn_synced(const std::string& xid); // -1=xid not found; 0=not synced; 1=synced int16_t set_aio_compl(const std::string& xid, const u_int64_t rid); // -2=rid not found; -1=xid not found; 0=done bool data_exists(const std::string& xid, const u_int64_t rid); bool is_enq(const u_int64_t rid); inline void clear() { _map.clear(); } inline bool empty() const { return _map.empty(); } inline size_t size() const { return _map.size(); } void xid_list(std::vector& xv); private: u_int32_t cnt(const bool enq_flag); const txn_data_list get_tdata_list_nolock(const std::string& xid); }; } // namespace journal } // namespace mrg #endif // ifndef mrg_journal_txn_map_hpp qpid-cpp-store-debian-0.16/lib/jrnl/jrec.hpp0000644000176300017630000002040111422075452017701 0ustar cajuscajus/** * \file jrec.hpp * * Qpid asynchronous store plugin library * * File containing source code for class mrg::journal::jrec (abstract journal * jrecord). See class documentation for details. * * \author Kim van der Riet * * Copyright (c) 2007, 2008 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have jreceived a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #ifndef mrg_journal_jrec_hpp #define mrg_journal_jrec_hpp namespace mrg { namespace journal { class jrec; } } #include #include #include "jrnl/rec_hdr.hpp" #include "jrnl/rec_tail.hpp" #include #include namespace mrg { namespace journal { /** * \class jrec * \brief Abstract class for all file jrecords, both data and log. This class establishes * the common data format and structure for these jrecords. */ class jrec { public: jrec(); virtual ~jrec(); /** * \brief Encode this instance of jrec into the write buffer at the disk-block-aligned * pointer wptr starting at position rec_offs_dblks in the encoded record to a * maximum size of max_size_dblks. * * This call encodes the content of the data contianed in this instance of jrec into a * disk-softblock-aligned (defined by JRNL_SBLK_SIZE) buffer pointed to by parameter * wptr. No more than paramter max_size_dblks data-blocks may be written to the buffer. * The parameter rec_offs_dblks is the offset in data-blocks within the fully encoded * data block this instance represents at which to start encoding. * * Encoding entails writing the record header (struct enq_hdr), the data and the record tail * (struct enq_tail). The record must be data-block-aligned (defined by JRNL_DBLK_SIZE), * thus any remaining space in the final data-block is ignored; the returned value is the * number of data-blocks consumed from the page by the encode action. Provided the initial * alignment requirements are met, records may be of arbitrary size and may span multiple * data-blocks, disk-blocks and/or pages. * * Since the record size in data-blocks is known, the general usage pattern is to call * encode() as many times as is needed to fully encode the data. Each call to encode() * will encode as much of the record as it can to what remains of the current page cache, * and will return the number of data-blocks actually encoded. * * Example: Assume that record r1 was previously written to page 0, and that this * is an instance representing record r2. Being larger than the page size ps, r2 would span * multiple pages as follows: *
        *       |<---ps--->|
        *       +----------+----------+----------+----...
        *       |      |r2a|   r2b    |  r2c   | |
        *       |<-r1-><----------r2---------->  |
        *       +----------+----------+----------+----...
        * page:      p0         p1         p2
        * 
* Encoding record r2 will require multiple calls to encode; one for each page which * is involved. Record r2 is divided logically into sections r2a, r2b and r2c at the * points where the page boundaries intersect with the record. Assuming a page size * of ps, the page boundary pointers are represented by their names p0, p1... and the * sizes of the record segments are represented by their names r1, r2a, r2b..., the calls * should be as follows: *
        * encode(p0+r1, 0, ps-r1); (returns r2a data-blocks)
        * encode(p1, r2a, ps);     (returns r2b data-blocks which equals ps)
        * encode(p2, r2a+r2b, ps); (returns r2c data-blocks)
        * 
* * \param wptr Data-block-aligned pointer to position in page buffer where encoding is to * take place. * \param rec_offs_dblks Offset in data-blocks within record from which to start encoding. * \param max_size_dblks Maximum number of data-blocks to write to pointer wptr. * \returns Number of data-blocks encoded. */ virtual u_int32_t encode(void* wptr, u_int32_t rec_offs_dblks, u_int32_t max_size_dblks) = 0; /** * \brief Decode into this instance of jrec from the read buffer at the disk-block-aligned * pointer rptr starting at position jrec_offs_dblks in the encoded record to a * maximum size of max_size_blks. * * This call decodes a record in the page buffer pointed to by the data-block-aligned * (defined by JRNL_DBLK_SIZE) parameter rptr into this instance of jrec. No more than * paramter max_size_dblks data-blocks may be read from the buffer. The parameter * jrec_offs_dblks is the offset in data-blocks within the encoded record at which to start * decoding. * * Decoding entails reading the record header, the data and the tail. The record is * data-block-aligned (defined by JRNL_DBLK_SIZE); the returned value is the number of * data-blocks read from the buffer by the decode action. As the record data size is only * known once the header is read, the number of calls required to complete reading the * record will depend on the vlaues within this instance which are set when the * header is decoded. * * A non-zero value for jrec_offs_dblks implies that this is not the first call to * decode and the record data will be appended at this offset. * * \param h Reference to instance of struct hdr, already read from page buffer and used * to determine record type * \param rptr Data-block-aligned pointer to position in page buffer where decoding is to * begin. * \param rec_offs_dblks Offset within record from which to start appending the decoded * record. * \param max_size_dblks Maximum number of data-blocks to read from pointer rptr. * \returns Number of data-blocks read (consumed). */ virtual u_int32_t decode(rec_hdr& h, void* rptr, u_int32_t rec_offs_dblks, u_int32_t max_size_dblks) = 0; virtual bool rcv_decode(rec_hdr h, std::ifstream* ifsp, std::size_t& rec_offs) = 0; virtual std::string& str(std::string& str) const = 0; virtual std::size_t data_size() const = 0; virtual std::size_t xid_size() const = 0; virtual std::size_t rec_size() const = 0; inline virtual u_int32_t rec_size_dblks() const { return size_dblks(rec_size()); } static inline u_int32_t size_dblks(const std::size_t size) { return size_blks(size, JRNL_DBLK_SIZE); } static inline u_int32_t size_sblks(const std::size_t size) { return size_blks(size, JRNL_DBLK_SIZE * JRNL_SBLK_SIZE); } static inline u_int32_t size_blks(const std::size_t size, const std::size_t blksize) { return (size + blksize - 1)/blksize; } virtual u_int64_t rid() const = 0; protected: virtual void chk_hdr() const = 0; virtual void chk_hdr(u_int64_t rid) const = 0; virtual void chk_tail() const = 0; static void chk_hdr(const rec_hdr& hdr); static void chk_rid(const rec_hdr& hdr, u_int64_t rid); static void chk_tail(const rec_tail& tail, const rec_hdr& hdr); virtual void clean() = 0; }; // class jrec } // namespace journal } // namespace mrg #endif // ifndef mrg_journal_jrec_hpp qpid-cpp-store-debian-0.16/lib/jrnl/fcntl.hpp0000644000176300017630000001525511142067437020102 0ustar cajuscajus/** * \file fcntl.hpp * * Qpid asynchronous store plugin library * * File containing code for class mrg::journal::fcntl (non-logging file * handle), used for controlling journal log files. See class documentation for * details. * * \author Kim van der Riet * * Copyright (c) 2007, 2008, 2009 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #ifndef mrg_journal_fcntl_hpp #define mrg_journal_fcntl_hpp namespace mrg { namespace journal { class fcntl; } } #include #include #include "jrnl/rcvdat.hpp" #include namespace mrg { namespace journal { /** * \class fcntl * \brief Journal file controller. There is one instance per journal file. */ class fcntl { protected: std::string _fname; ///< File name u_int16_t _pfid; ///< Physical file ID (file number in order of creation) u_int16_t _lfid; ///< Logical file ID (ordinal number in ring store) const u_int32_t _ffull_dblks; ///< File size in dblks (incl. file header) int _wr_fh; ///< Write file handle u_int32_t _rec_enqcnt; ///< Count of enqueued records u_int32_t _rd_subm_cnt_dblks; ///< Read file count (data blocks) for submitted AIO u_int32_t _rd_cmpl_cnt_dblks; ///< Read file count (data blocks) for completed AIO u_int32_t _wr_subm_cnt_dblks; ///< Write file count (data blocks) for submitted AIO u_int32_t _wr_cmpl_cnt_dblks; ///< Write file count (data blocks) for completed AIO u_int16_t _aio_cnt; ///< Outstanding AIO operations on this file bool _fhdr_wr_aio_outstanding; ///< Outstanding file header write on this file public: // Constructors with implicit initialize() and open() fcntl(const std::string& fbasename, const u_int16_t pfid, const u_int16_t lfid, const u_int32_t jfsize_sblks, const rcvdat* const ro); virtual ~fcntl(); virtual bool reset(const rcvdat* const ro = 0); virtual void rd_reset(); virtual bool wr_reset(const rcvdat* const ro = 0); virtual int open_wr_fh(); virtual void close_wr_fh(); inline bool is_wr_fh_open() const { return _wr_fh >= 0; } inline const std::string& fname() const { return _fname; } inline u_int16_t pfid() const { return _pfid; } inline u_int16_t lfid() const { return _lfid; } inline void set_lfid(const u_int16_t lfid) { _lfid = lfid; } inline int wr_fh() const { return _wr_fh; } inline u_int32_t enqcnt() const { return _rec_enqcnt; } inline u_int32_t incr_enqcnt() { return ++_rec_enqcnt; } u_int32_t add_enqcnt(u_int32_t a); u_int32_t decr_enqcnt(); u_int32_t subtr_enqcnt(u_int32_t s); inline u_int32_t rd_subm_cnt_dblks() const { return _rd_subm_cnt_dblks; } inline std::size_t rd_subm_offs() const { return _rd_subm_cnt_dblks * JRNL_DBLK_SIZE; } u_int32_t add_rd_subm_cnt_dblks(u_int32_t a); inline u_int32_t rd_cmpl_cnt_dblks() const { return _rd_cmpl_cnt_dblks; } inline std::size_t rd_cmpl_offs() const { return _rd_cmpl_cnt_dblks * JRNL_DBLK_SIZE; } u_int32_t add_rd_cmpl_cnt_dblks(u_int32_t a); inline u_int32_t wr_subm_cnt_dblks() const { return _wr_subm_cnt_dblks; } inline std::size_t wr_subm_offs() const { return _wr_subm_cnt_dblks * JRNL_DBLK_SIZE; } u_int32_t add_wr_subm_cnt_dblks(u_int32_t a); inline u_int32_t wr_cmpl_cnt_dblks() const { return _wr_cmpl_cnt_dblks; } inline std::size_t wr_cmpl_offs() const { return _wr_cmpl_cnt_dblks * JRNL_DBLK_SIZE; } u_int32_t add_wr_cmpl_cnt_dblks(u_int32_t a); inline u_int16_t aio_cnt() const { return _aio_cnt; } inline u_int16_t incr_aio_cnt() { return ++_aio_cnt; } u_int16_t decr_aio_cnt(); inline bool wr_fhdr_aio_outstanding() { return _fhdr_wr_aio_outstanding; } inline void set_wr_fhdr_aio_outstanding(const bool wfao) { _fhdr_wr_aio_outstanding = wfao; } // Derived helper functions inline bool rd_void() const { return _wr_cmpl_cnt_dblks == 0; } inline bool rd_empty() const { return _wr_cmpl_cnt_dblks <= JRNL_SBLK_SIZE; } inline u_int32_t rd_remaining_dblks() const { return _wr_cmpl_cnt_dblks - _rd_subm_cnt_dblks; } inline bool is_rd_full() const { return _wr_cmpl_cnt_dblks == _rd_subm_cnt_dblks; } inline bool is_rd_compl() const { return _wr_cmpl_cnt_dblks == _rd_cmpl_cnt_dblks; } inline u_int32_t rd_aio_outstanding_dblks() const { return _rd_subm_cnt_dblks - _rd_cmpl_cnt_dblks; } inline bool rd_file_rotate() const { return is_rd_full() && is_wr_compl(); } inline bool wr_void() const { return _wr_subm_cnt_dblks == 0; } inline bool wr_empty() const { return _wr_subm_cnt_dblks <= JRNL_SBLK_SIZE; } inline u_int32_t wr_remaining_dblks() const { return _ffull_dblks - _wr_subm_cnt_dblks; } inline bool is_wr_full() const { return _ffull_dblks == _wr_subm_cnt_dblks; } inline bool is_wr_compl() const { return _ffull_dblks == _wr_cmpl_cnt_dblks; } inline u_int32_t wr_aio_outstanding_dblks() const { return _wr_subm_cnt_dblks - _wr_cmpl_cnt_dblks; } inline bool wr_file_rotate() const { return is_wr_full(); } // Debug aid const std::string status_str() const; protected: virtual void initialize(const std::string& fbasename, const u_int16_t pfid, const u_int16_t lfid, const u_int32_t jfsize_sblks, const rcvdat* const ro); static std::string filename(const std::string& fbasename, const u_int16_t pfid); void clean_file(const u_int32_t jfsize_sblks); void create_jfile(const u_int32_t jfsize_sblks); }; } // namespace journal } // namespace mrg #endif // ifndef mrg_journal_fcntl_hpp qpid-cpp-store-debian-0.16/lib/jrnl/rrfc.cpp0000644000176300017630000000530511717537505017724 0ustar cajuscajus/** * \file rrfc.cpp * * Qpid asynchronous store plugin library * * File containing code for class mrg::journal::rrfc (rotating * file controller). See comments in file rrfc.hpp for details. * * \author Kim van der Riet * * Copyright (c) 2007, 2008, 2009 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #include "jrnl/rrfc.hpp" #include #include #include #include "jrnl/jerrno.hpp" #include "jrnl/jexception.hpp" namespace mrg { namespace journal { rrfc::rrfc(const lpmgr* lpmp): rfc(lpmp), _fh(-1), _valid(false) {} rrfc::~rrfc() { close_fh(); } void rrfc::finalize() { unset_findex(); rfc::finalize(); } void rrfc::set_findex(const u_int16_t fc_index) { rfc::set_findex(fc_index); open_fh(_curr_fc->fname()); } void rrfc::unset_findex() { set_invalid(); close_fh(); rfc::unset_findex(); } iores rrfc::rotate() { if (!_lpmp->num_jfiles()) throw jexception(jerrno::JERR__NINIT, "rrfc", "rotate"); u_int16_t next_fc_index = _fc_index + 1; if (next_fc_index == _lpmp->num_jfiles()) next_fc_index = 0; set_findex(next_fc_index); return RHM_IORES_SUCCESS; } std::string rrfc::status_str() const { std::ostringstream oss; oss << "rrfc: " << rfc::status_str(); if (is_active()) oss << " fcntl[" << _fc_index << "]: " << _curr_fc->status_str(); return oss.str(); } // === protected functions === void rrfc::open_fh(const std::string& fn) { close_fh(); _fh = ::open(fn.c_str(), O_RDONLY | O_DIRECT); if (_fh < 0) { std::ostringstream oss; oss << "file=\"" << fn << "\"" << FORMAT_SYSERR(errno); throw jexception(jerrno::JERR_RRFC_OPENRD, oss.str(), "rrfc", "open_fh"); } } void rrfc::close_fh() { if (_fh >= 0) { ::close(_fh); _fh = -1; } } } // namespace journal } // namespace mrg qpid-cpp-store-debian-0.16/lib/jrnl/txn_hdr.hpp0000644000176300017630000001032611142067437020434 0ustar cajuscajus/** * \file txn_hdr.hpp * * Qpid asynchronous store plugin library * * File containing code for class mrg::journal::txn_hdr (transaction * record header), used to start a transaction (commit or abort) record. * * \author Kim van der Riet * * Copyright (c) 2007, 2008 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #ifndef mrg_journal_txn_hdr_hpp #define mrg_journal_txn_hdr_hpp #include #include "jrnl/rec_hdr.hpp" namespace mrg { namespace journal { #pragma pack(1) /** * \brief Struct for transaction commit and abort records. * * Struct for DTX commit and abort records. Only the magic distinguishes between them. Since * this record must be used in the context of a valid XID, the xidsize field must not be zero. * Immediately following this record is the XID itself which is xidsize bytes long, followed by * a rec_tail. * * Note that this record had its own rid distinct from the rids of the record(s) making up the * transaction it is committing or aborting. * * Record header info in binary format (24 bytes): *
    *   0                           7
    * +---+---+---+---+---+---+---+---+  -+
    * |     magic     | v | e | flags |   |
    * +---+---+---+---+---+---+---+---+   | struct hdr
    * |              rid              |   |
    * +---+---+---+---+---+---+---+---+  -+
    * |            xidsize            |
    * +---+---+---+---+---+---+---+---+
    * v = file version (If the format or encoding of this file changes, then this
    *     number should be incremented)
    * e = endian flag, false (0x00) for little endian, true (0x01) for big endian
    * 
* * Note that journal files should be transferable between 32- and 64-bit * hardware of the same endianness, but not between hardware of opposite * entianness without some sort of binary conversion utility. Thus buffering * will be needed for types that change size between 32- and 64-bit compiles. */ struct txn_hdr : rec_hdr { #if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT) u_int32_t _filler0; ///< Big-endian filler for 32-bit size_t #endif std::size_t _xidsize; ///< XID size #if defined(JRNL_LITTLE_ENDIAN) && defined(JRNL_32_BIT) u_int32_t _filler0; ///< Little-endian filler for 32-bit size_t #endif /** * \brief Default constructor, which sets all values to 0. */ txn_hdr(): rec_hdr(), #if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT) _filler0(0), #endif _xidsize(0) #if defined(JRNL_LITTLE_ENDIAN) && defined(JRNL_32_BIT) , _filler0(0) #endif {} /** * \brief Convenience constructor which initializes values during construction. */ txn_hdr(const u_int32_t magic, const u_int8_t version, const u_int64_t rid, const std::size_t xidsize, const bool owi): rec_hdr(magic, version, rid, owi), #if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT) _filler0(0), #endif _xidsize(xidsize) #if defined(JRNL_LITTLE_ENDIAN) && defined(JRNL_32_BIT) , _filler0(0) #endif {} /** * \brief Returns the size of the header in bytes. */ inline static std::size_t size() { return sizeof(txn_hdr); } }; #pragma pack() } // namespace journal } // namespace mrg #endif // ifndef mrg_journal_txn_hdr_hpp qpid-cpp-store-debian-0.16/lib/jrnl/deq_hdr.hpp0000644000176300017630000001171411142067437020376 0ustar cajuscajus/** * \file deq_hdr.hpp * * Qpid asynchronous store plugin library * * File containing code for class mrg::journal::deq_hdr (dequeue record), * used to dequeue a previously enqueued record. * * \author Kim van der Riet * * Copyright (c) 2007, 2008 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #ifndef mrg_journal_deq_hdr_hpp #define mrg_journal_deq_hdr_hpp #include #include "jrnl/rec_hdr.hpp" namespace mrg { namespace journal { #pragma pack(1) /** * \brief Struct for dequeue record. * * Struct for dequeue record. If this record has a non-zero xidsize field (i.e., there is a * valid XID), then this header is followed by the XID of xidsize bytes and a rec_tail. If, * on the other hand, this record has a zero xidsize (i.e., there is no XID), then the rec_tail * is absent. * * Note that this record had its own rid distinct from the rid of the record it is dequeueing. * The rid field below is the rid of the dequeue record itself; the deq-rid field is the rid of a * previous enqueue record being dequeued by this record. * * Record header info in binary format (32 bytes): *
    *   0                           7
    * +---+---+---+---+---+---+---+---+  -+
    * |     magic     | v | e | flags |   |
    * +---+---+---+---+---+---+---+---+   | struct hdr
    * |              rid              |   |
    * +---+---+---+---+---+---+---+---+  -+
    * |            deq-rid            |
    * +---+---+---+---+---+---+---+---+
    * |            xidsize            |
    * +---+---+---+---+---+---+---+---+
    * v = file version (If the format or encoding of this file changes, then this
    *     number should be incremented)
    * e = endian flag, false (0x00) for little endian, true (0x01) for big endian
    * 
* * Note that journal files should be transferable between 32- and 64-bit * hardware of the same endianness, but not between hardware of opposite * entianness without some sort of binary conversion utility. Thus buffering * will be needed for types that change size between 32- and 64-bit compiles. */ struct deq_hdr : rec_hdr { u_int64_t _deq_rid; ///< Record ID of dequeued record #if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT) u_int32_t _filler0; ///< Big-endian filler for 32-bit size_t #endif std::size_t _xidsize; ///< XID size #if defined(JRNL_LITTLE_ENDIAN) && defined(JRNL_32_BIT) u_int32_t _filler0; ///< Little-endian filler for 32-bit size_t #endif static const u_int16_t DEQ_HDR_TXNCMPLCOMMIT_MASK = 0x10; /** * \brief Default constructor, which sets all values to 0. */ inline deq_hdr(): rec_hdr(), _deq_rid(0), #if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT) _filler0(0), #endif _xidsize(0) #if defined(JRNL_LITTLE_ENDIAN) && defined(JRNL_32_BIT) , _filler0(0) #endif {} /** * \brief Convenience constructor which initializes values during construction. */ inline deq_hdr(const u_int32_t magic, const u_int8_t version, const u_int64_t rid, const u_int64_t deq_rid, const std::size_t xidsize, const bool owi, const bool txn_coml_commit = false): rec_hdr(magic, version, rid, owi), _deq_rid(deq_rid), #if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT) _filler0(0), #endif _xidsize(xidsize) #if defined(JRNL_LITTLE_ENDIAN) && defined(JRNL_32_BIT) , _filler0(0) #endif { set_txn_coml_commit(txn_coml_commit); } inline bool is_txn_coml_commit() const { return _uflag & DEQ_HDR_TXNCMPLCOMMIT_MASK; } inline void set_txn_coml_commit(const bool commit) { _uflag = commit ? _uflag | DEQ_HDR_TXNCMPLCOMMIT_MASK : _uflag & (~DEQ_HDR_TXNCMPLCOMMIT_MASK); } /** * \brief Returns the size of the header in bytes. */ inline static std::size_t size() { return sizeof(deq_hdr); } }; #pragma pack() } // namespace journal } // namespace mrg #endif // ifndef mrg_journal_deq_hdr_hpp qpid-cpp-store-debian-0.16/lib/jrnl/txn_map.cpp0000644000176300017630000001372711433263011020424 0ustar cajuscajus/** * \file txn_map.cpp * * Qpid asynchronous store plugin library * * File containing code for class mrg::journal::txn_map (transaction map). See * comments in file txn_map.hpp for details. * * \author Kim van der Riet * * Copyright (c) 2007, 2008, 2009, 2010 Red Hat Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #include "jrnl/txn_map.hpp" #include #include "jrnl/jerrno.hpp" #include "jrnl/jexception.hpp" #include "jrnl/slock.hpp" #include namespace mrg { namespace journal { // return/error codes int16_t txn_map::TMAP_RID_NOT_FOUND = -2; int16_t txn_map::TMAP_XID_NOT_FOUND = -1; int16_t txn_map::TMAP_OK = 0; int16_t txn_map::TMAP_NOT_SYNCED = 0; int16_t txn_map::TMAP_SYNCED = 1; txn_data_struct::txn_data_struct(const u_int64_t rid, const u_int64_t drid, const u_int16_t pfid, const bool enq_flag, const bool commit_flag): _rid(rid), _drid(drid), _pfid(pfid), _enq_flag(enq_flag), _commit_flag(commit_flag), _aio_compl(false) {} txn_map::txn_map(): _map(), _pfid_txn_cnt() {} txn_map::~txn_map() {} void txn_map::set_num_jfiles(const u_int16_t num_jfiles) { _pfid_txn_cnt.resize(num_jfiles, 0); } u_int32_t txn_map::get_txn_pfid_cnt(const u_int16_t pfid) const { return _pfid_txn_cnt.at(pfid); } bool txn_map::insert_txn_data(const std::string& xid, const txn_data& td) { bool ok = true; slock s(_mutex); xmap_itr itr = _map.find(xid); if (itr == _map.end()) // not found in map { txn_data_list list; list.push_back(td); std::pair ret = _map.insert(xmap_param(xid, list)); if (!ret.second) // duplicate ok = false; } else itr->second.push_back(td); _pfid_txn_cnt.at(td._pfid)++; return ok; } const txn_data_list txn_map::get_tdata_list(const std::string& xid) { slock s(_mutex); return get_tdata_list_nolock(xid); } const txn_data_list txn_map::get_tdata_list_nolock(const std::string& xid) { xmap_itr itr = _map.find(xid); if (itr == _map.end()) // not found in map return _empty_data_list; return itr->second; } const txn_data_list txn_map::get_remove_tdata_list(const std::string& xid) { slock s(_mutex); xmap_itr itr = _map.find(xid); if (itr == _map.end()) // not found in map return _empty_data_list; txn_data_list list = itr->second; _map.erase(itr); for (tdl_itr i=list.begin(); i!=list.end(); i++) _pfid_txn_cnt.at(i->_pfid)--; return list; } bool txn_map::in_map(const std::string& xid) { slock s(_mutex); xmap_itr itr= _map.find(xid); return itr != _map.end(); } u_int32_t txn_map::enq_cnt() { return cnt(true); } u_int32_t txn_map::deq_cnt() { return cnt(true); } u_int32_t txn_map::cnt(const bool enq_flag) { slock s(_mutex); u_int32_t c = 0; for (xmap_itr i = _map.begin(); i != _map.end(); i++) { for (tdl_itr j = i->second.begin(); j < i->second.end(); j++) { if (j->_enq_flag == enq_flag) c++; } } return c; } int16_t txn_map::is_txn_synced(const std::string& xid) { slock s(_mutex); xmap_itr itr = _map.find(xid); if (itr == _map.end()) // not found in map return TMAP_XID_NOT_FOUND; bool is_synced = true; for (tdl_itr litr = itr->second.begin(); litr < itr->second.end(); litr++) { if (!litr->_aio_compl) { is_synced = false; break; } } return is_synced ? TMAP_SYNCED : TMAP_NOT_SYNCED; } int16_t txn_map::set_aio_compl(const std::string& xid, const u_int64_t rid) { slock s(_mutex); xmap_itr itr = _map.find(xid); if (itr == _map.end()) // xid not found in map return TMAP_XID_NOT_FOUND; for (tdl_itr litr = itr->second.begin(); litr < itr->second.end(); litr++) { if (litr->_rid == rid) { litr->_aio_compl = true; return TMAP_OK; // rid found } } // xid present, but rid not found return TMAP_RID_NOT_FOUND; } bool txn_map::data_exists(const std::string& xid, const u_int64_t rid) { bool found = false; { slock s(_mutex); txn_data_list tdl = get_tdata_list_nolock(xid); tdl_itr itr = tdl.begin(); while (itr != tdl.end() && !found) { found = itr->_rid == rid; itr++; } } return found; } bool txn_map::is_enq(const u_int64_t rid) { bool found = false; { slock s(_mutex); for (xmap_itr i = _map.begin(); i != _map.end() && !found; i++) { txn_data_list list = i->second; for (tdl_itr j = list.begin(); j < list.end() && !found; j++) { if (j->_enq_flag) found = j->_rid == rid; else found = j->_drid == rid; } } } return found; } void txn_map::xid_list(std::vector& xv) { xv.clear(); { slock s(_mutex); for (xmap_itr itr = _map.begin(); itr != _map.end(); itr++) xv.push_back(itr->first); } } } // namespace journal } // namespace mrg qpid-cpp-store-debian-0.16/lib/jrnl/cvar.hpp0000644000176300017630000000525611372046135017724 0ustar cajuscajus/** * \file cvar.hpp * * Qpid asynchronous store plugin library * * This file contains a posix condition variable class. * * \author Kim van der Riet * * Copyright (c) 2007, 2008 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #ifndef mrg_journal_cvar_hpp #define mrg_journal_cvar_hpp #include #include "jrnl/jerrno.hpp" #include "jrnl/jexception.hpp" #include "jrnl/smutex.hpp" #include "jrnl/time_ns.hpp" #include #include namespace mrg { namespace journal { // Ultra-simple thread condition variable class class cvar { private: const smutex& _sm; pthread_cond_t _c; public: inline cvar(const smutex& sm) : _sm(sm) { ::pthread_cond_init(&_c, 0); } inline ~cvar() { ::pthread_cond_destroy(&_c); } inline void wait() { PTHREAD_CHK(::pthread_cond_wait(&_c, _sm.get()), "::pthread_cond_wait", "cvar", "wait"); } inline void timedwait(timespec& ts) { PTHREAD_CHK(::pthread_cond_timedwait(&_c, _sm.get(), &ts), "::pthread_cond_timedwait", "cvar", "timedwait"); } inline bool waitintvl(const long intvl_ns) { time_ns t; t.now(); t+=intvl_ns; int ret = ::pthread_cond_timedwait(&_c, _sm.get(), &t); if (ret == ETIMEDOUT) return true; PTHREAD_CHK(ret, "::pthread_cond_timedwait", "cvar", "waitintvl"); return false; } inline void signal() { PTHREAD_CHK(::pthread_cond_signal(&_c), "::pthread_cond_signal", "cvar", "notify"); } inline void broadcast() { PTHREAD_CHK(::pthread_cond_broadcast(&_c), "::pthread_cond_broadcast", "cvar", "broadcast"); } }; } // namespace journal } // namespace mrg #endif // ifndef mrg_journal_cvar_hpp qpid-cpp-store-debian-0.16/lib/jrnl/smutex.cpp0000644000176300017630000000224111372046135020300 0ustar cajuscajus/** * \file smutex.cpp * * Qpid asynchronous store plugin library * * File containing code for class mrg::journal::smutex (scoped mutex). See * comments in file smutex.hpp for details. * * \author Kim van der Riet * * Copyright (c) 2010 Red Hat Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #include "jrnl/smutex.hpp" qpid-cpp-store-debian-0.16/lib/jrnl/rmgr.hpp0000644000176300017630000000763411423363204017736 0ustar cajuscajus/** * \file rmgr.hpp * * Qpid asynchronous store plugin library * * File containing code for class mrg::journal::rmgr (read manager). See * class documentation for details. * * \author Kim van der Riet * * Copyright (c) 2007, 2008, 2009 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #ifndef mrg_journal_rmgr_hpp #define mrg_journal_rmgr_hpp namespace mrg { namespace journal { class rmgr; } } #include #include "jrnl/enums.hpp" #include "jrnl/file_hdr.hpp" #include "jrnl/pmgr.hpp" #include "jrnl/rec_hdr.hpp" #include "jrnl/rrfc.hpp" namespace mrg { namespace journal { /** * \brief Class for managing a read page cache of arbitrary size and number of pages. * * The read page cache works on the principle of filling as many pages as possilbe in advance of * reading the data. This ensures that delays caused by AIO operations are minimized. */ class rmgr : public pmgr { private: rrfc& _rrfc; ///< Ref to read rotating file controller rec_hdr _hdr; ///< Header used to determind record type void* _fhdr_buffer; ///< Buffer used for fhdr reads aio_cb* _fhdr_aio_cb_ptr; ///< iocb pointer for fhdr reads file_hdr _fhdr; ///< file header instance for reading file headers bool _fhdr_rd_outstanding; ///< true if a fhdr read is outstanding public: rmgr(jcntl* jc, enq_map& emap, txn_map& tmap, rrfc& rrfc); virtual ~rmgr(); void initialize(aio_callback* const cbp); iores read(void** const datapp, std::size_t& dsize, void** const xidpp, std::size_t& xidsize, bool& transient, bool& external, data_tok* dtokp, bool ignore_pending_txns); int32_t get_events(page_state state, timespec* const timeout, bool flush = false); void recover_complete(); inline iores synchronize() { if (_rrfc.is_valid()) return RHM_IORES_SUCCESS; return aio_cycle(); } void invalidate(); bool wait_for_validity(timespec* const timeout, const bool throw_on_timeout = false); /* TODO (if required) const iores get(const u_int64_t& rid, const std::size_t& dsize, const std::size_t& dsize_avail, const void** const data, bool auto_discard); const iores discard(data_tok* dtok); */ private: void clean(); void flush(timespec* timeout); iores pre_read_check(data_tok* dtokp); iores read_enq(rec_hdr& h, void* rptr, data_tok* dtokp); void consume_xid_rec(rec_hdr& h, void* rptr, data_tok* dtokp); void consume_filler(); iores skip(data_tok* dtokp); iores aio_cycle(); iores init_aio_reads(const int16_t first_uninit, const u_int16_t num_uninit); void rotate_page(); u_int32_t dblks_rem() const; void set_params_null(void** const datapp, std::size_t& dsize, void** const xidpp, std::size_t& xidsize); void init_file_header_read(); }; } // namespace journal } // namespace mrg #endif // ifndef mrg_journal_rmgr_hpp qpid-cpp-store-debian-0.16/lib/jrnl/jdir.hpp0000644000176300017630000003770111306023452017713 0ustar cajuscajus/** * \file jdir.hpp * * Qpid asynchronous store plugin library * * File containing code for class mrg::journal::jdir (%journal data * directory), used for controlling and manipulating %journal data * directories and files. See class documentation for details. * * \author Kim van der Riet * * Copyright (c) 2007, 2008 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #ifndef mrg_journal_jdir_hpp #define mrg_journal_jdir_hpp namespace mrg { namespace journal { class jdir; } } #include "jrnl/jinf.hpp" #include namespace mrg { namespace journal { /** * \class jdir * \brief Class to manage the %journal directory */ class jdir { private: std::string _dirname; std::string _base_filename; public: /** * \brief Sole constructor * * \param dirname Name of directory to be managed. * \param base_filename Filename root used in the creation of %journal files * and sub-directories. */ jdir(const std::string& dirname, const std::string& base_filename); virtual ~jdir(); /** * \brief Create %journal directory as set in the dirname parameter of the constructor. * Recursive creation is supported. * * \exception jerrno::JERR_JDIR_MKDIR The creation of dirname failed. */ void create_dir(); /** * \brief Static function to create a directory. Recursive creation is supported. * * \param dirname C-string containing name of directory. * * \exception jerrno::JERR_JDIR_MKDIR The creation of dirname failed. */ static void create_dir(const char* dirname); /** * \brief Static function to create a directory. Recursive creation is supported. * * \param dirname String containing name of directory. * * \exception jerrno::JERR_JDIR_MKDIR The creation of dirname failed. */ static void create_dir(const std::string& dirname); /** * \brief Clear the %journal directory of files matching the base filename * by moving them into a subdirectory. This fn uses the dirname and base_filename * that were set on construction. * * \param create_flag If set, create dirname if it is non-existent, otherwise throw * exception. * * \exception jerrno::JERR_JDIR_OPENDIR The %journal directory could not be opened. * \exception jerrno::JERR_JDIR_FMOVE Moving the files from the %journal directory to the created backup * directory failed. * \exception jerrno::JERR_JDIR_CLOSEDIR The directory handle could not be closed. */ void clear_dir(const bool create_flag = true); /** * \brief Clear the directory dirname of %journal files matching base_filename * by moving them into a subdirectory. * * \param dirname C-string containing name of %journal directory. * \param base_filename C-string containing base filename of %journal files to be matched * for moving into subdirectory. * \param create_flag If set, create dirname if it is non-existent, otherwise throw * exception * * \exception jerrno::JERR_JDIR_OPENDIR The %journal directory could not be opened. * \exception jerrno::JERR_JDIR_FMOVE Moving the files from the %journal directory to the created backup * directory failed. * \exception jerrno::JERR_JDIR_CLOSEDIR The directory handle could not be closed. */ static void clear_dir(const char* dirname, const char* base_filename, const bool create_flag = true); /** * \brief Clear the directory dirname of %journal files matching base_filename * by moving them into a subdirectory. * * \param dirname String containing name of %journal directory. * \param base_filename String containing base filename of %journal files to be matched * for moving into subdirectory. * \param create_flag If set, create dirname if it is non-existent, otherwise throw * exception * * \exception jerrno::JERR_JDIR_OPENDIR The %journal directory could not be opened. * \exception jerrno::JERR_JDIR_FMOVE Moving the files from the %journal directory to the created backup * directory failed. * \exception jerrno::JERR_JDIR_CLOSEDIR The directory handle could not be closed. */ static void clear_dir(const std::string& dirname, const std::string& base_filename, const bool create_flag = true); /** * \brief Move (push down) the directory target_dir located in directory dirname into a backup directory * named _bak_dir_base.XXXX (note prepended underscore), where XXXX is an increasing hex serial number * starting at 0000. * * \param dirname Full path to directory containing directory to be pushed down. * \param target_dir Name of directory in dirname to be pushed down. * \param bak_dir_base Base name for backup directory to be created in dirname, into which target_dir will be moved. * \return Name of backup dir into which target_dir was pushed. */ static std::string push_down(const std::string& dirname, const std::string& target_dir, const std::string& bak_dir_base); /** * \brief Verify that dirname is a valid %journal directory. * * The validation reads the .%jinf file, and using this information verifies that all the expected %journal * (.jdat) files are present. * * \exception jerrno::JERR_JDIR_NOTDIR dirname is not a directory * \exception jerrno::JERR_JDIR_STAT Could not stat dirname * \exception jerrno::JERR__FILEIO Error reading %jinf file * \exception jerrno::JERR_JINF_CVALIDFAIL Error validating %jinf file * \exception jerrno::JERR_JDIR_NOSUCHFILE Expected jdat file is missing */ void verify_dir(); /** * \brief Verify that dirname is a valid %journal directory. * * The validation reads the .%jinf file, and using this information verifies that all the expected %journal * (.jdat) files are present. * * \param dirname C-string containing name of %journal directory. * \param base_filename C-string containing base filename of %journal files to be matched for moving into sub-directory. * * \exception jerrno::JERR_JDIR_NOTDIR dirname is not a directory * \exception jerrno::JERR_JDIR_STAT Could not stat dirname * \exception jerrno::JERR__FILEIO Error reading %jinf file * \exception jerrno::JERR_JINF_CVALIDFAIL Error validating %jinf file * \exception jerrno::JERR_JDIR_NOSUCHFILE Expected jdat file is missing */ static void verify_dir(const char* dirname, const char* base_filename); /** * \brief Verify that dirname is a valid %journal directory. * * The validation reads the .%jinf file, and using this information verifies that all the expected %journal * (.jdat) files are present. * * \param dirname String containing name of %journal directory. * \param base_filename String containing base filename of %journal files to be matched for moving into sub-directory. * * \exception jerrno::JERR_JDIR_NOTDIR dirname is not a directory * \exception jerrno::JERR_JDIR_STAT Could not stat dirname * \exception jerrno::JERR__FILEIO Error reading %jinf file * \exception jerrno::JERR_JINF_CVALIDFAIL Error validating %jinf file * \exception jerrno::JERR_JDIR_NOSUCHFILE Expected jdat file is missing */ static void verify_dir(const std::string& dirname, const std::string& base_filename); /** * \brief Delete the %journal directory and all files and sub--directories that it may * contain. This is equivilent of rm -rf. * * FIXME: links are not handled correctly. * * \param children_only If true, delete only children of dirname, but leave dirname itself. * * \exception jerrno::JERR_JDIR_OPENDIR The %journal directory could not be opened. * \exception jerrno::JERR_JDIR_STAT Could not stat dirname. * \exception jerrno::JERR_JDIR_UNLINK A file could not be deleted. * \exception jerrno::JERR_JDIR_BADFTYPE A dir entry is neiter a file nor a dir. * \exception jerrno::JERR_JDIR_CLOSEDIR The directory handle could not be closed. * \exception jerrno::JERR_JDIR_RMDIR A directory could not be deleted. */ void delete_dir(bool children_only = false ); /** * \brief Delete the %journal directory and all files and sub--directories that it may * contain. This is equivilent of rm -rf. * * FIXME: links are not handled correctly. * * \param dirname C-string containing name of directory to be deleted. * \param children_only If true, delete only children of dirname, but leave dirname itself. * * \exception jerrno::JERR_JDIR_OPENDIR The %journal directory could not be opened. * \exception jerrno::JERR_JDIR_STAT Could not stat dirname. * \exception jerrno::JERR_JDIR_UNLINK A file could not be deleted. * \exception jerrno::JERR_JDIR_BADFTYPE A dir entry is neiter a file nor a dir. * \exception jerrno::JERR_JDIR_CLOSEDIR The directory handle could not be closed. * \exception jerrno::JERR_JDIR_RMDIR A directory could not be deleted. */ static void delete_dir(const char* dirname, bool children_only = false); /** * \brief Delete the %journal directory and all files and sub--directories that it may * contain. This is equivilent of rm -rf. * * FIXME: links are not handled correctly. * * \param dirname String containing name of directory to be deleted. * \param children_only If true, delete only children of dirname, but leave dirname itself. * * \exception jerrno::JERR_JDIR_OPENDIR The %journal directory could not be opened. * \exception jerrno::JERR_JDIR_STAT Could not stat dirname. * \exception jerrno::JERR_JDIR_UNLINK A file could not be deleted. * \exception jerrno::JERR_JDIR_BADFTYPE A dir entry is neiter a file nor a dir. * \exception jerrno::JERR_JDIR_CLOSEDIR The directory handle could not be closed. * \exception jerrno::JERR_JDIR_RMDIR A directory could not be deleted. */ static void delete_dir(const std::string& dirname, bool children_only = false); /** * \brief Create bakup directory that is next in sequence and move all %journal files * matching base_filename into it. * * In directory dirname, search for existing backup directory using pattern * "_basename.bak.XXXX" where XXXX is a hexadecimal sequence, and create next directory * based on highest number found. Move all %journal files which match the base_fileaname * parameter into this new backup directory. * * \param dirname String containing name of %journal directory. * \param base_filename String containing base filename of %journal files to be matched * for moving into subdirectory. * * \exception jerrno::JERR_JDIR_OPENDIR The %journal directory could not be opened. * \exception jerrno::JERR_JDIR_CLOSEDIR The directory handle could not be closed. * \exception jerrno::JERR_JDIR_MKDIR The backup directory could not be deleted. */ static std::string create_bak_dir(const std::string& dirname, const std::string& base_filename); /** * \brief Return the directory name as a string. */ inline const std::string& dirname() const { return _dirname; } /** * \brief Return the %journal base filename name as a string. */ inline const std::string& base_filename() const { return _base_filename; } /** * \brief Test whether the named file is a directory. * * \param name Name of file to be tested. * \return true if the named file is a directory; false * otherwise. * \exception jerrno::JERR_JDIR_STAT Could not stat name. */ static bool is_dir(const char* name); /** * \brief Test whether the named file is a directory. * * \param name Name of file to be tested. * \return true if the named file is a directory; false * otherwise. * \exception jerrno::JERR_JDIR_STAT Could not stat name. */ static bool is_dir(const std::string& name); /** * \brief Test whether the named entity exists on the filesystem. * * If stat() fails with error ENOENT, then this will return false. If * stat() succeeds, then true is returned, irrespective of the file type. * If stat() fails with any other error, an exception is thrown. * * \param name Name of entity to be tested. * \return true if the named entity exists; false * otherwise. * \exception jerrno::JERR_JDIR_STAT Could not stat name. */ static bool exists(const char* name); /** * \brief Test whether the named entity exists on the filesystem. * * If stat() fails with error ENOENT, then this will return false. If * stat() succeeds, then true is returned, irrespective of the file type. * If stat() fails with any other error, an exception is thrown. * * \param name Name of entity to be tested. * \return true if the named entity exists; false * otherwise. * \exception jerrno::JERR_JDIR_STAT Could not stat name. */ static bool exists(const std::string& name); /** * \brief Stream operator */ friend std::ostream& operator<<(std::ostream& os, const jdir& jdir); /** * \brief Stream operator */ friend std::ostream& operator<<(std::ostream& os, const jdir* jdirPtr); private: /** * \brief Check for error, if non-zero close DIR handle and throw JERR_JDIR_READDIR * * \exception jerrno::JERR_JDIR_READDIR Error while reading contents of dir. */ static void check_err(const int err_num, DIR* dir, const std::string& dir_name, const std::string& fn_name); /** * \brief Close a DIR handle, throw JERR_JDIR_CLOSEDIR if error occurs during close * * \exception jerrno::JERR_JDIR_CLOSEDIR The directory handle could not be closed. */ static void close_dir(DIR* dir, const std::string& dir_name, const std::string& fn_name); }; } // namespace journal } // namespace mrg #endif // ifndef mrg_journal_jdir_hpp qpid-cpp-store-debian-0.16/lib/jrnl/jinf.hpp0000644000176300017630000001134411446152364017716 0ustar cajuscajus/** * \file jinf.hpp * * Qpid asynchronous store plugin library * * This file contains the code for the mrg::journal::jinf class. * * \author Kim van der Riet * * Copyright (c) 2007, 2008, 2009 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #ifndef mrg_journal_jinf_hpp #define mrg_journal_jinf_hpp #include #include #include #include namespace mrg { namespace journal { /** * \class jinf * \brief Class to handle the journal information file <basename>.jinf. */ class jinf { public: typedef std::vector pfid_list; // pfids typedef pfid_list::const_iterator pfidl_citr; private: u_int8_t _jver; std::string _jid; std::string _jdir; std::string _base_filename; std::string _filename; timespec _ts; u_int16_t _num_jfiles; bool _ae; u_int32_t _ae_max_jfiles; u_int32_t _jfsize_sblks; u_int16_t _sblk_size_dblks; u_int32_t _dblk_size; u_int32_t _wcache_pgsize_sblks; u_int16_t _wcache_num_pages; u_int32_t _rcache_pgsize_sblks; u_int16_t _rcache_num_pages; std::tm* _tm_ptr; bool _valid_flag; bool _analyzed_flag; pfid_list _pfid_list; bool _initial_owi; bool _frot; public: // constructor for reading existing jinf file jinf(const std::string& jinf_filename, bool validate_flag); // constructor for writing jinf file jinf(const std::string& jid, const std::string& jdir, const std::string& base_filename, const u_int16_t num_jfiles, const bool auto_expand, const u_int16_t ae_max_jfiles, const u_int32_t jfsize_sblks, const u_int32_t wcache_pgsize_sblks, const u_int16_t wcache_num_pages, const timespec& ts); virtual ~jinf(); void validate(); void analyze(); void write(); inline u_int8_t jver() const { return _jver; } inline const std::string& jid() const { return _jid; } inline const std::string& jdir() const { return _jdir; } inline void set_jdir(const std::string& jdir) { _jdir = jdir; } inline const std::string& base_filename() const { return _base_filename; } inline const timespec& ts() const { return _ts; } inline u_int16_t num_jfiles() const { return _num_jfiles; } u_int16_t incr_num_jfiles(); inline bool is_ae() const { return _ae; } inline u_int16_t ae_max_jfiles() const { return _ae_max_jfiles; } inline u_int32_t jfsize_sblks() const { return _jfsize_sblks; } inline u_int16_t sblk_size_dblks() const { return _sblk_size_dblks; } inline u_int32_t dblk_size() const { return _dblk_size; } inline u_int32_t wcache_pgsize_sblks() const { return _wcache_pgsize_sblks; } inline u_int16_t wcache_num_pages() const { return _wcache_num_pages; } inline u_int32_t rcache_pgsize_sblks() const { return _rcache_pgsize_sblks; } inline u_int16_t rcache_num_pages() const { return _rcache_num_pages; } u_int16_t get_first_pfid(); u_int16_t get_last_pfid(); pfid_list& get_pfid_list(); void get_normalized_pfid_list(pfid_list& pfid_list); bool get_initial_owi(); bool get_frot(); std::string to_string() const; std::string xml_str() const; private: void set_filename(); void read(const std::string& jinf_filename); bool bool_value(char* line) const; u_int16_t u_int16_value(char* line) const; u_int32_t u_int32_value(char* line) const; std::string& string_value(std::string& str, char* line) const; char* find_value(char* line) const; u_int32_t get_filesize(const std::string& file_name) const; }; } // namespace journal } // namespace mrg #endif // ifndef mrg_journal_jinf_hpp qpid-cpp-store-debian-0.16/lib/jrnl/pmgr.hpp0000644000176300017630000001275111423363204017730 0ustar cajuscajus/** * \file pmgr.hpp * * Qpid asynchronous store plugin library * * File containing code for class mrg::journal::pmgr (page manager). See * class documentation for details. * * \author Kim van der Riet * * Copyright (c) 2007, 2008 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #ifndef mrg_journal_pmgr_hpp #define mrg_journal_pmgr_hpp namespace mrg { namespace journal { class pmgr; class jcntl; } } #include #include "jrnl/aio.hpp" #include "jrnl/aio_callback.hpp" #include "jrnl/data_tok.hpp" #include "jrnl/deq_rec.hpp" #include "jrnl/enq_map.hpp" #include "jrnl/enq_rec.hpp" #include "jrnl/fcntl.hpp" #include "jrnl/txn_map.hpp" #include "jrnl/txn_rec.hpp" namespace mrg { namespace journal { /** * \brief Abstract class for managing either read or write page cache of arbitrary size and * number of cache_num_pages. */ class pmgr { public: /** * \brief Enumeration of possible stats of a page within a page cache. */ enum page_state { UNUSED, ///< A page is uninitialized, contains no data. IN_USE, ///< Page is in use. AIO_PENDING, ///< An AIO request outstanding. AIO_COMPLETE ///< An AIO request is complete. }; protected: /** * \brief Page control block, carries control and state information for each page in the * cache. */ struct page_cb { u_int16_t _index; ///< Index of this page page_state _state; ///< Status of page u_int64_t _frid; ///< First rid in page (used for fhdr init) u_int32_t _wdblks; ///< Total number of dblks in page so far u_int32_t _rdblks; ///< Total number of dblks in page std::deque* _pdtokl; ///< Page message tokens list fcntl* _wfh; ///< File handle for incrementing write compl counts fcntl* _rfh; ///< File handle for incrementing read compl counts void* _pbuff; ///< Page buffer page_cb(u_int16_t index); ///< Convenience constructor const char* state_str() const; ///< Return state as string for this pcb }; static const u_int32_t _sblksize; ///< Disk softblock size u_int32_t _cache_pgsize_sblks; ///< Size of page cache cache_num_pages u_int16_t _cache_num_pages; ///< Number of page cache cache_num_pages jcntl* _jc; ///< Pointer to journal controller enq_map& _emap; ///< Ref to enqueue map txn_map& _tmap; ///< Ref to transaction map void* _page_base_ptr; ///< Base pointer to page memory void** _page_ptr_arr; ///< Array of pointers to cache_num_pages in page memory page_cb* _page_cb_arr; ///< Array of page_cb structs aio_cb* _aio_cb_arr; ///< Array of iocb structs aio_event* _aio_event_arr; ///< Array of io_events io_context_t _ioctx; ///< AIO context for read/write operations u_int16_t _pg_index; ///< Index of current page being used u_int32_t _pg_cntr; ///< Page counter; determines if file rotation req'd u_int32_t _pg_offset_dblks; ///< Page offset (used so far) in data blocks u_int32_t _aio_evt_rem; ///< Remaining AIO events aio_callback* _cbp; ///< Pointer to callback object enq_rec _enq_rec; ///< Enqueue record used for encoding/decoding deq_rec _deq_rec; ///< Dequeue record used for encoding/decoding txn_rec _txn_rec; ///< Transaction record used for encoding/decoding public: pmgr(jcntl* jc, enq_map& emap, txn_map& tmap); virtual ~pmgr(); virtual int32_t get_events(page_state state, timespec* const timeout, bool flush = false) = 0; inline u_int32_t get_aio_evt_rem() const { return _aio_evt_rem; } static const char* page_state_str(page_state ps); inline u_int32_t cache_pgsize_sblks() const { return _cache_pgsize_sblks; } inline u_int16_t cache_num_pages() const { return _cache_num_pages; } protected: virtual void initialize(aio_callback* const cbp, const u_int32_t cache_pgsize_sblks, const u_int16_t cache_num_pages); virtual void rotate_page() = 0; virtual void clean(); }; } // namespace journal } // namespace mrg #endif // ifndef mrg_journal_pmgr_hpp qpid-cpp-store-debian-0.16/lib/jrnl/time_ns.hpp0000644000176300017630000000737111670231232020422 0ustar cajuscajus/** * \file time_ns.hpp * * Qpid asynchronous store plugin library * * Messaging journal time struct mrg::journal::time_ns, derived from * the timespec struct and provided with helper functions. * * \author Kim van der Riet * * Copyright (c) 2008 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #ifndef mrg_jtt_time_ns_hpp #define mrg_jtt_time_ns_hpp #include #include #include namespace mrg { namespace journal { struct time_ns : public timespec { inline time_ns() { tv_sec = 0; tv_nsec = 0; } inline time_ns(const std::time_t sec, const long nsec = 0) { tv_sec = sec; tv_nsec = nsec; } inline time_ns(const time_ns& t) { tv_sec = t.tv_sec; tv_nsec = t.tv_nsec; } inline void set_zero() { tv_sec = 0; tv_nsec = 0; } inline bool is_zero() const { return tv_sec == 0 && tv_nsec == 0; } inline int now() { if(::clock_gettime(CLOCK_REALTIME, this)) return errno; return 0; } const std::string str(int precision = 6) const; inline time_ns& operator=(const time_ns& rhs) { tv_sec = rhs.tv_sec; tv_nsec = rhs.tv_nsec; return *this; } inline time_ns& operator+=(const time_ns& rhs) { tv_nsec += rhs.tv_nsec; if (tv_nsec >= 1000000000L) { tv_sec++; tv_nsec -= 1000000000L; } tv_sec += rhs.tv_sec; return *this; } inline time_ns& operator+=(const long ns) { tv_nsec += ns; if (tv_nsec >= 1000000000L) { tv_sec++; tv_nsec -= 1000000000L; } return *this; } inline time_ns& operator-=(const long ns) { tv_nsec -= ns; if (tv_nsec < 0) { tv_sec--; tv_nsec += 1000000000L; } return *this; } inline time_ns& operator-=(const time_ns& rhs) { tv_nsec -= rhs.tv_nsec; if (tv_nsec < 0) { tv_sec--; tv_nsec += 1000000000L; } tv_sec -= rhs.tv_sec; return *this; } inline const time_ns operator+(const time_ns& rhs) { time_ns t(*this); t += rhs; return t; } inline const time_ns operator-(const time_ns& rhs) { time_ns t(*this); t -= rhs; return t; } inline bool operator==(const time_ns& rhs) { return tv_sec == rhs.tv_sec && tv_nsec == rhs.tv_nsec; } inline bool operator!=(const time_ns& rhs) { return tv_sec != rhs.tv_sec || tv_nsec != rhs.tv_nsec; } inline bool operator>(const time_ns& rhs) { if(tv_sec == rhs.tv_sec) return tv_nsec > rhs.tv_nsec; return tv_sec > rhs.tv_sec; } inline bool operator>=(const time_ns& rhs) { if(tv_sec == rhs.tv_sec) return tv_nsec >= rhs.tv_nsec; return tv_sec >= rhs.tv_sec; } inline bool operator<(const time_ns& rhs) { if(tv_sec == rhs.tv_sec) return tv_nsec < rhs.tv_nsec; return tv_sec < rhs.tv_sec; } inline bool operator<=(const time_ns& rhs) { if(tv_sec == rhs.tv_sec) return tv_nsec <= rhs.tv_nsec; return tv_sec <= rhs.tv_sec; } }; } // namespace journal } // namespace mrg #endif // ifndef mrg_jtt_time_ns_hpp qpid-cpp-store-debian-0.16/lib/jrnl/enq_map.hpp0000644000176300017630000001050011433263011020365 0ustar cajuscajus/** * \file enq_map.hpp * * Qpid asynchronous store plugin library * * File containing code for class mrg::journal::enq_map (enqueue map). * See class documentation for details. * * \author Kim van der Riet * * Copyright (c) 2007, 2008, 2009, 2010 Red Hat Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #ifndef mrg_journal_enq_map_hpp #define mrg_journal_enq_map_hpp namespace mrg { namespace journal { class enq_map; } } #include "jrnl/jexception.hpp" #include "jrnl/smutex.hpp" #include #include #include namespace mrg { namespace journal { /** * \class enq_map * \brief Class for storing the physical file id (pfid) and a transaction locked flag for each enqueued * data block using the record id (rid) as a key. This is the primary mechanism for * deterimining the enqueue low water mark: if a pfid exists in this map, then there is * at least one still-enqueued record in that file. (The transaction map must also be * clear, however.) * * Map rids against pfid and lock status. As records are enqueued, they are added to this * map, and as they are dequeued, they are removed. An enqueue is locked when a transactional * dequeue is pending that has been neither committed nor aborted. *
    *   key      data
    *
    *   rid1 --- [ pfid, txn_lock ]
    *   rid2 --- [ pfid, txn_lock ]
    *   rid3 --- [ pfid, txn_lock ]
    *   ...
    * 
*/ class enq_map { public: // return/error codes static int16_t EMAP_DUP_RID; static int16_t EMAP_LOCKED; static int16_t EMAP_RID_NOT_FOUND; static int16_t EMAP_OK; static int16_t EMAP_FALSE; static int16_t EMAP_TRUE; private: struct emap_data_struct { u_int16_t _pfid; bool _lock; emap_data_struct(const u_int16_t pfid, const bool lock) : _pfid(pfid), _lock(lock) {} }; typedef std::pair emap_param; typedef std::map emap; typedef emap::iterator emap_itr; emap _map; smutex _mutex; std::vector _pfid_enq_cnt; public: enq_map(); virtual ~enq_map(); void set_num_jfiles(const u_int16_t num_jfiles); inline u_int32_t get_enq_cnt(const u_int16_t pfid) const { return _pfid_enq_cnt.at(pfid); }; int16_t insert_pfid(const u_int64_t rid, const u_int16_t pfid); // 0=ok; -3=duplicate rid; int16_t insert_pfid(const u_int64_t rid, const u_int16_t pfid, const bool locked); // 0=ok; -3=duplicate rid; int16_t get_pfid(const u_int64_t rid); // >=0=pfid; -1=rid not found; -2=locked int16_t get_remove_pfid(const u_int64_t rid, const bool txn_flag = false); // >=0=pfid; -1=rid not found; -2=locked bool is_enqueued(const u_int64_t rid, bool ignore_lock = false); int16_t lock(const u_int64_t rid); // 0=ok; -1=rid not found int16_t unlock(const u_int64_t rid); // 0=ok; -1=rid not found int16_t is_locked(const u_int64_t rid); // 1=true; 0=false; -1=rid not found inline void clear() { _map.clear(); } inline bool empty() const { return _map.empty(); } inline u_int32_t size() const { return u_int32_t(_map.size()); } void rid_list(std::vector& rv); void pfid_list(std::vector& fv); }; } // namespace journal } // namespace mrg #endif // ifndef mrg_journal_enq_map_hpp qpid-cpp-store-debian-0.16/lib/jrnl/fcntl.cpp0000644000176300017630000002561111717442050020066 0ustar cajuscajus/** * \file fcntl.cpp * * Qpid asynchronous store plugin library * * File containing code for class mrg::journal::fcntl (non-logging file * handle), used for controlling journal log files. See comments in file * fcntl.hpp for details. * * Copyright (c) 2007, 2008, 2009 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #include "jrnl/fcntl.hpp" #include #include #include #include #include #include "jrnl/jerrno.hpp" #include "jrnl/jexception.hpp" #include #include namespace mrg { namespace journal { fcntl::fcntl(const std::string& fbasename, const u_int16_t pfid, const u_int16_t lfid, const u_int32_t jfsize_sblks, const rcvdat* const ro): _fname(), _pfid(pfid), _lfid(lfid), _ffull_dblks(JRNL_SBLK_SIZE * (jfsize_sblks + 1)), _wr_fh(-1), _rec_enqcnt(0), _rd_subm_cnt_dblks(0), _rd_cmpl_cnt_dblks(0), _wr_subm_cnt_dblks(0), _wr_cmpl_cnt_dblks(0), _aio_cnt(0), _fhdr_wr_aio_outstanding(false) { initialize(fbasename, pfid, lfid, jfsize_sblks, ro); open_wr_fh(); } fcntl::~fcntl() { close_wr_fh(); } bool fcntl::reset(const rcvdat* const ro) { rd_reset(); return wr_reset(ro); } void fcntl::rd_reset() { _rd_subm_cnt_dblks = 0; _rd_cmpl_cnt_dblks = 0; } bool fcntl::wr_reset(const rcvdat* const ro) { if (ro) { if (!ro->_jempty) { if (ro->_lfid == _pfid) { _wr_subm_cnt_dblks = ro->_eo/JRNL_DBLK_SIZE; _wr_cmpl_cnt_dblks = ro->_eo/JRNL_DBLK_SIZE; } else { _wr_subm_cnt_dblks = _ffull_dblks; _wr_cmpl_cnt_dblks = _ffull_dblks; } _rec_enqcnt = ro->_enq_cnt_list[_pfid]; return true; } } // Journal overflow test - checks if the file to be reset still contains enqueued records // or outstanding aios if (_rec_enqcnt || _aio_cnt) return false; _wr_subm_cnt_dblks = 0; _wr_cmpl_cnt_dblks = 0; return true; } int fcntl::open_wr_fh() { if (_wr_fh < 0) { _wr_fh = ::open(_fname.c_str(), O_WRONLY | O_DIRECT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); // 0644 -rw-r--r-- if (_wr_fh < 0) { std::ostringstream oss; oss << "pfid=" << _pfid << " lfid=" << _lfid << " file=\"" << _fname << "\"" << FORMAT_SYSERR(errno); throw jexception(jerrno::JERR_FCNTL_OPENWR, oss.str(), "fcntl", "open_fh"); } } return _wr_fh; } void fcntl::close_wr_fh() { if (_wr_fh >= 0) { ::close(_wr_fh); _wr_fh = -1; } } u_int32_t fcntl::add_enqcnt(u_int32_t a) { _rec_enqcnt += a; return _rec_enqcnt; } u_int32_t fcntl::decr_enqcnt() { if (_rec_enqcnt == 0) { std::ostringstream oss; oss << "pfid=" << _pfid << " lfid=" << _lfid; throw jexception(jerrno::JERR__UNDERFLOW, oss.str(), "fcntl", "decr_enqcnt"); } return --_rec_enqcnt; } u_int32_t fcntl::subtr_enqcnt(u_int32_t s) { if (_rec_enqcnt < s) { std::ostringstream oss; oss << "pfid=" << _pfid << " lfid=" << _lfid << " rec_enqcnt=" << _rec_enqcnt << " decr=" << s; throw jexception(jerrno::JERR__UNDERFLOW, oss.str(), "fcntl", "subtr_enqcnt"); } _rec_enqcnt -= s; return _rec_enqcnt; } u_int32_t fcntl::add_rd_subm_cnt_dblks(u_int32_t a) { if (_rd_subm_cnt_dblks + a > _wr_subm_cnt_dblks) { std::ostringstream oss; oss << "pfid=" << _pfid << " lfid=" << _lfid << " rd_subm_cnt_dblks=" << _rd_subm_cnt_dblks << " incr=" << a; oss << " wr_subm_cnt_dblks=" << _wr_subm_cnt_dblks; throw jexception(jerrno::JERR_FCNTL_RDOFFSOVFL, oss.str(), "fcntl", "add_rd_subm_cnt_dblks"); } _rd_subm_cnt_dblks += a; return _rd_subm_cnt_dblks; } u_int32_t fcntl::add_rd_cmpl_cnt_dblks(u_int32_t a) { if (_rd_cmpl_cnt_dblks + a > _rd_subm_cnt_dblks) { std::ostringstream oss; oss << "pfid=" << _pfid << " lfid=" << _lfid << " rd_cmpl_cnt_dblks=" << _rd_cmpl_cnt_dblks << " incr=" << a; oss << " rd_subm_cnt_dblks=" << _rd_subm_cnt_dblks; throw jexception(jerrno::JERR_FCNTL_CMPLOFFSOVFL, oss.str(), "fcntl", "add_rd_cmpl_cnt_dblks"); } _rd_cmpl_cnt_dblks += a; return _rd_cmpl_cnt_dblks; } u_int32_t fcntl::add_wr_subm_cnt_dblks(u_int32_t a) { if (_wr_subm_cnt_dblks + a > _ffull_dblks) // Allow for file header { std::ostringstream oss; oss << "pfid=" << _pfid << " lfid=" << _lfid << " wr_subm_cnt_dblks=" << _wr_subm_cnt_dblks << " incr=" << a; oss << " fsize=" << _ffull_dblks << " dblks"; throw jexception(jerrno::JERR_FCNTL_FILEOFFSOVFL, oss.str(), "fcntl", "add_wr_subm_cnt_dblks"); } _wr_subm_cnt_dblks += a; return _wr_subm_cnt_dblks; } u_int32_t fcntl::add_wr_cmpl_cnt_dblks(u_int32_t a) { if (_wr_cmpl_cnt_dblks + a > _wr_subm_cnt_dblks) { std::ostringstream oss; oss << "pfid=" << _pfid << " lfid=" << _lfid << " wr_cmpl_cnt_dblks=" << _wr_cmpl_cnt_dblks << " incr=" << a; oss << " wr_subm_cnt_dblks=" << _wr_subm_cnt_dblks; throw jexception(jerrno::JERR_FCNTL_CMPLOFFSOVFL, oss.str(), "fcntl", "add_wr_cmpl_cnt_dblks"); } _wr_cmpl_cnt_dblks += a; return _wr_cmpl_cnt_dblks; } u_int16_t fcntl::decr_aio_cnt() { if(_aio_cnt == 0) { std::ostringstream oss; oss << "pfid=" << _pfid << " lfid=" << _lfid << " Decremented aio_cnt to below zero"; throw jexception(jerrno::JERR__UNDERFLOW, oss.str(), "fcntl", "decr_aio_cnt"); } return --_aio_cnt; } // Debug function const std::string fcntl::status_str() const { std::ostringstream oss; oss << "pfid=" << _pfid << " ws=" << _wr_subm_cnt_dblks << " wc=" << _wr_cmpl_cnt_dblks; oss << " rs=" << _rd_subm_cnt_dblks << " rc=" << _rd_cmpl_cnt_dblks; oss << " ec=" << _rec_enqcnt << " ac=" << _aio_cnt; return oss.str(); } // Protected functions void fcntl::initialize(const std::string& fbasename, const u_int16_t pfid, const u_int16_t lfid, const u_int32_t jfsize_sblks, const rcvdat* const ro) { _pfid = pfid; _lfid = lfid; _fname = filename(fbasename, pfid); #ifdef RHM_JOWRITE // In test mode, only create file if it does not exist struct stat s; if (::stat(_fname.c_str(), &s)) { #endif if (ro) // Recovery initialization: set counters only { if (!ro->_jempty) { // For last file only, set write counters to end of last record (the // continuation point); for all others, set to eof. if (ro->_lfid == _pfid) { _wr_subm_cnt_dblks = ro->_eo/JRNL_DBLK_SIZE; _wr_cmpl_cnt_dblks = ro->_eo/JRNL_DBLK_SIZE; } else { _wr_subm_cnt_dblks = _ffull_dblks; _wr_cmpl_cnt_dblks = _ffull_dblks; } // Set the number of enqueued records for this file. _rec_enqcnt = ro->_enq_cnt_list[_pfid]; } } else // Normal initialization: create empty journal files create_jfile(jfsize_sblks); #ifdef RHM_JOWRITE } #endif } std::string fcntl::filename(const std::string& fbasename, const u_int16_t pfid) { std::ostringstream oss; oss << fbasename << "."; oss << std::setw(4) << std::setfill('0') << std::hex << pfid; oss << "." << JRNL_DATA_EXTENSION; return oss.str(); } void fcntl::clean_file(const u_int32_t jfsize_sblks) { // NOTE: The journal file size is always one sblock bigger than the specified journal // file size, which is the data content size. The extra block is for the journal file // header which precedes all data on each file and is exactly one sblock in size. u_int32_t nsblks = jfsize_sblks + 1; // TODO - look at more efficient alternatives to allocating a null block: // 1. mmap() against /dev/zero, but can alignment for O_DIRECT be assured? // 2. ftruncate(), but does this result in a sparse file? If so, then this is no good. // Create temp null block for writing const std::size_t sblksize = JRNL_DBLK_SIZE * JRNL_SBLK_SIZE; void* nullbuf = 0; // Allocate no more than 2MB (4096 sblks) as a null buffer const u_int32_t nullbuffsize_sblks = nsblks > 4096 ? 4096 : nsblks; const std::size_t nullbuffsize = nullbuffsize_sblks * sblksize; if (::posix_memalign(&nullbuf, sblksize, nullbuffsize)) { std::ostringstream oss; oss << "posix_memalign() failed: size=" << nullbuffsize << " blk_size=" << sblksize; oss << FORMAT_SYSERR(errno); throw jexception(jerrno::JERR__MALLOC, oss.str(), "fcntl", "clean_file"); } std::memset(nullbuf, 0, nullbuffsize); int fh = ::open(_fname.c_str(), O_WRONLY | O_CREAT | O_DIRECT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); // 0644 -rw-r--r-- if (fh < 0) { std::free(nullbuf); std::ostringstream oss; oss << "open() failed:" << FORMAT_SYSERR(errno); throw jexception(jerrno::JERR_FCNTL_OPENWR, oss.str(), "fcntl", "clean_file"); } while (nsblks > 0) { u_int32_t this_write_sblks = nsblks >= nullbuffsize_sblks ? nullbuffsize_sblks : nsblks; if (::write(fh, nullbuf, this_write_sblks * sblksize) == -1) { ::close(fh); std::free(nullbuf); std::ostringstream oss; oss << "wr_size=" << (this_write_sblks * sblksize) << FORMAT_SYSERR(errno); throw jexception(jerrno::JERR_FCNTL_WRITE, oss.str(), "fcntl", "clean_file"); } nsblks -= this_write_sblks; } // Clean up std::free(nullbuf); if (::close(fh)) { std::ostringstream oss; oss << FORMAT_SYSERR(errno); throw jexception(jerrno::JERR_FCNTL_CLOSE, oss.str(), "fcntl", "clean_file"); } } void fcntl::create_jfile(const u_int32_t jfsize_sblks) { clean_file(jfsize_sblks); } } // namespace journal } // namespace mrg qpid-cpp-store-debian-0.16/lib/jrnl/file_hdr.hpp0000644000176300017630000001630511142067437020545 0ustar cajuscajus/** * \file file_hdr.hpp * * Qpid asynchronous store plugin library * * File containing code for class mrg::journal::file_hdr (file * record header), used to start a journal file. It contains some * file metadata and information to aid journal recovery. * * \author Kim van der Riet * * Copyright (c) 2007, 2008, 2009 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #ifndef mrg_journal_file_hdr_hpp #define mrg_journal_file_hdr_hpp #include #include #include "jrnl/rec_hdr.hpp" #include "jrnl/jerrno.hpp" #include "jrnl/jexception.hpp" #include namespace mrg { namespace journal { #pragma pack(1) /** * \brief Struct for data common to the head of all journal files. In addition to * the common data, this includes the record ID and offset of the first record in * the file. * * This header precedes all data in journal files and occupies the first complete * block in the file. The record ID and offset are updated on each overwrite of the * file. * * File header info in binary format (48 bytes): *
    *   0                           7
    * +---+---+---+---+---+---+---+---+  -+
    * |     magic     | v | e | flags |   |
    * +---+---+---+---+---+---+---+---+   | struct hdr
    * |       first rid in file       |   |
    * +---+---+---+---+---+---+---+---+  -+
    * | pfid  | lfid  |  reserved (0) |
    * +---+---+---+---+---+---+---+---+
    * |              fro              |
    * +---+---+---+---+---+---+---+---+
    * |           timestamp (sec)     |
    * +---+---+---+---+---+---+---+---+
    * |           timestamp (ns)      |
    * +---+---+---+---+---+---+---+---+
    * v = file version (If the format or encoding of this file changes, then this
    *     number should be incremented)
    * e = endian flag, false (0x00) for little endian, true (0x01) for big endian
    * pfid = File ID (number used in naming file)
    * lfid = Logical ID (order used in circular buffer)
    * fro = First record offset, offset from start of file to first record header
    * 
* * Note that journal files should be transferable between 32- and 64-bit * hardware of the same endianness, but not between hardware of opposite * entianness without some sort of binary conversion utility. Thus buffering * will be needed for types that change size between 32- and 64-bit compiles. */ struct file_hdr : rec_hdr { u_int16_t _pfid; ///< Physical file ID (pfid) u_int16_t _lfid; ///< Logical file ID (lfid) u_int32_t _res; ///< Reserved (for alignment/flags) #if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT) u_int32_t _filler0; ///< Big-endian filler for 32-bit size_t #endif std::size_t _fro; ///< First record offset #if defined(JRNL_LITTLE_ENDIAN) && defined(JRNL_32_BIT) u_int32_t _filler0; ///< Little-endian filler for 32-bit size_t #endif #if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT) u_int32_t _filler1; ///< Big-endian filler for 32-bit time_t #endif std::time_t _ts_sec; ///< Timestamp of journal initilailization #if defined(JRNL_LITTLE_ENDIAN) && defined(JRNL_32_BIT) u_int32_t _filler1; ///< Little-endian filler for 32-bit time_t #endif #if defined(JRNL_BIG_ENDIAN) u_int32_t _filler2; ///< Big endian filler for u_int32_t #endif u_int32_t _ts_nsec; ///< Timestamp of journal initilailization #if defined(JRNL_LITTLE_ENDIAN) u_int32_t _filler2; ///< Little-endian filler for u_int32_t #endif /** * \brief Default constructor, which sets all values to 0. */ inline file_hdr(): rec_hdr(), _pfid(0), _lfid(0), _res(0), #if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT) _filler0(0), #endif _fro(0), #if defined(JRNL_LITTLE_ENDIAN) && defined(JRNL_32_BIT) _filler0(0), #endif #if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT) _filler1(0), #endif _ts_sec(0), #if defined(JRNL_LITTLE_ENDIAN) && defined(JRNL_32_BIT) _filler1(0), #endif #if defined(JRNL_BIG_ENDIAN) _filler2(0), #endif _ts_nsec(0) #if defined(JRNL_LITTLE_ENDIAN) , _filler2(0) #endif {} /** * \brief Convenience constructor which initializes values during construction. */ inline file_hdr(const u_int32_t magic, const u_int8_t version, const u_int64_t rid, const u_int16_t pfid, const u_int16_t lfid, const std::size_t fro, const bool owi, const bool settime = false): rec_hdr(magic, version, rid, owi), _pfid(pfid), _lfid(lfid), _res(0), #if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT) _filler0(0), #endif _fro(fro), #if defined(JRNL_LITTLE_ENDIAN) && defined(JRNL_32_BIT) _filler0(0), #endif #if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT) _filler1(0), #endif _ts_sec(0), #if defined(JRNL_LITTLE_ENDIAN) && defined(JRNL_32_BIT) _filler1(0), #endif #if defined(JRNL_BIG_ENDIAN) _filler2(0), #endif _ts_nsec(0) #if defined(JRNL_LITTLE_ENDIAN) , _filler2(0) #endif { if (settime) set_time(); } /** * \brief Gets the current time from the system clock and sets the timestamp in the struct. */ inline void set_time() { // TODO: Standardize on method for getting time that does not requrie a context switch. timespec ts; if (::clock_gettime(CLOCK_REALTIME, &ts)) { std::ostringstream oss; oss << FORMAT_SYSERR(errno); throw jexception(jerrno::JERR__RTCLOCK, oss.str(), "file_hdr", "set_time"); } _ts_sec = ts.tv_sec; _ts_nsec = ts.tv_nsec; } /** * \brief Sets the timestamp in the struct to the provided value (in seconds and * nanoseconds). */ inline void set_time(timespec& ts) { _ts_sec = ts.tv_sec; _ts_nsec = ts.tv_nsec; } /** * \brief Returns the size of the header in bytes. */ inline static std::size_t size() { return sizeof(file_hdr); } }; // struct file_hdr #pragma pack() } // namespace journal } // namespace mrg #endif // ifndef mrg_journal_file_hdr_hpp qpid-cpp-store-debian-0.16/lib/jrnl/lp_map.hpp0000644000176300017630000000477711142067437020253 0ustar cajuscajus/** * \file lp_map.hpp * * Qpid asynchronous store plugin library * * File containing code for class mrg::journal::lp_map (logical file map). * See class documentation for details. * * \author Kim van der Riet * * Copyright (c) 2008, 2009 Red Hat Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #ifndef mrg_journal_lp_map_hpp #define mrg_journal_lp_map_hpp #include #include #include #include namespace mrg { namespace journal { /** * \class lp_map * \brief Maps the logical file id (lfid) to the physical file id (pfid) in the journal. * * NOTE: NOT THREAD SAFE */ class lp_map { public: typedef std::map lp_map_t; typedef lp_map_t::const_iterator lp_map_citr_t; typedef lp_map_t::const_reverse_iterator lp_map_critr_t; private: typedef std::pair lfpair; typedef std::pair lfret; lp_map_t _map; public: lp_map(); virtual ~lp_map(); void insert(u_int16_t lfid, u_int16_t pfid); inline u_int16_t size() const { return u_int16_t(_map.size()); } inline bool empty() const { return _map.empty(); } inline lp_map_citr_t begin() { return _map.begin(); } inline lp_map_citr_t end() { return _map.end(); } inline lp_map_critr_t rbegin() { return _map.rbegin(); } inline lp_map_critr_t rend() { return _map.rend(); } void get_pfid_list(std::vector& pfid_list); // debug aid std::string to_string(); }; } // namespace journal } // namespace mrg #endif // ifndef mrg_journal_lp_map_hpp qpid-cpp-store-debian-0.16/lib/jrnl/slock.cpp0000644000176300017630000000224211142067437020072 0ustar cajuscajus/** * \file slock.cpp * * Qpid asynchronous store plugin library * * File containing code for class mrg::journal::slock (scoped lock). See * comments in file slock.hpp for details. * * \author Kim van der Riet * * Copyright (c) 2007, 2008 Red Hat Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #include "jrnl/slock.hpp" qpid-cpp-store-debian-0.16/lib/jrnl/aio.cpp0000644000176300017630000000237611142067437017537 0ustar cajuscajus/** * \file aio.cpp * * Qpid asynchronous store plugin library * * File containing code for class mrg::journal::aio (libaio interface * encapsulation). See comments in file aio.hpp for details. * * \author Kim van der Riet * * Copyright (c) 2007, 2008, 2009 Red Hat Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #include "aio.hpp" namespace mrg { namespace journal { } // namespace journal } // namespace mrg qpid-cpp-store-debian-0.16/lib/jrnl/rfc.cpp0000644000176300017630000000353311142067437017535 0ustar cajuscajus/** * \file rfc.cpp * * Qpid asynchronous store plugin library * * File containing code for class mrg::journal::rfc (rotating * file controller). See comments in file rfc.hpp for details. * * \author Kim van der Riet * * Copyright (c) 2008, 2009 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #include "jrnl/rfc.hpp" #include namespace mrg { namespace journal { rfc::rfc(const lpmgr* lpmp): _lpmp(lpmp), _fc_index(0), _curr_fc(0) {} rfc::~rfc() {} void rfc::finalize() { unset_findex(); } void rfc::set_findex(const u_int16_t fc_index) { _fc_index = fc_index; _curr_fc = _lpmp->get_fcntlp(fc_index); _curr_fc->rd_reset(); } void rfc::unset_findex() { _fc_index = 0; _curr_fc = 0; } std::string rfc::status_str() const { if (!_lpmp->is_init()) return "state: Uninitialized"; if (_curr_fc == 0) return "state: Inactive"; std::ostringstream oss; oss << "state: Active"; return oss.str(); } } // namespace journal } // namespace mrg qpid-cpp-store-debian-0.16/lib/jrnl/enq_rec.cpp0000644000176300017630000005613311403227064020375 0ustar cajuscajus/** * \file enq_rec.cpp * * Qpid asynchronous store plugin library * * This file contains the code for the mrg::journal::enq_rec (journal enqueue * record) class. See comments in file enq_rec.hpp for details. * * \author Kim van der Riet * * Copyright (c) 2007, 2008, 2009 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #include "jrnl/enq_rec.hpp" #include #include #include #include #include #include "jrnl/jerrno.hpp" #include "jrnl/jexception.hpp" #include namespace mrg { namespace journal { // Constructor used for read operations, where buf contains preallocated space to receive data. enq_rec::enq_rec(): jrec(), // superclass _enq_hdr(RHM_JDAT_ENQ_MAGIC, RHM_JDAT_VERSION, 0, 0, 0, false, false), _xidp(0), _data(0), _buff(0), _enq_tail(_enq_hdr) {} // Constructor used for transactional write operations, where dbuf contains data to be written. enq_rec::enq_rec(const u_int64_t rid, const void* const dbuf, const std::size_t dlen, const void* const xidp, const std::size_t xidlen, const bool owi, const bool transient): jrec(), // superclass _enq_hdr(RHM_JDAT_ENQ_MAGIC, RHM_JDAT_VERSION, rid, xidlen, dlen, owi, transient), _xidp(xidp), _data(dbuf), _buff(0), _enq_tail(_enq_hdr) {} enq_rec::~enq_rec() { clean(); } // Prepare instance for use in reading data from journal, where buf contains preallocated space // to receive data. void enq_rec::reset() { _enq_hdr._rid = 0; _enq_hdr.set_owi(false); _enq_hdr.set_transient(false); _enq_hdr._xidsize = 0; _enq_hdr._dsize = 0; _xidp = 0; _data = 0; _buff = 0; _enq_tail._rid = 0; } // Prepare instance for use in writing transactional data to journal, where dbuf contains data to // be written. void enq_rec::reset(const u_int64_t rid, const void* const dbuf, const std::size_t dlen, const void* const xidp, const std::size_t xidlen, const bool owi, const bool transient, const bool external) { _enq_hdr._rid = rid; _enq_hdr.set_owi(owi); _enq_hdr.set_transient(transient); _enq_hdr.set_external(external); _enq_hdr._xidsize = xidlen; _enq_hdr._dsize = dlen; _xidp = xidp; _data = dbuf; _buff = 0; _enq_tail._rid = rid; } u_int32_t enq_rec::encode(void* wptr, u_int32_t rec_offs_dblks, u_int32_t max_size_dblks) { assert(wptr != 0); assert(max_size_dblks > 0); if (_xidp == 0) assert(_enq_hdr._xidsize == 0); std::size_t rec_offs = rec_offs_dblks * JRNL_DBLK_SIZE; std::size_t rem = max_size_dblks * JRNL_DBLK_SIZE; std::size_t wr_cnt = 0; if (rec_offs_dblks) // Continuation of split data record (over 2 or more pages) { if (size_dblks(rec_size()) - rec_offs_dblks > max_size_dblks) // Further split required { rec_offs -= sizeof(_enq_hdr); std::size_t wsize = _enq_hdr._xidsize > rec_offs ? _enq_hdr._xidsize - rec_offs : 0; std::size_t wsize2 = wsize; if (wsize) { if (wsize > rem) wsize = rem; std::memcpy(wptr, (const char*)_xidp + rec_offs, wsize); wr_cnt = wsize; rem -= wsize; } rec_offs -= _enq_hdr._xidsize - wsize2; if (rem && !_enq_hdr.is_external()) { wsize = _enq_hdr._dsize > rec_offs ? _enq_hdr._dsize - rec_offs : 0; wsize2 = wsize; if (wsize) { if (wsize > rem) wsize = rem; std::memcpy((char*)wptr + wr_cnt, (char*)_data + rec_offs, wsize); wr_cnt += wsize; rem -= wsize; } rec_offs -= _enq_hdr._dsize - wsize2; } if (rem) { wsize = sizeof(_enq_tail) > rec_offs ? sizeof(_enq_tail) - rec_offs : 0; wsize2 = wsize; if (wsize) { if (wsize > rem) wsize = rem; std::memcpy((char*)wptr + wr_cnt, (char*)&_enq_tail + rec_offs, wsize); wr_cnt += wsize; rem -= wsize; } rec_offs -= sizeof(_enq_tail) - wsize2; } assert(rem == 0); assert(rec_offs == 0); } else // No further split required { rec_offs -= sizeof(_enq_hdr); std::size_t wsize = _enq_hdr._xidsize > rec_offs ? _enq_hdr._xidsize - rec_offs : 0; if (wsize) { std::memcpy(wptr, (char*)_xidp + rec_offs, wsize); wr_cnt += wsize; } rec_offs -= _enq_hdr._xidsize - wsize; wsize = _enq_hdr._dsize > rec_offs ? _enq_hdr._dsize - rec_offs : 0; if (wsize && !_enq_hdr.is_external()) { std::memcpy((char*)wptr + wr_cnt, (char*)_data + rec_offs, wsize); wr_cnt += wsize; } rec_offs -= _enq_hdr._dsize - wsize; wsize = sizeof(_enq_tail) > rec_offs ? sizeof(_enq_tail) - rec_offs : 0; if (wsize) { std::memcpy((char*)wptr + wr_cnt, (char*)&_enq_tail + rec_offs, wsize); wr_cnt += wsize; #ifdef RHM_CLEAN std::size_t rec_offs = rec_offs_dblks * JRNL_DBLK_SIZE; std::size_t dblk_rec_size = size_dblks(rec_size() - rec_offs) * JRNL_DBLK_SIZE; std::memset((char*)wptr + wr_cnt, RHM_CLEAN_CHAR, dblk_rec_size - wr_cnt); #endif } rec_offs -= sizeof(_enq_tail) - wsize; assert(rec_offs == 0); } } else // Start at beginning of data record { // Assumption: the header will always fit into the first dblk std::memcpy(wptr, (void*)&_enq_hdr, sizeof(_enq_hdr)); wr_cnt = sizeof(_enq_hdr); if (size_dblks(rec_size()) > max_size_dblks) // Split required { std::size_t wsize; rem -= sizeof(_enq_hdr); if (rem) { wsize = rem >= _enq_hdr._xidsize ? _enq_hdr._xidsize : rem; std::memcpy((char*)wptr + wr_cnt, _xidp, wsize); wr_cnt += wsize; rem -= wsize; } if (rem && !_enq_hdr.is_external()) { wsize = rem >= _enq_hdr._dsize ? _enq_hdr._dsize : rem; std::memcpy((char*)wptr + wr_cnt, _data, wsize); wr_cnt += wsize; rem -= wsize; } if (rem) { wsize = rem >= sizeof(_enq_tail) ? sizeof(_enq_tail) : rem; std::memcpy((char*)wptr + wr_cnt, (void*)&_enq_tail, wsize); wr_cnt += wsize; rem -= wsize; } assert(rem == 0); } else // No split required { if (_enq_hdr._xidsize) { std::memcpy((char*)wptr + wr_cnt, _xidp, _enq_hdr._xidsize); wr_cnt += _enq_hdr._xidsize; } if (!_enq_hdr.is_external()) { std::memcpy((char*)wptr + wr_cnt, _data, _enq_hdr._dsize); wr_cnt += _enq_hdr._dsize; } std::memcpy((char*)wptr + wr_cnt, (void*)&_enq_tail, sizeof(_enq_tail)); wr_cnt += sizeof(_enq_tail); #ifdef RHM_CLEAN std::size_t dblk_rec_size = size_dblks(rec_size()) * JRNL_DBLK_SIZE; std::memset((char*)wptr + wr_cnt, RHM_CLEAN_CHAR, dblk_rec_size - wr_cnt); #endif } } return size_dblks(wr_cnt); } u_int32_t enq_rec::decode(rec_hdr& h, void* rptr, u_int32_t rec_offs_dblks, u_int32_t max_size_dblks) { assert(rptr != 0); assert(max_size_dblks > 0); std::size_t rd_cnt = 0; if (rec_offs_dblks) // Continuation of record on new page { const u_int32_t hdr_xid_data_size = enq_hdr::size() + _enq_hdr._xidsize + (_enq_hdr.is_external() ? 0 : _enq_hdr._dsize); const u_int32_t hdr_xid_data_tail_size = hdr_xid_data_size + rec_tail::size(); const u_int32_t hdr_data_dblks = size_dblks(hdr_xid_data_size); const u_int32_t hdr_tail_dblks = size_dblks(hdr_xid_data_tail_size); const std::size_t rec_offs = rec_offs_dblks * JRNL_DBLK_SIZE; const std::size_t offs = rec_offs - enq_hdr::size(); if (hdr_tail_dblks - rec_offs_dblks <= max_size_dblks) { // Remainder of record fits within this page if (offs < _enq_hdr._xidsize) { // some XID still outstanding, copy remainder of XID, data and tail const std::size_t rem = _enq_hdr._xidsize + _enq_hdr._dsize - offs; std::memcpy((char*)_buff + offs, rptr, rem); rd_cnt += rem; std::memcpy((void*)&_enq_tail, ((char*)rptr + rd_cnt), sizeof(_enq_tail)); chk_tail(); rd_cnt += sizeof(_enq_tail); } else if (offs < _enq_hdr._xidsize + _enq_hdr._dsize && !_enq_hdr.is_external()) { // some data still outstanding, copy remainder of data and tail const std::size_t data_offs = offs - _enq_hdr._xidsize; const std::size_t data_rem = _enq_hdr._dsize - data_offs; std::memcpy((char*)_buff + offs, rptr, data_rem); rd_cnt += data_rem; std::memcpy((void*)&_enq_tail, ((char*)rptr + rd_cnt), sizeof(_enq_tail)); chk_tail(); rd_cnt += sizeof(_enq_tail); } else { // Tail or part of tail only outstanding, complete tail const std::size_t tail_offs = rec_offs - enq_hdr::size() - _enq_hdr._xidsize - _enq_hdr._dsize; const std::size_t tail_rem = rec_tail::size() - tail_offs; std::memcpy((char*)&_enq_tail + tail_offs, rptr, tail_rem); chk_tail(); rd_cnt = tail_rem; } } else if (hdr_data_dblks - rec_offs_dblks <= max_size_dblks) { // Remainder of xid & data fits within this page; tail split /* * TODO: This section needs revision. Since it is known that the end of the page falls within the * tail record, it is only necessary to write from the current offset to the end of the page under * all circumstances. The multiple if/else combinations may be eliminated, as well as one memcpy() * operation. * * Also note that Coverity has detected a possible memory overwrite in this block. It occurs if * both the following two if() stmsts (numbered) are false. With rd_cnt = 0, this would result in * the value of tail_rem > sizeof(tail_rec). Practically, this could only happen if the start and * end of a page both fall within the same tail record, in which case the tail would have to be * (much!) larger. However, the logic here does not account for this possibility. * * If the optimization above is undertaken, this code would probably be removed. */ if (offs < _enq_hdr._xidsize) // 1 { // some XID still outstanding, copy remainder of XID and data const std::size_t rem = _enq_hdr._xidsize + _enq_hdr._dsize - offs; std::memcpy((char*)_buff + offs, rptr, rem); rd_cnt += rem; } else if (offs < _enq_hdr._xidsize + _enq_hdr._dsize && !_enq_hdr.is_external()) // 2 { // some data still outstanding, copy remainder of data const std::size_t data_offs = offs - _enq_hdr._xidsize; const std::size_t data_rem = _enq_hdr._dsize - data_offs; std::memcpy((char*)_buff + offs, rptr, data_rem); rd_cnt += data_rem; } const std::size_t tail_rem = (max_size_dblks * JRNL_DBLK_SIZE) - rd_cnt; if (tail_rem) { std::memcpy((void*)&_enq_tail, ((char*)rptr + rd_cnt), tail_rem); rd_cnt += tail_rem; } } else { // Since xid and data are contiguous, both fit within current page - copy whole page const std::size_t data_cp_size = (max_size_dblks * JRNL_DBLK_SIZE); std::memcpy((char*)_buff + offs, rptr, data_cp_size); rd_cnt += data_cp_size; } } else // Start of record { // Get and check header _enq_hdr.hdr_copy(h); rd_cnt = sizeof(rec_hdr); #if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT) rd_cnt += sizeof(u_int32_t); // Filler 0 #endif _enq_hdr._xidsize = *(std::size_t*)((char*)rptr + rd_cnt); rd_cnt += sizeof(std::size_t); #if defined(JRNL_LITTLE_ENDIAN) && defined(JRNL_32_BIT) rd_cnt += sizeof(u_int32_t); // Filler 0 #endif #if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT) rd_cnt += sizeof(u_int32_t); // Filler 1 #endif _enq_hdr._dsize = *(std::size_t*)((char*)rptr + rd_cnt); rd_cnt = _enq_hdr.size(); chk_hdr(); if (_enq_hdr._xidsize + (_enq_hdr.is_external() ? 0 : _enq_hdr._dsize)) { _buff = std::malloc(_enq_hdr._xidsize + (_enq_hdr.is_external() ? 0 : _enq_hdr._dsize)); MALLOC_CHK(_buff, "_buff", "enq_rec", "decode"); const u_int32_t hdr_xid_size = enq_hdr::size() + _enq_hdr._xidsize; const u_int32_t hdr_xid_data_size = hdr_xid_size + (_enq_hdr.is_external() ? 0 : _enq_hdr._dsize); const u_int32_t hdr_xid_data_tail_size = hdr_xid_data_size + rec_tail::size(); const u_int32_t hdr_xid_dblks = size_dblks(hdr_xid_size); const u_int32_t hdr_data_dblks = size_dblks(hdr_xid_data_size); const u_int32_t hdr_tail_dblks = size_dblks(hdr_xid_data_tail_size); // Check if record (header + data + tail) fits within this page, we can check the // tail before the expense of copying data to memory if (hdr_tail_dblks <= max_size_dblks) { // Header, xid, data and tail fits within this page if (_enq_hdr._xidsize) { std::memcpy(_buff, (char*)rptr + rd_cnt, _enq_hdr._xidsize); rd_cnt += _enq_hdr._xidsize; } if (_enq_hdr._dsize && !_enq_hdr.is_external()) { std::memcpy((char*)_buff + _enq_hdr._xidsize, (char*)rptr + rd_cnt, _enq_hdr._dsize); rd_cnt += _enq_hdr._dsize; } std::memcpy((void*)&_enq_tail, (char*)rptr + rd_cnt, sizeof(_enq_tail)); chk_tail(); rd_cnt += sizeof(_enq_tail); } else if (hdr_data_dblks <= max_size_dblks) { // Header, xid and data fit within this page, tail split or separated if (_enq_hdr._xidsize) { std::memcpy(_buff, (char*)rptr + rd_cnt, _enq_hdr._xidsize); rd_cnt += _enq_hdr._xidsize; } if (_enq_hdr._dsize && !_enq_hdr.is_external()) { std::memcpy((char*)_buff + _enq_hdr._xidsize, (char*)rptr + rd_cnt, _enq_hdr._dsize); rd_cnt += _enq_hdr._dsize; } const std::size_t tail_rem = (max_size_dblks * JRNL_DBLK_SIZE) - rd_cnt; if (tail_rem) { std::memcpy((void*)&_enq_tail, (char*)rptr + rd_cnt, tail_rem); rd_cnt += tail_rem; } } else if (hdr_xid_dblks <= max_size_dblks) { // Header and xid fits within this page, data split or separated if (_enq_hdr._xidsize) { std::memcpy(_buff, (char*)rptr + rd_cnt, _enq_hdr._xidsize); rd_cnt += _enq_hdr._xidsize; } if (_enq_hdr._dsize && !_enq_hdr.is_external()) { const std::size_t data_cp_size = (max_size_dblks * JRNL_DBLK_SIZE) - rd_cnt; std::memcpy((char*)_buff + _enq_hdr._xidsize, (char*)rptr + rd_cnt, data_cp_size); rd_cnt += data_cp_size; } } else { // Header fits within this page, xid split or separated const std::size_t data_cp_size = (max_size_dblks * JRNL_DBLK_SIZE) - rd_cnt; std::memcpy(_buff, (char*)rptr + rd_cnt, data_cp_size); rd_cnt += data_cp_size; } } } return size_dblks(rd_cnt); } bool enq_rec::rcv_decode(rec_hdr h, std::ifstream* ifsp, std::size_t& rec_offs) { if (rec_offs == 0) { // Read header, allocate (if req'd) for xid _enq_hdr.hdr_copy(h); #if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT) ifsp->ignore(sizeof(u_int32_t)); // _filler0 #endif ifsp->read((char*)&_enq_hdr._xidsize, sizeof(std::size_t)); #if defined(JRNL_LITTLE_ENDIAN) && defined(JRNL_32_BIT) ifsp->ignore(sizeof(u_int32_t)); // _filler0 #endif #if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT) ifsp->ignore(sizeof(u_int32_t)); // _filler1 #endif ifsp->read((char*)&_enq_hdr._dsize, sizeof(std::size_t)); #if defined(JRNL_LITTLE_ENDIAN) && defined(JRNL_32_BIT) ifsp->ignore(sizeof(u_int32_t)); // _filler1 #endif rec_offs = sizeof(_enq_hdr); if (_enq_hdr._xidsize) { _buff = std::malloc(_enq_hdr._xidsize); MALLOC_CHK(_buff, "_buff", "enq_rec", "rcv_decode"); } } if (rec_offs < sizeof(_enq_hdr) + _enq_hdr._xidsize) { // Read xid (or continue reading xid) std::size_t offs = rec_offs - sizeof(_enq_hdr); ifsp->read((char*)_buff + offs, _enq_hdr._xidsize - offs); std::size_t size_read = ifsp->gcount(); rec_offs += size_read; if (size_read < _enq_hdr._xidsize - offs) { assert(ifsp->eof()); // As we may have read past eof, turn off fail bit ifsp->clear(ifsp->rdstate()&(~std::ifstream::failbit)); assert(!ifsp->fail() && !ifsp->bad()); return false; } } if (!_enq_hdr.is_external()) { if (rec_offs < sizeof(_enq_hdr) + _enq_hdr._xidsize + _enq_hdr._dsize) { // Ignore data (or continue ignoring data) std::size_t offs = rec_offs - sizeof(_enq_hdr) - _enq_hdr._xidsize; ifsp->ignore(_enq_hdr._dsize - offs); std::size_t size_read = ifsp->gcount(); rec_offs += size_read; if (size_read < _enq_hdr._dsize - offs) { assert(ifsp->eof()); // As we may have read past eof, turn off fail bit ifsp->clear(ifsp->rdstate()&(~std::ifstream::failbit)); assert(!ifsp->fail() && !ifsp->bad()); return false; } } } if (rec_offs < sizeof(_enq_hdr) + _enq_hdr._xidsize + (_enq_hdr.is_external() ? 0 : _enq_hdr._dsize) + sizeof(rec_tail)) { // Read tail (or continue reading tail) std::size_t offs = rec_offs - sizeof(_enq_hdr) - _enq_hdr._xidsize; if (!_enq_hdr.is_external()) offs -= _enq_hdr._dsize; ifsp->read((char*)&_enq_tail + offs, sizeof(rec_tail) - offs); std::size_t size_read = ifsp->gcount(); rec_offs += size_read; if (size_read < sizeof(rec_tail) - offs) { assert(ifsp->eof()); // As we may have read past eof, turn off fail bit ifsp->clear(ifsp->rdstate()&(~std::ifstream::failbit)); assert(!ifsp->fail() && !ifsp->bad()); return false; } } ifsp->ignore(rec_size_dblks() * JRNL_DBLK_SIZE - rec_size()); chk_tail(); // Throws if tail invalid or record incomplete assert(!ifsp->fail() && !ifsp->bad()); return true; } std::size_t enq_rec::get_xid(void** const xidpp) { if (!_buff || !_enq_hdr._xidsize) { *xidpp = 0; return 0; } *xidpp = _buff; return _enq_hdr._xidsize; } std::size_t enq_rec::get_data(void** const datapp) { if (!_buff) { *datapp = 0; return 0; } if (_enq_hdr.is_external()) *datapp = 0; else *datapp = (void*)((char*)_buff + _enq_hdr._xidsize); return _enq_hdr._dsize; } std::string& enq_rec::str(std::string& str) const { std::ostringstream oss; oss << "enq_rec: m=" << _enq_hdr._magic; oss << " v=" << (int)_enq_hdr._version; oss << " rid=" << _enq_hdr._rid; if (_xidp) oss << " xid=\"" << _xidp << "\""; oss << " len=" << _enq_hdr._dsize; str.append(oss.str()); return str; } std::size_t enq_rec::rec_size() const { return rec_size(_enq_hdr._xidsize, _enq_hdr._dsize, _enq_hdr.is_external()); } std::size_t enq_rec::rec_size(const std::size_t xidsize, const std::size_t dsize, const bool external) { if (external) return enq_hdr::size() + xidsize + rec_tail::size(); return enq_hdr::size() + xidsize + dsize + rec_tail::size(); } void enq_rec::set_rid(const u_int64_t rid) { _enq_hdr._rid = rid; _enq_tail._rid = rid; } void enq_rec::chk_hdr() const { jrec::chk_hdr(_enq_hdr); if (_enq_hdr._magic != RHM_JDAT_ENQ_MAGIC) { std::ostringstream oss; oss << std::hex << std::setfill('0'); oss << "enq magic: rid=0x" << std::setw(16) << _enq_hdr._rid; oss << ": expected=0x" << std::setw(8) << RHM_JDAT_ENQ_MAGIC; oss << " read=0x" << std::setw(2) << (int)_enq_hdr._magic; throw jexception(jerrno::JERR_JREC_BADRECHDR, oss.str(), "enq_rec", "chk_hdr"); } } void enq_rec::chk_hdr(u_int64_t rid) const { chk_hdr(); jrec::chk_rid(_enq_hdr, rid); } void enq_rec::chk_tail() const { jrec::chk_tail(_enq_tail, _enq_hdr); } void enq_rec::clean() { // clean up allocated memory here } } // namespace journal } // namespace mrg qpid-cpp-store-debian-0.16/lib/DataTokenImpl.cpp0000644000176300017630000000202411307246366020507 0ustar cajuscajus/* Copyright (c) 2007, 2008 Red Hat, Inc. This file is part of the Qpid async store library msgstore.so. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA The GNU Lesser General Public License is available in the file COPYING. */ #include "DataTokenImpl.h" using namespace mrg::msgstore; DataTokenImpl::DataTokenImpl():data_tok() {} DataTokenImpl::~DataTokenImpl() {} qpid-cpp-store-debian-0.16/lib/TxnCtxt.h0000644000176300017630000000640411717442050017073 0ustar cajuscajus/* Copyright (c) 2007, 2008 Red Hat, Inc. This file is part of the Qpid async store library msgstore.so. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA The GNU Lesser General Public License is available in the file COPYING. */ #ifndef _TxnCtxt_ #define _TxnCtxt_ #include #include #include #include #include "DataTokenImpl.h" #include "IdSequence.h" #include "JournalImpl.h" #include "qpid/broker/PersistableQueue.h" #include "qpid/broker/TransactionalStore.h" #include "qpid/sys/Mutex.h" #include "qpid/sys/uuid.h" #include namespace mrg { namespace msgstore { class TxnCtxt : public qpid::broker::TransactionContext { protected: static qpid::sys::Mutex globalSerialiser; static uuid_t uuid; static IdSequence uuidSeq; static bool staticInit; static bool setUuid(); typedef std::set ipqdef; typedef ipqdef::iterator ipqItr; typedef std::auto_ptr AutoScopedLock; ipqdef impactedQueues; // list of Queues used in the txn IdSequence* loggedtx; boost::intrusive_ptr dtokp; AutoScopedLock globalHolder; JournalImpl* preparedXidStorePtr; /** * local txn id, if non XA. */ std::string tid; DbTxn* txn; virtual void completeTxn(bool commit); void commitTxn(JournalImpl* jc, bool commit); void jrnl_flush(JournalImpl* jc); void jrnl_sync(JournalImpl* jc, timespec* timeout); public: TxnCtxt(IdSequence* _loggedtx=NULL); TxnCtxt(std::string _tid, IdSequence* _loggedtx); virtual ~TxnCtxt(); /** * Call to make sure all the data for this txn is written to safe store * *@return if the data successfully synced. */ void sync(); void begin(DbEnv* env, bool sync = false); void commit(); void abort(); DbTxn* get(); virtual bool isTPC(); virtual const std::string& getXid(); void addXidRecord(qpid::broker::ExternalQueueStore* queue); inline void prepare(JournalImpl* _preparedXidStorePtr) { preparedXidStorePtr = _preparedXidStorePtr; } void complete(bool commit); bool impactedQueuesEmpty(); DataTokenImpl* getDtok(); void incrDtokRef(); void recoverDtok(const u_int64_t rid, const std::string xid); }; class TPCTxnCtxt : public TxnCtxt, public qpid::broker::TPCTransactionContext { protected: const std::string xid; public: TPCTxnCtxt(const std::string& _xid, IdSequence* _loggedtx); inline virtual bool isTPC() { return true; } inline virtual const std::string& getXid() { return xid; } }; }} #endif qpid-cpp-store-debian-0.16/lib/jrnl2/0000755000176300017630000000000011761423162016333 5ustar cajuscajusqpid-cpp-store-debian-0.16/lib/jrnl2/DataTokenState.cpp0000644000176300017630000001607411500164314021712 0ustar cajuscajus/** * \file DataTokenState.cpp * * Qpid asynchronous store plugin library * * This file contains async journal code (v.2). * * \author Kim van der Riet * * Copyright (c) 2010 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #include "DataTokenState.hpp" #include "JournalException.hpp" namespace mrg { namespace journal2 { DataTokenState::DataTokenState() : _opState(OP_NONE), _txnState(TX_NONE), _bufferWriteState(IO_NONE), _aioSubmissionState(IO_NONE), _aioCompletionState(IO_NONE) {} DataTokenState::DataTokenState(const DataTokenState& s) : _opState(s._opState), _txnState(s._txnState), _bufferWriteState(s._bufferWriteState), _aioSubmissionState(s._aioSubmissionState), _aioCompletionState(s._aioCompletionState) {} DataTokenState::DataTokenState(const opState_t o, const txnState_t t, const aioState_t b, const aioState_t s, const aioState_t c) : _opState(o), _txnState(t), _bufferWriteState(b), _aioSubmissionState(s), _aioCompletionState(c) {} void DataTokenState::reset() { _opState = OP_NONE; _txnState = TX_NONE; _bufferWriteState = IO_NONE; _aioSubmissionState = IO_NONE; _aioCompletionState = IO_NONE; } void DataTokenState::setOpStateToEnqueue() { if (_opState != OP_NONE) THROW_STATE_EXCEPTION(JournalErrors::JERR_BADDTOKOPSTATE, s_getStateAsString(OP_ENQUEUE), s_getStateAsString(_opState), "dtok_state", "set_enqueue"); _opState = OP_ENQUEUE; } void DataTokenState::setOpStateToDequeue() { if (_opState != OP_ENQUEUE) THROW_STATE_EXCEPTION(JournalErrors::JERR_BADDTOKOPSTATE, s_getStateAsString(OP_DEQUEUE), s_getStateAsString(_opState), "dtok_state", "set_dequeue"); _opState = OP_DEQUEUE; } void DataTokenState::setTxnStateToCommit() { if (_txnState != TX_NONE) THROW_STATE_EXCEPTION(JournalErrors::JERR_BADDTOKTXNSTATE, s_getStateAsString(TX_COMMIT), s_getStateAsString(_txnState), "dtok_state", "set_txn_commit"); _txnState = TX_COMMIT; } void DataTokenState::setTxnStateToAbort() { if (_txnState != TX_NONE) THROW_STATE_EXCEPTION(JournalErrors::JERR_BADDTOKTXNSTATE, s_getStateAsString(TX_ABORT), s_getStateAsString(_txnState), "dtok_state", "set_txn_abort"); _txnState = TX_ABORT; } void DataTokenState::setBufferWriteStateToPart() { if (_bufferWriteState != IO_NONE) THROW_STATE_EXCEPTION(JournalErrors::JERR_BADDTOKIOSTATE, s_getStateAsString(IO_PART), s_getStateAsString(_bufferWriteState), "dtok_state", "set_buff_part"); _bufferWriteState = IO_PART; } void DataTokenState::setBufferWriteStateToComplete() { _bufferWriteState = IO_COMPLETE; } void DataTokenState::setAioSubmissionStateToPart() { if (_aioSubmissionState != IO_NONE) THROW_STATE_EXCEPTION(JournalErrors::JERR_BADDTOKIOSTATE, s_getStateAsString(IO_PART), s_getStateAsString(_aioSubmissionState), "dtok_state", "set_iosubm_part"); _aioSubmissionState = IO_PART; } void DataTokenState::setAioSubmissionStateToComplete() { _aioSubmissionState = IO_COMPLETE; } void DataTokenState::setAioCompletionStateToPart() { if (_aioCompletionState != IO_NONE) THROW_STATE_EXCEPTION(JournalErrors::JERR_BADDTOKIOSTATE, s_getStateAsString(IO_PART), s_getStateAsString(_aioCompletionState), "dtok_state", "set_iocompl_part"); _aioCompletionState = IO_PART; } void DataTokenState::setAioCompletionStateToComplete() { _aioCompletionState = IO_COMPLETE; } //static std::string DataTokenState::s_getStateAsString(opState_t s) { switch (s) { case OP_NONE: return "OP_NONE"; case OP_ENQUEUE: return "OP_ENQUEUE"; case OP_DEQUEUE: return "OP_DEQUEUE"; default: std::ostringstream oss; oss << ""; return oss.str(); } } //static std::string DataTokenState::s_getStateAsString(txnState_t s) { switch (s) { case TX_NONE: return "TX_NONE"; case TX_COMMIT: return "TX_COMMIT"; case TX_ABORT: return "TX_ABORT"; default: std::ostringstream oss; oss << ""; return oss.str(); } } //static std::string DataTokenState::s_getStateAsString(aioState_t s) { switch (s) { case IO_NONE: return "IO_NONE"; case IO_PART: return "IO_PART"; case IO_COMPLETE: return "IO_COMPLETE"; default: std::ostringstream oss; oss << ""; return oss.str(); } } } // namespace journal2 } // namespace mrg qpid-cpp-store-debian-0.16/lib/jrnl2/DataToken.cpp0000644000176300017630000000366711500511440020711 0ustar cajuscajus/** * \file DataToken.cpp * * Qpid asynchronous store plugin library * * This file contains async journal code (v.2). * * \author Kim van der Riet * * Copyright (c) 2010 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #include "DataToken.hpp" namespace mrg { namespace journal2 { // static recordId_t AtomicRecordIdCounter::_recordId; // static ScopedMutex AtomicRecordIdCounter::_recordIdMutex; DataToken::DataToken() : _dataTokenState(), _recordId(AtomicRecordIdCounter::s_getNextRecordId()), _externalRecordIdFlag(false) {} DataToken::DataToken(const recordId_t rid) : _dataTokenState(), _recordId(rid), _externalRecordIdFlag(true) {} void DataToken::setRecordId(const recordId_t rid) { _recordId = rid; _externalRecordIdFlag = true; } void DataToken::setDequeueRecordId(const recordId_t drid) { _dequeueRecordId = drid; } std::string DataToken::statusStr() { return "status string"; } } // namespace journal2 } // namespace mrg qpid-cpp-store-debian-0.16/lib/jrnl2/JournalState.hpp0000644000176300017630000000531511500511440021447 0ustar cajuscajus/** * \file JournalState.hpp * * Qpid asynchronous store plugin library * * This file contains async journal code (v.2). * * \author Kim van der Riet * * Copyright (c) 2010 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #ifndef mrg_journal2_JournalState_hpp #define mrg_journal2_JournalState_hpp #include namespace mrg { namespace journal2 { /** * Journal state machine * * JS_NONE * * / \ * / v * | o JS_RECOVERING_PHASE_1 * v | * JS_INITIALIZING o v * | o JS_RECOVERING_PHASE_2 * \ / * v v * JS_RUNNING o * | * v * JS_STOPPING o * | * v * JS_STOPPED * */ typedef enum { JS_NONE = 0, JS_RECOVERING_PHASE_1, JS_RECOVERING_PHASE_2, JS_INITIALIZING, JS_RUNNING, JS_STOPPING, JS_STOPPED } journalState_t; class JournalState { protected: journalState_t _journalState; public: JournalState(); JournalState(const JournalState& s); JournalState(const journalState_t s); // Raw state get/set functions const journalState_t& get() const; void set(const journalState_t s); // State change functions void reset(); void setStateInitializing(); void setStateRecoverPhase1(); void setStateRecoverPhase2(); void setStateRunning(); void setStateStopping(); void setStateStopped(); static std::string s_getStateAsString(journalState_t s); }; } // namespace journal2 } // namespace mrg #endif // mrg_journal2_JournalState_hpp qpid-cpp-store-debian-0.16/lib/jrnl2/Configuration.hpp0000644000176300017630000001112211514124615021645 0ustar cajuscajus/** * \file Configuration.hpp * * Qpid asynchronous store plugin library * * This file contains async journal code (v.2). * * \author Kim van der Riet * * Copyright (c) 2010 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #ifndef mrg_journal2_Configuration_hpp #define mrg_journal2_Configuration_hpp #include // uint8_t #if defined(__i386__) /* little endian, 32 bits */ #define JRNL_LITTLE_ENDIAN // #define JRNL_32_BIT #elif defined(__PPC__) || defined(__s390__) /* big endian, 32 bits */ #define JRNL_BIG_ENDIAN // #define JRNL_32_BIT #elif defined(__ia64__) || defined(__x86_64__) || defined(__alpha__) /* little endian, 64 bits */ #define JRNL_LITTLE_ENDIAN // #define JRNL_64_BIT #elif defined(__powerpc64__) || defined(__s390x__) /* big endian, 64 bits */ #define JRNL_BIG_ENDIAN // #define JRNL_64_BIT #else #error Unable to determine endianness #endif /** * Rule: Data block size (JRNL_DBLK_SIZE) MUST be a power of 2 such that *
* JRNL_DBLK_SIZE * JRNL_SBLK_SIZE == n * 512 (n = 1,2,3...)
* 
* (The disk softblock size is 512 for Linux kernels >= 2.6) */ //#define JRNL_DBLK_SIZE 128 ///< Data block size in bytes (CANNOT BE LESS THAN 32!) //#define JRNL_SBLK_SIZE 4 ///< Disk softblock size in multiples of JRNL_DBLK_SIZE //#define JRNL_MIN_FILE_SIZE 128 ///< Min. jrnl file size in sblks (excl. FileHeader) //#define JRNL_MAX_FILE_SIZE 4194304 ///< Max. jrnl file size in sblks (excl. FileHeader) //#define JRNL_MIN_NUM_FILES 4 ///< Min. number of journal files //#define JRNL_MAX_NUM_FILES 64 ///< Max. number of journal files //#define JRNL_ENQ_THRESHOLD 80 ///< Percent full when enqueue connection will be closed // //#define JRNL_RMGR_PAGE_SIZE 128 ///< Journal page size in softblocks //#define JRNL_RMGR_PAGES 16 ///< Number of pages to use in wmgr // //#define JRNL_WMGR_DEF_PAGE_SIZE 64 ///< Journal write page size in softblocks (default) //#define JRNL_WMGR_DEF_PAGES 32 ///< Number of pages to use in wmgr (default) // //#define JRNL_WMGR_MAXDTOKPP 1024 ///< Max. dtoks (data blocks) per page in wmgr //#define JRNL_WMGR_MAXWAITUS 100 ///< Max. wait time (us) before submitting AIO // //#define JRNL_INFO_EXTENSION "jinf" ///< Extension for journal info files //#define JRNL_DATA_EXTENSION "jdat" ///< Extension for journal data files //#define RHM_JDAT_TXA_MAGIC 0x614d4852 ///< ("RHMa" in little endian) Magic for dtx abort hdrs //#define RHM_JDAT_TXC_MAGIC 0x634d4852 ///< ("RHMc" in little endian) Magic for dtx commit hdrs //#define RHM_JDAT_DEQ_MAGIC 0x644d4852 ///< ("RHMd" in little endian) Magic for deq rec hdrs //#define RHM_JDAT_ENQ_MAGIC 0x654d4852 ///< ("RHMe" in little endian) Magic for enq rec hdrs //#define RHM_JDAT_FILE_MAGIC 0x664d4852 ///< ("RHMf" in little endian) Magic for file hdrs //#define RHM_JDAT_EMPTY_MAGIC 0x784d4852 ///< ("RHMx" in little endian) Magic for empty dblk //#define RHM_JDAT_VERSION 0x01 ///< Version (of file layout) //#define RHM_CLEAN_CHAR 0xff ///< Char used to clear empty space on disk #define RHM_LENDIAN_FLAG 0x0 ///< Value of little endian flag on disk #define RHM_BENDIAN_FLAG 0x1 ///< Value of big endian flag on disk namespace mrg { namespace journal2 { struct Configuration { static const uint8_t _s_endianValue = #if defined(JRNL_LITTLE_ENDIAN) RHM_LENDIAN_FLAG; #elif defined(JRNL_BIG_ENDIAN) RHM_BENDIAN_FLAG; #else #error Unknown endianness #endif }; } // namespace journal2 } // namespace mrg #endif // ifndef mrg_journal2_Configuration_hpp qpid-cpp-store-debian-0.16/lib/jrnl2/JournalDirectory.hpp0000644000176300017630000000323511500164314022336 0ustar cajuscajus/** * \file JournalDirectory.hpp * * Qpid asynchronous store plugin library * * This file contains async journal code (v.2). * * \author Kim van der Riet * * Copyright (c) 2010 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #ifndef mrg_journal2_JournalDirectory_hpp #define mrg_journal2_JournalDirectory_hpp #include namespace mrg { namespace journal2 { class JournalDirectory { public: static bool s_exists(const std::string& name); static bool s_exists(const char* name); static void s_create(const std::string& name); static void s_create(const char* name); static void s_delete(const std::string& name); static void s_delete(const char* name); }; } // namespace journal2 } // namespace mrg #endif // mrg_journal2_JournalDirectory_hpp qpid-cpp-store-debian-0.16/lib/jrnl2/Journal.hpp0000644000176300017630000000715111514124615020457 0ustar cajuscajus/** * \file Journal.hpp * * Qpid asynchronous store plugin library * * This file contains async journal code (v.2). * * \author Kim van der Riet * * Copyright (c) 2010 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #ifndef mrg_journal2_Journal_hpp #define mrg_journal2_Journal_hpp #include #include // uint64_t, uint32_t, etc. #include // timespec #include "AioCallback.hpp" #include "DataToken.hpp" #include "JournalState.hpp" #include "JournalParameters.hpp" // --- temp code --- #include "ScopedLock.hpp" // ScopedMutex #include #include "RecordHeader.hpp" // --- end temp code --- namespace mrg { namespace journal2 { // TODO - decide if this is the right place to expose these codes and flags typedef uint64_t ioRes; // TODO - this needs to be expressed as flags const ioRes RHM_IORES_ENQCAPTHRESH = 0x1; const ioRes RHM_IORES_BUSY = 0x2; std::string g_ioResAsString(const ioRes /*res*/); class Journal { protected: std::string _jrnlId; std::string _jrnlDir; std::string _baseFileName; JournalState _jrnlState; const JournalParameters* _jrnlParamsPtr; AioCallback* _aioCallbackPtr; // --- temp code --- static uint32_t _s_listSizeThreshold; std::vector _writeDataTokenList; std::vector _callBackDataTokenList[2]; bool _callBackDataTokenListSwitch; ScopedMutex _writeDataTokenListLock; ScopedMutex _callBackDataTokenListLock; // --- end temp code --- void flushNoLock(const bool blockTillAioCompleteFlag); public: Journal(const std::string& jrnlId, const std::string& jrnlDir, const std::string& baseFileName); // get functions inline std::string getJournalId() { return _jrnlId; } inline std::string getJournalDir() { return _jrnlDir; } inline std::string getBaseFileName() { return _baseFileName; } inline const JournalState& getJournalState() { return _jrnlState; } inline const JournalParameters* getJournalParameters() const { return _jrnlParamsPtr; } // msg ops void initialize(const JournalParameters* jpPtr, AioCallback* const aiocbPtr); ioRes enqueue(const void* const msgPtr, const std::size_t msgSize, DataToken* const dtokPtr); ioRes dequeue(DataToken* const dtokPtr); ioRes commit(); ioRes abort(); // aio ops and status // --- temp code --- uint32_t getWriteAioEventsRemaining() const; // --- end of temp code --- void flush(const bool blockTillAioCompleteFlag); void processCompletedAioWriteEvents(timespec* const timeout); }; } // namespace journal2 } // namespace mrg #endif // mrg_journal2_Journal_hpp qpid-cpp-store-debian-0.16/lib/jrnl2/JournalState.cpp0000644000176300017630000001226111500511440021440 0ustar cajuscajus/** * \file JournalState.cpp * * Qpid asynchronous store plugin library * * This file contains async journal code (v.2). * * \author Kim van der Riet * * Copyright (c) 2010 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #include "JournalState.hpp" #include "JournalException.hpp" namespace mrg { namespace journal2 { JournalState::JournalState() : _journalState(JS_NONE) {} JournalState::JournalState(const JournalState& s) : _journalState(s._journalState) {} JournalState::JournalState(const journalState_t s) : _journalState(s) {} const journalState_t& JournalState::get() const { return _journalState; } void JournalState::set(const journalState_t s) { _journalState = s; } void JournalState::reset() { _journalState = JS_NONE; } void JournalState::setStateInitializing() { if (_journalState != JS_NONE) THROW_STATE_EXCEPTION(JournalErrors::JERR_BADJRNLSTATE, s_getStateAsString(JS_INITIALIZING), s_getStateAsString(_journalState), "JournalState", "setStateInitializing"); _journalState = JS_INITIALIZING; } void JournalState::setStateRecoverPhase1() { if (_journalState != JS_NONE) THROW_STATE_EXCEPTION(JournalErrors::JERR_BADJRNLSTATE, s_getStateAsString(JS_RECOVERING_PHASE_1), s_getStateAsString(_journalState), "JournalState", "setStateRecoverPhase1"); _journalState = JS_RECOVERING_PHASE_1; } void JournalState::setStateRecoverPhase2() { if (_journalState != JS_RECOVERING_PHASE_1) THROW_STATE_EXCEPTION(JournalErrors::JERR_BADJRNLSTATE, s_getStateAsString(JS_RECOVERING_PHASE_2), s_getStateAsString(_journalState), "JournalState", "setStateRecoverPhase2"); _journalState = JS_RECOVERING_PHASE_2; } void JournalState::setStateRunning() { if (_journalState != JS_INITIALIZING ||_journalState != JS_RECOVERING_PHASE_2 ) THROW_STATE_EXCEPTION(JournalErrors::JERR_BADJRNLSTATE, s_getStateAsString(JS_RUNNING), s_getStateAsString(_journalState), "JournalState", "setStateRunning"); _journalState = JS_RUNNING; } void JournalState::setStateStopping() { if (_journalState != JS_RUNNING) THROW_STATE_EXCEPTION(JournalErrors::JERR_BADJRNLSTATE, s_getStateAsString(JS_STOPPING), s_getStateAsString(_journalState), "JournalState", "setStateStopping"); _journalState = JS_STOPPING; } void JournalState::setStateStopped() { if (_journalState != JS_STOPPING) THROW_STATE_EXCEPTION(JournalErrors::JERR_BADJRNLSTATE, s_getStateAsString(JS_STOPPED), s_getStateAsString(_journalState), "JournalState", "setStateStopped"); _journalState = JS_STOPPED; } //static std::string JournalState::s_getStateAsString(journalState_t s) { switch (s) { case JS_NONE: return "JS_NONE"; case JS_RECOVERING_PHASE_1: return "JS_RECOVERING_PHASE_1"; case JS_RECOVERING_PHASE_2: return "JS_RECOVERING_PHASE_2"; case JS_INITIALIZING: return "JS_INITIALIZING"; case JS_RUNNING: return "JS_RUNNING"; case JS_STOPPING: return "JS_STOPPING"; case JS_STOPPED: return "JS_STOPPED"; default: std::ostringstream oss; oss << ""; return oss.str(); } } } // namespace journal2 } // namespace mrg qpid-cpp-store-debian-0.16/lib/jrnl2/JournalParameters.cpp0000644000176300017630000001134211514124615022473 0ustar cajuscajus/** * \file jrnl2/JournalParameters.cpp * * Qpid asynchronous store plugin library * * This file contains async journal code (v.2). * * \author Kim van der Riet * * Copyright (c) 2010 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #include "JournalParameters.hpp" #include namespace mrg { namespace journal2 { // static declarations std::string JournalParameters::_s_defaultJrnlDir = "/tmp/store"; std::string JournalParameters::_s_defaultJrnlBaseFileName = "JournalData"; uint16_t JournalParameters::_s_defaultNumJrnlFiles = 8; uint32_t JournalParameters::_s_defaultJrnlFileSize_sblks = 3072; bool JournalParameters::_s_defaultAutoExpand = false; uint16_t JournalParameters::_s_defaultAutoExpandMaxJrnlFiles = 0; uint16_t JournalParameters::_s_defaultWriteBuffNumPgs = 32; uint32_t JournalParameters::_s_defaultWriteBuffPgSize_sblks = 128; JournalParameters::JournalParameters() : _jrnlDir(_s_defaultJrnlDir), _jrnlBaseFileName(_s_defaultJrnlBaseFileName), _numJrnlFiles(_s_defaultNumJrnlFiles), _jrnlFileSize_sblks(_s_defaultJrnlFileSize_sblks), _autoExpand(_s_defaultAutoExpand), _autoExpandMaxJrnlFiles(_s_defaultAutoExpandMaxJrnlFiles), _writeBuffNumPgs(_s_defaultWriteBuffNumPgs), _writeBuffPgSize_sblks(_s_defaultWriteBuffPgSize_sblks) {} JournalParameters::JournalParameters(const std::string& jrnlDir, const std::string& jrnlBaseFileName, const uint16_t numJrnlFiles, const bool autoExpand, const uint16_t autoExpandMaxJrnlFiles, const uint32_t jrnlFileSize_sblks, const uint16_t writeBuffNumPgs, const uint32_t writeBuffPgSize_sblks) : _jrnlDir(jrnlDir), _jrnlBaseFileName(jrnlBaseFileName), _numJrnlFiles(numJrnlFiles), _jrnlFileSize_sblks(jrnlFileSize_sblks), _autoExpand(autoExpand), _autoExpandMaxJrnlFiles(autoExpandMaxJrnlFiles), _writeBuffNumPgs(writeBuffNumPgs), _writeBuffPgSize_sblks(writeBuffPgSize_sblks) {} JournalParameters::JournalParameters(const JournalParameters& sp) : _jrnlDir(sp._jrnlDir), _jrnlBaseFileName(sp._jrnlBaseFileName), _numJrnlFiles(sp._numJrnlFiles), _jrnlFileSize_sblks(sp._jrnlFileSize_sblks), _autoExpand(sp._autoExpand), _autoExpandMaxJrnlFiles(sp._autoExpandMaxJrnlFiles), _writeBuffNumPgs(sp._writeBuffNumPgs), _writeBuffPgSize_sblks(sp._writeBuffPgSize_sblks) {} void JournalParameters::toStream(std::ostream& os) const { os << "Store Parameters:" << std::endl; os << " jrnlDir = \"" << _jrnlDir << "\"" << std::endl; os << " jrnlBaseFileName = \"" << _jrnlBaseFileName << "\"" << std::endl; os << " numJrnlFiles = " << _numJrnlFiles << std::endl; os << " jrnlFileSize_sblks = " << _jrnlFileSize_sblks << std::endl; os << " autoExpand = " << _autoExpand << std::endl; os << " autoExpandMaxJrnlFiles = " << _autoExpandMaxJrnlFiles << std::endl; os << " writeBuffNumPgs = " << _writeBuffNumPgs << std::endl; os << " writeBuffPgSize_sblks = " << _writeBuffPgSize_sblks << std::endl; } std::string JournalParameters::toString() const { std::ostringstream oss; toStream(oss); return oss.str(); } std::ostream& operator<<(std::ostream& os, const JournalParameters& jp) { jp.toStream(os); return os; } std::ostream& operator<<(std::ostream& os, const JournalParameters* jpPtr) { jpPtr->toStream(os); return os; } } // namespace journal2 } // namespace mrg qpid-cpp-store-debian-0.16/lib/jrnl2/JournalException.hpp0000644000176300017630000001037211514124615022335 0ustar cajuscajus/** * \file JournalException.hpp * * Qpid asynchronous store plugin library * * This file contains async journal code (v.2). * * \author Kim van der Riet * * Copyright (c) 2010 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #ifndef mrg_journal2_JournalException_hpp #define mrg_journal2_JournalException_hpp #include // std::strerror #include #include #include // uint32_t #include "JournalErrors.hpp" // Macro definitions #define FORMAT_SYSERR(errno) " errno=" << errno << " (" << std::strerror(errno) << ")" #define PTHREAD_CHK(err, pfn, cls, fn) if(err != 0) { \ std::ostringstream oss; \ oss << pfn << " failed: " << FORMAT_SYSERR(err); \ throw JournalException(JournalErrors::JERR_PTHREAD, oss.str(), cls, fn); \ } #define THROW_STATE_EXCEPTION(jerrno, target_st, curr_st, cls, fn) { \ std::ostringstream oss; \ oss << cls << "::" << fn << "() in state " << curr_st << " cannot be moved to state " << target_st << "."; \ throw JournalException(jerrno, oss.str(), cls, fn); \ } namespace mrg { namespace journal2 { class JournalException : public std::exception { protected: uint32_t _errorCode; std::string _additionalInfo; std::string _throwingClass; std::string _throwingFunction; std::string _what; void _formatWhatStr() throw (); public: JournalException() throw (); JournalException(const uint32_t errorCode) throw (); JournalException(const char* additionalInfo) throw (); JournalException(const std::string& additionalInfo) throw (); JournalException(const uint32_t errorCode, const char* additionalInfo) throw (); JournalException(const uint32_t errorCode, const std::string& additionalInfo) throw (); JournalException(const uint32_t errorCode, const char* throwingClass, const char* throwingFunction) throw (); JournalException(const uint32_t errorCode, const std::string& throwingClass, const std::string& throwingFunction) throw (); JournalException(const uint32_t errorCode, const char* additionalInfo, const char* throwingClass, const char* throwingFunction) throw (); JournalException(const uint32_t errorCode, const std::string& additionalInfo, const std::string& throwingClass, const std::string& throwingFunction) throw (); virtual ~JournalException() throw () {} const char* what() const throw (); // override std::exception::what() inline uint32_t getErrorCode() const throw () { return _errorCode; } inline const std::string getAdditionalInfo() const throw () { return _additionalInfo; } inline const std::string getThrowingClass() const throw () { return _throwingClass; } inline const std::string getThrowingFunction() const throw () { return _throwingFunction; } friend std::ostream& operator<<(std::ostream& os, const JournalException& je); friend std::ostream& operator<<(std::ostream& os, const JournalException* jePtr); }; // class JournalException } // namespace journal2 } // namespace mrg #endif // mrg_journal2_JournalException_hpp qpid-cpp-store-debian-0.16/lib/jrnl2/DataToken.hpp0000644000176300017630000000535511514124615020723 0ustar cajuscajus/** * \file DataToken.hpp * * Qpid asynchronous store plugin library * * This file contains async journal code (v.2). * * \author Kim van der Riet * * Copyright (c) 2010 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #ifndef mrg_journal2_DataToken_hpp #define mrg_journal2_DataToken_hpp #include #include // uint64_t #include "DataTokenState.hpp" #include "ScopedLock.hpp" namespace mrg { namespace journal2 { typedef uint64_t recordId_t; class AtomicRecordIdCounter { static recordId_t _recordId; static ScopedMutex _recordIdMutex; public: inline static recordId_t s_getNextRecordId() { // --- START OF CRITICAL SECTION --- ScopedLock l(_recordIdMutex); return ++_recordId; } // --- START OF CRITICAL SECTION --- }; class DataToken { DataTokenState _dataTokenState; bool _transientFlag; bool _externalFlag; std::string _externalPath; recordId_t _recordId; bool _externalRecordIdFlag; recordId_t _dequeueRecordId; public: DataToken(); DataToken(const recordId_t rid); inline DataTokenState& getDataTokenState() { return _dataTokenState; } inline bool isTransient() const { return _transientFlag; } inline bool isExternal() const { return _externalFlag; } inline std::string& getExternalPath() { return _externalPath; } void setRecordId(const recordId_t rid); inline recordId_t getRecordId() const { return _recordId; } inline bool isRecordIdExternal() { return _externalRecordIdFlag; } void setDequeueRecordId(const recordId_t drid); inline recordId_t getDequeueRecordId() const { return _dequeueRecordId; } // debug aids std::string statusStr(); }; } // namespace journal2 } // namespace mrg #endif // mrg_journal2_DataToken_hpp qpid-cpp-store-debian-0.16/lib/jrnl2/JournalErrors.cpp0000644000176300017630000000444211514124615021647 0ustar cajuscajus/** * \file JournalErrors.cpp * * Qpid asynchronous store plugin library * * This file contains async journal code (v.2). * * \author Kim van der Riet * * Copyright (c) 2010 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #include "JournalErrors.hpp" namespace mrg { namespace journal2 { std::map JournalErrors::_s_errorMap; std::map::iterator JournalErrors::_s_errorMapIterator; bool JournalErrors::_s_initializedFlag = JournalErrors::_s_initialize(); bool JournalErrors::_s_initialize() { _s_errorMap[JERR_PTHREAD] = "JERR_PTHREAD: pthread operation failure"; _s_errorMap[JERR_RTCLOCK] = "JERR_RTCLOCK: realtime clock operation failure"; _s_errorMap[JERR_BADJRNLSTATE] = "JERR_BADJRNLSTATE: Illegal journal state"; _s_errorMap[JERR_BADDTOKOPSTATE] = "JERR_BADDTOKOPSTATE: Illegal data token op state"; _s_errorMap[JERR_BADDTOKTXNSTATE] = "JERR_BADDTOKTXNSTATE: Illegal data token txn state"; _s_errorMap[JERR_BADDTOKIOSTATE] = "JERR_BADDTOKIOSTATE: Illegal data token io state"; return true; } const char* JournalErrors::s_errorMessage(const uint32_t err_no) throw () { _s_errorMapIterator = _s_errorMap.find(err_no); if (_s_errorMapIterator == _s_errorMap.end()) return ""; return _s_errorMapIterator->second; } } // namespace journal2 } // namespace mrg qpid-cpp-store-debian-0.16/lib/jrnl2/JournalParameters.hpp0000644000176300017630000000570211514124615022503 0ustar cajuscajus/** * \file jrnl2/JournalParameters.hpp * * Qpid asynchronous store plugin library * * This file contains async journal code (v.2). * * \author Kim van der Riet * * Copyright (c) 2010 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #ifndef mrg_journal2_JournalParameters_hpp #define mrg_journal2_JournalParameters_hpp #include #include #include // uint16_t, uint32_t namespace mrg { namespace journal2 { struct JournalParameters { // static default store params static std::string _s_defaultJrnlDir; static std::string _s_defaultJrnlBaseFileName; static uint16_t _s_defaultNumJrnlFiles; static uint32_t _s_defaultJrnlFileSize_sblks; static bool _s_defaultAutoExpand; static uint16_t _s_defaultAutoExpandMaxJrnlFiles; static uint16_t _s_defaultWriteBuffNumPgs; static uint32_t _s_defaultWriteBuffPgSize_sblks; std::string _jrnlDir; std::string _jrnlBaseFileName; uint16_t _numJrnlFiles; uint32_t _jrnlFileSize_sblks; bool _autoExpand; uint16_t _autoExpandMaxJrnlFiles; uint16_t _writeBuffNumPgs; uint32_t _writeBuffPgSize_sblks; JournalParameters(); JournalParameters(const std::string& jrnlDir, const std::string& jrnlBaseFileName, const uint16_t numJrnlFiles, const bool autoExpand, const uint16_t autoExpandMaxJrnlFiles, const uint32_t jrnlFileSize_sblks, const uint16_t writeBuffNumPgs, const uint32_t writeBuffPgSize_sblks); JournalParameters(const JournalParameters& sp); void toStream(std::ostream& os = std::cout) const; std::string toString() const; friend std::ostream& operator<<(std::ostream& os, const JournalParameters& jp); friend std::ostream& operator<<(std::ostream& os, const JournalParameters* jpPtr); }; } // namespace journal2 } // namespace mrg #endif // mrg_journal2_JournalParameters_hpp qpid-cpp-store-debian-0.16/lib/jrnl2/JournalDirectory.cpp0000644000176300017630000000367011500164314022334 0ustar cajuscajus/** * \file JournalDirectory.cpp * * Qpid asynchronous store plugin library * * This file contains async journal code (v.2). * * \author Kim van der Riet * * Copyright (c) 2010 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #include "JournalDirectory.hpp" namespace mrg { namespace journal2 { // static bool JournalDirectory::s_exists(const std::string& name) { return s_exists(name.c_str()); } // static bool JournalDirectory::s_exists(const char* /*name*/) { // TODO - add impl here return false; } // static void JournalDirectory::s_create(const std::string& name) { s_create(name.c_str()); } // static void JournalDirectory::s_create(const char* /*name*/) { // TODO - add impl here } // static void JournalDirectory::s_delete(const std::string& name) { s_delete(name.c_str()); } // static void JournalDirectory::s_delete(const char* /*name*/) { // TODO - add impl here } } // namespace journal2 } // namespace mrg qpid-cpp-store-debian-0.16/lib/jrnl2/JournalException.cpp0000644000176300017630000001310711514124615022327 0ustar cajuscajus/** * \file JournalException.cpp * * Qpid asynchronous store plugin library * * This file contains async journal code (v.2). * * \author Kim van der Riet * * Copyright (c) 2010 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #include "JournalException.hpp" #include namespace mrg { namespace journal2 { JournalException::JournalException() throw (): std::exception(), _errorCode(0) { _formatWhatStr(); } JournalException::JournalException(const uint32_t errorCode) throw (): std::exception(), _errorCode(errorCode) { _formatWhatStr(); } JournalException::JournalException(const char* additionalInfo) throw (): std::exception(), _errorCode(0), _additionalInfo(additionalInfo) { _formatWhatStr(); } JournalException::JournalException(const std::string& additionalInfo) throw (): std::exception(), _errorCode(0), _additionalInfo(additionalInfo) { _formatWhatStr(); } JournalException::JournalException(const uint32_t errorCode, const char* additionalInfo) throw (): std::exception(), _errorCode(errorCode), _additionalInfo(additionalInfo) { _formatWhatStr(); } JournalException::JournalException(const uint32_t errorCode, const std::string& additionalInfo) throw (): std::exception(), _errorCode(errorCode), _additionalInfo(additionalInfo) { _formatWhatStr(); } JournalException::JournalException(const uint32_t errorCode, const char* throwingClass, const char* throwingFunction) throw (): std::exception(), _errorCode(errorCode), _throwingClass(throwingClass), _throwingFunction(throwingFunction) { _formatWhatStr(); } JournalException::JournalException(const uint32_t errorCode, const std::string& throwingClass, const std::string& throwingFunction) throw (): std::exception(), _errorCode(errorCode), _throwingClass(throwingClass), _throwingFunction(throwingFunction) { _formatWhatStr(); } JournalException::JournalException(const uint32_t errorCode, const char* additionalInfo, const char* throwingClass, const char* throwingFunction) throw (): std::exception(), _errorCode(errorCode), _additionalInfo(additionalInfo), _throwingClass(throwingClass), _throwingFunction(throwingFunction) { _formatWhatStr(); } JournalException::JournalException(const uint32_t errorCode, const std::string& additionalInfo, const std::string& throwingClass, const std::string& throwingFunction) throw (): std::exception(), _errorCode(errorCode), _additionalInfo(additionalInfo), _throwingClass(throwingClass), _throwingFunction(throwingFunction) { _formatWhatStr(); } void JournalException::_formatWhatStr() throw () { try { const bool ai = !_additionalInfo.empty(); const bool tc = !_throwingClass.empty(); const bool tf = !_throwingFunction.empty(); std::ostringstream oss; oss << "JournalException 0x" << std::hex << std::setfill('0') << std::setw(4) << _errorCode << " "; if (tc) { oss << _throwingClass; if (tf) { oss << "::"; } else { oss << " "; } } if (tf) { oss << _throwingFunction << "() "; } if (tc || tf) { oss << "threw " << JournalErrors::s_errorMessage(_errorCode); } if (ai) { oss << " (" << _additionalInfo << ")"; } _what.assign(oss.str()); } catch (...) {} } const char* JournalException::what() const throw () { return _what.c_str(); } std::ostream& operator<<(std::ostream& os, const JournalException& je) { os << je.what(); return os; } std::ostream& operator<<(std::ostream& os, const JournalException* jePtr) { os << jePtr->what(); return os; } } // namespace journal2 } // namespace mrg qpid-cpp-store-debian-0.16/lib/jrnl2/RecordHeader.hpp0000644000176300017630000013473611556264621021416 0ustar cajuscajus/** * \file RecordHeader.hpp * * Qpid asynchronous store plugin library * * This file contains async journal code (v.2). * * \author Kim van der Riet * * Copyright (c) 2010, 2011 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. * * List of structs in this file: * struct DequeueHeader * struct EnqueueHeader * struct EventHeader * struct FileHeader * struct RecordHeader * struct RecordTail * struct TransactionHeader * * Overview of structs in this file: * *
 *  +------------+                   +--------------+
 *  | RecordTail |                   | RecordHeader |
 *  +------------+                   |  (abstract)  |
 *                                   +--------------+
 *                                           ^
 *                                           |
 *        +----------------+-----------------+-------------------+
 *        |                |                 |                   |
 *  +------------+  +-------------+  +---------------+  +-------------------+
 *  | FileHeader |  | EventHeader |  | DequeueHeader |  | TransactionHeader |
 *  +------------+  +-------------+  +---------------+  +-------------------+
 *                         ^
 *                         |
 *                 +---------------+
 *                 | EnqueueHeader |
 *                 +---------------+
 *  
*/ #ifndef mrg_journal2_RecordHeader_hpp #define mrg_journal2_RecordHeader_hpp #include // std::size_t #include // std::time_t #include "Configuration.hpp" #include "JournalException.hpp" #include // uint8_t, uint16_t, uint32_t, uint64_t namespace mrg { namespace journal2 { #pragma pack(1) /** * \brief Struct for data common to the head of all journal files and records. * This includes identification for the file type, the encoding version, endian * indicator and a record ID. * * File layout in binary format (16 bytes): *
     *        0x0                                       0x7
     *      +-----+-----+-----+-----+-----+-----+-----+-----+
     * 0x00 |        _magic         |  v  |  e  |  _flags   |
     *      +-----+-----+-----+-----+-----+-----+-----+-----+
     * 0x08 |                  _recordId                    |
     *      +-----+-----+-----+-----+-----+-----+-----+-----+
     * 
* * * * * * * * * *
vfile version [ _version ] (If the format or encoding of * this file changes, then this number should be incremented)
eendian flag [ _bigEndianFlag ], false (0x00) for * little endian, true (0x01) for big endian
*/ struct RecordHeader { uint32_t _magic; ///< File type identifier (magic number) uint8_t _version; ///< File encoding version uint8_t _bigEndianFlag; ///< Flag for determining endianness uint16_t _flags; ///< User and system flags uint64_t _recordId; ///< Record ID (rotating 64-bit counter) static const uint16_t HDR_OVERWRITE_INDICATOR_MASK = 0x1; /** * \brief Default constructor, which sets all values to 0. */ inline RecordHeader() : _magic(0), _version(0), _bigEndianFlag(0), _flags(0), _recordId(0) {} /** * \brief Convenience constructor which initializes values during construction. * * \param magic Magic for this record * \param version Version of this record * \param recordId Record identifier for this record * \param overwriteIndicator Overwrite indicator for this record */ inline RecordHeader(const uint32_t magic, const uint8_t version, const uint64_t recordId, const bool overwriteIndicator) : _magic(magic), _version(version), _bigEndianFlag(Configuration::_s_endianValue), _flags(overwriteIndicator ? HDR_OVERWRITE_INDICATOR_MASK : 0), _recordId(recordId) {} /** * \brief Copy constructor. */ inline RecordHeader(const RecordHeader& rh) : _magic(rh._magic), _version(rh._version), _bigEndianFlag(rh._bigEndianFlag), _flags(rh._flags), _recordId(rh._recordId) {} /** * \brief Destructor. */ virtual inline ~RecordHeader() {} /** * \brief Convenience copy method. */ virtual inline void copy(const RecordHeader& rh) { _magic = rh._magic; _version = rh._version; _bigEndianFlag = rh._bigEndianFlag; _flags = rh._flags; _recordId = rh._recordId; } /** * \brief Resets all fields to default values (mostly 0). */ virtual inline void reset() { _magic = 0; _version = 0; _bigEndianFlag = 0; _flags = 0; _recordId = 0; } /** * \brief Return the value of the Overwrite Indicator for this record. * * \return true if the Overwrite Indicator flag is set, false otherwise. */ inline bool getOverwriteIndicator() const { return _flags & HDR_OVERWRITE_INDICATOR_MASK; } /** * \brief Set the value of the Overwrite Indicator for this record */ inline void setOverwriteIndicator(const bool owi) { _flags = owi ? _flags | HDR_OVERWRITE_INDICATOR_MASK : _flags & (~HDR_OVERWRITE_INDICATOR_MASK); } /** * \brief Return the header size of this record in bytes. Must be implemented by * subclasses. * * \return Size of record header in bytes. */ static inline std::size_t getHeaderSize() { return sizeof(RecordHeader); } /** * \brief Return the body (data) size of this record in bytes. Must be implemented * by subclasses. * * \return Size of record body in bytes. */ virtual std::size_t getBodySize() const = 0; /** * \brief Return total size of this record in bytes, being the sum of the header, * xid (if present), data (if present) and tail (if present). Must be implemented * by subclasses. * * \returns The size of the entire record, including header, body (xid and data, * if present) and record tail (if persent) in bytes. */ virtual std::size_t getRecordSize() const = 0; // TODO - Is this the right place for encode/decode fns? ///** // * \brief Encode (write) this record instance into the buffer pointed to by the buffer // * pointer. Must be implemented by subclasses. // */ //virtual std::size_t encode(char* bufferPtr, // const std::size_t bufferSize, // const std::size_t encodeOffset = 0) = 0; /** * \brief Return a uint32_t checksum for the header and body content of this record. * * \param initialValue The initial (or seed) value of the checksum. * * \return Checksum for header and body of record. Tail (if any) is excluded. */ inline uint32_t getCheckSum(uint32_t initialValue = 0) const { uint32_t cs = initialValue; for (unsigned char* p = (unsigned char*)this; p < (unsigned char*)this + getHeaderSize() + getBodySize(); p++) { cs ^= (uint32_t)(*p); bool carry = cs & uint32_t(0x80000000); cs <<= 1; if (carry) cs++; } return cs; } }; // struct RecordHeader /** * \brief Struct for data common to the tail of all records. The magic number * used here is the binary inverse (1's complement) of the magic used in the * record header; this minimizes possible confusion with other headers that may * be present during recovery. The tail is used with all records that have either * XIDs or data - ie any size-variable content. Currently the only records that * do NOT use the tail are non-transactional dequeues and filler records. * * Record layout in binary format (16 bytes): *
     *        0x0                                       0x7
     *      +-----+-----+-----+-----+-----+-----+-----+-----+
     * 0x00 |       _xMagic         |       _checkSum       |
     *      +-----+-----+-----+-----+-----+-----+-----+-----+
     * 0x08 |                   _recordId                   |
     *      +-----+-----+-----+-----+-----+-----+-----+-----+
     * 
*/ struct RecordTail { uint32_t _xMagic; ///< Binary inverse (1's complement) of hdr magic number uint32_t _checkSum; ///< Checksum for header and body of record uint64_t _recordId; ///< Record identifier matching that of the header for this record /** * \brief Default constructor, which sets most values to 0. */ inline RecordTail() : _xMagic(0xffffffff), _checkSum(0), _recordId(0) {} /** * \brief Convenience constructor which initializes values during construction. * * \param xMagic The inverse of the record header magic (ie ~rh._magic). * \param checkSum The checksum for this record header and body. * \param recordId The record identifier matching the record header. */ inline RecordTail(const uint32_t xMagic, const uint32_t checkSum, const uint64_t recordId) : _xMagic(xMagic), _checkSum(checkSum), _recordId(recordId) {} /** * \brief Convenience constructor which initializes values during construction from * existing RecordHeader instance. * * \param rh Header instance for which the RecordTail is to be created. */ inline RecordTail(const RecordHeader& rh) : _xMagic(~rh._magic), _checkSum(rh.getCheckSum()), _recordId(rh._recordId) {} /** * \brief Copy constructor. * * \param rt Instance to be copied. */ inline RecordTail(const RecordTail& rt) : _xMagic(rt._xMagic), _checkSum(rt._checkSum), _recordId(rt._recordId) {} /** * \brief Destructor. */ virtual inline ~RecordTail() {} /** * \brief Convenience copy method. * * \param rt Instance to be copied. */ inline void copy(const RecordTail& rt) { _xMagic = rt._xMagic; _checkSum = rt._checkSum; _recordId = rt._recordId; } /** * \brief Resets all fields to default values (mostly 0). */ inline void reset() { _xMagic = 0xffffffff; _checkSum = 0; _recordId = 0; } /** * \brief Returns the size of the header in bytes. */ inline static std::size_t getSize() { return sizeof(RecordTail); } }; // struct RecordTail /** * \brief Struct for data common to the head of all journal files. In addition to * the common data, this includes the record ID and offset of the first record in * the file. * * This header precedes all data in journal files and occupies the first complete * block in the file. The record ID and offset are updated on each overwrite of the * file. * * File layout in binary format (48 bytes): *
     *        0x0                                       0x7
     *      +-----+-----+-----+-----+-----+-----+-----+-----+  -+
     * 0x00 |        _magic         |  v  |  e  |  _flags   |   |
     *      +-----+-----+-----+-----+-----+-----+-----+-----+   | struct RecordHeader
     * 0x08 |   _recordId (used to show first rid in file)  |   |
     *      +-----+-----+-----+-----+-----+-----+-----+-----+  -+
     * 0x10 |    _physicalFileId    |    _logicalFileId     |
     *      +-----+-----+-----+-----+-----+-----+-----+-----+
     * 0x18 |              _firstRecordOffset               |
     *      +-----+-----+-----+-----+-----+-----+-----+-----+
     * 0x20 |              _timestampSeconds                |
     *      +-----+-----+-----+-----+-----+-----+-----+-----+
     * 0x28 | _timestampNanoSeconds |       _reserved       |
     *      +-----+-----+-----+-----+-----+-----+-----+-----+
     * 
* * * * * * * * * *
vfile version [ _version ] (If the format or encoding of * this file changes, then this number should be incremented)
eendian flag [ _bigEndianFlag ], false (0x00) for * little endian, true (0x01) for big endian
*/ struct FileHeader : public RecordHeader { uint32_t _physicalFileId; ///< Physical file ID (pfid) uint32_t _logicalFileId; ///< Logical file ID (lfid) #if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT) uint32_t _filler0; ///< Big-endian filler for 32-bit size_t #endif std::size_t _firstRecordOffset; ///< First record offset #if defined(JRNL_LITTLE_ENDIAN) && defined(JRNL_32_BIT) uint32_t _filler0; ///< Little-endian filler for 32-bit size_t #endif #if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT) uint32_t _filler1; ///< Big-endian filler for 32-bit time_t #endif std::time_t _timestampSeconds; ///< Timestamp of journal initialization, seconds component #if defined(JRNL_LITTLE_ENDIAN) && defined(JRNL_32_BIT) uint32_t _filler1; ///< Little-endian filler for 32-bit time_t #endif uint32_t _timestampNanoSeconds; ///< Timestamp of journal initialization, nanoseconds component uint32_t _reserved; ///< Little-endian filler for uint32_t /** * \brief Default constructor, which sets all values to 0. */ inline FileHeader() : RecordHeader(), _physicalFileId(0), _logicalFileId(0), #if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT) _filler0(0), #endif _firstRecordOffset(0), #if defined(JRNL_LITTLE_ENDIAN) && defined(JRNL_32_BIT) _filler0(0), #endif #if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT) _filler1(0), #endif _timestampSeconds(0), #if defined(JRNL_LITTLE_ENDIAN) && defined(JRNL_32_BIT) _filler1(0), #endif _timestampNanoSeconds(0), _reserved(0) {} /** * \brief Convenience constructor which initializes values during construction. * * \param magic Magic for this record * \param version Version of this record * \param recordId RecordId for this record * \param overwriteIndicator Overwrite indicator for this record * \param physicalFileId Physical file ID (file number on disk) * \param logicalFileId Logical file ID (file number as seen by circular file buffer) * \param firstRecordOffset First record offset in bytes from beginning of file * \param setTimestampFlag If true, causes the timestamp to be initialized with the current system time */ inline FileHeader(const uint32_t magic, const uint8_t version, const uint64_t recordId, const bool overwriteIndicator, const uint16_t physicalFileId, const uint16_t logicalFileId, const std::size_t firstRecordOffset, const bool setTimestampFlag = false) : RecordHeader(magic, version, recordId, overwriteIndicator), _physicalFileId(physicalFileId), _logicalFileId(logicalFileId), #if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT) _filler0(0), #endif _firstRecordOffset(firstRecordOffset), #if defined(JRNL_LITTLE_ENDIAN) && defined(JRNL_32_BIT) _filler0(0), #endif #if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT) _filler1(0), #endif _timestampSeconds(0), #if defined(JRNL_LITTLE_ENDIAN) && defined(JRNL_32_BIT) _filler1(0), #endif _timestampNanoSeconds(0), _reserved(0) { if (setTimestampFlag) setTimestamp(); } /** * \brief Copy constructor. * * \param fh FileHeader instance to be copied */ inline FileHeader(const FileHeader& fh) : RecordHeader(fh), _physicalFileId(fh._physicalFileId), _logicalFileId(fh._logicalFileId), #if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT) _filler0(fh._filler0), #endif _firstRecordOffset(fh._firstRecordOffset), #if defined(JRNL_LITTLE_ENDIAN) && defined(JRNL_32_BIT) _filler0(fh._filler0), #endif #if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT) _filler1(fh._filler1), #endif _timestampSeconds(fh._timestampSeconds), #if defined(JRNL_LITTLE_ENDIAN) && defined(JRNL_32_BIT) _filler1(fh._filler1), #endif _timestampNanoSeconds(fh._timestampNanoSeconds), _reserved(fh._reserved) {} /** * \brief Destructor. */ virtual ~FileHeader() {} /** * \brief Convenience copy method. * * \param fh FileHeader instance to be copied */ inline void copy(const FileHeader& fh) { RecordHeader::copy(fh); _physicalFileId = fh._physicalFileId; _logicalFileId = fh._logicalFileId; _firstRecordOffset = fh._firstRecordOffset; _timestampSeconds = fh._timestampSeconds; _timestampNanoSeconds = fh._timestampNanoSeconds; _reserved = fh._reserved; #if defined(JRNL_32_BIT) _filler0 = fh._filler0; _filler1 = fh._filler1; #endif } /** * \brief Resets all fields to default values (mostly 0). */ inline void reset() { RecordHeader::reset(); _physicalFileId = 0; _logicalFileId = 0; _firstRecordOffset = 0; _timestampSeconds = 0; _timestampNanoSeconds = 0; _reserved = 0; #if defined(JRNL_32_BIT) _filler0 = 0; _filler1 = 0; #endif } /** * \brief Return the header size of this record in bytes. * * \return Size of record header in bytes. */ static inline std::size_t getHeaderSize() { return sizeof(FileHeader); } /** * \brief Return the body (data) size of this record in bytes. * * \return Size of record body in bytes. By definition, a FileHeader has no body. */ inline std::size_t getBodySize() const { return 0; } /** * \brief Return total size of this record in bytes, being in the case of the * FileHeader the size of the header itself only. */ inline std::size_t getRecordSize() const { return getHeaderSize(); } /** * \brief Gets the current time from the system clock and sets the timestamp in the struct. */ inline void setTimestamp() { // TODO: Standardize on method for getting time that does not require a context switch. timespec ts; if (::clock_gettime(CLOCK_REALTIME, &ts)) { std::ostringstream oss; oss << FORMAT_SYSERR(errno); throw JournalException(JournalErrors::JERR_RTCLOCK, oss.str(), "FileHeader", "setTimestamp"); } setTimestamp(ts); } /** * \brief Sets the timestamp in the struct to the provided value (in seconds and nanoseconds). * * \param ts Timestamp from which the file header time stamp is to be copied */ inline void setTimestamp(const timespec& ts) { _timestampSeconds = ts.tv_sec; _timestampNanoSeconds = ts.tv_nsec; } }; // struct FileHeader /** * \brief Struct for event records, which can be used to record system events in the * store. * * The EventHeader record type may be used to store events into the journal which do * not constitute data content but changes of state in the broker. These can be * recovered and used to set appropriate state in the broker. * * This record is almost identical to the EnqueueRecord, but without the flags. I * am uncertain at this time whether it is necessary to set an XID on an event * record, but in case, I have left this feature in. In any event, there is only a * 1 byte size penalty in the header size for doing so. * * Record layout in binary format (32 bytes): *
     *        0x0                                       0x7
     *      +-----+-----+-----+-----+-----+-----+-----+-----+  -+
     * 0x00 |        _magic         |  v  |  e  |  _flags   |   |
     *      +-----+-----+-----+-----+-----+-----+-----+-----+   | struct RecordHeader
     * 0x08 |                   _recordId                   |   |
     *      +-----+-----+-----+-----+-----+-----+-----+-----+  -+
     * 0x10 |                   _xidSize                    |
     *      +-----+-----+-----+-----+-----+-----+-----+-----+
     * 0x18 |                   _dataSize                   |
     *      +-----+-----+-----+-----+-----+-----+-----+-----+
     * 
* * * * * * * * * *
vfile version [ _version ] (If the format or encoding of * this file changes, then this number should be incremented)
eendian flag [ _bigEndianFlag ], false (0x00) for * little endian, true (0x01) for big endian
*/ struct EventHeader : public RecordHeader { #if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT) uint32_t _filler0; ///< Big-endian filler for 32-bit size_t #endif std::size_t _xidSize; ///< XID size #if defined(JRNL_LITTLE_ENDIAN) && defined(JRNL_32_BIT) uint32_t _filler0; ///< Little-endian filler for 32-bit size_t #endif #if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT) uint32_t _filler1; ///< Big-endian filler for 32-bit size_t #endif std::size_t _dataSize; ///< Record data size #if defined(JRNL_LITTLE_ENDIAN) && defined(JRNL_32_BIT) uint32_t _filler1; ///< Little-endian filler for 32-bit size_t #endif /** * \brief Default constructor, which sets all values to 0. */ inline EventHeader() : RecordHeader(), #if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT) _filler0(0), #endif _xidSize(0), #if defined(JRNL_LITTLE_ENDIAN) && defined(JRNL_32_BIT) _filler0(0), #endif #if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT) _filler1(0), #endif _dataSize(0) #if defined(JRNL_LITTLE_ENDIAN) && defined(JRNL_32_BIT) , _filler1(0) #endif {} /** * \brief Convenience constructor which initializes values during construction. * * \param magic The magic for this record * \param version Version of this record * \param recordId Record identifier for this record * \param xidSize Size of the transaction (or distributed transaction) ID for this record * \param dataSize Size of the opaque data block for this record * \param overwriteIndicator Flag indicating the present value of the overwrite indicator when writing this * record */ inline EventHeader(const uint32_t magic, const uint8_t version, const uint64_t recordId, const std::size_t xidSize, const std::size_t dataSize, const bool overwriteIndicator) : RecordHeader(magic, version, recordId, overwriteIndicator), #if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT) _filler0(0), #endif _xidSize(xidSize), #if defined(JRNL_LITTLE_ENDIAN) && defined(JRNL_32_BIT) _filler0(0), #endif #if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT) _filler1(0), #endif _dataSize(dataSize) #if defined(JRNL_LITTLE_ENDIAN) && defined(JRNL_32_BIT) , _filler1(0) #endif {} /** * \brief Copy constructor * * \param eh Instance to be copied */ inline EventHeader(const EventHeader& eh) : RecordHeader(eh), #if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT) _filler0(eh._filler0), #endif _xidSize(eh._xidSize), #if defined(JRNL_LITTLE_ENDIAN) && defined(JRNL_32_BIT) _filler0(eh._filler0), #endif #if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT) _filler1(eh._filler1), #endif _dataSize(eh._dataSize) #if defined(JRNL_LITTLE_ENDIAN) && defined(JRNL_32_BIT) , _filler1(eh._filler1) #endif {} /** * \brief Destructor. */ virtual ~EventHeader() {} /** * \brief Convenience copy method. */ virtual inline void copy(const EventHeader& e) { RecordHeader::copy(e); _xidSize = e._xidSize; _dataSize = e._dataSize; #if defined(JRNL_32_BIT) _filler0 = e._filler0; _filler1 = e._filler1; #endif } /** * \brief Reset this record to default values (mostly 0) */ virtual inline void reset() { RecordHeader::reset(); _xidSize = 0; _dataSize = 0; #if defined(JRNL_32_BIT) _filler0 = 0; _filler1 = 0; #endif } /** * \brief Return the header size of this record in bytes. * * \return Size of record header in bytes. */ static inline std::size_t getHeaderSize() { return sizeof(EventHeader); } /** * \brief Return the body (data) size of this record in bytes. * * \return Size of record body in bytes. */ virtual inline std::size_t getBodySize() const { return _xidSize + _dataSize; } /** * \brief Return total size of this record in bytes, being in the case of the * enqueue record the size of the header, the size of the body (xid and data) * and the size of the tail. */ virtual inline std::size_t getRecordSize() const { return getHeaderSize() + (getBodySize() ? getBodySize() + RecordTail::getSize() : 0); } }; // struct EventHeader /** * \brief Struct for enqueue record. * * Struct for enqueue record. In addition to the common data, this header includes both the * xid and data blob sizes. * * This header precedes all enqueue data in journal files. * * Record layout in binary format (32 bytes): *
     *        0x0                                       0x7
     *      +-----+-----+-----+-----+-----+-----+-----+-----+  -+
     * 0x00 |        _magic         |  v  |  e  |  _flags   |   |
     *      +-----+-----+-----+-----+-----+-----+-----+-----+   | struct RecordHeader
     * 0x08 |                   _recordId                   |   |
     *      +-----+-----+-----+-----+-----+-----+-----+-----+  -+
     * 0x10 |                   _xidSize                    |
     *      +-----+-----+-----+-----+-----+-----+-----+-----+
     * 0x18 |                   _dataSize                   |
     *      +-----+-----+-----+-----+-----+-----+-----+-----+
     * 
* * * * * * * * * *
vfile version [ _version ] (If the format or encoding of * this file changes, then this number should be incremented)
eendian flag [ _bigEndianFlag ], false (0x00) for * little endian, true (0x01) for big endian
*/ struct EnqueueHeader : public EventHeader { static const uint16_t ENQ_HDR_TRANSIENT_MASK = 0x10; static const uint16_t ENQ_HDR_EXTERNAL_MASK = 0x20; /** * \brief Default constructor, which sets all values to 0. */ inline EnqueueHeader() : EventHeader() {} /** * \brief Convenience constructor which initializes values during construction. * * \param magic The magic for this record * \param version Version of this record * \param recordId Record identifier for this record * \param xidSize Size of the transaction (or distributed transaction) ID for this record * \param dataSize Size of the opaque data block for this record * \param overwriteIndicator Flag indicating the present value of the overwrite indicator when writing this * record * \param transient Flag indicating that this record is transient (ie to be discarded on recovery) * \param external Flag indicating that this record's data is stored externally to the journal, the data portion * of the record identifies the storage location. */ inline EnqueueHeader(const uint32_t magic, const uint8_t version, const uint64_t recordId, const std::size_t xidSize, const std::size_t dataSize, const bool overwriteIndicator, const bool transient = false, const bool external = false) : EventHeader(magic, version, recordId, xidSize, dataSize, overwriteIndicator) { setTransientFlag(transient); setExternalFlag(external); } /** * \brief Copy constructor * * \param eh Instance to be copied */ inline EnqueueHeader(const EnqueueHeader& eh) : EventHeader(eh) {} /** * \brief Destructor. */ virtual ~EnqueueHeader() {} /** * \brief Return the value of the Transient flag for this record. * If set, this record is ignored during recovery. * * \return true if the Transient flag for this record is set, false otherwise. */ inline bool getTransientFlag() const { return _flags & ENQ_HDR_TRANSIENT_MASK; } /** * \brief Set the value of the Transient flag for this record. * * \param transient The value to be set in the transient flag. */ inline void setTransientFlag(const bool transient = true) { _flags = transient ? _flags | ENQ_HDR_TRANSIENT_MASK : _flags & (~ENQ_HDR_TRANSIENT_MASK); } /** * \brief Return the value of the External flag for this record. If set, this record data is not within the * journal but external to it. The data part of this record contains the location of the stored data. * * \return true if the Transient flag for this record is set, false otherwise. */ inline bool getExternalFlag() const { return _flags & ENQ_HDR_EXTERNAL_MASK; } /** * \brief Set the value of the External flag for this record. * * \param external The value to be set in the External flag. */ inline void setExternalFlag(const bool external = true) { _flags = external ? _flags | ENQ_HDR_EXTERNAL_MASK : _flags & (~ENQ_HDR_EXTERNAL_MASK); } /** * \brief Return the header size of this record in bytes. * * \return Size of record header in bytes. */ static inline std::size_t getHeaderSize() { return sizeof(EnqueueHeader); } }; // struct EnqueueHeader /** * \brief Struct for dequeue record. * * Struct for dequeue record. If this record has a non-zero xidsize field (i.e., there is a * valid XID), then this header is followed by the XID of xidsize bytes and a rec_tail. If, * on the other hand, this record has a zero xidsize (i.e., there is no XID), then the rec_tail * is absent. * * Note that this record had its own rid distinct from the rid of the record it is dequeuing. * The rid field below is the rid of the dequeue record itself; the deq-rid field is the rid of a * previous enqueue record being dequeued by this record. * * Record layout in binary format (32 bytes): *
     *        0x0                                       0x7
     *      +-----+-----+-----+-----+-----+-----+-----+-----+  -+
     * 0x00 |        _magic         |  v  |  e  |  _flags   |   |
     *      +-----+-----+-----+-----+-----+-----+-----+-----+   | struct RecordHeader
     * 0x08 |                   _recordId                   |   |
     *      +-----+-----+-----+-----+-----+-----+-----+-----+  -+
     * 0x10 |               _dequeuedRecordId               |
     *      +-----+-----+-----+-----+-----+-----+-----+-----+
     * 0x18 |                   _xidSize                    |
     *      +-----+-----+-----+-----+-----+-----+-----+-----+
     * 
* * * * * * * * * *
vfile version [ _version ] (If the format or encoding of * this file changes, then this number should be incremented)
eendian flag [ _bigEndianFlag ], false (0x00) for * little endian, true (0x01) for big endian
*/ struct DequeueHeader : public RecordHeader { uint64_t _dequeuedRecordId; ///< Record ID of dequeued record #if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT) uint32_t _filler0; ///< Big-endian filler for 32-bit size_t #endif std::size_t _xidSize; ///< XID size #if defined(JRNL_LITTLE_ENDIAN) && defined(JRNL_32_BIT) uint32_t _filler0; ///< Little-endian filler for 32-bit size_t #endif static const uint16_t DEQ_HDR_TPL_COMMIT_ON_TXN_COMPL_MASK = 0x10; /** * \brief Default constructor, which sets all values to 0. */ inline DequeueHeader() : RecordHeader(), _dequeuedRecordId(0), #if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT) _filler0(0), #endif _xidSize(0) #if defined(JRNL_LITTLE_ENDIAN) && defined(JRNL_32_BIT) , _filler0(0) #endif {} /** * \brief Convenience constructor which initializes values during construction. * * \param magic The magic for this record * \param version Version of this record * \param recordId Record identifier for this record * \param dequeuedRecordId Record identifier of the record being dequeued by this record * \param xidSize Size of the transaction (or distributed transaction) ID for this record * \param overwriteIndicator Flag indicating the present value of the overwrite indicator when writing this * record * \param tplCommitOnTxnComplFlag */ inline DequeueHeader(const uint32_t magic, const uint8_t version, const uint64_t recordId, const uint64_t dequeuedRecordId, const std::size_t xidSize, const bool overwriteIndicator, const bool tplCommitOnTxnComplFlag = false) : RecordHeader(magic, version, recordId, overwriteIndicator), _dequeuedRecordId(dequeuedRecordId), #if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT) _filler0(0), #endif _xidSize(xidSize) #if defined(JRNL_LITTLE_ENDIAN) && defined(JRNL_32_BIT) , _filler0(0) #endif { setTplCommitOnTxnComplFlag(tplCommitOnTxnComplFlag); } /** * \brief Copy constructor * * \param dh Instance to be copied */ inline DequeueHeader(const DequeueHeader& dh) : RecordHeader(dh), _dequeuedRecordId(dh._dequeuedRecordId), #if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT) _filler0(dh._filler0), #endif _xidSize(dh._xidSize) #if defined(JRNL_LITTLE_ENDIAN) && defined(JRNL_32_BIT) , _filler0(dh._filler0) #endif {} /** * \brief Destructor. */ virtual ~DequeueHeader() {} /** * \brief Convenience copy method. */ inline void copy(const DequeueHeader& dh) { RecordHeader::copy(dh); _dequeuedRecordId = dh._dequeuedRecordId; _xidSize = dh._xidSize; #if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT) _filler0 = dh._filler0; #endif } /** * \brief Reset this record to default values (mostly 0) */ inline void reset() { RecordHeader::reset(); _dequeuedRecordId = 0; _xidSize = 0; #if defined(JRNL_32_BIT) _filler0 = 0; #endif } /** * \brief Return the value of the tplCommitOnTxnComplFlag for this record. This * flag is used only within the TPL, and if set, indicates that the transaction was * closed using a commit. If not set, the transaction was closed using an abort. * This is used during recovery of the transactions in the store. * * \return true if the tplCommitOnTxnComplFlag flag for this record is set, false * otherwise. */ inline bool getTplCommitOnTxnComplFlag() const { return _flags & DEQ_HDR_TPL_COMMIT_ON_TXN_COMPL_MASK; } /** * \brief Set the value of the tplCommitOnTxnComplFlag for this record. This is * only used in the TPL, and is ignored elsewhere. * * \param commitOnTxnCompl The value to be set in the tplCommitOnTxnComplFlag. If * true, the transaction was closed with a commit; if false, with an abort. */ inline void setTplCommitOnTxnComplFlag(const bool commitOnTxnCompl) { _flags = commitOnTxnCompl ? _flags | DEQ_HDR_TPL_COMMIT_ON_TXN_COMPL_MASK : _flags & (~DEQ_HDR_TPL_COMMIT_ON_TXN_COMPL_MASK); } /** * \brief Return the header size of this record in bytes. * * \return Size of record header in bytes. */ static std::size_t getHeaderSize() { return sizeof(DequeueHeader); } /** * \brief Return the body (xid and data) size of this record in bytes. * * \return Size of record body in bytes. */ std::size_t getBodySize() const { return _xidSize; } /** * \brief Return total size of this record in bytes, being in the case of the * dequeue record the size of the header, the size of the body (xid only) and * the size of the tail. */ inline std::size_t getRecordSize() const { return getHeaderSize() + (getBodySize() ? getBodySize() + RecordTail::getSize() : 0); } }; // struct DequeueHeader /** * \brief Struct for transaction commit and abort records. * * Struct for DTX commit and abort records. Only the magic distinguishes between them. Since * this record must be used in the context of a valid XID, the xidsize field must not be zero. * Immediately following this record is the XID itself which is xidsize bytes long, followed by * a rec_tail. * * Note that this record had its own rid distinct from the rids of the record(s) making up the * transaction it is committing or aborting. * * Record layout in binary format (24 bytes): *
     *        0x0                                       0x7
     *      +-----+-----+-----+-----+-----+-----+-----+-----+  -+
     * 0x00 |        _magic         |  v  |  e  |  _flags   |   |
     *      +-----+-----+-----+-----+-----+-----+-----+-----+   | struct RecordHeader
     * 0x08 |                   _recordId                   |   |
     *      +-----+-----+-----+-----+-----+-----+-----+-----+  -+
     * 0x10 |                   _xidSize                    |
     *      +-----+-----+-----+-----+-----+-----+-----+-----+
     * 
* * * * * * * * * *
vfile version [ _version ] (If the format or encoding of * this file changes, then this number should be incremented)
eendian flag [ _bigEndianFlag ], false (0x00) for * little endian, true (0x01) for big endian
*/ struct TransactionHeader : public RecordHeader { #if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT) uint32_t _filler0; ///< Big-endian filler for 32-bit size_t #endif std::size_t _xidSize; ///< XID size #if defined(JRNL_LITTLE_ENDIAN) && defined(JRNL_32_BIT) uint32_t _filler0; ///< Little-endian filler for 32-bit size_t #endif /** * \brief Default constructor, which sets all values to 0. */ TransactionHeader() : RecordHeader(), #if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT) _filler0(0), #endif _xidSize(0) #if defined(JRNL_LITTLE_ENDIAN) && defined(JRNL_32_BIT) , _filler0(0) #endif {} /** * \brief Convenience constructor which initializes values during construction. * * \param magic The magic for this record * \param version Version of this record * \param recordId Record identifier for this record * \param xidSize Size of the transaction (or distributed transaction) ID for this record * \param overwriteIndicator Flag indicating the present value of the overwrite indicator when writing this * record */ TransactionHeader(const uint32_t magic, const uint8_t version, const uint64_t recordId, const std::size_t xidSize, const bool overwriteIndicator) : RecordHeader(magic, version, recordId, overwriteIndicator), #if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT) _filler0(0), #endif _xidSize(xidSize) #if defined(JRNL_LITTLE_ENDIAN) && defined(JRNL_32_BIT) , _filler0(0) #endif {} /** * \brief Copy constructor * * \param th Instance to be copied */ TransactionHeader(const TransactionHeader& th) : RecordHeader(th), #if defined(JRNL_BIG_ENDIAN) && defined(JRNL_32_BIT) _filler0(th._filler0), #endif _xidSize(th._xidSize) #if defined(JRNL_LITTLE_ENDIAN) && defined(JRNL_32_BIT) , _filler0(th._filler0) #endif {} /** * \brief Destructor. */ virtual ~TransactionHeader() {} /** * \brief Convenience copy method. */ inline void copy(const TransactionHeader& th) { RecordHeader::copy(th); _xidSize = th._xidSize; #if defined(JRNL_32_BIT) _filler0 = th._filler0; #endif } /** * \brief Reset this record to default values (mostly 0) */ inline void reset() { RecordHeader::reset(); _xidSize = 0; #if defined(JRNL_32_BIT) _filler0 = 0; #endif } /** * \brief Return the header size of this record in bytes. * * \return Size of record header in bytes. */ static std::size_t getHeaderSize() { return sizeof(TransactionHeader); } /** * \brief Return the body (data) size of this record in bytes. * * \return Size of record body in bytes. */ std::size_t getBodySize() const { return _xidSize; } /** * \brief Return total size of this record in bytes, being in the case of the * dequeue record the size of the header, the size of the body (xid only) and * the size of the tail. */ inline std::size_t getRecordSize() const { // By definition, TransactionRecords must always have an xid, hence a record // tail as well. No check on body size required in this case. return getHeaderSize() + getBodySize() + RecordTail::getSize(); } }; // struct TransactionHeader #pragma pack() } // namespace journal2 } // namespace mrg #endif // ifndef mrg_journal2_RecordHeader_hpp qpid-cpp-store-debian-0.16/lib/jrnl2/AioCallback.hpp0000644000176300017630000000314711514124615021173 0ustar cajuscajus/** * \file AioCallback.hpp * * Qpid asynchronous store plugin library * * This file contains async journal code (v.2). * * \author Kim van der Riet * * Copyright (c) 2010 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #ifndef mrg_journal2_AioCallback_hpp #define mrg_journal2_AioCallback_hpp #include "DataToken.hpp" #include // uint16_t #include namespace mrg { namespace journal2 { class AioCallback { public: virtual ~AioCallback() {} virtual void writeAioCompleteCallback(std::vector& dataTokenList) = 0; virtual void readAioCompleteCallback(std::vector& buffPageCtrlBlkIndexList) = 0; }; } // namespace journal2 } // namespace mrg #endif // mrg_journal2_AioCallback_hpp qpid-cpp-store-debian-0.16/lib/jrnl2/Journal.cpp0000644000176300017630000001326011514124615020450 0ustar cajuscajus/** * \file Journal.cpp * * Qpid asynchronous store plugin library * * This file contains async journal code (v.2). * * \author Kim van der Riet * * Copyright (c) 2010 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #include "Journal.hpp" // --- temp code --- #include // --- end temp code --- namespace mrg { namespace journal2 { std::string g_ioResAsString(const ioRes /*res*/) { // TODO - provide implementation return ".[g_ioResAsString]."; } // static uint32_t Journal::_s_listSizeThreshold = 50; Journal::Journal(const std::string& jrnlId, const std::string& jrnlDir, const std::string& baseFileName) : _jrnlId(jrnlId), _jrnlDir(jrnlDir), _baseFileName(baseFileName), _jrnlParamsPtr(0), _aioCallbackPtr(0) // --- temp code --- , _callBackDataTokenListSwitch(false) // --- end temp code --- {} void Journal::initialize(const JournalParameters* jpPtr, AioCallback* const aiocbPtr) { _jrnlParamsPtr = jpPtr; _aioCallbackPtr = aiocbPtr; } ioRes Journal::enqueue(const void* const /*msgPtr*/, const std::size_t /*msgSize*/, DataToken* const dtokPtr) { dtokPtr->getDataTokenState().setOpStateToEnqueue(); // --- temp code --- bool flushFlag; { // --- START OF CRITICAL SECTION --- ScopedLock l(_writeDataTokenListLock); _writeDataTokenList.push_back(dtokPtr); flushFlag = _writeDataTokenList.size() >= _s_listSizeThreshold; if (flushFlag) flushNoLock(false); } // --- END OF CRITICAL SECTION --- if (flushFlag) processCompletedAioWriteEvents(0); // --- end temp code --- return 0; } ioRes Journal::dequeue(DataToken* const dtokPtr) { dtokPtr->getDataTokenState().setOpStateToDequeue(); dtokPtr->setDequeueRecordId(dtokPtr->getRecordId()); // --- temp code --- bool flushFlag; { // --- START OF CRITICAL SECTION --- ScopedLock l(_writeDataTokenListLock); _writeDataTokenList.push_back(dtokPtr); flushFlag = _writeDataTokenList.size() >= _s_listSizeThreshold; if (flushFlag) flushNoLock(false); } // --- END OF CRITICAL SECTION --- if (flushFlag) processCompletedAioWriteEvents(0); // --- end temp code --- return 0; } ioRes Journal::commit() { // TODO return 0; } ioRes Journal::abort() { // TODO return 0; } uint32_t Journal::getWriteAioEventsRemaining() const { while (true) { // --- START OF CRITICAL SECTION --- ScopedTryLock l1(_callBackDataTokenListLock); ScopedTryLock l2(_writeDataTokenListLock); if (l1.isLocked() && l2.isLocked()) { return _callBackDataTokenList[0].size() + _callBackDataTokenList[1].size(); } else { //::usleep(10); } }; // --- END OF CRITICAL SECTION --- } void Journal::flush(const bool blockTillAioCompleteFlag) { // --- temp code --- // --- START OF CRITICAL SECTION --- ScopedTryLock l(_writeDataTokenListLock); if (l.isLocked()) { flushNoLock(blockTillAioCompleteFlag); } // --- END OF CRITICAL SECTION --- // --- end temp code --- } // protected void Journal::flushNoLock(const bool /*blockTillAioCompleteFlag*/) { // --- temp code --- int i = _callBackDataTokenListSwitch ? 1 : 0; while (_writeDataTokenList.size()) { _callBackDataTokenList[i].push_back(_writeDataTokenList.back()); _writeDataTokenList.pop_back(); } // --- end temp code --- } void Journal::processCompletedAioWriteEvents(timespec* const /*timeout*/) { // --- temp code --- // --- START OF CRITICAL SECTION 1 --- ScopedTryLock l1(_callBackDataTokenListLock); if (l1.isLocked()) { int i = _callBackDataTokenListSwitch ? 0 : 1; if (_callBackDataTokenList[i].size() && _aioCallbackPtr) { // NOTE: Callback made with _callBackDataTokenListLock isLocked _aioCallbackPtr->writeAioCompleteCallback(_callBackDataTokenList[i]); } _callBackDataTokenList[i].clear(); // take both locks before allowing switch to change { // --- START OF CRITICAL SECTION 2 --- ScopedLock l2(_writeDataTokenListLock); _callBackDataTokenListSwitch = !_callBackDataTokenListSwitch; } // --- END OF CRITICAL SECTION 2 --- } // --- END OF CRITICAL SECTION 1 --- // --- end temp code --- } } // namespace journal2 } // namespace mrg qpid-cpp-store-debian-0.16/lib/jrnl2/JournalErrors.hpp0000644000176300017630000000470511556264621021666 0ustar cajuscajus/** * \file JournalErrors.hpp * * Qpid asynchronous store plugin library * * This file contains async journal code (v.2). * * \author Kim van der Riet * * Copyright (c) 2010 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #ifndef mrg_journal2_JournalErrors_hpp #define mrg_journal2_JournalErrors_hpp #include #include // uint32_t namespace mrg { namespace journal2 { class JournalErrors { static std::map _s_errorMap; ///< Map of error messages static std::map::iterator _s_errorMapIterator; ///< Iterator static bool _s_initializedFlag; ///< Dummy flag, used to initialize map. static bool _s_initialize(); ///< Static fn for initializing static data public: // generic errors static const uint32_t JERR_PTHREAD = 0x0001; ///< pthread operation failure static const uint32_t JERR_RTCLOCK = 0x0002; ///< realtime clock operation failure // illegal states static const uint32_t JERR_BADJRNLSTATE = 0x0101; ///< Illegal journal state static const uint32_t JERR_BADDTOKOPSTATE = 0x0102; ///< Illegal data token op state static const uint32_t JERR_BADDTOKTXNSTATE = 0x0103; ///< Illegal data token txn state static const uint32_t JERR_BADDTOKIOSTATE = 0x0104; ///< Illegal data token io state static const char* s_errorMessage(const uint32_t err_no) throw (); }; } // namespace journal2 } // namespace mrg #endif // mrg_journal2_JournalErrors_hpp qpid-cpp-store-debian-0.16/lib/jrnl2/Makefile.am0000644000176300017630000000264211514124615020370 0ustar cajuscajus# Copyright (c) 2007, 2008, 2009 Red Hat, Inc. # # This file is part of the Qpid async store library msgstore.so. # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 # USA # # The GNU Lesser General Public License is available in the file COPYING. AM_CXXFLAGS = $(WARNING_CFLAGS) -pthread lib_LTLIBRARIES = libasyncjrnl2.la libasyncjrnl2_la_SOURCES = \ DataToken.cpp \ DataTokenState.cpp \ Journal.cpp \ JournalDirectory.cpp \ JournalErrors.cpp \ JournalException.cpp \ JournalParameters.cpp \ JournalState.cpp \ AioCallback.hpp \ Configuration.hpp \ DataToken.hpp \ DataTokenState.hpp \ Journal.hpp \ JournalDirectory.hpp \ JournalErrors.hpp \ JournalException.hpp \ JournalParameters.hpp \ JournalState.hpp \ RecordHeader.hpp \ ScopedLock.hpp qpid-cpp-store-debian-0.16/lib/jrnl2/DataTokenState.hpp0000644000176300017630000001021511500164314021706 0ustar cajuscajus/** * \file DataTokenState.hpp * * Qpid asynchronous store plugin library * * This file contains async journal code (v.2). * * \author Kim van der Riet * * Copyright (c) 2010 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #ifndef mrg_journal2_DataTokenState_hpp #define mrg_journal2_DataTokenState_hpp #include namespace mrg { namespace journal2 { typedef enum { OP_NONE = 0, OP_ENQUEUE, OP_DEQUEUE } opState_t; typedef enum { TX_NONE = 0, TX_COMMIT, TX_ABORT } txnState_t; typedef enum { IO_NONE = 0, IO_PART, IO_COMPLETE } aioState_t; class DataTokenState { opState_t _opState; txnState_t _txnState; aioState_t _bufferWriteState; aioState_t _aioSubmissionState; aioState_t _aioCompletionState; public: DataTokenState(); DataTokenState(const DataTokenState& s); DataTokenState(const opState_t o, const txnState_t t, const aioState_t b, const aioState_t s, const aioState_t c); // Raw state get/set functions inline opState_t getOpState() const { return _opState; } inline void setOpState(const opState_t s) { _opState = s; } inline txnState_t getTxnState() const { return _txnState; } inline void setTxnState(const txnState_t t) { _txnState = t; } inline aioState_t getBufferWriteState() const { return _bufferWriteState; } inline void setBufferWriteState(const aioState_t b) { _bufferWriteState = b; } inline aioState_t getAioSubmissionState() const { return _aioSubmissionState; } inline void setAioSubmissionState(const aioState_t s) { _aioSubmissionState = s; } inline aioState_t getAioCompletionState() const { return _aioCompletionState; } inline void setAioCompletionState(const aioState_t c) { _aioCompletionState = c; } // State change functions void reset(); void setOpStateToEnqueue(); void setOpStateToDequeue(); void setTxnStateToCommit(); void setTxnStateToAbort(); void setBufferWriteStateToPart(); void setBufferWriteStateToComplete(); void setAioSubmissionStateToPart(); void setAioSubmissionStateToComplete(); void setAioCompletionStateToPart(); void setAioCompletionStateToComplete(); // State query functions inline bool isEnqueueable() const { return _opState == OP_NONE; } inline bool isDequeueable() const { return _opState == OP_ENQUEUE; } inline bool isTxnOpen() const { return _txnState == TX_NONE; } inline bool isBufferWriteStateComplete() const { return _bufferWriteState == IO_COMPLETE; } inline bool isAioSubmissionStateComplete() const { return _aioSubmissionState == IO_COMPLETE; } inline bool isAioCompletionStateComplete() const { return _aioCompletionState == IO_COMPLETE; } // State-to-string function(s) static std::string s_getStateAsString(opState_t s); static std::string s_getStateAsString(txnState_t s); static std::string s_getStateAsString(aioState_t s); }; } // namespace journal2 } // namespace mrg #endif // mrg_journal2_DataTokenState_hpp qpid-cpp-store-debian-0.16/lib/jrnl2/ScopedLock.hpp0000644000176300017630000001135511556264621021104 0ustar cajuscajus/** * \file ScopedLock.hpp * * Qpid asynchronous store plugin library * * This file contains async journal code (v.2). * * \author Kim van der Riet * * Copyright (c) 2010, 2011 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #ifndef mrg_journal2_ScopedLock_hpp #define mrg_journal2_ScopedLock_hpp #include // EBUSY #include #include "JournalException.hpp" namespace mrg { namespace journal2 { // Ultra-simple scoped mutex class that allows a posix mutex to be initialized and destroyed with error checks class ScopedMutex { protected: mutable ::pthread_mutex_t _m; public: inline ScopedMutex() { PTHREAD_CHK(::pthread_mutex_init(&_m, 0), "::pthread_mutex_init", "ScopedMutex", "ScopedMutex"); } inline ~ScopedMutex() { PTHREAD_CHK(::pthread_mutex_destroy(&_m), "::pthread_mutex_destroy", "ScopedMutex", "~ScopedMutex"); } inline ::pthread_mutex_t* get() const { return &_m; } }; // Scoped-mutex (sm) container, superclass for scoped lock classes class ScopedMutexContainer { protected: const ScopedMutex& _sm; public: ScopedMutexContainer(const ScopedMutex& sm) : _sm(sm) {} virtual ~ScopedMutexContainer() {} inline ::pthread_mutex_t* get() const { return _sm.get(); } }; // Ultra-simple scoped lock class, auto-releases mutex when it goes out-of-scope class ScopedLock : public ScopedMutexContainer { public: inline ScopedLock(const ScopedMutex& sm) : ScopedMutexContainer(sm) { PTHREAD_CHK(::pthread_mutex_lock(_sm.get()), "::pthread_mutex_lock", "ScopedLock", "ScopedLock"); } virtual inline ~ScopedLock() { PTHREAD_CHK(::pthread_mutex_unlock(_sm.get()), "::pthread_mutex_unlock", "ScopedLock", "~ScopedLock"); } }; // Ultra-simple scoped try-lock class, auto-releases mutex when it goes out-of-scope class ScopedTryLock : public ScopedMutexContainer { protected: bool _lockedFlag; public: inline ScopedTryLock(const ScopedMutex& sm) : ScopedMutexContainer(sm), _lockedFlag(false) { int ret = ::pthread_mutex_trylock(_sm.get()); _lockedFlag = (ret == 0); // check if lock obtained if (!_lockedFlag && ret != EBUSY) PTHREAD_CHK(ret, "::pthread_mutex_trylock", "ScopedTryLock", "ScopedTryLock"); } virtual inline ~ScopedTryLock() { if (_lockedFlag) PTHREAD_CHK(::pthread_mutex_unlock(_sm.get()), "::pthread_mutex_unlock", "ScopedTryLock", "~ScopedTryLock"); } inline bool isLocked() const { return _lockedFlag; } }; // Ultra-simple scoped condition variable class class ScopedConditionVariable { protected: mutable ::pthread_cond_t _cv; public: inline ScopedConditionVariable() { PTHREAD_CHK(::pthread_cond_init(&_cv, 0), "pthread_cond_init", "ScopedConditionVariable", "ScopedConditionVariable"); } inline ~ScopedConditionVariable() { PTHREAD_CHK(::pthread_cond_destroy(&_cv), "pthread_cond_destroy", "ScopedConditionVariable", "~ScopedConditionVariable"); } inline void wait(ScopedLock& sl) { PTHREAD_CHK(::pthread_cond_wait(&_cv, sl.get()), "pthread_cond_wait", "ScopedConditionVariable", "wait"); } inline void notify_one() { PTHREAD_CHK(::pthread_cond_signal(&_cv), "pthread_cond_signal", "ScopedConditionVariable", "notify_one"); } inline void notify_all() { PTHREAD_CHK(::pthread_cond_broadcast(&_cv), "pthread_cond_broadcast", "ScopedConditionVariable", "notify_all"); } }; } // namespace journal2 } // namespace mrg #endif // ifndef mrg_journal2_ScopedLock_hpp qpid-cpp-store-debian-0.16/lib/jrnl2/README0000644000176300017630000000170711514124615017215 0ustar cajuscajusThis directory contains experimental code. It is not (currently) a part of the main store, and can be safely ignored for all normal builds. This is a refactorization of the lib/jrnl directory, with completely renamed classes according to normal C++ naming standards used elsewhere in this project. Also included are the following enhancements: * A new record type for journal events. This will allow the redelivered flag to be set correctly (amongst other things) * A new record tail layout which contains a checksum. This will solve the issue of losing data unknowingly if the first and last pages of a multi-page write occur, but some in-between pages are still pending at time of failure. * Some minor refactoring of the FileHeader type * A clean-up with a proper heirarchy of these classes NOTE: these will break binary compatibility with earlier journals - an upgrade issue. This journal will need to have its revision number incremented from 1 to 2. qpid-cpp-store-debian-0.16/lib/StorePlugin.cpp0000644000176300017630000000543511627773271020304 0ustar cajuscajus/* Copyright (c) 2007, 2008 Red Hat, Inc. This file is part of the Qpid async store library msgstore.so. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA The GNU Lesser General Public License is available in the file COPYING. */ #include "qpid/broker/Broker.h" #include "qpid/Plugin.h" #include "qpid/Options.h" #include "qpid/DataDir.h" #include "qpid/log/Statement.h" #include "MessageStoreImpl.h" using mrg::msgstore::MessageStoreImpl; namespace qpid { namespace broker { using namespace std; struct StorePlugin : public Plugin { MessageStoreImpl::StoreOptions options; boost::shared_ptr store; Options* getOptions() { return &options; } void earlyInitialize (Plugin::Target& target) { Broker* broker = dynamic_cast(&target); if (!broker) return; store.reset(new MessageStoreImpl(broker->getTimer())); DataDir& dataDir = broker->getDataDir (); if (options.storeDir.empty ()) { if (!dataDir.isEnabled ()) throw Exception ("msgstore: If --data-dir is blank or --no-data-dir is specified, --store-dir must be present."); options.storeDir = dataDir.getPath (); } store->init(&options); boost::shared_ptr brokerStore(store); broker->setStore(brokerStore); target.addFinalizer(boost::bind(&StorePlugin::finalize, this)); } void initialize(Plugin::Target& target) { Broker* broker = dynamic_cast(&target); if (!broker) return; if (!store) return; // Not done in earlyInitialize as the Broker::isInCluster test won't work there. if (broker->isInCluster()) { QPID_LOG(info, "Disabling management instrumentation for the store in a cluster."); } else { QPID_LOG(info, "Enabling management instrumentation for the store."); store->initManagement(broker); } } void finalize() { store.reset(); } const char* id() {return "StorePlugin";} }; static StorePlugin instance; // Static initialization. }} // namespace qpid::broker qpid-cpp-store-debian-0.16/lib/IdSequence.h0000644000176300017630000000231411142067437017504 0ustar cajuscajus/* Copyright (c) 2007, 2008 Red Hat, Inc. This file is part of the Qpid async store library msgstore.so. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA The GNU Lesser General Public License is available in the file COPYING. */ #ifndef _IdSequence_ #define _IdSequence_ #include #include #include namespace mrg{ namespace msgstore{ class IdSequence { qpid::sys::Mutex lock; uint64_t id; public: IdSequence(); uint64_t next(); void reset(uint64_t value); }; }} #endif qpid-cpp-store-debian-0.16/lib/IdDbt.h0000644000176300017630000000213511142067437016446 0ustar cajuscajus/* Copyright (c) 2007, 2008 Red Hat, Inc. This file is part of the Qpid async store library msgstore.so. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA The GNU Lesser General Public License is available in the file COPYING. */ #ifndef _IdDbt_ #define _IdDbt_ #include "db-inc.h" namespace mrg{ namespace msgstore{ class IdDbt : public Dbt { void init(); public: u_int64_t id; IdDbt(u_int64_t id); IdDbt(); }; }} #endif qpid-cpp-store-debian-0.16/lib/Makefile.am0000644000176300017630000001205511530777234017351 0ustar cajuscajus# Copyright (c) 2007, 2008, 2009 Red Hat, Inc. # # This file is part of the Qpid async store library msgstore.so. # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 # USA # # The GNU Lesser General Public License is available in the file COPYING. AM_CXXFLAGS = $(WARNING_CFLAGS) $(APR_CXXFLAGS) $(QPID_CXXFLAGS) -I$(srcdir)/gen -DRHM_CLEAN -pthread #SUBDIRS = jrnl2 # Definitions for client and daemon plugins PLUGINLDFLAGS=-no-undefined -module -avoid-version dmoduledir=$(libdir)/qpid/daemon dmodule_LTLIBRARIES = msgstore.la msgstore_la_LIBADD = \ $(APR_LIBS) \ $(LIB_DLOPEN) \ $(LIB_BERKELEY_DB) \ $(LIB_CLOCK_GETTIME) \ $(QPID_LIBS) msgstore_la_LDFLAGS = \ $(PLUGINLDFLAGS) msgstore_la_SOURCES = \ StorePlugin.cpp \ BindingDbt.cpp \ BufferValue.cpp \ DataTokenImpl.cpp \ IdDbt.cpp \ IdSequence.cpp \ JournalImpl.cpp \ MessageStoreImpl.cpp \ PreparedTransaction.cpp \ TxnCtxt.cpp \ BindingDbt.h \ BufferValue.h \ Cursor.h \ DataTokenImpl.h \ IdDbt.h \ IdSequence.h \ JournalImpl.h \ MessageStoreImpl.h \ PreparedTransaction.h \ StoreException.h \ TxnCtxt.h \ jrnl/aio.cpp \ jrnl/cvar.cpp \ jrnl/data_tok.cpp \ jrnl/deq_rec.cpp \ jrnl/enq_map.cpp \ jrnl/enq_rec.cpp \ jrnl/fcntl.cpp \ jrnl/jcntl.cpp \ jrnl/jdir.cpp \ jrnl/jerrno.cpp \ jrnl/jexception.cpp \ jrnl/jinf.cpp \ jrnl/jrec.cpp \ jrnl/lp_map.cpp \ jrnl/lpmgr.cpp \ jrnl/pmgr.cpp \ jrnl/rmgr.cpp \ jrnl/rfc.cpp \ jrnl/rrfc.cpp \ jrnl/slock.cpp \ jrnl/smutex.cpp \ jrnl/time_ns.cpp \ jrnl/txn_map.cpp \ jrnl/txn_rec.cpp \ jrnl/wmgr.cpp \ jrnl/wrfc.cpp \ jrnl/aio.hpp \ jrnl/aio_callback.hpp \ jrnl/cvar.hpp \ jrnl/data_tok.hpp \ jrnl/deq_hdr.hpp \ jrnl/deq_rec.hpp \ jrnl/enq_hdr.hpp \ jrnl/enq_map.hpp \ jrnl/enq_rec.hpp \ jrnl/enums.hpp \ jrnl/fcntl.hpp \ jrnl/file_hdr.hpp \ jrnl/jcfg.hpp \ jrnl/jcntl.hpp \ jrnl/jdir.hpp \ jrnl/jerrno.hpp \ jrnl/jexception.hpp \ jrnl/jinf.hpp \ jrnl/jrec.hpp \ jrnl/lp_map.hpp \ jrnl/lpmgr.hpp \ jrnl/pmgr.hpp \ jrnl/rcvdat.hpp \ jrnl/rec_hdr.hpp \ jrnl/rec_tail.hpp \ jrnl/rmgr.hpp \ jrnl/rfc.hpp \ jrnl/rrfc.hpp \ jrnl/slock.hpp \ jrnl/smutex.hpp \ jrnl/time_ns.hpp \ jrnl/txn_hdr.hpp \ jrnl/txn_map.hpp \ jrnl/txn_rec.hpp \ jrnl/wmgr.hpp \ jrnl/wrfc.hpp \ gen/qmf/com/redhat/rhm/store/EventCreated.cpp \ gen/qmf/com/redhat/rhm/store/EventCreated.h \ gen/qmf/com/redhat/rhm/store/EventEnqThresholdExceeded.cpp \ gen/qmf/com/redhat/rhm/store/EventEnqThresholdExceeded.h \ gen/qmf/com/redhat/rhm/store/EventFull.cpp \ gen/qmf/com/redhat/rhm/store/EventFull.h \ gen/qmf/com/redhat/rhm/store/EventRecovered.cpp \ gen/qmf/com/redhat/rhm/store/EventRecovered.h \ gen/qmf/com/redhat/rhm/store/Package.cpp \ gen/qmf/com/redhat/rhm/store/Package.h \ gen/qmf/com/redhat/rhm/store/Journal.cpp \ gen/qmf/com/redhat/rhm/store/Journal.h \ gen/qmf/com/redhat/rhm/store/Store.cpp \ gen/qmf/com/redhat/rhm/store/Store.h \ gen/qmf/com/redhat/rhm/store/ArgsJournalExpand.h BUILT_SOURCES = db-inc.h db-inc.h: Makefile.in echo '#include <$(DB_CXX_HEADER_PREFIX)db_cxx.h>' > $@-t mv $@-t $@ qpid-cpp-store-debian-0.16/lib/DataTokenImpl.h0000644000176300017630000000310011717442050020141 0ustar cajuscajus/* Copyright (c) 2007, 2008 Red Hat, Inc. This file is part of the Qpid async store library msgstore.so. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA The GNU Lesser General Public License is available in the file COPYING. */ #ifndef _DataTokenImpl_ #define _DataTokenImpl_ #include "jrnl/data_tok.hpp" #include #include namespace mrg { namespace msgstore { class DataTokenImpl : public journal::data_tok, public qpid::RefCounted { private: boost::intrusive_ptr sourceMsg; public: DataTokenImpl(); virtual ~DataTokenImpl(); inline boost::intrusive_ptr& getSourceMessage() { return sourceMsg; } inline void setSourceMessage(const boost::intrusive_ptr& msg) { sourceMsg = msg; } }; } // namespace msgstore } // namespace mrg #endif qpid-cpp-store-debian-0.16/lib/JournalImpl.cpp0000644000176300017630000005602311732624457020262 0ustar cajuscajus/* Copyright (c) 2007, 2008, 2009, 2010 Red Hat, Inc. This file is part of the Qpid async store library msgstore.so. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA The GNU Lesser General Public License is available in the file COPYING. */ #include "JournalImpl.h" #include "jrnl/jerrno.hpp" #include "jrnl/jexception.hpp" #include "qpid/log/Statement.h" #include "qpid/management/ManagementAgent.h" #include "qmf/com/redhat/rhm/store/ArgsJournalExpand.h" #include "qmf/com/redhat/rhm/store/EventCreated.h" #include "qmf/com/redhat/rhm/store/EventEnqThresholdExceeded.h" #include "qmf/com/redhat/rhm/store/EventFull.h" #include "qmf/com/redhat/rhm/store/EventRecovered.h" #include "qpid/sys/Monitor.h" #include "qpid/sys/Timer.h" #include "StoreException.h" using namespace mrg::msgstore; using namespace mrg::journal; using qpid::management::ManagementAgent; namespace _qmf = qmf::com::redhat::rhm::store; InactivityFireEvent::InactivityFireEvent(JournalImpl* p, const qpid::sys::Duration timeout): qpid::sys::TimerTask(timeout, "JournalInactive:"+p->id()), _parent(p) {} void InactivityFireEvent::fire() { qpid::sys::Mutex::ScopedLock sl(_ife_lock); if (_parent) _parent->flushFire(); } GetEventsFireEvent::GetEventsFireEvent(JournalImpl* p, const qpid::sys::Duration timeout): qpid::sys::TimerTask(timeout, "JournalGetEvents:"+p->id()), _parent(p) {} void GetEventsFireEvent::fire() { qpid::sys::Mutex::ScopedLock sl(_gefe_lock); if (_parent) _parent->getEventsFire(); } JournalImpl::JournalImpl(qpid::sys::Timer& timer_, const std::string& journalId, const std::string& journalDirectory, const std::string& journalBaseFilename, const qpid::sys::Duration getEventsTimeout, const qpid::sys::Duration flushTimeout, qpid::management::ManagementAgent* a, DeleteCallback onDelete): jcntl(journalId, journalDirectory, journalBaseFilename), timer(timer_), getEventsTimerSetFlag(false), lastReadRid(0), writeActivityFlag(false), flushTriggeredFlag(true), _xidp(0), _datap(0), _dlen(0), _dtok(), _external(false), _mgmtObject(0), deleteCallback(onDelete) { getEventsFireEventsPtr = new GetEventsFireEvent(this, getEventsTimeout); inactivityFireEventPtr = new InactivityFireEvent(this, flushTimeout); { timer.start(); timer.add(inactivityFireEventPtr); } initManagement(a); log(LOG_NOTICE, "Created"); std::ostringstream oss; oss << "Journal directory = \"" << journalDirectory << "\"; Base file name = \"" << journalBaseFilename << "\""; log(LOG_DEBUG, oss.str()); } JournalImpl::~JournalImpl() { if (deleteCallback) deleteCallback(*this); if (_init_flag && !_stop_flag){ try { stop(true); } // NOTE: This will *block* until all outstanding disk aio calls are complete! catch (const jexception& e) { log(LOG_ERROR, e.what()); } } getEventsFireEventsPtr->cancel(); inactivityFireEventPtr->cancel(); free_read_buffers(); if (_mgmtObject != 0) { _mgmtObject->resourceDestroy(); _mgmtObject = 0; } log(LOG_NOTICE, "Destroyed"); } void JournalImpl::initManagement(qpid::management::ManagementAgent* a) { _agent = a; if (_agent != 0) { _mgmtObject = new _qmf::Journal (_agent, (qpid::management::Manageable*) this); _mgmtObject->set_name(_jid); _mgmtObject->set_directory(_jdir.dirname()); _mgmtObject->set_baseFileName(_base_filename); _mgmtObject->set_readPageSize(JRNL_RMGR_PAGE_SIZE * JRNL_SBLK_SIZE * JRNL_DBLK_SIZE); _mgmtObject->set_readPages(JRNL_RMGR_PAGES); // The following will be set on initialize(), but being properties, these must be set to 0 in the meantime _mgmtObject->set_initialFileCount(0); _mgmtObject->set_dataFileSize(0); _mgmtObject->set_currentFileCount(0); _mgmtObject->set_writePageSize(0); _mgmtObject->set_writePages(0); _agent->addObject(_mgmtObject, 0, true); } } void JournalImpl::initialize(const u_int16_t num_jfiles, const bool auto_expand, const u_int16_t ae_max_jfiles, const u_int32_t jfsize_sblks, const u_int16_t wcache_num_pages, const u_int32_t wcache_pgsize_sblks, mrg::journal::aio_callback* const cbp) { std::ostringstream oss; oss << "Initialize; num_jfiles=" << num_jfiles << " jfsize_sblks=" << jfsize_sblks; oss << " wcache_pgsize_sblks=" << wcache_pgsize_sblks; oss << " wcache_num_pages=" << wcache_num_pages; log(LOG_DEBUG, oss.str()); jcntl::initialize(num_jfiles, auto_expand, ae_max_jfiles, jfsize_sblks, wcache_num_pages, wcache_pgsize_sblks, cbp); log(LOG_DEBUG, "Initialization complete"); if (_mgmtObject != 0) { _mgmtObject->set_initialFileCount(_lpmgr.num_jfiles()); _mgmtObject->set_autoExpand(_lpmgr.is_ae()); _mgmtObject->set_currentFileCount(_lpmgr.num_jfiles()); _mgmtObject->set_maxFileCount(_lpmgr.ae_max_jfiles()); _mgmtObject->set_dataFileSize(_jfsize_sblks * JRNL_SBLK_SIZE * JRNL_DBLK_SIZE); _mgmtObject->set_writePageSize(wcache_pgsize_sblks * JRNL_SBLK_SIZE * JRNL_DBLK_SIZE); _mgmtObject->set_writePages(wcache_num_pages); } if (_agent != 0) _agent->raiseEvent(qmf::com::redhat::rhm::store::EventCreated(_jid, _jfsize_sblks * JRNL_SBLK_SIZE * JRNL_DBLK_SIZE, _lpmgr.num_jfiles()), qpid::management::ManagementAgent::SEV_NOTE); } void JournalImpl::recover(const u_int16_t num_jfiles, const bool auto_expand, const u_int16_t ae_max_jfiles, const u_int32_t jfsize_sblks, const u_int16_t wcache_num_pages, const u_int32_t wcache_pgsize_sblks, mrg::journal::aio_callback* const cbp, boost::ptr_list* prep_tx_list_ptr, u_int64_t& highest_rid, u_int64_t queue_id) { std::ostringstream oss1; oss1 << "Recover; num_jfiles=" << num_jfiles << " jfsize_sblks=" << jfsize_sblks; oss1 << " queue_id = 0x" << std::hex << queue_id << std::dec; oss1 << " wcache_pgsize_sblks=" << wcache_pgsize_sblks; oss1 << " wcache_num_pages=" << wcache_num_pages; log(LOG_DEBUG, oss1.str()); if (_mgmtObject != 0) { _mgmtObject->set_initialFileCount(_lpmgr.num_jfiles()); _mgmtObject->set_autoExpand(_lpmgr.is_ae()); _mgmtObject->set_currentFileCount(_lpmgr.num_jfiles()); _mgmtObject->set_maxFileCount(_lpmgr.ae_max_jfiles()); _mgmtObject->set_dataFileSize(_jfsize_sblks * JRNL_SBLK_SIZE * JRNL_DBLK_SIZE); _mgmtObject->set_writePageSize(wcache_pgsize_sblks * JRNL_SBLK_SIZE * JRNL_DBLK_SIZE); _mgmtObject->set_writePages(wcache_num_pages); } if (prep_tx_list_ptr) { // Create list of prepared xids std::vector prep_xid_list; for (msgstore::PreparedTransaction::list::iterator i = prep_tx_list_ptr->begin(); i != prep_tx_list_ptr->end(); i++) { prep_xid_list.push_back(i->xid); } jcntl::recover(num_jfiles, auto_expand, ae_max_jfiles, jfsize_sblks, wcache_num_pages, wcache_pgsize_sblks, cbp, &prep_xid_list, highest_rid); } else { jcntl::recover(num_jfiles, auto_expand, ae_max_jfiles, jfsize_sblks, wcache_num_pages, wcache_pgsize_sblks, cbp, 0, highest_rid); } // Populate PreparedTransaction lists from _tmap if (prep_tx_list_ptr) { for (msgstore::PreparedTransaction::list::iterator i = prep_tx_list_ptr->begin(); i != prep_tx_list_ptr->end(); i++) { txn_data_list tdl = _tmap.get_tdata_list(i->xid); // tdl will be empty if xid not found for (tdl_itr tdl_itr = tdl.begin(); tdl_itr < tdl.end(); tdl_itr++) { if (tdl_itr->_enq_flag) { // enqueue op i->enqueues->add(queue_id, tdl_itr->_rid); } else { // dequeue op i->dequeues->add(queue_id, tdl_itr->_drid); } } } } std::ostringstream oss2; oss2 << "Recover phase 1 complete; highest rid found = 0x" << std::hex << highest_rid; oss2 << std::dec << "; emap.size=" << _emap.size() << "; tmap.size=" << _tmap.size(); oss2 << "; journal now read-only."; log(LOG_DEBUG, oss2.str()); if (_mgmtObject != 0) { _mgmtObject->inc_recordDepth(_emap.size()); _mgmtObject->inc_enqueues(_emap.size()); _mgmtObject->inc_txn(_tmap.size()); _mgmtObject->inc_txnEnqueues(_tmap.enq_cnt()); _mgmtObject->inc_txnDequeues(_tmap.deq_cnt()); } } void JournalImpl::recover_complete() { jcntl::recover_complete(); log(LOG_DEBUG, "Recover phase 2 complete; journal now writable."); if (_agent != 0) _agent->raiseEvent(qmf::com::redhat::rhm::store::EventRecovered(_jid, _jfsize_sblks * JRNL_SBLK_SIZE * JRNL_DBLK_SIZE, _lpmgr.num_jfiles(), _emap.size(), _tmap.size(), _tmap.enq_cnt(), _tmap.deq_cnt()), qpid::management::ManagementAgent::SEV_NOTE); } //#define MAX_AIO_SLEEPS 1000000 // tot: ~10 sec //#define AIO_SLEEP_TIME_US 10 // 0.01 ms // Return true if content is recovered from store; false if content is external and must be recovered from an external store. // Throw exception for all errors. bool JournalImpl::loadMsgContent(u_int64_t rid, std::string& data, size_t length, size_t offset) { qpid::sys::Mutex::ScopedLock sl(_read_lock); if (_dtok.rid() != rid) { // Free any previous msg free_read_buffers(); // Last read encountered out-of-order rids, check if this rid is in that list bool oooFlag = false; for (std::vector::const_iterator i=oooRidList.begin(); i!=oooRidList.end() && !oooFlag; i++) { if (*i == rid) { oooFlag = true; } } // TODO: This is a brutal approach - very inefficient and slow. Rather introduce a system of remembering // jumpover points and allow the read to jump back to the first known jumpover point - but this needs // a mechanism in rrfc to accomplish it. Also helpful is a struct containing a journal address - a // combination of lid/offset. // NOTE: The second part of the if stmt (rid < lastReadRid) is required to handle browsing. if (oooFlag || rid < lastReadRid) { _rmgr.invalidate(); oooRidList.clear(); } _dlen = 0; _dtok.reset(); _dtok.set_wstate(DataTokenImpl::ENQ); _dtok.set_rid(0); _external = false; size_t xlen = 0; bool transient = false; bool done = false; bool rid_found = false; while (!done) { iores res = read_data_record(&_datap, _dlen, &_xidp, xlen, transient, _external, &_dtok); switch (res) { case mrg::journal::RHM_IORES_SUCCESS: if (_dtok.rid() != rid) { // Check if this is an out-of-order rid that may impact next read if (_dtok.rid() > rid) oooRidList.push_back(_dtok.rid()); free_read_buffers(); // Reset data token for next read _dlen = 0; _dtok.reset(); _dtok.set_wstate(DataTokenImpl::ENQ); _dtok.set_rid(0); } else { rid_found = _dtok.rid() == rid; lastReadRid = rid; done = true; } break; case mrg::journal::RHM_IORES_PAGE_AIOWAIT: if (get_wr_events(&_aio_cmpl_timeout) == journal::jerrno::AIO_TIMEOUT) { std::stringstream ss; ss << "read_data_record() returned " << mrg::journal::iores_str(res); ss << "; timed out waiting for page to be processed."; throw jexception(mrg::journal::jerrno::JERR__TIMEOUT, ss.str().c_str(), "JournalImpl", "loadMsgContent"); } break; default: std::stringstream ss; ss << "read_data_record() returned " << mrg::journal::iores_str(res); throw jexception(mrg::journal::jerrno::JERR__UNEXPRESPONSE, ss.str().c_str(), "JournalImpl", "loadMsgContent"); } } if (!rid_found) { std::stringstream ss; ss << "read_data_record() was unable to find rid 0x" << std::hex << rid << std::dec; ss << " (" << rid << "); last rid found was 0x" << std::hex << _dtok.rid() << std::dec; ss << " (" << _dtok.rid() << ")"; throw jexception(mrg::journal::jerrno::JERR__RECNFOUND, ss.str().c_str(), "JournalImpl", "loadMsgContent"); } } if (_external) return false; u_int32_t hdr_offs = qpid::framing::Buffer(static_cast(_datap), sizeof(u_int32_t)).getLong() + sizeof(u_int32_t); if (hdr_offs + offset + length > _dlen) { data.append((const char*)_datap + hdr_offs + offset, _dlen - hdr_offs - offset); } else { data.append((const char*)_datap + hdr_offs + offset, length); } return true; } void JournalImpl::enqueue_data_record(const void* const data_buff, const size_t tot_data_len, const size_t this_data_len, data_tok* dtokp, const bool transient) { handleIoResult(jcntl::enqueue_data_record(data_buff, tot_data_len, this_data_len, dtokp, transient)); if (_mgmtObject != 0) { _mgmtObject->inc_enqueues(); _mgmtObject->inc_recordDepth(); } } void JournalImpl::enqueue_extern_data_record(const size_t tot_data_len, data_tok* dtokp, const bool transient) { handleIoResult(jcntl::enqueue_extern_data_record(tot_data_len, dtokp, transient)); if (_mgmtObject != 0) { _mgmtObject->inc_enqueues(); _mgmtObject->inc_recordDepth(); } } void JournalImpl::enqueue_txn_data_record(const void* const data_buff, const size_t tot_data_len, const size_t this_data_len, data_tok* dtokp, const std::string& xid, const bool transient) { bool txn_incr = _mgmtObject != 0 ? _tmap.in_map(xid) : false; handleIoResult(jcntl::enqueue_txn_data_record(data_buff, tot_data_len, this_data_len, dtokp, xid, transient)); if (_mgmtObject != 0) { if (!txn_incr) // If this xid was not in _tmap, it will be now... _mgmtObject->inc_txn(); _mgmtObject->inc_enqueues(); _mgmtObject->inc_txnEnqueues(); _mgmtObject->inc_recordDepth(); } } void JournalImpl::enqueue_extern_txn_data_record(const size_t tot_data_len, data_tok* dtokp, const std::string& xid, const bool transient) { bool txn_incr = _mgmtObject != 0 ? _tmap.in_map(xid) : false; handleIoResult(jcntl::enqueue_extern_txn_data_record(tot_data_len, dtokp, xid, transient)); if (_mgmtObject != 0) { if (!txn_incr) // If this xid was not in _tmap, it will be now... _mgmtObject->inc_txn(); _mgmtObject->inc_enqueues(); _mgmtObject->inc_txnEnqueues(); _mgmtObject->inc_recordDepth(); } } void JournalImpl::dequeue_data_record(data_tok* const dtokp, const bool txn_coml_commit) { handleIoResult(jcntl::dequeue_data_record(dtokp, txn_coml_commit)); if (_mgmtObject != 0) { _mgmtObject->inc_dequeues(); _mgmtObject->inc_txnDequeues(); _mgmtObject->dec_recordDepth(); } } void JournalImpl::dequeue_txn_data_record(data_tok* const dtokp, const std::string& xid, const bool txn_coml_commit) { bool txn_incr = _mgmtObject != 0 ? _tmap.in_map(xid) : false; handleIoResult(jcntl::dequeue_txn_data_record(dtokp, xid, txn_coml_commit)); if (_mgmtObject != 0) { if (!txn_incr) // If this xid was not in _tmap, it will be now... _mgmtObject->inc_txn(); _mgmtObject->inc_dequeues(); _mgmtObject->inc_txnDequeues(); _mgmtObject->dec_recordDepth(); } } void JournalImpl::txn_abort(data_tok* const dtokp, const std::string& xid) { handleIoResult(jcntl::txn_abort(dtokp, xid)); if (_mgmtObject != 0) { _mgmtObject->dec_txn(); _mgmtObject->inc_txnAborts(); } } void JournalImpl::txn_commit(data_tok* const dtokp, const std::string& xid) { handleIoResult(jcntl::txn_commit(dtokp, xid)); if (_mgmtObject != 0) { _mgmtObject->dec_txn(); _mgmtObject->inc_txnCommits(); } } void JournalImpl::stop(bool block_till_aio_cmpl) { InactivityFireEvent* ifep = dynamic_cast(inactivityFireEventPtr.get()); assert(ifep); // dynamic_cast can return null if the cast fails ifep->cancel(); jcntl::stop(block_till_aio_cmpl); if (_mgmtObject != 0) { _mgmtObject->resourceDestroy(); _mgmtObject = 0; } } iores JournalImpl::flush(const bool block_till_aio_cmpl) { const iores res = jcntl::flush(block_till_aio_cmpl); { qpid::sys::Mutex::ScopedLock sl(_getf_lock); if (_wmgr.get_aio_evt_rem() && !getEventsTimerSetFlag) { setGetEventTimer(); } } return res; } void JournalImpl::log(mrg::journal::log_level ll, const std::string& log_stmt) const { log(ll, log_stmt.c_str()); } void JournalImpl::log(mrg::journal::log_level ll, const char* const log_stmt) const { switch (ll) { case LOG_TRACE: QPID_LOG(trace, "Journal \"" << _jid << "\": " << log_stmt); break; case LOG_DEBUG: QPID_LOG(debug, "Journal \"" << _jid << "\": " << log_stmt); break; case LOG_INFO: QPID_LOG(info, "Journal \"" << _jid << "\": " << log_stmt); break; case LOG_NOTICE: QPID_LOG(notice, "Journal \"" << _jid << "\": " << log_stmt); break; case LOG_WARN: QPID_LOG(warning, "Journal \"" << _jid << "\": " << log_stmt); break; case LOG_ERROR: QPID_LOG(error, "Journal \"" << _jid << "\": " << log_stmt); break; case LOG_CRITICAL: QPID_LOG(critical, "Journal \"" << _jid << "\": " << log_stmt); break; } } void JournalImpl::getEventsFire() { qpid::sys::Mutex::ScopedLock sl(_getf_lock); getEventsTimerSetFlag = false; if (_wmgr.get_aio_evt_rem()) { jcntl::get_wr_events(0); } if (_wmgr.get_aio_evt_rem()) { setGetEventTimer(); } } void JournalImpl::flushFire() { if (writeActivityFlag) { writeActivityFlag = false; flushTriggeredFlag = false; } else { if (!flushTriggeredFlag) { flush(); flushTriggeredFlag = true; } } inactivityFireEventPtr->setupNextFire(); { timer.add(inactivityFireEventPtr); } } void JournalImpl::wr_aio_cb(std::vector& dtokl) { for (std::vector::const_iterator i=dtokl.begin(); i!=dtokl.end(); i++) { DataTokenImpl* dtokp = static_cast(*i); if (/*!is_stopped() &&*/ dtokp->getSourceMessage()) { switch (dtokp->wstate()) { case data_tok::ENQ: dtokp->getSourceMessage()->enqueueComplete(); break; case data_tok::DEQ: /* Don't need to signal until we have a way to ack completion of dequeue in AMQP dtokp->getSourceMessage()->dequeueComplete(); if ( dtokp->getSourceMessage()->isDequeueComplete() ) // clear id after last dequeue dtokp->getSourceMessage()->setPersistenceId(0); */ break; default: ; } } dtokp->release(); } } void JournalImpl::rd_aio_cb(std::vector& /*pil*/) {} void JournalImpl::free_read_buffers() { if (_xidp) { ::free(_xidp); _xidp = 0; _datap = 0; } else if (_datap) { ::free(_datap); _datap = 0; } } void JournalImpl::handleIoResult(const iores r) { writeActivityFlag = true; switch (r) { case mrg::journal::RHM_IORES_SUCCESS: return; case mrg::journal::RHM_IORES_ENQCAPTHRESH: { std::ostringstream oss; oss << "Enqueue capacity threshold exceeded on queue \"" << _jid << "\"."; log(LOG_WARN, oss.str()); if (_agent != 0) _agent->raiseEvent(qmf::com::redhat::rhm::store::EventEnqThresholdExceeded(_jid, "Journal enqueue capacity threshold exceeded"), qpid::management::ManagementAgent::SEV_WARN); THROW_STORE_FULL_EXCEPTION(oss.str()); } case mrg::journal::RHM_IORES_FULL: { std::ostringstream oss; oss << "Journal full on queue \"" << _jid << "\"."; log(LOG_CRITICAL, oss.str()); if (_agent != 0) _agent->raiseEvent(qmf::com::redhat::rhm::store::EventFull(_jid, "Journal full"), qpid::management::ManagementAgent::SEV_ERROR); THROW_STORE_FULL_EXCEPTION(oss.str()); } default: { std::ostringstream oss; oss << "Unexpected I/O response (" << mrg::journal::iores_str(r) << ") on queue " << _jid << "\"."; log(LOG_ERROR, oss.str()); THROW_STORE_FULL_EXCEPTION(oss.str()); } } } qpid::management::Manageable::status_t JournalImpl::ManagementMethod (uint32_t methodId, qpid::management::Args& /*args*/, std::string& /*text*/) { Manageable::status_t status = Manageable::STATUS_UNKNOWN_METHOD; switch (methodId) { case _qmf::Journal::METHOD_EXPAND : //_qmf::ArgsJournalExpand& eArgs = (_qmf::ArgsJournalExpand&) args; // Implement "expand" using eArgs.i_by (expand-by argument) status = Manageable::STATUS_NOT_IMPLEMENTED; break; } return status; } qpid-cpp-store-debian-0.16/ChangeLog0000644000176300017630000001157710663056723016330 0ustar cajuscajus2007-08-22 Alan Conway * tests/run-unit-tests: added LD_PRELOAD libdlclose. Keeps DllPlugInTester symbols in memory for valgrind output. * tests/SimpleTest.cpp: Fixed memory leak in test. * tests/Makefile.am: Added libdlclose for CppUnit tests. * tests/.vg-supp: Copied latest qpid suppressions. 2007-04-15 Alan Conway * configure.ac: build against installed or checked-out qpid. - check for installed qpid headers/libs - --with-qpid-checkout option, use checkout instead of installed.y * README: Update build instructions. * tests/Makeifile.am: Use qpid info from configure. * tests/*.cpp.h: #include to include from installed. * tests/system_test.sh: Use qpid info from configure. * tests/test_plugin.h: Local copy of qpid's test_plugin.h file. * lib/Makefile.am: Use qpid info from configure. * lib/*.cpp.h: #include to include from installed. * Makefile.am Add rpmbuild targets. * rhm.spec.in: RPM spec file. 2007-02-07 Alan Conway * bootstrap: automake --add-missing to install missing build-aux files. * build-aux: added, required by autoconf/automake. 2007-01-26 Jim Meyering * tests/setup (trap): Kill with "-2", not "-SIGINT", since the former is more portable. 2007-01-18 Jim Meyering * options.mk: Remove this file. No longer used. 2007-01-15 Jim Meyering Instrument all tests so that they are run via valgrind: check for both errors and leaks. * configure.ac: Check for valgrind. * tests/Makefile.am (TESTS_ENVIRONMENT): Export VALGRIND. * tests/.vg-supp: New file, exempting known leaks on rawhide and Debian/unstable. Leaks on the latter system all seem to originate in cppunit. Some of the rawhide ones look suspicious. * tests/run-unit-tests: Use new "setup" file. Invoke DllPlugInTester via $vg (aka valgrind). Refer to the source directory using $pwd, since we're now running from a temporary subdirectory. * tests/system_test.sh: Remove traps. That is now done by "setup". [VERBOSE]: Print qpidd --version. Invoke qpidd via $vg. Add a kludgey "sleep 3", because it can take a while for libtool to start valgrind to start qpidd, in the background. Ideally, the python script would simply sleep-0.3-and-retry for a couple seconds, upon failure of the initial connection attempt. * tests/setup: New file. 2007-01-10 Jim Meyering system_test.sh didn't work at all (would seg-fault) * lib/BdbMessageStore.cpp: Comment out all attempts to write diagnostics to std::cout. A shared library must not do that, and this one would segfault when invoked via qpidd -s .... (~BdbMessageStore): Rather than simply writing to std::cout, re-throw any close-provoked exception. 2007-01-09 Jim Meyering * tests/system_test.sh (xml_spec): Reflect name change: s/amqp-8.0.xml/amqp.0-8.xml/. 2006-12-22 Jim Meyering * lib/Makefile.am (libbdbstore_la_LIBADD): Put libqpidcommon.la here, not in LDADD. (LDADD): Remove. * configure.ac: Also check for libdb_cxx-4.6, 4.5, and 4.4. Rawhide is at 4.5. * tests/system_test.sh: Rewrite to ensure that it doesn't leave a running qpidd process or temporary files in the current directory. * tests/Makefile.am (TESTS_ENVIRONMENT): Add these: abs_builddir, abs_srcdir. * tests/system_test.sh (xml_spec): Adjust to work in new framework. Check, up front, for xml spec file. * Makefile: Remove. Adapt to updated ->deliver signature. * tests/SimpleTest.cpp: Include . (CppUnit::TestCase): Adjust callers by adding an argument: &(qpid::framing::highestProtocolVersion). 2006-12-21 Jim Meyering * lib/BdbMessageStore.cpp: Avoid some warnings: (BdbMessageStore::getRecordSize): Remove decl of unused "status". (BdbMessageStore::recoverQueues): Remove decl of unused "status". (BdbMessageStore::recoverQueues): Declare maxQueueId to be of type u_int64_t, not int, to avoid the warning about "comparison between signed and unsigned integer expressions". (BdbMessageStore::recoverMessages): Likewise, but for maxMessageId. Avoid three warnings like this: error: ISO C++ forbids variable-size array 'buffer' by using 'new/delete' instead. * configure.ac: Determine how to set DB_CXX_HEADER_PREFIX, i.e., to "db/" or to "", used to include or . * lib/Makefile.am (db-inc.h): New generated file. * lib/BdbMessageStore.h (qpid): Include the generated file, "db-inc.h". * m4: New directory. * build-aux: New directory. * tests/run-unit-tests: New file. * configure.ac, bootstrap: New files. * Makefile.am, lib/Makefile.am, tests/Makefile.am: New files. * tests: New directory, renamed from test/. * test: Remove directory. Renamed to tests/. * src: Remove directory. Renamed to lib/. * lib: New directory. Renamed from src/. Begin autoconfiscation. qpid-cpp-store-debian-0.16/rhel4-support/0000755000176300017630000000000011761423612017266 5ustar cajuscajusqpid-cpp-store-debian-0.16/rhel4-support/Makefile0000644000176300017630000000203711142067437020732 0ustar cajuscajus# Copyright (c) 2007, 2008 Red Hat, Inc. # # This file is part of the Qpid async store library msgstore.so. # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 # USA # # The GNU Lesser General Public License is available in the file COPYING. .PHONY: default apply default: @echo "Run 'make apply' to patch the source for RHEL4" apply: rhel4.patch patch -d .. -p0 < rhel4.patch qpid-cpp-store-debian-0.16/rhel4-support/rhel4.patch0000644000176300017630000001222711433540772021334 0ustar cajuscajusIndex: tests/.valgrind.supp =================================================================== --- tests/.valgrind.supp (revision 4214) +++ tests/.valgrind.supp (working copy) @@ -1,33 +1,74 @@ { - Benign error in libcpg. - Memcheck:Param - socketcall.sendmsg(msg.msg_iov[i]) - obj:*/libpthread-2.5.so - obj:*/libcpg.so.2.0.0 + + Memcheck:Leak + fun:_Znw? + fun:_ZNSs4_Rep9_S_createE??RKSaIcE + obj:*/libstdc++.so.6.0.3 + fun:_ZNSsC1EPKcRKSaIcE + fun:_ZN4qpid34options_description_less_easy_initclEPKcPKN5boost15program_options14value_semanticES2_ + fun:_ZN4qpid3log5posix11SinkOptionsC1ERKSs + fun:_ZN4qpid3log11SinkOptions6createERKSs + fun:_ZN4qpid3log7OptionsC1ERKSsS3_ + fun:_ZN4qpid3log6LoggerC1Ev } - { - Uninitialised value problem in _dl_relocate (F7, F8) - Memcheck:Cond - fun:_dl_relocate_object - fun:*dl_* + + Memcheck:Leak + fun:_Znwm + fun:_ZN5boost15program_options29options_description_easy_initclEPKcPKNS0_14value_semanticES3_ + fun:_ZN4qpid34options_description_less_easy_initclEPKcPKN5boost15program_options14value_semanticES2_ + fun:_ZN4qpid3log5posix11SinkOptionsC1ERKSs + fun:_ZN4qpid3log11SinkOptions6createERKSs + fun:_ZN4qpid3log7OptionsC1ERKSsS3_ + fun:_ZN4qpid3log6LoggerC1Ev } - { - False "possibly leaked" in boost program_options - global std::string var. + Memcheck:Leak - fun:_Znwj - fun:_ZNSs4_Rep9_S_createEjjRKSaIcE - obj:/usr/lib/libstdc++.so.6.0.8 - fun:_ZNSsC1EPKcRKSaIcE - obj:/usr/lib/libboost_program_options.so.1.33.1 + fun:_Znwm + fun:_ZN5boost15program_options19options_description3addERKS1_ + fun:_ZN4qpid3log7OptionsC1ERKSsS3_ + fun:_ZN4qpid3log6LoggerC1Ev } - { - Probable use after delete problem in boost::unit_test - Memcheck:Addr8 - fun:_ZN5boost9unit_test14framework_implD1Ev - fun:exit - fun:(below main) + + Memcheck:Leak + fun:_Znw? + fun:_ZNSt6vectorISsSaISsEE13_M_insert_auxEN9__gnu_cxx17__normal_iteratorIPSsS1_EERKSs + fun:_ZN4qpid7Options14register_namesESs + fun:_ZN4qpid34options_description_less_easy_initclEPKcPKN5boost15program_options14value_semanticES2_ + fun:_ZN4qpid3log7OptionsC1ERKSsS3_ + fun:_ZN4qpid3log6LoggerC1Ev } +{ + + Memcheck:Leak + fun:_Znwm + fun:_ZN5boost15program_options19options_description3addERKS1_ + fun:_ZN4qpid3log7OptionsC1ERKSsS3_ + fun:_ZN4qpid3log6LoggerC1Ev +} +{ + + Memcheck:Leak + fun:_Znwm + fun:_ZNSt6vectorISsSaISsEE13_M_insert_auxEN9__gnu_cxx17__normal_iteratorIPSsS1_EERKSs + fun:_ZN4qpid7Options14register_namesESs + fun:_ZN4qpid34options_description_less_easy_initclEPKcPKN5boost15program_options14value_semanticES2_ + fun:_ZN4qpid3log7OptionsC1ERKSsS3_ + fun:_ZN4qpid3log6LoggerC1Ev +} +{ + + Memcheck:Leak + fun:calloc + fun:_dl_allocate_tls + fun:pthread_create@@GLIBC_2.* +} +{ + + Memcheck:Leak + fun:_Znwm + fun:_ZN5boost9unit_test9test_caseC2ENS0_13basic_cstringIKcEEbmb +} Index: configure.ac =================================================================== --- configure.ac (revision 4214) +++ configure.ac (working copy) @@ -62,7 +62,6 @@ # -Wshadow - warns about boost headers. if test "$enable_WARNINGS" = yes; then - gl_COMPILER_FLAGS(-Werror) gl_COMPILER_FLAGS(-pedantic) gl_COMPILER_FLAGS(-Wall) gl_COMPILER_FLAGS(-Wextra) @@ -71,7 +70,6 @@ gl_COMPILER_FLAGS(-Wcast-qual) gl_COMPILER_FLAGS(-Wcast-align) gl_COMPILER_FLAGS(-Wno-long-long) - gl_COMPILER_FLAGS(-Wvolatile-register-var) gl_COMPILER_FLAGS(-Winvalid-pch) gl_COMPILER_FLAGS(-Wno-system-headers) AC_SUBST([WARNING_CFLAGS], [$COMPILER_FLAGS]) Index: lib/MessageStoreImpl.cpp =================================================================== --- lib/MessageStoreImpl.cpp (revision 4214) +++ lib/MessageStoreImpl.cpp (working copy) @@ -355,13 +355,6 @@ tplStorePtr.reset(new TplJournalImpl(timer, "TplStore", getTplBaseDir(), "tpl", defJournalGetEventsTimeout, defJournalFlushTimeout, agent)); isInit = true; } catch (const DbException& e) { - if (e.get_errno() == DB_VERSION_MISMATCH) - { - QPID_LOG(error, "Database environment mismatch: This version of db4 does not match that which created the store database.: " << e.what()); - THROW_STORE_EXCEPTION_2("Database environment mismatch: This version of db4 does not match that which created the store database. " - "(If recovery is not important, delete the contents of the store directory. Otherwise, try upgrading the database using " - "db_upgrade or using db_recover - but the db4-utils package must also be installed to use these utilities.)", e); - } QPID_LOG(error, "BDB exception occurred while initializing store: " << e.what()); if (bdbRetryCnt >= retryMax) THROW_STORE_EXCEPTION_2("BDB exception occurred while initializing store", e); qpid-cpp-store-debian-0.16/rhel4-support/README0000644000176300017630000000043311044376475020156 0ustar cajuscajusTo compile on RHEL4, run: make apply This will apply all necessary patches. Change dirs back to cpp and run ./bootstrap and ./configure as ususal. Make sure that if ./configure is run with --with-qpid-checkout, it points to a version of qpid which has also been patched for RHEL4. qpid-cpp-store-debian-0.16/build-aux/0000755000176300017630000000000011761423613016431 5ustar cajuscajusqpid-cpp-store-debian-0.16/docs/0000755000176300017630000000000011761423605015470 5ustar cajuscajusqpid-cpp-store-debian-0.16/docs/jrnl_tmpl.dox0000644000176300017630000015526411476244624020227 0ustar cajuscajus# Doxyfile 1.5.4 # Copyright (c) 2007, 2008 Red Hat, Inc. # # This file is part of the Qpid async store library msgstore.so. # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 # USA # # The GNU Lesser General Public License is available in the file COPYING. # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project # # All text after a hash (#) is considered a comment and will be ignored # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" ") #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file that # follow. The default is UTF-8 which is also the encoding used for all text before # the first occurrence of this tag. Doxygen uses libiconv (or the iconv built into # libc) for the transcoding. See http://www.gnu.org/software/libiconv for the list of # possible encodings. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded # by quotes) that should identify the project. PROJECT_NAME = "Qpid Asynchronous Store Library" # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create # 4096 sub-directories (in 2 levels) under the output directory of each output # format and will distribute the generated files over these directories. # Enabling this option can be useful when feeding doxygen a huge amount of # source files, where putting all generated files in the same directory would # otherwise cause performance problems for the file system. CREATE_SUBDIRS = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, # Croatian, Czech, Danish, Dutch, Finnish, French, German, Greek, Hungarian, # Italian, Japanese, Japanese-en (Japanese with English messages), Korean, # Korean-en, Lithuanian, Norwegian, Polish, Portuguese, Romanian, Russian, # Serbian, Slovak, Slovene, Spanish, Swedish, and Ukrainian. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator # that is used to form the text in various listings. Each string # in this list, if found as the leading text of the brief description, will be # stripped from the text and the result after processing the whole list, is # used as the annotated text. Otherwise, the brief description is used as-is. # If left blank, the following values are used ("$name" is automatically # replaced with the name of the entity): "The $name class" "The $name widget" # "The $name file" "is" "provides" "specifies" "contains" # "represents" "a" "an" "the" ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = YES # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user-defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the # path to strip. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of # the path mentioned in the documentation of a class, which tells # the reader which header file to include in order to use a class. # If left blank only the name of the header file containing the class # definition is used. Otherwise one should specify the include paths that # are normally passed to the compiler using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful is your file systems # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like regular Qt-style comments # (thus requiring an explicit @brief command for a brief description.) JAVADOC_AUTOBRIEF = NO # If the QT_AUTOBRIEF tag is set to YES then Doxygen will # interpret the first line (until the first dot) of a Qt-style # comment as the brief description. If set to NO, the comments # will behave just like regular Qt-style comments (thus requiring # an explicit \brief command for a brief description.) QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = NO # If the DETAILS_AT_TOP tag is set to YES then Doxygen # will output the detailed description near the top, like JavaDoc. # If set to NO, the detailed description appears after the member # documentation. DETAILS_AT_TOP = NO # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # re-implements. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce # a new page for each member. If set to NO, the documentation of a member will # be part of the file/class/namespace that contains it. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 4 # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C # sources only. Doxygen will then generate output that is more tailored for C. # For instance, some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = NO # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java # sources only. Doxygen will then generate output that is more tailored for Java. # For instance, namespaces will be presented as packages, qualified scopes # will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want to # include (a tag file for) the STL sources as input, then you should # set this tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); v.s. # func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. BUILTIN_STL_SUPPORT = YES # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. # Doxygen will parse them like normal C++ but will assume all classes use public # instead of private inheritance when no explicit protection keyword is present. SIP_SUPPORT = NO # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES (the default) to allow class member groups of # the same type (for instance a group of public functions) to be put as a # subgroup of that type (e.g. under the Public Functions section). Set it to # NO to prevent subgrouping. Alternatively, this can be done per class using # the \nosubgrouping command. SUBGROUPING = YES # When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct (or union) is # documented as struct with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically # be useful for C code where the coding convention is that all structs are # typedef'ed and only the typedef is referenced never the struct's name. TYPEDEF_HIDES_STRUCT = NO #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = YES # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = YES # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = YES # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. When set to YES local # methods, which are defined in the implementation section but not in # the interface are included in the documentation. # If set to NO (the default) only methods in the interface are included. EXTRACT_LOCAL_METHODS = YES # If this flag is set to YES, the members of anonymous namespaces will be extracted # and appear in the documentation as a namespace called 'anonymous_namespace{file}', # where file will be replaced with the base name of the file that contains the anonymous # namespace. By default anonymous namespace are hidden. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = YES # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the # brief documentation of file, namespace and class members alphabetically # by member name. If set to NO (the default) the members will appear in # declaration order. SORT_BRIEF_DOCS = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be # sorted by fully-qualified names, including namespaces. If set to # NO (the default), the class list will be sorted only by class name, # not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the # alphabetical list. SORT_BY_SCOPE_NAME = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if sectionname ... \endif. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or define consists of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and defines in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES # If the sources in your project are distributed over multiple directories # then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy # in the documentation. The default is NO. SHOW_DIRECTORIES = NO # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from the # version control system). Doxygen will invoke the program by executing (via # popen()) the command , where is the value of # the FILE_VERSION_FILTER tag, and is the name of an input file # provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. FILE_VERSION_FILTER = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = YES # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = YES # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be abled to get warnings for # functions that are documented, but have no documentation for their parameters # or return value. If set to NO (the default) doxygen will only warn about # wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = YES # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. Optionally the format may contain # $version, which will be replaced by the version of the file (if it could # be obtained via FILE_VERSION_FILTER) WARN_FORMAT = "$file:$line: $text " # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = ../lib \ ../lib/jrnl \ ../lib/jrnl2 \ ../perf \ ../tools # This tag can be used to specify the character encoding of the source files that # doxygen parses. Internally doxygen uses the UTF-8 encoding, which is also the default # input encoding. Doxygen uses libiconv (or the iconv built into libc) for the transcoding. # See http://www.gnu.org/software/libiconv for the list of possible encodings. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx # *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 FILE_PATTERNS = *.cpp \ *.hpp \ *.h # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = NO # The EXCLUDE tag can be used to specify files and/or directories that should # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used select whether or not files or # directories that are symbolic links (a Unix filesystem feature) are excluded # from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. Note that the wildcards are matched # against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the output. # The symbol name can be a fully qualified name, a word, or if the wildcard * is used, # a substring. Examples: ANamespace, AClass, AClass::ANamespace, ANamespace::*Test EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. If FILTER_PATTERNS is specified, this tag will be # ignored. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. Doxygen will compare the file name with each pattern and apply the # filter if there is a match. The filters are a list of the form: # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further # info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER # is applied to all files. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. # Note: To get rid of all source code in the generated output, make sure also # VERBATIM_HEADERS is set to NO. If you have enabled CALL_GRAPH or CALLER_GRAPH # then you must also enable this option. If you don't then doxygen will produce # a warning and turn it on anyway SOURCE_BROWSER = YES # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C and C++ comments will always remain visible. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES (the default) # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = YES # If the REFERENCES_RELATION tag is set to YES (the default) # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = YES # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will # link to the source code. Otherwise they will link to the documentstion. REFERENCES_LINK_SOURCE = YES # If the USE_HTAGS tag is set to YES then the references to source code # will point to the HTML generated by the htags(1) tool instead of doxygen # built-in source browser. The htags tool is part of GNU's global source # tagging system (see http://www.gnu.org/software/global/global.html). You # will need version 4.8.6 or higher. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = NO # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If the tag is left blank doxygen # will generate a default style sheet. Note that doxygen will try to copy # the style sheet file to the HTML output directory, so don't put your own # stylesheet in the HTML output directory as well, or it will be erased! HTML_STYLESHEET = # If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, # files or namespaces will be aligned in HTML using tables. If set to # NO a bullet list will be used. HTML_ALIGN_MEMBERS = YES # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compressed HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. For this to work a browser that supports # JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox # Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). HTML_DYNAMIC_SECTIONS = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output directory. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run # the HTML help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. TOC_EXPAND = NO # The DISABLE_INDEX tag can be used to turn on/off the condensed index at # top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. DISABLE_INDEX = NO # This tag can be used to set the number of enum values (range [1..20]) # that doxygen will group on one line in the generated HTML documentation. ENUM_VALUES_PER_LINE = 4 # If the GENERATE_TREEVIEW tag is set to YES, a side panel will be # generated containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, # Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are # probably better off using the HTML help feature. GENERATE_TREEVIEW = NO # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = YES # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, a4wide, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = a4wide # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = NO # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = NO # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO # If LATEX_HIDE_INDICES is set to YES then doxygen will not # include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimized for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load stylesheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = YES # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. GENERATE_XML = NO # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml # The XML_SCHEMA tag can be used to specify an XML schema, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_DTD = # If the XML_PROGRAMLISTING tag is set to YES Doxygen will # dump the program listings (including syntax highlighting # and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. This is useful # if you want to understand what is going on. On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = NO # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_DEFINED tags. EXPAND_ONLY_PREDEF = NO # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # in the INCLUDE_PATH (see below) will be search if a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. To prevent a macro definition from being # undefined via #undef or recursively expanded use the := operator # instead of the = operator. PREDEFINED = # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all function-like macros that are alone # on a line, have an all uppercase name, and do not end with a semicolon. Such # function macros are typically used for boiler-plate code, and will confuse # the parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- # The TAGFILES option can be used to specify one or more tagfiles. # Optionally an initial location of the external documentation # can be added for each tagfile. The format of a tag file without # this location is as follows: # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths or # URLs. If a location is present for each tag, the installdox tool # does not have to be run to correct the links. # Note that each tag file must have a unique name # (where the name does NOT include the path) # If a tag file is not located in the directory in which doxygen # is run, you must also specify the path to the tagfile here. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base # or super classes. Setting the tag to NO turns the diagrams off. Note that # this option is superseded by the HAVE_DOT option below. This is only a # fallback. It is recommended to install and use dot, since it yields more # powerful graphs. CLASS_DIAGRAMS = YES # You can define message sequence charts within doxygen comments using the \msc # command. Doxygen will then run the mscgen tool (see http://www.mcternan.me.uk/mscgen/) to # produce the chart and insert it in the documentation. The MSCGEN_PATH tag allows you to # specify the directory where the mscgen tool resides. If left empty the tool is assumed to # be found in the default search path. MSCGEN_PATH = # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = NO # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # the CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = YES # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = YES # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = NO # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = NO # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = YES # If the CALL_GRAPH, SOURCE_BROWSER and HAVE_DOT tags are set to YES then doxygen will # generate a call dependency graph for every global function or class method. # Note that enabling this option will significantly increase the time of a run. # So in most cases it will be better to enable call graphs for selected # functions only using the \callgraph command. CALL_GRAPH = NO # If the CALLER_GRAPH, SOURCE_BROWSER and HAVE_DOT tags are set to YES then doxygen will # generate a caller dependency graph for every global function or class method. # Note that enabling this option will significantly increase the time of a run. # So in most cases it will be better to enable caller graphs for selected # functions only using the \callergraph command. CALLER_GRAPH = NO # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = YES # If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are png, jpg, or gif # If left blank png will be used. DOT_IMAGE_FORMAT = png # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The MAX_DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of # nodes that will be shown in the graph. If the number of nodes in a graph # becomes larger than this value, doxygen will truncate the graph, which is # visualized by representing a node as a red box. Note that doxygen if the number # of direct children of the root node in a graph is already larger than # MAX_DOT_GRAPH_NOTES then the graph will not be shown at all. Also note # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. DOT_GRAPH_MAX_NODES = 50 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the # graphs generated by dot. A depth value of 3 means that only nodes reachable # from the root by following a path via at most 3 edges will be shown. Nodes # that lay further from the root node will be omitted. Note that setting this # option to 1 or 2 may greatly reduce the computation time needed for large # code bases. Also note that the size of a graph can be further restricted by # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. MAX_DOT_GRAPH_DEPTH = 0 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent # background. This is disabled by default, which results in a white background. # Warning: Depending on the platform used, enabling this option may lead to # badly anti-aliased labels on the edges of a graph (i.e. they become hard to # read). DOT_TRANSPARENT = NO # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) # support this, this feature is disabled by default. DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES #--------------------------------------------------------------------------- # Configuration::additions related to the search engine #--------------------------------------------------------------------------- # The SEARCHENGINE tag specifies whether or not a search engine should be # used. If set to NO the values of all tags below this one will be ignored. SEARCHENGINE = NO qpid-cpp-store-debian-0.16/docs/Makefile.am0000644000176300017630000000324311627756270017536 0ustar cajuscajus# Copyright (c) 2007, 2008 Red Hat, Inc. # # This file is part of the Qpid async store library msgstore.so. # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 # USA # # The GNU Lesser General Public License is available in the file COPYING. # # Run doxygen to generate HTML doc. # Generate dependency files so its rebuilt only when needed. # DOXYGEN = @DOXYGEN@ EXTRA_DIST = jrnl_tmpl.dox html man #rhmj.pdf rhmj.ps all: doxygen man: doxygen #rhmj: doxygen #rhmj: doxygen doxygen: if DOXYGEN @doxygen ${srcdir}/jrnl_tmpl.dox # FIXME: doxygen seems to create files that do not compile under latex on 64-bit # so the following section is disabled until this is sorted out. # @make -C latex # @cd latex; dvipdf refman.dvi refman.pdf # @cd latex; dvips refman.dvi -o refman.ps # @ln -fs latex/refman.pdf rhmj.pdf # @ln -fs latex/refman.ps rhmj.ps else @echo "Doxygen not found during configuration; documentation skipped." endif clean-local: @rm -f rhmj.pdf @rm -f rhmj.ps @rm -rf latex @rm -rf html @rm -rf man qpid-cpp-store-debian-0.16/Makefile.am0000644000176300017630000000346711477473140016611 0ustar cajuscajus# Copyright (c) 2007, 2008 Red Hat, Inc. # # This file is part of the Qpid async store library msgstore.so. # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 # USA # # The GNU Lesser General Public License is available in the file COPYING. AUTOMAKE_OPTIONS = 1.9.2 foreign ACLOCAL_AMFLAGS = -I m4 EXTRA_DIST = README etc/rhmd.conf DISTCHECK_CONFIGURE_FLAGS = --with-qpid-checkout=/home/kpvdr/mrg/qpid sysconf_DATA = etc/rhmd.conf SUBDIRS = lib tests tools docs # Update libtool, if needed. libtool: $(LIBTOOL_DEPS) $(SHELL) ./config.status --recheck # # Build RPMs from the distribution tarball. # RPMDIRS=rpm/BUILD rpm/RPMS rpm/SPECS rpm/SRPMS RPMMACROS=--define "_topdir @abs_builddir@/rpm" --define "_sourcedir @abs_builddir@" # Override this variable e.g. with -bs to produce srpm only RPMOPTS=-ba clean-local: -rm -rf $(RPMDIRS) .PHONY: rpmbuild rpmbuild: $(SPEC) dist-gzip mkdir -p $(RPMDIRS) rpmbuild $(RPMMACROS) $(RPMOPTS) rhm.spec if HAS_RPMLINT rpmlint `find rpm -name '*.rpm'` else @echo "WARNING: rpmlint not found, could not validate RPMs." endif check-long: all $(MAKE) -C tests check-long check-short: all $(MAKE) -C tests check-short qpid-cpp-store-debian-0.16/tests/0000755000176300017630000000000011761423044015677 5ustar cajuscajusqpid-cpp-store-debian-0.16/tests/run_long_python_tests0000755000176300017630000000167711361352000022273 0ustar cajuscajus#!/bin/bash # # Copyright (c) 2008, 2009 Red Hat, Inc. # # This file is part of the Qpid async store library msgstore.so. # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 # USA # # The GNU Lesser General Public License is available in the file COPYING. ./run_python_tests LONG_TEST qpid-cpp-store-debian-0.16/tests/tests_env.sh0000644000176300017630000001641311611361170020245 0ustar cajuscajus# Copyright (c) 2008, 2009 Red Hat, Inc. # # This file is part of the Qpid async store library msgstore.so. # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 # USA # # The GNU Lesser General Public License is available in the file COPYING. # --- Function definitions --- func_check_required_env () #------------------------- # Check that EITHER: # QPID_DIR is set (for running against svn QPID) # OR # QPID_PREFIX is set (for running against installed QPID # Will exit with error code 1 if neither of these is defined. # Params: None # Returns: 0 if env vars ok, 1 otherwise { if test -z "${QPID_DIR}" -a -z "${QPID_PREFIX}"; then # Try to find qpidd in the normal installed location if test -x /usr/sbin/qpidd; then QPID_PREFIX=/usr else echo "ERROR: Could not find installed Qpid" echo "Either of the following must be set in the environment for this script to run:" echo " QPID_DIR for running against a Qpid svn build" echo " QPID_PREFIX for running against an installed Qpid" return 1 fi fi return 0 } func_check_qpid_python () #------------------------ # Check that Qpid python environment is ok # Params: None # Returns: 0 if Python environment is ok; 1 otherwise { if ! python -c "import qpid" ; then cat < #include "MessageUtils.h" #include "StoreException.h" #include #include #include #include #include #include "qpid/log/Logger.h" #include "qpid/sys/Timer.h" qpid::sys::Timer timer; #define SET_LOG_LEVEL(level) \ qpid::log::Options opts(""); \ opts.selectors.clear(); \ opts.selectors.push_back(level); \ qpid::log::Logger::instance().configure(opts); using boost::intrusive_ptr; using boost::static_pointer_cast; using namespace qpid; using namespace qpid::broker; using namespace qpid::framing; using namespace mrg::msgstore; using namespace std; QPID_AUTO_TEST_SUITE(SimpleTest) const string test_filename("SimpleTest"); const char* tdp = getenv("TMP_DATA_DIR"); const string test_dir(tdp && strlen(tdp) > 0 ? tdp : "/tmp/SimpleTest"); // === Helper fns === struct DummyHandler : OutputHandler { std::vector frames; virtual void send(AMQFrame& frame){ frames.push_back(frame); } }; void recover(MessageStoreImpl& store, QueueRegistry& queues, ExchangeRegistry& exchanges, LinkRegistry& links) { sys::Timer t; DtxManager mgr(t); mgr.setStore (&store); RecoveryManagerImpl recovery(queues, exchanges, links, mgr); store.recover(recovery); } void recover(MessageStoreImpl& store, ExchangeRegistry& exchanges) { QueueRegistry queues; LinkRegistry links; recover(store, queues, exchanges, links); } void recover(MessageStoreImpl& store, QueueRegistry& queues) { ExchangeRegistry exchanges; LinkRegistry links; recover(store, queues, exchanges, links); } void bindAndUnbind(const string& exchangeName, const string& queueName, const string& key, const FieldTable& args) { { MessageStoreImpl store(timer); store.init(test_dir, 4, 1, true); // truncate store Exchange::shared_ptr exchange(new DirectExchange(exchangeName, true, args)); Queue::shared_ptr queue(new Queue(queueName, 0, &store, 0)); store.create(*exchange, qpid::framing::FieldTable()); store.create(*queue, qpid::framing::FieldTable()); BOOST_REQUIRE(exchange->bind(queue, key, &args)); store.bind(*exchange, *queue, key, args); }//db will be closed { MessageStoreImpl store(timer); store.init(test_dir, 4, 1); ExchangeRegistry exchanges; QueueRegistry queues; LinkRegistry links; recover(store, queues, exchanges, links); Exchange::shared_ptr exchange = exchanges.get(exchangeName); Queue::shared_ptr queue = queues.find(queueName); // check exchange args are still set for (FieldTable::ValueMap::const_iterator i = args.begin(); i!=args.end(); i++) { BOOST_CHECK(exchange->getArgs().get((*i).first)->getData() == (*i).second->getData()); } //check it is bound by unbinding BOOST_REQUIRE(exchange->unbind(queue, key, &args)); store.unbind(*exchange, *queue, key, args); } { MessageStoreImpl store(timer); store.init(test_dir, 4, 1); ExchangeRegistry exchanges; QueueRegistry queues; LinkRegistry links; recover(store, queues, exchanges, links); Exchange::shared_ptr exchange = exchanges.get(exchangeName); Queue::shared_ptr queue = queues.find(queueName); // check exchange args are still set for (FieldTable::ValueMap::const_iterator i = args.begin(); i!=args.end(); i++) { BOOST_CHECK(exchange->getArgs().get((*i).first)->getData() == (*i).second->getData()); } //make sure it is no longer bound BOOST_REQUIRE(!exchange->unbind(queue, key, &args)); } } // === Test suite === QPID_AUTO_TEST_CASE(CreateDelete) { SET_LOG_LEVEL("error+"); // This only needs to be set once. cout << test_filename << ".CreateDelete: " << flush; MessageStoreImpl store(timer); store.init(test_dir, 4, 1, true); // truncate store string name("CreateDeleteQueue"); Queue queue(name, 0, &store, 0); store.create(queue, qpid::framing::FieldTable()); // TODO - check dir exists BOOST_REQUIRE(queue.getPersistenceId()); store.destroy(queue); // TODO - check dir is deleted cout << "ok" << endl; } QPID_AUTO_TEST_CASE(EmptyRecover) { cout << test_filename << ".EmptyRecover: " << flush; MessageStoreImpl store(timer); store.init(test_dir, 4, 1, true); // truncate store QueueRegistry registry; registry.setStore (&store); recover(store, registry); //nothing to assert, just testing it doesn't blow up cout << "ok" << endl; } QPID_AUTO_TEST_CASE(QueueCreate) { cout << test_filename << ".QueueCreate: " << flush; uint64_t id(0); string name("MyDurableQueue"); { MessageStoreImpl store(timer); store.init(test_dir, 4, 1, true); // truncate store Queue queue(name, 0, &store, 0); store.create(queue, qpid::framing::FieldTable()); BOOST_REQUIRE(queue.getPersistenceId()); id = queue.getPersistenceId(); }//db will be closed { MessageStoreImpl store(timer); store.init(test_dir, 4, 1); QueueRegistry registry; registry.setStore (&store); recover(store, registry); Queue::shared_ptr queue = registry.find(name); BOOST_REQUIRE(queue.get()); BOOST_CHECK_EQUAL(id, queue->getPersistenceId()); } cout << "ok" << endl; } QPID_AUTO_TEST_CASE(QueueCreateWithSettings) { cout << test_filename << ".QueueCreateWithSettings: " << flush; std::auto_ptr policy( QueuePolicy::createQueuePolicy(101, 202)); string name("MyDurableQueue"); { MessageStoreImpl store(timer); store.init(test_dir, 4, 1, true); // truncate store Queue queue(name, 0, &store, 0); FieldTable settings; policy->update(settings); queue.create(settings); BOOST_REQUIRE(queue.getPersistenceId()); }//db will be closed { MessageStoreImpl store(timer); store.init(test_dir, 4, 1); QueueRegistry registry; registry.setStore (&store); recover(store, registry); Queue::shared_ptr queue = registry.find(name); BOOST_REQUIRE(queue); BOOST_REQUIRE(queue->getPolicy()); BOOST_CHECK_EQUAL(policy->getMaxCount(), queue->getPolicy()->getMaxCount()); BOOST_CHECK_EQUAL(policy->getMaxSize(), queue->getPolicy()->getMaxSize()); } cout << "ok" << endl; } QPID_AUTO_TEST_CASE(QueueDestroy) { cout << test_filename << ".QueueDestroy: " << flush; string name("MyDurableQueue"); { MessageStoreImpl store(timer); store.init(test_dir, 4, 1, true); // truncate store Queue queue(name, 0, &store, 0); store.create(queue, qpid::framing::FieldTable()); store.destroy(queue); }//db will be closed { MessageStoreImpl store(timer); store.init(test_dir, 4, 1); QueueRegistry registry; registry.setStore (&store); recover(store, registry); BOOST_REQUIRE(!registry.find(name)); } cout << "ok" << endl; } QPID_AUTO_TEST_CASE(Enqueue) { cout << test_filename << ".Enqueue: " << flush; //TODO: this is largely copy & paste'd from MessageTest in //qpid tree. ideally need some helper routines for reducing //this to a simpler less duplicated form string name("MyDurableQueue"); string exchange("MyExchange"); string routingKey("MyRoutingKey"); Uuid messageId(true); string data1("abcdefg"); string data2("hijklmn"); { MessageStoreImpl store(timer); store.init(test_dir, 4, 1, true); // truncate store Queue::shared_ptr queue(new Queue(name, 0, &store, 0)); FieldTable settings; queue->create(settings); boost::intrusive_ptr msg = MessageUtils::createMessage(exchange, routingKey, messageId, true, 14); MessageUtils::addContent(msg, data1); MessageUtils::addContent(msg, data2); msg->insertCustomProperty("abc", "xyz"); queue->enqueue(0, msg); }//db will be closed { MessageStoreImpl store(timer); store.init(test_dir, 4, 1); QueueRegistry registry; registry.setStore (&store); recover(store, registry); Queue::shared_ptr queue = registry.find(name); BOOST_REQUIRE(queue); BOOST_CHECK_EQUAL((u_int32_t) 1, queue->getMessageCount()); boost::intrusive_ptr msg = queue->get().payload; BOOST_CHECK_EQUAL(exchange, msg->getExchangeName()); BOOST_CHECK_EQUAL(routingKey, msg->getRoutingKey()); BOOST_CHECK_EQUAL(messageId, msg->getProperties()->getMessageId()); BOOST_CHECK_EQUAL((uint8_t) PERSISTENT, msg->getProperties()->getDeliveryMode()); BOOST_REQUIRE(Str16Value("xyz") == *msg->getProperties()->getApplicationHeaders().get("abc")); BOOST_CHECK_EQUAL((u_int64_t) 14, msg->contentSize()); DummyHandler handler; QueuedMessage qm(queue.get(),msg,0); MessageUtils::deliver(qm, handler, 100); BOOST_CHECK_EQUAL((size_t) 2, handler.frames.size()); AMQContentBody* contentBody(dynamic_cast(handler.frames[1].getBody())); BOOST_REQUIRE(contentBody); BOOST_CHECK_EQUAL(data1.size() + data2.size(), contentBody->getData().size()); BOOST_CHECK_EQUAL(data1 + data2, contentBody->getData()); } cout << "ok" << endl; } QPID_AUTO_TEST_CASE(Dequeue) { cout << test_filename << ".Dequeue: " << flush; //TODO: reduce the duplication in these tests string name("MyDurableQueue"); { string exchange("MyExchange"); string routingKey("MyRoutingKey"); Uuid messageId(true); string data("abcdefg"); MessageStoreImpl store(timer); store.init(test_dir, 4, 1, true); // truncate store Queue::shared_ptr queue(new Queue(name, 0, &store, 0)); FieldTable settings; queue->create(settings); boost::intrusive_ptr msg = MessageUtils::createMessage(exchange, routingKey, messageId, true, 7); MessageUtils::addContent(msg, data); QueuedMessage qm; qm.payload = msg; queue->enqueue(0, msg); queue->dequeue(0, qm); }//db will be closed { MessageStoreImpl store(timer); store.init(test_dir, 4, 1); QueueRegistry registry; registry.setStore (&store); recover(store, registry); Queue::shared_ptr queue = registry.find(name); BOOST_REQUIRE(queue); BOOST_CHECK_EQUAL((u_int32_t) 0, queue->getMessageCount()); } cout << "ok" << endl; } QPID_AUTO_TEST_CASE(ExchangeCreateAndDestroy) { cout << test_filename << ".ExchangeCreateAndDestroy: " << flush; uint64_t id(0); string name("MyDurableExchange"); string type("direct"); FieldTable args; args.setString("a", "A"); { MessageStoreImpl store(timer); store.init(test_dir, 4, 1, true); // truncate store ExchangeRegistry registry; Exchange::shared_ptr exchange = registry.declare(name, type, true, args).first; store.create(*exchange, qpid::framing::FieldTable()); id = exchange->getPersistenceId(); BOOST_REQUIRE(id); }//db will be closed { MessageStoreImpl store(timer); store.init(test_dir, 4, 1); ExchangeRegistry registry; recover(store, registry); Exchange::shared_ptr exchange = registry.get(name); BOOST_CHECK_EQUAL(id, exchange->getPersistenceId()); BOOST_CHECK_EQUAL(type, exchange->getType()); BOOST_REQUIRE(exchange->isDurable()); BOOST_CHECK_EQUAL(*args.get("a"), *exchange->getArgs().get("a")); store.destroy(*exchange); } { MessageStoreImpl store(timer); store.init(test_dir, 4, 1); ExchangeRegistry registry; recover(store, registry); try { Exchange::shared_ptr exchange = registry.get(name); BOOST_FAIL("Expected exchange not to be found"); } catch (const SessionException& e) { BOOST_CHECK_EQUAL((framing::ReplyCode) 404, e.code); } } cout << "ok" << endl; } QPID_AUTO_TEST_CASE(ExchangeBindAndUnbind) { cout << test_filename << ".ExchangeBindAndUnbind: " << flush; bindAndUnbind("MyDurableExchange", "MyDurableQueue", "my-routing-key", FieldTable()); cout << "ok" << endl; } QPID_AUTO_TEST_CASE(ExchangeBindAndUnbindWithArgs) { cout << test_filename << ".ExchangeBindAndUnbindWithArgs: " << flush; FieldTable args; args.setString("a", "A"); args.setString("b", "B"); bindAndUnbind("MyDurableExchange", "MyDurableQueue", "my-routing-key", args); cout << "ok" << endl; } QPID_AUTO_TEST_CASE(ExchangeImplicitUnbind) { cout << test_filename << ".ExchangeImplicitUnbind: " << flush; string exchangeName("MyDurableExchange"); string queueName1("MyDurableQueue1"); string queueName2("MyDurableQueue2"); string key("my-routing-key"); FieldTable args; { MessageStoreImpl store(timer); store.init(test_dir, 4, 1, true); // truncate store Exchange::shared_ptr exchange(new DirectExchange(exchangeName, true, args)); Queue::shared_ptr queue1(new Queue(queueName1, 0, &store, 0)); Queue::shared_ptr queue2(new Queue(queueName2, 0, &store, 0)); store.create(*exchange, qpid::framing::FieldTable()); store.create(*queue1, qpid::framing::FieldTable()); store.create(*queue2, qpid::framing::FieldTable()); store.bind(*exchange, *queue1, key, args); store.bind(*exchange, *queue2, key, args); //delete queue1: store.destroy(*queue1); }//db will be closed { MessageStoreImpl store(timer); store.init(test_dir, 4, 1); ExchangeRegistry exchanges; QueueRegistry queues; LinkRegistry links; //ensure recovery works ok: recover(store, queues, exchanges, links); Exchange::shared_ptr exchange = exchanges.get(exchangeName); BOOST_REQUIRE(!queues.find(queueName1).get()); BOOST_REQUIRE(queues.find(queueName2).get()); //delete exchange: store.destroy(*exchange); } { MessageStoreImpl store(timer); store.init(test_dir, 4, 1); ExchangeRegistry exchanges; QueueRegistry queues; LinkRegistry links; //ensure recovery works ok: recover(store, queues, exchanges, links); try { Exchange::shared_ptr exchange = exchanges.get(exchangeName); BOOST_FAIL("Expected exchange not to be found"); } catch (const SessionException& e) { BOOST_CHECK_EQUAL((framing::ReplyCode) 404, e.code); } Queue::shared_ptr queue = queues.find(queueName2); store.destroy(*queue); } cout << "ok" << endl; } QPID_AUTO_TEST_SUITE_END() qpid-cpp-store-debian-0.16/tests/OrderingTest.cpp0000644000176300017630000001011711761163730021017 0ustar cajuscajus/* Copyright (c) 2007, 2008, 2009 Red Hat, Inc. This file is part of the Qpid async store library msgstore.so. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA The GNU Lesser General Public License is available in the file COPYING. */ #include "unit_test.h" #include "MessageStoreImpl.h" #include #include "MessageUtils.h" #include #include #include #include "qpid/log/Logger.h" #include "qpid/sys/Timer.h" qpid::sys::Timer timer; #define SET_LOG_LEVEL(level) \ qpid::log::Options opts(""); \ opts.selectors.clear(); \ opts.selectors.push_back(level); \ qpid::log::Logger::instance().configure(opts); using namespace qpid; using namespace qpid::broker; using namespace qpid::framing; using namespace mrg::msgstore; QPID_AUTO_TEST_SUITE(OrderingTest) const std::string test_filename("OrderingTest"); const char* tdp = getenv("TMP_DATA_DIR"); const std::string test_dir(tdp && strlen(tdp) > 0 ? tdp : "/tmp/OrderingTest"); // === Helper fns === const std::string name("OrderingQueue"); std::auto_ptr store; QueueRegistry queues; Queue::shared_ptr queue; std::queue ids; int counter = 1; void setup() { store = std::auto_ptr(new MessageStoreImpl(timer)); store->init(test_dir, 4, 1, true); // truncate store queue = Queue::shared_ptr(new Queue(name, 0, store.get(), 0)); FieldTable settings; queue->create(settings); } void push() { Uuid messageId(true); ids.push(messageId); boost::intrusive_ptr msg = MessageUtils::createMessage("exchange", "routing_key", messageId, true, 0); queue->deliver(msg); } bool pop() { boost::intrusive_ptr msg = queue->get().payload; if (msg) { QueuedMessage qm; qm.payload = msg; queue->dequeue(0, qm); BOOST_CHECK_EQUAL(ids.front(), msg->getProperties()->getMessageId()); ids.pop(); return true; } else { return false; } } void restart() { queue.reset(); store.reset(); store = std::auto_ptr(new MessageStoreImpl(timer)); store->init(test_dir, 4, 1); ExchangeRegistry exchanges; LinkRegistry links; sys::Timer t; DtxManager mgr(t); mgr.setStore (store.get()); RecoveryManagerImpl recoveryMgr(queues, exchanges, links, mgr); store->recover(recoveryMgr); queue = queues.find(name); } void check() { BOOST_REQUIRE(queue); BOOST_CHECK_EQUAL((u_int32_t) ids.size(), queue->getMessageCount()); while (pop()) ;//keeping popping 'till all messages are dequeued BOOST_CHECK_EQUAL((u_int32_t) 0, queue->getMessageCount()); BOOST_CHECK_EQUAL((size_t) 0, ids.size()); } // === Test suite === QPID_AUTO_TEST_CASE(Basic) { SET_LOG_LEVEL("error+"); // This only needs to be set once. std::cout << test_filename << ".Basic: " << std::flush; setup(); //push on 10 messages for (int i = 0; i < 10; i++) push(); restart(); check(); std::cout << "ok" << std::endl; } QPID_AUTO_TEST_CASE(Cycle) { std::cout << test_filename << ".Cycle: " << std::flush; setup(); //push on 10 messages: for (int i = 0; i < 10; i++) push(); //pop 5: for (int i = 0; i < 5; i++) pop(); //push on another 5: for (int i = 0; i < 5; i++) push(); restart(); check(); std::cout << "ok" << std::endl; } QPID_AUTO_TEST_SUITE_END() qpid-cpp-store-debian-0.16/tests/.valgrind.supp0000644000176300017630000000146111627774357020517 0ustar cajuscajus{ Benign error in libcpg. Memcheck:Param socketcall.sendmsg(msg.msg_iov[i]) obj:*/libpthread-2.5.so obj:*/libcpg.so.2.0.0 } { Uninitialised value problem in _dl_relocate (F7, F8) Memcheck:Cond fun:_dl_relocate_object fun:*dl_* } { False "possibly leaked" in boost program_options - global std::string var. Memcheck:Leak fun:_Znwj fun:_ZNSs4_Rep9_S_createEjjRKSaIcE obj:/usr/lib/libstdc++.so.6.0.8 fun:_ZNSsC1EPKcRKSaIcE obj:/usr/lib/libboost_program_options.so.1.33.1 } { Probable use after delete problem in boost::unit_test Memcheck:Addr8 fun:_ZN5boost9unit_test14framework_implD1Ev fun:exit fun:(below main) } { Memcheck:Addr4 fun:_ZN5boost9unit_test14framework_implD1Ev fun:exit fun:(below main) } qpid-cpp-store-debian-0.16/tests/system_test.sh0000755000176300017630000000350611725440047020627 0ustar cajuscajus#!/bin/bash # Copyright (c) 2007, 2008 Red Hat, Inc. # # This file is part of the Qpid async store library msgstore.so. # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 # USA # # The GNU Lesser General Public License is available in the file COPYING. error() { echo $*; exit 1; } # Make sure $QPID_DIR contains what we need. if ! test -d "$QPID_DIR" ; then echo "WARNING: QPID_DIR is not set skipping system tests." exit fi STORE_LIB=../lib/.libs/msgstore.so xml_spec=$QPID_DIR/specs/amqp.0-10-qpid-errata.xml test -f $xml_spec || error "$xml_spec not found: invalid \$QPID_DIR ?" export PYTHONPATH=$QPID_DIR/python:$QPID_DIR/extras/qmf/src/py:$QPID_DIR/tools/src/py echo "Using directory $TMP_DATA_DIR" fail=0 # Run the tests with a given set of flags BROKER_OPTS="--no-module-dir --load-module=$STORE_LIB --data-dir=$TMP_DATA_DIR --auth=no --wcache-page-size 16" run_tests() { for p in `seq 1 8`; do $abs_srcdir/start_broker "$@" ${BROKER_OPTS} || { echo "FAIL broker start"; return 1; } python "$abs_srcdir/persistence.py" -s "$xml_spec" -b localhost:`cat qpidd.port` -p $p -r 3 || fail=1; $abs_srcdir/stop_broker done } run_tests || fail=1 exit $fail qpid-cpp-store-debian-0.16/tests/TransactionalTest.cpp0000644000176300017630000002562511611361170022052 0ustar cajuscajus/* Copyright (c) 2007, 2008, 2009 Red Hat, Inc. This file is part of the Qpid async store library msgstore.so. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA The GNU Lesser General Public License is available in the file COPYING. */ #include "unit_test.h" #include "MessageStoreImpl.h" #include #include "MessageUtils.h" #include "StoreException.h" #include "qpid/broker/Queue.h" #include "qpid/broker/RecoveryManagerImpl.h" #include "qpid/framing/AMQHeaderBody.h" #include "qpid/log/Statement.h" #include "qpid/log/Logger.h" #include "qpid/sys/Timer.h" qpid::sys::Timer timer; #define SET_LOG_LEVEL(level) \ qpid::log::Options opts(""); \ opts.selectors.clear(); \ opts.selectors.push_back(level); \ qpid::log::Logger::instance().configure(opts); using namespace mrg::msgstore; using namespace qpid; using namespace qpid::broker; using namespace qpid::framing; using namespace std; QPID_AUTO_TEST_SUITE(TransactionalTest) const string test_filename("TransactionalTest"); const char* tdp = getenv("TMP_DATA_DIR"); const string test_dir(tdp && strlen(tdp) > 0 ? tdp : "/tmp/TransactionalTest"); // Test txn context which has special setCompleteFailure() method which prevents entire "txn complete" process from hapenning class TestTxnCtxt : public TxnCtxt { public: TestTxnCtxt(IdSequence* _loggedtx) : TxnCtxt(_loggedtx) {} void setCompleteFailure(const unsigned num_queues_rem) { // Remove queue members from back of impactedQueues until queues_rem reamin. // to end to simulate multi-queue txn complete failure. while (impactedQueues.size() > num_queues_rem) impactedQueues.erase(impactedQueues.begin()); } void resetPreparedXidStorePtr() { preparedXidStorePtr = 0; } }; // Test store which has sepcial begin() which returns a TestTPCTxnCtxt, and a method to check for // reamining open transactions class TestMessageStore: public MessageStoreImpl { public: TestMessageStore(qpid::sys::Timer& timer, const char* envpath = 0) : MessageStoreImpl(timer, envpath) {} std::auto_ptr begin() { checkInit(); // pass sequence number for c/a return auto_ptr(new TestTxnCtxt(&messageIdSequence)); } void commit(TransactionContext& ctxt, const bool complete_prepared_list) { checkInit(); TxnCtxt* txn(check(&ctxt)); if (!txn->isTPC()) { localPrepare(dynamic_cast(txn)); if (!complete_prepared_list) dynamic_cast(txn)->resetPreparedXidStorePtr(); } completed(*dynamic_cast(txn), true); } void abort(TransactionContext& ctxt, const bool complete_prepared_list) { checkInit(); TxnCtxt* txn(check(&ctxt)); if (!txn->isTPC()) { localPrepare(dynamic_cast(txn)); if (!complete_prepared_list) dynamic_cast(txn)->resetPreparedXidStorePtr(); } completed(*dynamic_cast(txn), false); } }; // === Helper fns === const string nameA("queueA"); const string nameB("queueB"); //const Uuid messageId(true); std::auto_ptr store; std::auto_ptr queues; Queue::shared_ptr queueA; Queue::shared_ptr queueB; template void setup() { store = std::auto_ptr(new T(timer)); store->init(test_dir, 4, 1, true); // truncate store //create two queues: FieldTable settings; queueA = Queue::shared_ptr(new Queue(nameA, 0, store.get(), 0)); queueA->create(settings); queueB = Queue::shared_ptr(new Queue(nameB, 0, store.get(), 0)); queueB->create(settings); } template void restart() { queueA.reset(); queueB.reset(); queues.reset(); store.reset(); store = std::auto_ptr(new T(timer)); store->init(test_dir, 4, 1); queues = std::auto_ptr(new QueueRegistry); ExchangeRegistry exchanges; LinkRegistry links; sys::Timer t; DtxManager mgr(t); mgr.setStore (store.get()); RecoveryManagerImpl recovery(*queues, exchanges, links, mgr); store->recover(recovery); queueA = queues->find(nameA); queueB = queues->find(nameB); } boost::intrusive_ptr createMessage(const string& id, const string& exchange="exchange", const string& key="routing_key") { boost::intrusive_ptr msg = MessageUtils::createMessage(exchange, key, Uuid(), true, 0, id); return msg; } void checkMsg(Queue::shared_ptr& queue, u_int32_t size, const string& msgid = "") { BOOST_REQUIRE(queue); BOOST_CHECK_EQUAL(size, queue->getMessageCount()); if (size > 0) { boost::intrusive_ptr msg = queue->get().payload; BOOST_REQUIRE(msg); BOOST_CHECK_EQUAL(msgid, msg->getProperties()->getCorrelationId()); } } void swap(bool commit) { setup(); //create message and enqueue it onto first queue: boost::intrusive_ptr msgA = createMessage("Message", "exchange", "routing_key"); queueA->deliver(msgA); boost::intrusive_ptr msgB = queueA->get().payload; BOOST_REQUIRE(msgB); //move the message from one queue to the other as a transaction std::auto_ptr txn = store->begin(); queueB->enqueue(txn.get(), msgB);//note: need to enqueue it first to avoid message being deleted QueuedMessage qmB; qmB.payload = msgB; queueA->dequeue(txn.get(), qmB); if (commit) { store->commit(*txn); } else { store->abort(*txn); } restart(); // Check outcome BOOST_REQUIRE(queueA); BOOST_REQUIRE(queueB); Queue::shared_ptr x;//the queue from which the message was swapped Queue::shared_ptr y;//the queue on which the message is expected to be if (commit) { x = queueA; y = queueB; } else { x = queueB; y = queueA; } checkMsg(x, 0); checkMsg(y, 1, "Message"); checkMsg(y, 0); } void testMultiQueueTxn(const unsigned num_queues_rem, const bool complete_prepared_list, const bool commit) { setup(); TestMessageStore* tmsp = static_cast(store.get()); std::auto_ptr txn(tmsp->begin()); //create two messages and enqueue them onto both queues: boost::intrusive_ptr msgA = createMessage("MessageA", "exchange", "routing_key"); queueA->enqueue(txn.get(), msgA); queueB->enqueue(txn.get(), msgA); boost::intrusive_ptr msgB = createMessage("MessageB", "exchange", "routing_key"); queueA->enqueue(txn.get(), msgB); queueB->enqueue(txn.get(), msgB); static_cast(txn.get())->setCompleteFailure(num_queues_rem); if (commit) tmsp->commit(*txn, complete_prepared_list); else tmsp->abort(*txn, complete_prepared_list); restart(); // Check outcome if (commit) { checkMsg(queueA, 2, "MessageA"); checkMsg(queueB, 2, "MessageA"); checkMsg(queueA, 1, "MessageB"); checkMsg(queueB, 1, "MessageB"); } checkMsg(queueA, 0); checkMsg(queueB, 0); } boost::intrusive_ptr nonTxEnq(Queue::shared_ptr q) { boost::intrusive_ptr msg = createMessage("Message", "exchange", "routingKey"); q->deliver(msg); return msg; } QueuedMessage getMsg(Queue::shared_ptr q) { boost::intrusive_ptr msg = q->get().payload; BOOST_REQUIRE(msg); QueuedMessage qm; qm.payload = msg; return qm; } void txDeq(Queue::shared_ptr q, QueuedMessage& qm, TransactionContext* tp) { q->dequeue(tp, qm); } void testLock(Queue::shared_ptr q, QueuedMessage qm) { try { q->dequeue(0, qm); BOOST_ERROR("Did not throw JERR_MAP_LOCKED exception as expected."); } catch (const mrg::msgstore::StoreException& e) { if (std::strstr(e.what(), "JERR_MAP_LOCKED") == 0) BOOST_ERROR("Unexpected StoreException: " << e.what()); } catch (const std::exception& e) { BOOST_ERROR("Unexpected exception: " << e.what()); } } // === Test suite === QPID_AUTO_TEST_CASE(Commit) { SET_LOG_LEVEL("error+"); // This only needs to be set once. cout << test_filename << ".Commit: " << flush; swap(true); cout << "ok" << endl; } QPID_AUTO_TEST_CASE(Abort) { cout << test_filename << ".Abort: " << flush; swap(false); cout << "ok" << endl; } QPID_AUTO_TEST_CASE(MultiQueueCommit) { cout << test_filename << ".MultiQueueCommit: " << flush; testMultiQueueTxn(2, true, true); cout << "ok" << endl; } QPID_AUTO_TEST_CASE(MultiQueueAbort) { cout << test_filename << ".MultiQueueAbort: " << flush; testMultiQueueTxn(2, true, false); cout << "ok" << endl; } QPID_AUTO_TEST_CASE(MultiQueueNoQueueCommitRecover) { cout << test_filename << ".MultiQueueNoQueueCommitRecover: " << flush; testMultiQueueTxn(0, false, true); cout << "ok" << endl; } QPID_AUTO_TEST_CASE(MultiQueueNoQueueAbortRecover) { cout << test_filename << ".MultiQueueNoQueueAbortRecover: " << flush; testMultiQueueTxn(0, false, false); cout << "ok" << endl; } QPID_AUTO_TEST_CASE(MultiQueueSomeQueueCommitRecover) { cout << test_filename << ".MultiQueueSomeQueueCommitRecover: " << flush; testMultiQueueTxn(1, false, true); cout << "ok" << endl; } QPID_AUTO_TEST_CASE(MultiQueueSomeQueueAbortRecover) { cout << test_filename << ".MultiQueueSomeQueueAbortRecover: " << flush; testMultiQueueTxn(1, false, false); cout << "ok" << endl; } QPID_AUTO_TEST_CASE(MultiQueueAllQueueCommitRecover) { cout << test_filename << ".MultiQueueAllQueueCommitRecover: " << flush; testMultiQueueTxn(2, false, true); cout << "ok" << endl; } QPID_AUTO_TEST_CASE(MultiQueueAllQueueAbortRecover) { cout << test_filename << ".MultiQueueAllQueueAbortRecover: " << flush; testMultiQueueTxn(2, false, false); cout << "ok" << endl; } QPID_AUTO_TEST_CASE(LockedRecordTest) { cout << test_filename << ".LockedRecordTest: " << flush; setup(); nonTxEnq(queueA); std::auto_ptr txn = store->begin(); QueuedMessage qm = getMsg(queueA); txDeq(queueA, qm, txn.get()); testLock(queueA, qm); store->commit(*txn); checkMsg(queueA, 0); cout << "ok" << endl; } QPID_AUTO_TEST_SUITE_END() qpid-cpp-store-debian-0.16/tests/cluster/0000755000176300017630000000000011761422501017355 5ustar cajuscajusqpid-cpp-store-debian-0.16/tests/cluster/run_python_cluster_tests0000755000176300017630000000315011533251120024464 0ustar cajuscajus#!/bin/bash # Copyright (c) 2008, 2009 Red Hat, Inc. # # This file is part of the Qpid async store library msgstore.so. # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 # USA # # The GNU Lesser General Public License is available in the file COPYING. . `dirname $0`/cluster_tests_env.sh func_check_qpid_python || exit 0 # A warning, not a failure. echo "Running Python cluster tests..." OUTDIR=brokertest.tmp rm -rf $OUTDIR # Ignore tests known to fail. CLUSTER_TESTS_IGNORE=${CLUSTER_TESTS_IGNORE:-"-I ${CLUSTER_TESTS_FAIL}"} # Ignore tests that don't work in the store environment # SASL test needs sasl test database which is not installed. CLUSTER_TESTS_IGNORE="${CLUSTER_TESTS_IGNORE} -i cluster_tests.ShortTests.test_sasl -i cluster_tests.ShortTests.test_user_id_update" CLUSTER_TESTS=${CLUSTER_TESTS:-$*} TEST_CMD="${QPID_PYTHON_TEST} -m cluster_tests ${CLUSTER_TESTS_IGNORE} ${CLUSTER_TESTS} -DOUTDIR=$OUTDIR" $TEST_CMD && rm -rf $OUTDIR qpid-cpp-store-debian-0.16/tests/cluster/run_long_python_cluster_tests0000755000176300017630000000170111342015302025501 0ustar cajuscajus#!/bin/bash # Copyright (c) 2008, 2009 Red Hat, Inc. # # This file is part of the Qpid async store library msgstore.so. # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 # USA # # The GNU Lesser General Public License is available in the file COPYING. ./run_python_cluster_tests long qpid-cpp-store-debian-0.16/tests/cluster/system_test.sh0000755000176300017630000000366011734071131022303 0ustar cajuscajus#!/bin/bash # Copyright (c) 2007, 2008 Red Hat, Inc. # # This file is part of the Qpid async store library msgstore.so. # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 # USA # # The GNU Lesser General Public License is available in the file COPYING. error() { echo $*; exit 1; } # Make sure $QPID_DIR contains what we need. if ! test -d "$QPID_DIR" ; then echo "WARNING: QPID_DIR is not set skipping system tests." exit fi STORE_LIB=../../lib/.libs/msgstore.so CLUSTER_LIB=$QPID_BLD/src/.libs/cluster.so xml_spec=$QPID_DIR/specs/amqp.0-10-qpid-errata.xml test -f $xml_spec || error "$xml_spec not found: invalid \$QPID_DIR ?" export PYTHONPATH=$QPID_DIR/python:$QPID_DIR/extras/qmf/src/py:$QPID_DIR/tools/src/py echo "Using directory $TMP_DATA_DIR" fail=0 # Run the broker as part of a cluster. BROKER_OPTS="--no-module-dir --load-module=$STORE_LIB --load-module=$CLUSTER_LIB --data-dir=$TMP_DATA_DIR --auth=no --wcache-page-size 16 --cluster-name=$HOSTNAME.$$" run_tests() { for p in `seq 1 8`; do $abs_srcdir/../start_broker "$@" ${BROKER_OPTS} || { echo "FAIL broker start"; return 1; } python "$abs_srcdir/../persistence.py" -s "$xml_spec" -b localhost:`cat qpidd.port` -p $p -r 3 || fail=1; $abs_srcdir/../stop_broker done } run_tests || fail=1 exit $fail qpid-cpp-store-debian-0.16/tests/cluster/run_cpp_cluster_tests0000755000176300017630000000216211345453556023751 0ustar cajuscajus#!/bin/bash # Copyright (c) 2008, 2009 Red Hat, Inc. # # This file is part of the Qpid async store library msgstore.so. # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 # USA # # The GNU Lesser General Public License is available in the file COPYING. . `dirname $0`/cluster_tests_env.sh echo "Running C++ cluster tests..." if test $COROSYNC; then ${CPP_CLUSTER_EXEC} RETCODE=$? else sg ais -c "${CPP_CLUSTER_EXEC}" RETCODE=$? fi exit $RETCODEqpid-cpp-store-debian-0.16/tests/cluster/cluster_tests_env.sh0000755000176300017630000002234511611361170023473 0ustar cajuscajus# Copyright (c) 2008, 2009 Red Hat, Inc. # # This file is part of the Qpid async store library msgstore.so. # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 # USA # # The GNU Lesser General Public License is available in the file COPYING. # --- Function definitions --- func_check_required_env () #------------------------- # Check that EITHER: # QPID_DIR is set (for running against svn QPID) # OR # QPID_PREFIX is set (for running against installed QPID # Will exit with error code 1 if neither of these is defined. # Params: None # Returns: 0 if env vars ok, 1 otherwise { if test -z "${QPID_DIR}" -a -z "${QPID_PREFIX}"; then # Try to find qpidd in the normal installed location if test -x /usr/sbin/qpidd; then QPID_PREFIX=/usr else echo "ERROR: Could not find installed Qpid" echo "Either of the following must be set in the environment for this script to run:" echo " QPID_DIR for running against a Qpid svn build" echo " QPID_PREFIX for running against an installed Qpid" return 1 fi fi return 0 } func_check_clustering () #----------------------- # Check openAIS/corosync is running and user has correct privileges # Params: None # Returns: 0 if openAIS/corosync is running, 1 otherwise # Sets env var COROSYNC to 1 if corosync is running, not set otherwise { # Check either aisexec or corosync is running as root cluster_prog=`ps -u root | grep 'aisexec\|corosync'` test -n "$cluster_prog" || NODAEMON="Neither aisexec nor corosync is running as root" if test -z "$NODAEMON"; then # Test for corosync running echo $cluster_prog | grep "aisexec" > /dev/null || COROSYNC=1 if test -n "$COROSYNC"; then # Corosync auth test user=`whoami` ls /etc/corosync/uidgid.d | grep $user > /dev/null || NOAUTH="You are not authorized to use corosync." else # OpenAis auth test id -nG | grep '\' >/dev/null || NOAUTH="You are not a member of the ais group." fi fi if test -n "$NODAEMON" -o -n "$NOAUTH"; then cat < qpidd.port qpid-cpp-store-debian-0.16/tests/.valgrindrc0000644000176300017630000000017511003452126020026 0ustar cajuscajus--gen-suppressions=all --leak-check=full --demangle=yes --suppressions=.valgrind.supp --num-callers=25 --trace-children=yes qpid-cpp-store-debian-0.16/tests/stop_broker0000755000176300017630000000274411234644746020176 0ustar cajuscajus#!/bin/bash # Copyright (c) 2008 Red Hat, Inc. # # This file is part of the Qpid async store library msgstore.so. # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 # USA # # The GNU Lesser General Public License is available in the file COPYING. # Stop the broker, check for errors. # if test -f qpidd.port; then export QPID_PORT=`cat qpidd.port` QPIDD=$QPID_BLD/src/qpidd rm -f qpidd.port $QPIDD --quit || ERROR=$? # Check qpidd.log. grep -a 'warning\|error\|critical' qpidd.log && { echo "WARNING: Suspicious broker log entries in qpidd.log, above." } # Check valgrind log. if test -n "$VALGRIND"; then source `dirname $0`/vg_check $VG_LOG* vg_check qpidd.vglog* fi exit $ERROR else echo "No qpidd.port file found - cannot stop broker." exit 1; fi qpid-cpp-store-debian-0.16/tests/federation/0000755000176300017630000000000011761422467020027 5ustar cajuscajusqpid-cpp-store-debian-0.16/tests/federation/run_federation_sys_tests0000755000176300017630000001067711752237112025103 0ustar cajuscajus#!/bin/bash # # Copyright (c) 2008, 2009 Red Hat, Inc. # # This file is part of the Qpid async store library msgstore.so. # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 # USA # # The GNU Lesser General Public License is available in the file COPYING. # Run the federation tests. source ${abs_srcdir}/federation_tests_env.sh MODULENAME=federation_sys # Test for long test if [[ "$1" == "LONG_TEST" ]]; then USE_LONG_TEST=1 shift # get rid of this param so it is not treated as a test name fi trap stop_brokers INT TERM QUIT MODULES="--load-module ${STORE_LIB} --jfile-size 12 --num-jfiles 4" CLUSTER_MODULE="--load-module ${CLUSTER_LIB} " if [ -z ${USE_LONG_TEST} ]; then SKIPTESTS="-i federation_sys.A_Long* -i federation_sys.B_Long* -i federation_sys.E_Long* -i federation_sys.F_Long*" fi if [ -z ${CLUSTERING_ENABLED} ]; then SKIPTESTS="${SKIPTESTS} -i federation_sys.C_* -i federation_sys.D_* -i federation_sys.G_* -i federation_sys.H_*" elif [ -z ${USE_LONG_TEST} ]; then SKIPTESTS="${SKIPTESTS} -i federation_sys.C_Long* -i federation_sys.D_Long* -i federation_sys.G_Long* -i federation_sys.H_Long*" fi start_brokers() { clean_or_create_dir() { if [ -n "$1" -a -d $1 ]; then rm -rf $1/* else mkdir -p $1 fi } start_broker() { clean_or_create_dir $1 ${QPIDD_EXEC} --daemon --port 0 --auth no --data-dir $1 $2 > qpidd.port PORT=`cat qpidd.port` eval "$3=${PORT}" } start_broker ${TMP_DATA_DIR}/local "${MODULES} --log-enable info+ --log-to-file ${TMP_DATA_DIR}/qpidd.log.local" LOCAL_PORT start_broker ${TMP_DATA_DIR}/remote "${MODULES} --log-enable info+ --log-to-file ${TMP_DATA_DIR}/qpidd.log.remote" REMOTE_PORT if [ -n "$CLUSTERING_ENABLED" ]; then start_broker ${TMP_DATA_DIR}/cluster/c1.1 "${MODULES} ${CLUSTER_MODULE} --cluster-name test-cluster-1 --log-enable info+ --log-to-file ${TMP_DATA_DIR}/qpidd.log.cluster1.1" CLUSTER_C1_1 start_broker ${TMP_DATA_DIR}/cluster/c1.2 "${MODULES} ${CLUSTER_MODULE} --cluster-name test-cluster-1 --log-enable info+ --log-to-file ${TMP_DATA_DIR}/qpidd.log.cluster1.2" CLUSTER_C1_2 start_broker ${TMP_DATA_DIR}/cluster/c2.1 "${MODULES} ${CLUSTER_MODULE} --cluster-name test-cluster-2 --log-enable info+ --log-to-file ${TMP_DATA_DIR}/qpidd.log.cluster2.1" CLUSTER_C2_1 start_broker ${TMP_DATA_DIR}/cluster/c2.2 "${MODULES} ${CLUSTER_MODULE} --cluster-name test-cluster-2 --log-enable info+ --log-to-file ${TMP_DATA_DIR}/qpidd.log.cluster2.2" CLUSTER_C2_2 fi rm qpidd.port } stop_brokers() { ${QPIDD_EXEC} -q --port ${LOCAL_PORT} ${QPIDD_EXEC} -q --port ${REMOTE_PORT} if [ -n "${CLUSTERING_ENABLED}" ]; then ${QPID_CLUSTER_EXEC} --all-stop --force localhost:${CLUSTER_C1_1} ${QPID_CLUSTER_EXEC} --all-stop --force localhost:${CLUSTER_C2_1} fi } if test -d ${PYTHON_DIR} ; then start_brokers if [ -z ${CLUSTERING_ENABLED} ]; then echo "Running federation tests using brokers on local port ${LOCAL_PORT}, remote port ${REMOTE_PORT} (NOTE: clustering is DISABLED)" else echo "Running federation tests using brokers on local port ${LOCAL_PORT}, remote port ${REMOTE_PORT}, local cluster nodes ${CLUSTER_C1_1} ${CLUSTER_C1_2}, remote cluster nodes ${CLUSTER_C2_1} ${CLUSTER_C2_2}" fi if [ -z ${USE_LONG_TEST} ]; then echo "NOTE: To run a full set of federation system tests, use \"make check-long\"." fi ${QPID_PYTHON_TEST} -m ${MODULENAME} ${SKIPTESTS} -b localhost:$REMOTE_PORT -Dlocal-port=$LOCAL_PORT -Dremote-port=$REMOTE_PORT -Dlocal-cluster-ports="$CLUSTER_C1_1 $CLUSTER_C1_2" -Dremote-cluster-ports="$CLUSTER_C2_1 $CLUSTER_C2_2" $@ RETCODE=$? stop_brokers if test x$RETCODE != x0; then echo "FAIL federation tests"; exit 1; fi fi qpid-cpp-store-debian-0.16/tests/federation/run_long_federation_sys_tests0000755000176300017630000000177111611361170026111 0ustar cajuscajus#! /bin/bash # # Copyright (c) 2010 Red Hat, Inc. # # This file is part of the Qpid async store library msgstore.so. # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 # USA # # The GNU Lesser General Public License is available in the file COPYING. # Run the federation system tests (long version). ./run_federation_sys_tests LONG_TEST $@ qpid-cpp-store-debian-0.16/tests/federation/federation_tests_env.sh0000755000176300017630000002321411701102677024572 0ustar cajuscajus# Copyright (c) 2008, 2009 Red Hat, Inc. # # This file is part of the Qpid async store library msgstore.so. # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 # USA # # The GNU Lesser General Public License is available in the file COPYING. # --- Function definitions --- func_check_required_env () #------------------------- # Check that EITHER: # QPID_DIR is set (for running against svn QPID) # OR # QPID_PREFIX is set (for running against installed QPID # Will exit with error code 1 if neither of these is defined. # Params: None # Returns: 0 if env vars ok, 1 otherwise { if test -z "${QPID_DIR}" -a -z "${QPID_PREFIX}"; then # Try to find qpidd in the normal installed location if test -x /usr/sbin/qpidd; then QPID_PREFIX=/usr else echo "ERROR: Could not find installed Qpid" echo "Either of the following must be set in the environment for this script to run:" echo " QPID_DIR for running against a Qpid svn build" echo " QPID_PREFIX for running against an installed Qpid" return 1 fi fi return 0 } func_check_clustering () #----------------------- # Check openAIS/corosync is running and user has correct privileges # Params: None # Returns: 0 if openAIS/corosync is running, 1 otherwise # Sets env var COROSYNC to 1 if corosync is running, not set otherwise { # Check either aisexec or corosync is running as root cluster_prog=`ps -u root | grep 'aisexec\|corosync'` test -n "$cluster_prog" || NODAEMON="Neither aisexec nor corosync is running as root" if test -z "$NODAEMON"; then # Test for corosync running echo $cluster_prog | grep "aisexec" > /dev/null || COROSYNC=1 if test -n "$COROSYNC"; then # Corosync auth test user=`whoami` ls /etc/corosync/uidgid.d | grep $user > /dev/null || NOAUTH="You are not authorized to use corosync." else # OpenAis auth test id -nG | grep '\' >/dev/null || NOAUTH="You are not a member of the ais group." fi fi if test -n "$NODAEMON" -o -n "$NOAUTH"; then cat < #if (BOOST_VERSION < 103400) // v.1.33 and earlier # include #else // v.1.34 and later # include #endif // Keep the test function for compilation but do not not register it. // TODO aconway 2008-04-23: better workaround for expected failures. // The following causes the test testUpdateTxState not to run at all. # define QPID_AUTO_TEST_CASE_EXPECTED_FAILURES(test_name,n) \ namespace { struct test_name { void test_method(); }; } \ void test_name::test_method() // The following runs the test testUpdateTxState, but it fails. /*#define QPID_AUTO_TEST_CASE_EXPECTED_FAILURES(test_name,n) \ namespace { struct test_name { void test_method(); }; } \ BOOST_AUTO_TEST_CASE(name)*/ #if (BOOST_VERSION < 103300) // v.1.32 and earlier # define QPID_AUTO_TEST_SUITE(name) # define QPID_AUTO_TEST_CASE(name) BOOST_AUTO_UNIT_TEST(name) # define QPID_AUTO_TEST_SUITE_END() #elif (BOOST_VERSION < 103400) // v.1.33 // Note the trailing ';' # define QPID_AUTO_TEST_SUITE(name) BOOST_AUTO_TEST_SUITE(name); # define QPID_AUTO_TEST_CASE(name) BOOST_AUTO_TEST_CASE(name) # define QPID_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END(); #else // v.1.34 and later # define QPID_AUTO_TEST_SUITE(name) BOOST_AUTO_TEST_SUITE(name) # define QPID_AUTO_TEST_CASE(name) BOOST_AUTO_TEST_CASE(name) # define QPID_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END() #endif #endif /*!QPIPD_TEST_UNIT_TEST_H_*/ qpid-cpp-store-debian-0.16/tests/persistence.py0000644000176300017630000006167211752237112020610 0ustar cajuscajus# Copyright (c) 2007, 2008 Red Hat, Inc. # # This file is part of the Qpid async store library msgstore.so. # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 # USA # # The GNU Lesser General Public License is available in the file COPYING. import sys, re, traceback, socket from getopt import getopt, GetoptError from qpid.connection import Connection from qpid.util import connect from qpid.datatypes import Message, RangedSet from qpid.queue import Empty from qpid.session import SessionException from qpid.testlib import TestBase010 from time import sleep class PersistenceTest(TestBase010): XA_RBROLLBACK = 1 XA_RBTIMEOUT = 2 XA_OK = 0 def createMessage(self, **kwargs): session = self.session dp = {} dp['delivery_mode'] = 2 mp = {} for k, v in kwargs.iteritems(): if k in ['routing_key', 'delivery_mode']: dp[k] = v if k in ['message_id', 'correlation_id', 'application_headers']: mp[k] = v args = [] args.append(session.delivery_properties(**dp)) if len(mp): args.append(session.message_properties(**mp)) if kwargs.has_key('body'): args.append(kwargs['body']) return Message(*args) def phase1(self): session = self.session session.queue_declare(queue="queue-a", durable=True) session.queue_declare(queue="queue-b", durable=True) session.exchange_bind(queue="queue-a", exchange="amq.direct", binding_key="a") session.exchange_bind(queue="queue-b", exchange="amq.direct", binding_key="b") session.message_transfer(destination="amq.direct", message=self.createMessage(routing_key="a", correlation_id="Msg0001", body="A_Message1")) session.message_transfer(destination="amq.direct", message=self.createMessage(routing_key="b", correlation_id="Msg0002", body="B_Message1")) # session.queue_declare(queue="lvq-test", durable=True, arguments={"qpid.last_value_queue":True}) # session.message_transfer(message=self.createMessage(routing_key="lvq-test", application_headers={"qpid.LVQ_key":"B"}, body="B1")) # session.message_transfer(message=self.createMessage(routing_key="lvq-test", application_headers={"qpid.LVQ_key":"A"}, body="A1")) # session.message_transfer(message=self.createMessage(routing_key="lvq-test", application_headers={"qpid.LVQ_key":"A"}, body="A2")) # session.message_transfer(message=self.createMessage(routing_key="lvq-test", application_headers={"qpid.LVQ_key":"B"}, body="B2")) # session.message_transfer(message=self.createMessage(routing_key="lvq-test", application_headers={"qpid.LVQ_key":"B"}, body="B3")) # session.message_transfer(message=self.createMessage(routing_key="lvq-test", application_headers={"qpid.LVQ_key":"C"}, body="C1")) def phase2(self): session = self.session #check queues exists session.queue_declare(queue="queue-a", durable=True, passive=True) session.queue_declare(queue="queue-b", durable=True, passive=True) #check they are still bound to amq.direct correctly responses = [] responses.append(session.exchange_bound(queue="queue-a", exchange="amq.direct", binding_key="a")) responses.append(session.exchange_bound(queue="queue-b", exchange="amq.direct", binding_key="b")) for r in responses: self.assert_(not r.exchange_not_found) self.assert_(not r.queue_not_found) self.assert_(not r.key_not_matched) #check expected messages are there self.assertMessageOnQueue("queue-a", "Msg0001", "A_Message1") self.assertMessageOnQueue("queue-b", "Msg0002", "B_Message1") self.assertEmptyQueue("queue-a") self.assertEmptyQueue("queue-b") session.queue_declare(queue="queue-c", durable=True) #send a message to a topic such that it reaches all queues session.exchange_bind(queue="queue-a", exchange="amq.topic", binding_key="abc") session.exchange_bind(queue="queue-b", exchange="amq.topic", binding_key="abc") session.exchange_bind(queue="queue-c", exchange="amq.topic", binding_key="abc") session.message_transfer(destination="amq.topic", message=self.createMessage(routing_key="abc", correlation_id="Msg0003", body="AB_Message2")) # #check LVQ exists and has exepected messages: # session.queue_declare(queue="lvq-test", durable=True, passive=True) # session.message_subscribe(destination="lvq", queue="lvq-test") # lvq = session.incoming("lvq") # lvq.start() # accepted = RangedSet() # for m in ["A2", "B3", "C1"]: # msg = lvq.get(timeout=1) # self.assertEquals(m, msg.body) # accepted.add(msg.id) # try: # extra = lvq.get(timeout=1) # self.fail("lvq-test not empty, contains: " + extra.body) # except Empty: None # #publish some more messages while subscriber is active (no replacement): # session.message_transfer(message=self.createMessage(routing_key="lvq-test", application_headers={"qpid.LVQ_key":"C"}, body="C2")) # session.message_transfer(message=self.createMessage(routing_key="lvq-test", application_headers={"qpid.LVQ_key":"C"}, body="C3")) # session.message_transfer(message=self.createMessage(routing_key="lvq-test", application_headers={"qpid.LVQ_key":"A"}, body="A3")) # session.message_transfer(message=self.createMessage(routing_key="lvq-test", application_headers={"qpid.LVQ_key":"A"}, body="A4")) # session.message_transfer(message=self.createMessage(routing_key="lvq-test", application_headers={"qpid.LVQ_key":"C"}, body="C4")) # #check that accepting replaced messages is safe # session.message_accept(accepted) def phase3(self): session = self.session # #lvq recovery validation # session.queue_declare(queue="lvq-test", durable=True, passive=True) # session.message_subscribe(destination="lvq", queue="lvq-test") # lvq = session.incoming("lvq") # lvq.start() # accepted = RangedSet() # lvq.start() # for m in ["C4", "A4"]: # msg = lvq.get(timeout=1) # self.assertEquals(m, msg.body) # accepted.add(msg.id) # session.message_accept(accepted) # try: # extra = lvq.get(timeout=1) # self.fail("lvq-test not empty, contains: " + extra.body) # except Empty: None # session.message_cancel(destination="lvq") # session.queue_delete(queue="lvq-test") #check queues exists session.queue_declare(queue="queue-a", durable=True, passive=True) session.queue_declare(queue="queue-b", durable=True, passive=True) session.queue_declare(queue="queue-c", durable=True, passive=True) session.tx_select() #check expected messages are there self.assertMessageOnQueue("queue-a", "Msg0003", "AB_Message2") self.assertMessageOnQueue("queue-b", "Msg0003", "AB_Message2") self.assertMessageOnQueue("queue-c", "Msg0003", "AB_Message2") self.assertEmptyQueue("queue-a") self.assertEmptyQueue("queue-b") self.assertEmptyQueue("queue-c") #note: default bindings must be restored for this to work session.message_transfer(message=self.createMessage( routing_key="queue-a", correlation_id="Msg0004", body="A_Message3")) session.message_transfer(message=self.createMessage( routing_key="queue-a", correlation_id="Msg0005", body="A_Message4")) session.message_transfer(message=self.createMessage( routing_key="queue-a", correlation_id="Msg0006", body="A_Message5")) session.tx_commit() #delete a queue session.queue_delete(queue="queue-c") session.message_subscribe(destination="ctag", queue="queue-a", accept_mode=0) session.message_flow(destination="ctag", unit=0, value=0xFFFFFFFF) session.message_flow(destination="ctag", unit=1, value=0xFFFFFFFF) included = session.incoming("ctag") msg1 = included.get(timeout=1) self.assertExpectedContent(msg1, "Msg0004", "A_Message3") msg2 = included.get(timeout=1) self.assertExpectedContent(msg2, "Msg0005", "A_Message4") msg3 = included.get(timeout=1) self.assertExpectedContent(msg3, "Msg0006", "A_Message5") self.ack(msg1, msg2, msg3) session.message_transfer(destination="amq.direct", message=self.createMessage( routing_key="queue-b", correlation_id="Msg0007", body="B_Message3")) session.tx_rollback() def phase4(self): session = self.session #check queues exists session.queue_declare(queue="queue-a", durable=True, passive=True) session.queue_declare(queue="queue-b", durable=True, passive=True) self.assertMessageOnQueue("queue-a", "Msg0004", "A_Message3") self.assertMessageOnQueue("queue-a", "Msg0005", "A_Message4") self.assertMessageOnQueue("queue-a", "Msg0006", "A_Message5") self.assertEmptyQueue("queue-a") self.assertEmptyQueue("queue-b") #check this queue doesn't exist try: session.queue_declare(queue="queue-c", durable=True, passive=True) raise Exception("Expected queue-c to have been deleted") except SessionException, e: self.assertEquals(404, e.args[0].error_code) def phase5(self): session = self.session queues = ["queue-a1", "queue-a2", "queue-b1", "queue-b2", "queue-c1", "queue-c2", "queue-d1", "queue-d2"] for q in queues: session.queue_declare(queue=q, durable=True) session.queue_purge(queue=q) session.message_transfer(message=self.createMessage( routing_key="queue-a1", correlation_id="MsgA", body="MessageA")) session.message_transfer(message=self.createMessage( routing_key="queue-b1", correlation_id="MsgB", body="MessageB")) session.message_transfer(message=self.createMessage( routing_key="queue-c1", correlation_id="MsgC", body="MessageC")) session.message_transfer(message=self.createMessage( routing_key="queue-d1", correlation_id="MsgD", body="MessageD")) session.dtx_select() txa = self.xid('a') txb = self.xid('b') txc = self.xid('c') txd = self.xid('d') self.txswap("queue-a1", "queue-a2", txa) self.txswap("queue-b1", "queue-b2", txb) self.txswap("queue-c1", "queue-c2", txc) self.txswap("queue-d1", "queue-d2", txd) #no queue should have any messages accessible for q in queues: self.assertEqual(0, session.queue_query(queue=q).message_count, "Bad count for %s" % (q)) self.assertEqual(self.XA_OK, session.dtx_commit(xid=txa, one_phase=True).status) self.assertEqual(self.XA_OK, session.dtx_rollback(xid=txb).status) self.assertEqual(self.XA_OK, session.dtx_prepare(xid=txc).status) self.assertEqual(self.XA_OK, session.dtx_prepare(xid=txd).status) #further checks not_empty = ["queue-a2", "queue-b1"] for q in queues: if q in not_empty: self.assertEqual(1, session.queue_query(queue=q).message_count, "Bad count for %s" % (q)) else: self.assertEqual(0, session.queue_query(queue=q).message_count, "Bad count for %s" % (q)) def phase6(self): session = self.session #check prepared transaction are reported correctly by recover txc = self.xid('c') txd = self.xid('d') xids = session.dtx_recover().in_doubt ids = [x.global_id for x in xids] #TODO: come up with nicer way to test these if txc.global_id not in ids: self.fail("Recovered xids not as expected. missing: %s" % (txc)) if txd.global_id not in ids: self.fail("Recovered xids not as expected. missing: %s" % (txd)) self.assertEqual(2, len(xids)) queues = ["queue-a1", "queue-a2", "queue-b1", "queue-b2", "queue-c1", "queue-c2", "queue-d1", "queue-d2"] not_empty = ["queue-a2", "queue-b1"] #re-check not_empty = ["queue-a2", "queue-b1"] for q in queues: if q in not_empty: self.assertEqual(1, session.queue_query(queue=q).message_count, "Bad count for %s" % (q)) else: self.assertEqual(0, session.queue_query(queue=q).message_count, "Bad count for %s" % (q)) #complete the prepared transactions self.assertEqual(self.XA_OK, session.dtx_commit(xid=txc).status) self.assertEqual(self.XA_OK, session.dtx_rollback(xid=txd).status) not_empty.append("queue-c2") not_empty.append("queue-d1") for q in queues: if q in not_empty: self.assertEqual(1, session.queue_query(queue=q).message_count) else: self.assertEqual(0, session.queue_query(queue=q).message_count) def phase7(self): session = self.session session.synchronous = False # check xids from phase 6 are gone txc = self.xid('c') txd = self.xid('d') xids = session.dtx_recover().in_doubt ids = [x.global_id for x in xids] #TODO: come up with nicer way to test these if txc.global_id in ids: self.fail("Xid still present : %s" % (txc)) if txd.global_id in ids: self.fail("Xid still present : %s" % (txc)) self.assertEqual(0, len(xids)) #test deletion of queue after publish #create queue session.queue_declare(queue = "q", auto_delete=True, durable=True) #send message for i in range(1, 10): session.message_transfer(message=self.createMessage(routing_key = "q", body = "my-message")) session.synchronous = True #explicitly delete queue session.queue_delete(queue = "q") #test acking of message from auto-deleted queue #create queue session.queue_declare(queue = "q", auto_delete=True, durable=True) #send message session.message_transfer(message=self.createMessage(routing_key = "q", body = "my-message")) #create consumer session.message_subscribe(queue = "q", destination = "a", accept_mode=0, acquire_mode=0) session.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "a") session.message_flow(unit = 0, value = 10, destination = "a") queue = session.incoming("a") #consume the message, cancel subscription (triggering auto-delete), then ack it msg = queue.get(timeout = 5) session.message_cancel(destination = "a") self.ack(msg) #test implicit deletion of bindings when queue is deleted session.queue_declare(queue = "durable-subscriber-queue", exclusive=True, durable=True) session.exchange_bind(exchange="amq.topic", queue="durable-subscriber-queue", binding_key="xyz") session.message_transfer(destination= "amq.topic", message=self.createMessage(routing_key = "xyz", body = "my-message")) session.queue_delete(queue = "durable-subscriber-queue") #test unbind: #create a series of bindings to a queue session.queue_declare(queue = "binding-test-queue", durable=True) session.exchange_bind(exchange="amq.direct", queue="binding-test-queue", binding_key="abc") session.exchange_bind(exchange="amq.direct", queue="binding-test-queue", binding_key="pqr") session.exchange_bind(exchange="amq.direct", queue="binding-test-queue", binding_key="xyz") session.exchange_bind(exchange="amq.match", queue="binding-test-queue", binding_key="a", arguments={"x-match":"all", "p":"a"}) session.exchange_bind(exchange="amq.match", queue="binding-test-queue", binding_key="b", arguments={"x-match":"all", "p":"b"}) session.exchange_bind(exchange="amq.match", queue="binding-test-queue", binding_key="c", arguments={"x-match":"all", "p":"c"}) #then restart broker... def phase8(self): session = self.session #continue testing unbind: #send messages to the queue via each of the bindings for k in ["abc", "pqr", "xyz"]: data = "first %s" % (k) session.message_transfer(destination= "amq.direct", message=self.createMessage(routing_key=k, body=data)) for a in [{"p":"a"}, {"p":"b"}, {"p":"c"}]: data = "first %s" % (a["p"]) session.message_transfer(destination="amq.match", message=self.createMessage(application_headers=a, body=data)) #unbind some bindings (using final 0-10 semantics) session.exchange_unbind(exchange="amq.direct", queue="binding-test-queue", binding_key="pqr") session.exchange_unbind(exchange="amq.match", queue="binding-test-queue", binding_key="b") #send messages again for k in ["abc", "pqr", "xyz"]: data = "second %s" % (k) session.message_transfer(destination= "amq.direct", message=self.createMessage(routing_key=k, body=data)) for a in [{"p":"a"}, {"p":"b"}, {"p":"c"}]: data = "second %s" % (a["p"]) session.message_transfer(destination="amq.match", message=self.createMessage(application_headers=a, body=data)) #check that only the correct messages are received expected = [] for k in ["abc", "pqr", "xyz"]: expected.append("first %s" % (k)) for a in [{"p":"a"}, {"p":"b"}, {"p":"c"}]: expected.append("first %s" % (a["p"])) for k in ["abc", "xyz"]: expected.append("second %s" % (k)) for a in [{"p":"a"}, {"p":"c"}]: expected.append("second %s" % (a["p"])) session.message_subscribe(queue = "binding-test-queue", destination = "binding-test") session.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "binding-test") session.message_flow(unit = 0, value = 10, destination = "binding-test") queue = session.incoming("binding-test") while len(expected): msg = queue.get(timeout=1) if msg.body not in expected: self.fail("Missing message: %s" % msg.body) expected.remove(msg.body) try: msg = queue.get(timeout=1) self.fail("Got extra message: %s" % msg.body) except Empty: pass session.queue_declare(queue = "durable-subscriber-queue", exclusive=True, durable=True) session.exchange_bind(exchange="amq.topic", queue="durable-subscriber-queue", binding_key="xyz") session.message_transfer(destination= "amq.topic", message=self.createMessage(routing_key = "xyz", body = "my-message")) session.queue_delete(queue = "durable-subscriber-queue") def xid(self, txid, branchqual = ''): return self.session.xid(format=0, global_id=txid, branch_id=branchqual) def txswap(self, src, dest, tx): self.assertEqual(self.XA_OK, self.session.dtx_start(xid=tx).status) self.session.message_subscribe(destination="temp-swap", queue=src, accept_mode=0) self.session.message_flow(destination="temp-swap", unit=0, value=1) self.session.message_flow(destination="temp-swap", unit=1, value=0xFFFFFFFF) msg = self.session.incoming("temp-swap").get(timeout=1) self.session.message_cancel(destination="temp-swap") self.session.message_transfer(message=self.createMessage(routing_key=dest, correlation_id=self.getProperty(msg, 'correlation_id'), body=msg.body)) self.ack(msg) self.assertEqual(self.XA_OK, self.session.dtx_end(xid=tx).status) def assertEmptyQueue(self, name): self.assertEqual(0, self.session.queue_query(queue=name).message_count) def assertConnectionException(self, expectedCode, message): self.assertEqual("connection", message.method.klass.name) self.assertEqual("close", message.method.name) self.assertEqual(expectedCode, message.reply_code) def assertExpectedMethod(self, reply, klass, method): self.assertEqual(klass, reply.method.klass.name) self.assertEqual(method, reply.method.name) def assertExpectedContent(self, msg, id, body): self.assertEqual(id, self.getProperty(msg, 'correlation_id')) self.assertEqual(body, msg.body) return msg def getProperty(self, msg, name): for h in msg.headers: if hasattr(h, name): return getattr(h, name) return None def ack(self, *msgs): session = self.session set = RangedSet() for m in msgs: set.add(m.id) #TODO: tidy up completion session.receiver._completed.add(m.id) session.message_accept(set) session.channel.session_completed(session.receiver._completed) def assertExpectedGetResult(self, id, body): return self.assertExpectedContent(session.incoming("incoming-gets").get(timeout=1), id, body) def assertEqual(self, expected, actual, msg=''): if expected != actual: raise Exception("%s expected: %s actual: %s" % (msg, expected, actual)) def assertMessageOnQueue(self, queue, id, body): self.session.message_subscribe(destination="incoming-gets", queue=queue, accept_mode=0) self.session.message_flow(destination="incoming-gets", unit=0, value=1) self.session.message_flow(destination="incoming-gets", unit=1, value=0xFFFFFFFF) msg = self.session.incoming("incoming-gets").get(timeout=1) self.assertExpectedContent(msg, id, body) self.ack(msg) self.session.message_cancel(destination="incoming-gets") def __init__(self): TestBase010.__init__(self, "run") self.setBroker("localhost") self.errata = [] def connect(self): """ Connects to the broker """ self.conn = Connection(connect(self.host, self.port)) self.conn.start(timeout=10) self.session = self.conn.session("test-session", timeout=10) def run(self, args=sys.argv[1:]): try: opts, extra = getopt(args, "r:s:e:b:p:h", ["retry=", "spec=", "errata=", "broker=", "phase=", "help"]) except GetoptError, e: self._die(str(e)) phase = 0 retry = 0; for opt, value in opts: if opt in ("-h", "--help"): self._die() if opt in ("-s", "--spec"): self.spec = value if opt in ("-e", "--errata"): self.errata.append(value) if opt in ("-b", "--broker"): self.setBroker(value) if opt in ("-p", "--phase"): phase = int(value) if opt in ("-r", "--retry"): retry = int(value) if not phase: self._die("please specify the phase to run") phase = "phase%d" % phase self.connect() try: getattr(self, phase)() print phase, "succeeded" res = True; except Exception, e: print phase, "failed: ", e traceback.print_exc() res = False if not self.session.error(): self.session.close(timeout=10) self.conn.close(timeout=10) # Crude fix to wait for thread in client to exit after return from session_close() # Reduces occurrences of "Unhandled exception in thread" messages after each test import time time.sleep(1) return res def setBroker(self, broker): rex = re.compile(r""" # [ [ / ] @] [ : ] ^ (?: ([^/]*) (?: / ([^@]*) )? @)? ([^:]+) (?: :([0-9]+))?$""", re.X) match = rex.match(broker) if not match: self._die("'%s' is not a valid broker" % (broker)) self.user, self.password, self.host, self.port = match.groups() self.port = int(default(self.port, 5672)) self.user = default(self.user, "guest") self.password = default(self.password, "guest") def _die(self, message = None): if message: print message print """ Options: -h/--help : this message -s/--spec : file containing amqp XML spec -p/--phase : test phase to run -b/--broker [[/]@][:] : broker to connect to """ sys.exit(1) def default(value, default): if (value == None): return default else: return value if __name__ == "__main__": test = PersistenceTest() if not test.run(): sys.exit(1) qpid-cpp-store-debian-0.16/tests/jrnl/0000755000176300017630000000000011761423033016642 5ustar cajuscajusqpid-cpp-store-debian-0.16/tests/jrnl/cp_rtest_jrnl0000755000176300017630000000333411142067437021450 0ustar cajuscajus#!/bin/bash # Copyright (c) 2007 Red Hat, Inc. # # This file is part of the Qpid async store library msgstore.so. # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 # USA # # The GNU Lesser General Public License is available in the file COPYING. JDATA_DIR=jdata TAR_DIR=rd_test_jrnls function get_filename { local prefix=$1 local file_num=$2 local suffix=$3 if (( file_num < 10 )); then local num="000${file_num}" elif (( file_num < 100 )); then local num="00${file_num}" elif (( file_num < 1000 )); then local num="0${file_num}" else local num="${file_num}" fi FILENAME=${prefix}${num}${suffix} return 0 } if (( $# != 1 )); then echo "Incorrect args, expected 1 arg (usage: \"prep \")" exit fi get_filename "t" $1 ".tar.gz" if [[ -d ${JDATA_DIR} ]]; then rm -rf ${JDATA_DIR}/* else mkdir -p ${JDATA_DIR} fi if [[ -f "${TAR_DIR}/${FILENAME}" ]]; then tar -C ${JDATA_DIR} -xzf "${TAR_DIR}/${FILENAME}" else echo "Error: file \"${TAR_DIR}/${FILENAME}\" not found." fi qpid-cpp-store-debian-0.16/tests/jrnl/_st_read.cpp0000644000176300017630000003741411232164074021137 0ustar cajuscajus/* * Copyright (c) 2008 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #include "../unit_test.h" #include #include #include "jrnl/jcntl.hpp" using namespace boost::unit_test; using namespace mrg::journal; using namespace std; QPID_AUTO_TEST_SUITE(journal_read) const string test_filename("_st_read"); #include "_st_helper_fns.hpp" // === Test suite === #ifndef LONG_TEST /* * ============================================== * NORMAL TESTS * This section contains normal "make check" tests * for building/packaging. These are built when * LONG_TEST is _not_ defined. * ============================================== */ QPID_AUTO_TEST_CASE(empty_read) { string test_name = get_test_name(test_filename, "empty_read"); try { string msg; string rmsg; string xid; bool transientFlag; bool externalFlag; test_jrnl_cb cb; test_jrnl jc(test_name, test_dir, test_name, cb); jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); read_msg(jc, rmsg, xid, transientFlag, externalFlag, RHM_IORES_EMPTY); } catch(const exception& e) { BOOST_FAIL(e.what()); } cout << "ok" << endl; } QPID_AUTO_TEST_CASE(enqueue_read_dequeue_block) { string test_name = get_test_name(test_filename, "enqueue_read_dequeue_block"); try { string msg; string rmsg; string xid; bool transientFlag; bool externalFlag; test_jrnl_cb cb; test_jrnl jc(test_name, test_dir, test_name, cb); jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); for (int m=0; m #include #include "jrnl/txn_map.hpp" #include using namespace boost::unit_test; using namespace mrg::journal; using namespace std; QPID_AUTO_TEST_SUITE(txn_map_suite) const string test_filename("_ut_txn_map"); // === Helper functions === const string make_xid(u_int64_t rid) { stringstream ss; ss << "XID-" << setfill('0') << setw(16) << hex << rid; ss << "-0123456789abcdef"; return ss.str(); } void check_td_equal(txn_data& td1, txn_data& td2) { BOOST_CHECK_EQUAL(td1._rid, td2._rid); BOOST_CHECK_EQUAL(td1._drid, td2._drid); BOOST_CHECK_EQUAL(td1._pfid, td2._pfid); BOOST_CHECK_EQUAL(td1._enq_flag, td2._enq_flag); BOOST_CHECK_EQUAL(td1._aio_compl, td2._aio_compl); } // === Test suite === QPID_AUTO_TEST_CASE(constructor) { cout << test_filename << ".constructor: " << flush; const u_int64_t rid = 0x123456789abcdef0ULL; const u_int64_t drid = 0xfedcba9876543210ULL; const u_int16_t pfid = 0xfedcU; const bool enq_flag = true; txn_data td(rid, drid, pfid, enq_flag); BOOST_CHECK_EQUAL(td._rid, rid); BOOST_CHECK_EQUAL(td._drid, drid); BOOST_CHECK_EQUAL(td._pfid, pfid); BOOST_CHECK_EQUAL(td._enq_flag, enq_flag); BOOST_CHECK_EQUAL(td._aio_compl, false); txn_map t1; BOOST_CHECK(t1.empty()); BOOST_CHECK_EQUAL(t1.size(), u_int32_t(0)); cout << "ok" << endl; } QPID_AUTO_TEST_CASE(insert_get) { cout << test_filename << ".insert_get: " << flush; u_int16_t fid; u_int64_t rid; u_int16_t pfid_start = 0x2000U; u_int64_t rid_begin = 0xffffffff00000000ULL; u_int64_t rid_end = 0xffffffff00000200ULL; // insert with no dups u_int64_t rid_incr_1 = 4ULL; txn_map t2; t2.set_num_jfiles(pfid_start + (rid_end - rid_begin)/rid_incr_1); for (rid = rid_begin, fid = pfid_start; rid < rid_end; rid += rid_incr_1, fid++) t2.insert_txn_data(make_xid(rid), txn_data(rid, ~rid, fid, false)); BOOST_CHECK(!t2.empty()); BOOST_CHECK_EQUAL(t2.size(), u_int32_t(128)); // get u_int64_t rid_incr_2 = 6ULL; for (u_int64_t rid = rid_begin; rid < rid_end; rid += rid_incr_2) { string xid = make_xid(rid); BOOST_CHECK_EQUAL(t2.in_map(xid), (rid%rid_incr_1 ? false : true)); } cout << "ok" << endl; } QPID_AUTO_TEST_SUITE_END() qpid-cpp-store-debian-0.16/tests/jrnl/prof0000755000176300017630000000225711142067437017551 0ustar cajuscajus#!/bin/bash # Copyright (c) 2007 Red Hat, Inc. # # This file is part of the Qpid async store library msgstore.so. # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 # USA # # The GNU Lesser General Public License is available in the file COPYING. mkdir -p profile opcontrol --setup --no-vmlinux --separate=library opcontrol --start # -- Do stuff here -- ./jtest wtests.csv 264 # -- End of stuff -- opcontrol --stop opcontrol --dump opcontrol --shutdown opreport -l ./jtest opannotate --source --output-dir=profile ./jtest qpid-cpp-store-debian-0.16/tests/jrnl/_st_basic_txn.cpp0000644000176300017630000002000711232164074022164 0ustar cajuscajus/* * Copyright (c) 2008 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #include "../unit_test.h" #include #include #include "jrnl/jcntl.hpp" using namespace boost::unit_test; using namespace mrg::journal; using namespace std; QPID_AUTO_TEST_SUITE(journal_basic_txn) const string test_filename("_st_basic_txn"); #include "_st_helper_fns.hpp" // === Test suite === QPID_AUTO_TEST_CASE(enqueue_commit_dequeue_block) { string test_name = get_test_name(test_filename, "enqueue_commit_dequeue_block"); try { string msg; string xid; test_jrnl_cb cb; test_jrnl jc(test_name, test_dir, test_name, cb); jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); create_xid(xid, 0, XID_SIZE); for (int m=0; m #include #include "jrnl/jcntl.hpp" using namespace boost::unit_test; using namespace mrg::journal; using namespace std; QPID_AUTO_TEST_SUITE(jinf_suite) const string test_filename("_ut_jinf"); #include "_st_helper_fns.hpp" timespec ts; QPID_AUTO_TEST_CASE(write_constructor) { string test_name = get_test_name(test_filename, "write_constructor"); const string jid = test_name + "_jid"; const string base_filename = test_name + "_bfn"; jdir::create_dir(test_dir); // Check test dir exists; create it if not ::clock_gettime(CLOCK_REALTIME, &ts); jinf ji(jid, test_dir, base_filename, NUM_JFILES, false, 0, JFSIZE_SBLKS, JRNL_WMGR_DEF_PAGE_SIZE, JRNL_WMGR_DEF_PAGES, ts); BOOST_CHECK_EQUAL(ji.jver(), RHM_JDAT_VERSION); BOOST_CHECK(ji.jid().compare(jid) == 0); BOOST_CHECK(ji.jdir().compare(test_dir) == 0); BOOST_CHECK(ji.base_filename().compare(base_filename) == 0); const timespec this_ts = ji.ts(); BOOST_CHECK_EQUAL(this_ts.tv_sec, ts.tv_sec); BOOST_CHECK_EQUAL(this_ts.tv_nsec, ts.tv_nsec); BOOST_CHECK_EQUAL(ji.num_jfiles(), u_int16_t(NUM_JFILES)); BOOST_CHECK_EQUAL(ji.is_ae(), false); BOOST_CHECK_EQUAL(ji.ae_max_jfiles(), u_int16_t(0)); BOOST_CHECK_EQUAL(ji.jfsize_sblks(), u_int32_t(JFSIZE_SBLKS)); BOOST_CHECK_EQUAL(ji.sblk_size_dblks(), u_int16_t(JRNL_SBLK_SIZE)); BOOST_CHECK_EQUAL(ji.dblk_size(), u_int32_t(JRNL_DBLK_SIZE)); BOOST_CHECK_EQUAL(ji.wcache_pgsize_sblks(), u_int32_t(JRNL_WMGR_DEF_PAGE_SIZE)); BOOST_CHECK_EQUAL(ji.wcache_num_pages(), u_int16_t(JRNL_WMGR_DEF_PAGES)); BOOST_CHECK_EQUAL(ji.rcache_pgsize_sblks(), u_int32_t(JRNL_RMGR_PAGE_SIZE)); BOOST_CHECK_EQUAL(ji.rcache_num_pages(), u_int16_t(JRNL_RMGR_PAGES)); ji.write(); cout << "done" << endl; } QPID_AUTO_TEST_CASE(read_constructor) { string test_name = get_test_name(test_filename, "read_constructor"); const string jid = test_name + "_jid"; const string base_filename = test_name + "_bfn"; lfid_pfid_map::create_new_jinf(jid, base_filename, false); stringstream fn; fn << test_dir << "/" < sblk_buffer(expand_size, 0); of.write(&sblk_buffer[0], expand_size); of.close(); stringstream fn; fn << test_dir << "/" << base_filename << "." << JRNL_INFO_EXTENSION; jinf ji(fn.str(), false); try { ji.analyze(); BOOST_FAIL("Failed to detect irregular journal file size in file \"" << filename << "\""); } catch (const jexception& e) {} // ignore - expected m.destroy_journal(); } cout << "done" << endl; } QPID_AUTO_TEST_CASE(analyze_owi_in_non_ae_journal) { string test_name = get_test_name(test_filename, "analyze_owi_in_non_ae_journal"); const string jid = test_name + "_jid"; const string base_filename = test_name + "_bfn"; lfid_pfid_map m(jid, base_filename); for (u_int16_t oldest_file = 1; oldest_file < NUM_DEFAULT_JFILES-1; oldest_file++) { for (u_int16_t bad_owi_file = oldest_file + 1; bad_owi_file < NUM_DEFAULT_JFILES; bad_owi_file++) { m.journal_create(NUM_DEFAULT_JFILES, NUM_DEFAULT_JFILES, oldest_file, bad_owi_file); m.write_journal(false, 0); stringstream fn; fn << test_dir << "/" << base_filename << "." << JRNL_INFO_EXTENSION; jinf ji(fn.str(), false); try { ji.analyze(); BOOST_FAIL("Failed to detect irregular OWI flag in non-ae journal file \"" << fn << "\""); } catch (const jexception& e) {} // ignore - expected m.destroy_journal(); } } cout << "done" << endl; } QPID_AUTO_TEST_CASE(analyze_owi_in_ae_min_size_journal) { string test_name = get_test_name(test_filename, "analyze_owi_in_ae_min_size_journal"); const string jid = test_name + "_jid"; const string base_filename = test_name + "_bfn"; lfid_pfid_map m(jid, base_filename); for (u_int16_t oldest_file = 1; oldest_file < NUM_JFILES-1; oldest_file++) { for (u_int16_t bad_owi_file = oldest_file + 1; bad_owi_file < NUM_JFILES; bad_owi_file++) { m.journal_create(NUM_JFILES, NUM_JFILES, oldest_file, bad_owi_file); m.write_journal(true, 16); stringstream fn; fn << test_dir << "/" << base_filename << "." << JRNL_INFO_EXTENSION; jinf ji(fn.str(), false); try { ji.analyze(); BOOST_FAIL("Failed to detect irregular OWI flag in min-sized ae journal file \"" << fn << "\""); } catch (const jexception& e) {} // ignore - expected m.destroy_journal(); } } cout << "done" << endl; } QPID_AUTO_TEST_SUITE_END() qpid-cpp-store-debian-0.16/tests/jrnl/_ut_enq_map.cpp0000644000176300017630000002541511433263011021636 0ustar cajuscajus/* * Copyright (c) 2007, 2008, 2009, 2010 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #include "../unit_test.h" #include #include "jrnl/enq_map.hpp" #include "jrnl/jerrno.hpp" using namespace boost::unit_test; using namespace mrg::journal; using namespace std; QPID_AUTO_TEST_SUITE(enq_map_suite) const string test_filename("_ut_enq_map"); QPID_AUTO_TEST_CASE(constructor) { cout << test_filename << ".constructor: " << flush; enq_map e1; BOOST_CHECK(e1.empty()); BOOST_CHECK_EQUAL(e1.size(), u_int32_t(0)); cout << "ok" << endl; } QPID_AUTO_TEST_CASE(insert_get) { cout << test_filename << ".insert_get: " << flush; u_int16_t pfid; u_int64_t rid; u_int16_t pfid_start = 0x2000U; u_int64_t rid_begin = 0xffffffff00000000ULL; u_int64_t rid_end = 0xffffffff00000200ULL; // insert with no dups u_int64_t rid_incr_1 = 4ULL; enq_map e2; e2.set_num_jfiles(pfid_start + (rid_end - rid_begin)/rid_incr_1); for (rid = rid_begin, pfid = pfid_start; rid < rid_end; rid += rid_incr_1, pfid++) BOOST_CHECK_EQUAL(e2.insert_pfid(rid, pfid), enq_map::EMAP_OK); BOOST_CHECK(!e2.empty()); BOOST_CHECK_EQUAL(e2.size(), u_int32_t(128)); // get u_int64_t rid_incr_2 = 6ULL; for (u_int64_t rid = rid_begin; rid < rid_end; rid += rid_incr_2) { BOOST_CHECK_EQUAL(e2.is_enqueued(rid), (rid%rid_incr_1 ? false : true)); u_int16_t exp_pfid = pfid_start + (u_int16_t)((rid - rid_begin)/rid_incr_1); int16_t ret_fid = e2.get_pfid(rid); if (ret_fid < enq_map::EMAP_OK) // fail { BOOST_CHECK_EQUAL(ret_fid, enq_map::EMAP_RID_NOT_FOUND); BOOST_CHECK(rid%rid_incr_1); } else { BOOST_CHECK_EQUAL(ret_fid, exp_pfid); BOOST_CHECK(rid%rid_incr_1 == 0); } if ((rid + rid_incr_2)%(8 * rid_incr_2) == 0) pfid++; } // insert with dups for (rid = rid_begin, pfid = pfid_start; rid < rid_end; rid += rid_incr_2, pfid++) { int16_t res = e2.insert_pfid(rid, pfid); if (res < enq_map::EMAP_OK) // fail { BOOST_CHECK_EQUAL(res, enq_map::EMAP_DUP_RID); BOOST_CHECK(rid%rid_incr_1 == 0); } else BOOST_CHECK(rid%rid_incr_1); } BOOST_CHECK_EQUAL(e2.size(), u_int32_t(171)); e2.clear(); BOOST_CHECK(e2.empty()); BOOST_CHECK_EQUAL(e2.size(), u_int32_t(0)); cout << "ok" << endl; } QPID_AUTO_TEST_CASE(get_remove) { cout << test_filename << ".get_remove: " << flush; u_int16_t pfid; u_int64_t rid; u_int16_t pfid_start = 0x3000U; u_int64_t rid_begin = 0xeeeeeeee00000000ULL; u_int64_t rid_end = 0xeeeeeeee00000200ULL; u_int64_t rid_incr_1 = 4ULL; u_int64_t num_incr_1 = (rid_end - rid_begin)/rid_incr_1; enq_map e3; e3.set_num_jfiles(pfid_start + (rid_end - rid_begin)/rid_incr_1); for (rid = rid_begin, pfid = pfid_start; rid < rid_end; rid += rid_incr_1, pfid++) BOOST_CHECK_EQUAL(e3.insert_pfid(rid, pfid), enq_map::EMAP_OK); BOOST_CHECK_EQUAL(e3.size(), num_incr_1); u_int64_t rid_incr_2 = 6ULL; for (rid = rid_begin, pfid = pfid_start; rid < rid_end; rid += rid_incr_2, pfid++) { u_int16_t exp_pfid = pfid_start + (u_int16_t)((rid - rid_begin)/rid_incr_1); int16_t ret_fid = e3.get_remove_pfid(rid); if (ret_fid < enq_map::EMAP_OK) // fail { BOOST_CHECK_EQUAL(ret_fid, enq_map::EMAP_RID_NOT_FOUND); BOOST_CHECK(rid%rid_incr_1); } else { BOOST_CHECK_EQUAL(ret_fid, exp_pfid); BOOST_CHECK(rid%rid_incr_1 == 0); } } BOOST_CHECK_EQUAL(e3.size(), u_int32_t(85)); cout << "ok" << endl; } QPID_AUTO_TEST_CASE(lock) { cout << test_filename << ".lock: " << flush; u_int16_t pfid; u_int64_t rid; u_int16_t pfid_start = 0x4000U; u_int64_t rid_begin = 0xdddddddd00000000ULL; u_int64_t rid_end = 0xdddddddd00000200ULL; // insert, every second entry is locked u_int64_t rid_incr_1 = 4ULL; u_int64_t num_incr_1 = (rid_end - rid_begin)/rid_incr_1; bool locked = false; enq_map e4; e4.set_num_jfiles(pfid_start + (rid_end - rid_begin)/rid_incr_1); for (rid = rid_begin, pfid = pfid_start; rid < rid_end; rid += rid_incr_1, pfid++) { BOOST_CHECK_EQUAL(e4.insert_pfid(rid, pfid, locked), enq_map::EMAP_OK); locked = !locked; } BOOST_CHECK_EQUAL(e4.size(), num_incr_1); // unlock and lock non-existent rids int16_t res = e4.lock(1ULL); if (res < enq_map::EMAP_OK) BOOST_CHECK_EQUAL(res, enq_map::EMAP_RID_NOT_FOUND); else BOOST_ERROR("Failed to detect locking non-existent rid."); res = e4.unlock(2ULL); if (res < enq_map::EMAP_OK) BOOST_CHECK_EQUAL(res, enq_map::EMAP_RID_NOT_FOUND); else BOOST_ERROR("Failed to detect unlocking non-existent rid."); // get / unlock for (u_int64_t rid = rid_begin; rid < rid_end; rid += rid_incr_1) { int16_t fid = e4.get_pfid(rid); if (fid < enq_map::EMAP_OK) // fail { BOOST_CHECK_EQUAL(fid, enq_map::EMAP_LOCKED); BOOST_CHECK(rid%(2*rid_incr_1)); // unlock, read, then relock BOOST_CHECK_EQUAL(e4.unlock(rid), enq_map::EMAP_OK); BOOST_CHECK(e4.get_pfid(rid) >= enq_map::EMAP_OK); BOOST_CHECK_EQUAL(e4.lock(rid), enq_map::EMAP_OK); fid = e4.get_pfid(rid); if (fid < enq_map::EMAP_OK) // fail BOOST_CHECK_EQUAL(fid, enq_map::EMAP_LOCKED); else BOOST_ERROR("Failed to prevent getting locked record"); } } // remove all; if locked, use with txn_flag true; should ignore all locked records for (u_int64_t rid = rid_begin; rid < rid_end; rid += rid_incr_1) BOOST_CHECK(e4.get_remove_pfid(rid, true) >= enq_map::EMAP_OK); BOOST_CHECK(e4.empty()); cout << "ok" << endl; } QPID_AUTO_TEST_CASE(lists) { cout << test_filename << ".lists: " << flush; u_int16_t pfid; u_int64_t rid; u_int16_t pfid_start = 0x5000UL; u_int64_t rid_begin = 0xdddddddd00000000ULL; u_int64_t rid_end = 0xdddddddd00000200ULL; // insert, every second entry is locked u_int64_t rid_incr_1 = 4ULL; u_int64_t num_incr_1 = (rid_end - rid_begin)/rid_incr_1; vector rid_list; vector pfid_list; enq_map e5; e5.set_num_jfiles(pfid_start + (rid_end - rid_begin)/rid_incr_1); for (rid = rid_begin, pfid = pfid_start; rid < rid_end; rid += rid_incr_1, pfid++) { BOOST_CHECK_EQUAL(e5.insert_pfid(rid, pfid), enq_map::EMAP_OK); rid_list.push_back(rid); pfid_list.push_back(pfid); } BOOST_CHECK_EQUAL(e5.size(), num_incr_1); BOOST_CHECK_EQUAL(rid_list.size(), num_incr_1); BOOST_CHECK_EQUAL(pfid_list.size(), num_incr_1); vector ret_rid_list; e5.rid_list(ret_rid_list); BOOST_CHECK_EQUAL(ret_rid_list.size(), num_incr_1); for (unsigned i=0; i ret_pfid_list; e5.pfid_list(ret_pfid_list); BOOST_CHECK_EQUAL(ret_pfid_list.size(), num_incr_1); for (unsigned i=0; i= enq_map::EMAP_OK); for (u_int16_t pfid=0; pfid<4; pfid++) { if (pfid == 1) BOOST_CHECK_EQUAL(e6.get_enq_cnt(pfid), u_int32_t(90)); else BOOST_CHECK_EQUAL(e6.get_enq_cnt(pfid), u_int32_t(0)); } // Now resize the file up and make sure the count in file 1 still exists e6.set_num_jfiles(8); for (u_int16_t pfid=0; pfid<8; pfid++) { if (pfid == 1) BOOST_CHECK_EQUAL(e6.get_enq_cnt(pfid), u_int32_t(90)); else BOOST_CHECK_EQUAL(e6.get_enq_cnt(pfid), u_int32_t(0)); } cout << "ok" << endl; } QPID_AUTO_TEST_CASE(stress) { cout << test_filename << ".stress: " << flush; u_int64_t rid; u_int64_t rid_cnt; u_int64_t rid_begin = 0xffffffff00000000ULL; u_int64_t num_rid = 10; enq_map e7; e7.set_num_jfiles(rid_begin + num_rid); // insert even rids with no dups for (rid = rid_begin, rid_cnt = u_int64_t(0); rid_cnt < num_rid; rid += 2ULL, rid_cnt++) BOOST_CHECK_EQUAL(e7.insert_pfid(rid, u_int16_t(0)), enq_map::EMAP_OK); BOOST_CHECK_EQUAL(e7.size(), num_rid); // insert odd rids with no dups for (rid = rid_begin + 1, rid_cnt = u_int64_t(0); rid_cnt < num_rid; rid += 2ULL, rid_cnt++) BOOST_CHECK_EQUAL(e7.insert_pfid(rid, u_int16_t(0)), enq_map::EMAP_OK); BOOST_CHECK_EQUAL(e7.size(), num_rid * 2); // remove even rids for (rid = rid_begin, rid_cnt = u_int64_t(0); rid_cnt < num_rid; rid += 2ULL, rid_cnt++) BOOST_CHECK(e7.get_remove_pfid(rid) >= enq_map::EMAP_OK); BOOST_CHECK_EQUAL(e7.size(), num_rid); cout << "ok" << endl; } QPID_AUTO_TEST_SUITE_END() qpid-cpp-store-debian-0.16/tests/jrnl/_st_auto_expand.cpp0000644000176300017630000001173511232164074022531 0ustar cajuscajus/* * Copyright (c) 2008 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #include "../unit_test.h" #include #include #include "jrnl/jcntl.hpp" using namespace boost::unit_test; using namespace mrg::journal; using namespace std; QPID_AUTO_TEST_SUITE(journal_auto_expand) const string test_filename("_st_auto_expand"); #include "_st_helper_fns.hpp" // === Test suite === QPID_AUTO_TEST_CASE(no_ae_threshold) { string test_name = get_test_name(test_filename, "no_ae_threshold"); try { string msg; test_jrnl_cb cb; test_jrnl jc(test_name, test_dir, test_name, cb); jc.initialize(NUM_DEFAULT_JFILES, false, 0, DEFAULT_JFSIZE_SBLKS); unsigned m; // Fill journal to just below threshold u_int32_t t = num_msgs_to_threshold(NUM_DEFAULT_JFILES, DEFAULT_JFSIZE_SBLKS * JRNL_SBLK_SIZE, LARGE_MSG_REC_SIZE_DBLKS); for (m=0; m #include #include "jrnl/time_ns.hpp" using namespace boost::unit_test; using namespace mrg::journal; using namespace std; QPID_AUTO_TEST_SUITE(time_ns_suite) const string test_filename("_ut_time_ns"); QPID_AUTO_TEST_CASE(constructors) { cout << test_filename << ".constructors: " << flush; const std::time_t sec = 123; const long nsec = 123456789; time_ns t1; BOOST_CHECK_EQUAL(t1.tv_sec, 0); BOOST_CHECK_EQUAL(t1.tv_nsec, 0); BOOST_CHECK_EQUAL(t1.is_zero(), true); time_ns t2(sec, nsec); BOOST_CHECK_EQUAL(t2.tv_sec, sec); BOOST_CHECK_EQUAL(t2.tv_nsec, nsec); BOOST_CHECK_EQUAL(t2.is_zero(), false); time_ns t3(t1); BOOST_CHECK_EQUAL(t3.tv_sec, 0); BOOST_CHECK_EQUAL(t3.tv_nsec, 0); BOOST_CHECK_EQUAL(t3.is_zero(), true); time_ns t4(t2); BOOST_CHECK_EQUAL(t4.tv_sec, sec); BOOST_CHECK_EQUAL(t4.tv_nsec, nsec); BOOST_CHECK_EQUAL(t4.is_zero(), false); t4.set_zero(); BOOST_CHECK_EQUAL(t4.tv_sec, 0); BOOST_CHECK_EQUAL(t4.tv_nsec, 0); BOOST_CHECK_EQUAL(t4.is_zero(), true); cout << "ok" << endl; } QPID_AUTO_TEST_CASE(operators) { cout << test_filename << ".operators: " << flush; const std::time_t sec1 = 123; const long nsec1 = 123456789; const std::time_t sec2 = 1; const long nsec2 = 999999999; const std::time_t sec_sum = sec1 + sec2 + 1; const long nsec_sum = nsec1 + nsec2 - 1000000000; const std::time_t sec_1_minus_2 = sec1 - sec2 - 1; const long nsec_1_minus_2 = nsec1 - nsec2 + 1000000000; const std::time_t sec_2_minus_1 = sec2 - sec1; const long nsec_2_minus_1 = nsec2 - nsec1; time_ns z; time_ns t1(sec1, nsec1); time_ns t2(sec2, nsec2); time_ns t3 = z; BOOST_CHECK_EQUAL(t3.tv_sec, 0); BOOST_CHECK_EQUAL(t3.tv_nsec, 0); BOOST_CHECK_EQUAL(t3 == z, true); BOOST_CHECK_EQUAL(t3 != z, false); BOOST_CHECK_EQUAL(t3 > z, false); BOOST_CHECK_EQUAL(t3 >= z, true); BOOST_CHECK_EQUAL(t3 < z, false); BOOST_CHECK_EQUAL(t3 <= z, true); t3 = t1; BOOST_CHECK_EQUAL(t3.tv_sec, sec1); BOOST_CHECK_EQUAL(t3.tv_nsec, nsec1); BOOST_CHECK_EQUAL(t3 == t1, true); BOOST_CHECK_EQUAL(t3 != t1, false); BOOST_CHECK_EQUAL(t3 > t1, false); BOOST_CHECK_EQUAL(t3 >= t1, true); BOOST_CHECK_EQUAL(t3 < t1, false); BOOST_CHECK_EQUAL(t3 <= t1, true); t3 += z; BOOST_CHECK_EQUAL(t3.tv_sec, sec1); BOOST_CHECK_EQUAL(t3.tv_nsec, nsec1); t3 = t2; BOOST_CHECK_EQUAL(t3.tv_sec, sec2); BOOST_CHECK_EQUAL(t3.tv_nsec, nsec2); BOOST_CHECK_EQUAL(t3 == t2, true); BOOST_CHECK_EQUAL(t3 != t2, false); BOOST_CHECK_EQUAL(t3 > t2, false); BOOST_CHECK_EQUAL(t3 >= t2, true); BOOST_CHECK_EQUAL(t3 < t2, false); BOOST_CHECK_EQUAL(t3 <= t2, true); t3 += z; BOOST_CHECK_EQUAL(t3.tv_sec, sec2); BOOST_CHECK_EQUAL(t3.tv_nsec, nsec2); t3 = t1; t3 += t2; BOOST_CHECK_EQUAL(t3.tv_sec, sec_sum); BOOST_CHECK_EQUAL(t3.tv_nsec, nsec_sum); t3 = t1; t3 -= t2; BOOST_CHECK_EQUAL(t3.tv_sec, sec_1_minus_2); BOOST_CHECK_EQUAL(t3.tv_nsec, nsec_1_minus_2); t3 = t2; t3 -= t1; BOOST_CHECK_EQUAL(t3.tv_sec, sec_2_minus_1); BOOST_CHECK_EQUAL(t3.tv_nsec, nsec_2_minus_1); t3 = t1 + t2; BOOST_CHECK_EQUAL(t3.tv_sec, sec_sum); BOOST_CHECK_EQUAL(t3.tv_nsec, nsec_sum); t3 = t1 - t2; BOOST_CHECK_EQUAL(t3.tv_sec, sec_1_minus_2); BOOST_CHECK_EQUAL(t3.tv_nsec, nsec_1_minus_2); t3 = t2 - t1; BOOST_CHECK_EQUAL(t3.tv_sec, sec_2_minus_1); BOOST_CHECK_EQUAL(t3.tv_nsec, nsec_2_minus_1); cout << "ok" << endl; } QPID_AUTO_TEST_CASE(str) { cout << test_filename << ".str: " << flush; time_ns t1(123, 123456789); BOOST_CHECK_EQUAL(t1.str(), "123.123457"); BOOST_CHECK_EQUAL(t1.str(9), "123.123456789"); BOOST_CHECK_EQUAL(t1.str(0), "123"); time_ns t2(1, 1); BOOST_CHECK_EQUAL(t2.str(9), "1.000000001"); time_ns t3(-12, 345); BOOST_CHECK_EQUAL(t3.str(9), "-11.999999655"); cout << "ok" << endl; } QPID_AUTO_TEST_SUITE_END() qpid-cpp-store-debian-0.16/tests/jrnl/_st_basic.cpp0000644000176300017630000004646111232164074021307 0ustar cajuscajus/* * Copyright (c) 2008 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #include "../unit_test.h" #include #include #include "jrnl/jcntl.hpp" using namespace boost::unit_test; using namespace mrg::journal; using namespace std; QPID_AUTO_TEST_SUITE(journal_basic) const string test_filename("_st_basic"); #include "_st_helper_fns.hpp" // === Test suite === #ifndef LONG_TEST /* * ============================================== * NORMAL TESTS * This section contains normal "make check" tests * for building/packaging. These are built when * LONG_TEST is _not_ defined. * ============================================== */ QPID_AUTO_TEST_CASE(instantiation) { string test_name = get_test_name(test_filename, "instantiation"); try { test_jrnl_cb cb; test_jrnl jc(test_name, test_dir, test_name, cb); BOOST_CHECK_EQUAL(jc.is_ready(), false); } catch(const exception& e) { BOOST_FAIL(e.what()); } cout << "ok" << endl; } QPID_AUTO_TEST_CASE(initialization) { string test_name = get_test_name(test_filename, "initialization"); try { test_jrnl_cb cb; test_jrnl jc(test_name, test_dir, test_name, cb); BOOST_CHECK_EQUAL(jc.is_ready(), false); jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); BOOST_CHECK_EQUAL(jc.is_ready(), true); } catch(const exception& e) { BOOST_FAIL(e.what()); } cout << "ok" << endl; } QPID_AUTO_TEST_CASE(enqueue_dequeue_block) { string test_name = get_test_name(test_filename, "enqueue_dequeue_block"); try { string msg; test_jrnl_cb cb; test_jrnl jc(test_name, test_dir, test_name, cb); jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); for (int m=0; mô·÷ƒ{ñ]øž»¿üð_¿¾½ø)ô··iÕ‡ÿüÍMè?þùîêÉ››¦Õß¾»¸¿üè(~¾º¼þëç~¸¿÷é³g?ýôÓÓŸÔÓ›Ûº®Ÿu[‡~5ü½wïo¯º¿õúÕ³ÝÕ®ÝÙÝ3ñTøë¯þõWW¯†¿y 5Zó÷ijæo<Ùý­=M‡¿mˆ;Ï?ÏúÍÃ_¾{íýÕÿí÷_¾|õÃîíÅþ/_Nÿå'—×w÷×mËØKÚÒþ»»löM~÷ìAÑ›æúäÍūݓ׻WWwÍ–Í?õŸa˦ÿï¶/?ô‡Ë·ß½¿Û¼¼¸¾Û4'MsÊ»¿øöòê—ÏýêâÝÍÝgü­¾èÑæƒ_Ûþí'ßï®w·—M·Üýtyw÷Áßxwyÿª9qÿvq{Ù^í=›<²ç»ÿqñÿ¼ß|ùïÿÚí×{lý½£ûåî~÷vòðþé™·M]k_¼¿¿i>‘—¯žt¿ëaswÿÿA…^݈aŸ¶Ý•¿¹Æ\½{Ývìð¯nxòî¶9¿oï/ww›77Ÿ~w»»øë“ïvÍYØüÒöÜoµý§Ë×í5B<­KU]^Û¦~öà°&S.y˜Û§ºžu”jÙ£,u¡æ¦^ò0ÕÓBoåœÃ,–mͪ–ÅœÃ,—>5ͬÖ4˦,ä¬PµpkÚÌ9ÌzÙÃ4fVcŠí²‡YTó®›bÑo¡æÂ9ó3$þ*E)fçÒ_Df;ï8ý&jÎOUèYǹðW‘.«yí¹èw‘nú½š5K YÎ:Î…¿”Rõ¬ã\øëHo·óFñKéjÖç].ú}¤Ÿj3ïûH.Í"¡g|Žn}ȼ½ù 8Ʀôáö›ÚÂv—ßÿpß}ž«²=ŽÃÿþn÷äæÝýåÛ‹«'ÿùýíû¾ ž–ž_Ù}e.YÏ à4Uxsqu7£žÃü:(iîÏXâˆn¨­Áýüqv…o/îîw·OÞ]|¿{ân›½¹xuTïAÕú‡×—wï®.~±Gfc{—õòúû'oo^7¿íêöÉýw¾ƒîïÆúÝÍÍÕîâúÉøèÿP×ö8>ü«ö÷AÿþðExçû®Ø]]¹-ï.nÛ™¤î?µÉîçû®ÍÝ}¾»Ëÿ¯ùB¿»´/kÿÅç.ï/®._틲ûÝÍÕkô÷ÉÎ÷u¥*Í—ÉÍíçþ¾;l·[üÁùLvpöo´ó/loχg{ö)eF³ ÿpü©îš¬éÍï›Óîæým;Éøæòg·ƒÛÝ»ÝÅ0ùÁõÓýòæ.¾¿½x÷ÃG ¿ÿÅme®» ‹¦øíÅí÷—×O®voÚËÈŒáÇΧ¢ãO—ýUë§Ëë×Í«;¹íä¿h ·Ç›àîþøw7¯éþ­›°x×\5_ßý°ÛÝ÷GÔ_ˆºÿ·¥þ¸ÿõæúÍå÷ïo›_{Ó\oûM+Ö]ûâw·—ûNê~í¿×ŽÃ€ßÑÝ·—¾ú³jÀg¸_,©~±¢úÅú¨_Ü|Ï¿µŒöù£Ãh~5tíåÝMy]\½ß=¹ÿå]S~wßôð÷î75¿ªl÷Ï/šîõÅÕæƒs埞٭v·Ï>ÞïÄñôßI¶¡îžôW‚ÝëÏ©¡ÞÏ>ª8¢9äTsxw¯‰whö¯ÿðå_žÿË—ÿå//÷ÿþÛüööøæêæâþÃS ù*–Õèš²¨;öTõ»_îwwKìèyóM¹ùîêæÕ_7íøcsy½Áì›Ç9ñr¹sBŽ@/ÑM¯¿»úë2çÃå]s&ܼ¹ÿðœxÛ\~/ß5_±Í¯ÚÌúò8S~ó»/ÿm¡3EmD[¸D7Þ-u¾¸¯Â7—í¸Ñž-Ãùs·Ùýüêêi·õ/?¼¾Mê\ùßß//éÏ•ñ÷Ìüo™gêÿ‡n0Ð~Öÿǃþ=ñ—BZ㔯ÿï_ÿå¿~ñïK]nN8XYübÓÞt.6I]]†ócË‹(ǧFÉäÓöåÝæþfóþ®ëΟÞ~⯉´®3ÿ±ðu¦€ËE†À‹_eºU¿«¸ÖüÇ‚× zçyóµ†ÉyÔ~nÞ<Þ]ÿØŽã?Iív¦èÙ«a¯}8ìýÕÅçÚÔ>ýü›§ÿ"ô·¿n˜oÁÙ-ø7p‡;_ÂoÔï~Ü\ß\?i.ä'èïÑщ~acþ7›öÚ¼ùé‡ÝuÓ)›Ÿ/C»‚ÅitªSè\.ˆý©=ÞÝîîÚ7t§Ss.¹‚TΩӜO%þ|RãÛ•jþíÊu|yo„øïLŒ$íØˆéS. úÓîî~ó›ÛæŸþtsû×¼:hÙËÛŸšË[» ðÉ««›»ÝææzÓ|‰Æ½Î)ðl°«ˆ‡mnU±½šuëGûõŸ¸WÜåƒÒ¾äAÁ‹!ÿ£½N·²¤ZRh¨~qEõ‹kª_Ü>|ªCnGlW gìEÈVäüæ9Ç;½Ð·½ž¦õØoÔ6š^³Ls¦Ìù­ÅÙëqß3¾Q‹Â %õäÀ<š~£xïìîÝÅõõ6ûšJ>Ø*F–ßß}ßÍ‚#-¯Zpî^?™¬ ßšÿ·Fú¬j>q)Æm¹»þñ «¶\¬æEδæ÷?_Ÿ¦æ`Š¿Ä#ïü}\ ¢àþÍ wþî”;¿x{ôý˜#öÞŒR.¯_….<¢8‚¿{÷ý÷Ío»S·Àï/Oyüþâç\÷Ó|òÛ„Ñï â.»·×w—áóFÇÐüÿ®]ãzÂC8rîîèýùD]®ÿy×?|G¯nÞ¾=ù,18¤ß›±@ùᣛ%ø)>è—Oj,³;±%…E”¯ß]_Þ_^\µÏ&Ü\_ýB6Ÿ„Óe>;>;À5ÛÑÙ²]âb÷%á<÷iêÞŒ¶<ïÖIýfÁu—,Õà†Sßüö{eÔ /NÖ ³>É«(./Øäo¾øòåÑë(>j¹ßýæñ7OÿP|ûÙö³ožþ®øö“æsþ_ÚOû?|° çé?üË?ˆút«$Ácÿ×ûÝ—¿ûÿ7øcSàˆÕ·Ÿ<‹Ãë1¾Ã+(zà˦Ýš¶ÿÕ÷÷moŒ[ŠoûÞ7n¿ýä³í'œ®MûþùЬèkÑtËxþjrÓÔªëº}¿}ötǵƒ^¦ÑA¨ÕðowwwÝ#°ÿçþ¯áͯnw]5ž]:Ntÿ‰!EFkfIèiøòòí»«;mñݳö6Éw—×ݩ׽dê]›ÁYD8 ÎÎtÄlnÿ*#\ª³˜EÎuγ 00ú`gÔwúzÚ# bœg×Äàšíœ‰L —0-lÄ´H/cZ$2-†”i13í©.{b¤ÝKÄ賦E@Ø´àœ6M×IÜfø—?-ÆÔ"'P'?Æz.ûj™¨yb×1«~zšYSäeGs@ ]@´´Ñ2B@ôÂ6Ëä¢å-§¢=ÕEƒ`Y¶}•x/ì#¢e@D´DFDëñ;üõ/ñŸ"A"ÝcÖíû`%."ZŒ?È"‡Dó@ñF-Ï3‰8 b$Ð5KƒWSdbÌ"†Ëp–6ÃYFÈp^šÉg8Ë!ÃYNg8{ªËži÷Ò1úgâ,‘)ÎK#nŠ2ëÀ׸g1Žq9Ç9µá/À>ÇYæg¦È9Îøæ8çHÁpaÒÒ†IËô¤eòaÒr“–ÓaÒžê²Ç@Ú½4>NZÄIKdœôÒH£›ŽœohߥšÂ*$‰‹—ãsEä€i>C)^Ôè¦e)ÆçŠºf)lð ˜ÎØƆK¹–6åZ¦—r-“O¹–CʵœN¹öT—=6Òî¥ lô9×2 çZ"s®—ÆFÝtìÌC2ÚÀå^‹qðµÈÉש „ôÉ×2'_3e¯¸çÓ=jö@¯7Oî \ µ´Ô2B µ65% ’¤–C µœ¤öT—w]1r“} µ ¤–œ©é¤#çšM»[¶ËpIÔbE-r5ŸÛ©|>æ­+ú,jÉ+å7Ëâ,γkY`"Ë‚¥,\D´´Ñ2BDôÒ²H>"ZÑr:"ÚS]æ²H#}x¦,úˆh-9GD'ÒIÇN4ð¦.ZŒÃ¡EN‡N~hõ\öéÐ2§C35@N‡¶H(šp%ûuO‡ àÒ¡¥M‡–Ò¡—6@Ú¹Ãí rH‡–ÓéОê27@±Ã3 ÐgCË€lhÉ9:‘N:rvaXc´á\P´7±ÌAÑyžF-Ï38 cœgׯH((:6† Š–6(ZFŠ^ÚiG·ƒÔ!(ZNE{ªËÜi$Ï4F-b¢%ç˜èD:éØy†TËŒ–ã‹•Ì™ÑɲžË>3ZæÌh¦H 3z©ÇœOØCñó\yär£¥Í–éåFË´‰Û!å-§s£=Õe.‚4‰gŠ O–©Ñ’sjt"tì¬ÃÏ×Ì)€‹Œ–ãpF™#£ùŒŸø|Ô[_ô‘Ñò1ZåÄhžÀu̪-À¬)òú£9P.1ZÙÄh!1za ¨4BnX@ ‰Ñj:1ÚS]Þ ë#P}^´ È‹VȼèE-H'E˜mHd ’ÂEGËñuWæèh>#)>úV}t´âÊ›­ñÀ tÍRÖàÕÙ³¬á¢£•ŽV¢£—¶Fi·‡¬1DG«éèhOu™[ƒ¬8X£ŽVÁÑ ½¬5Òè¤ó©`—!-ÇÒ2gH§6\ÐgH«œ!ÍT dH/òô3ª!NüôózÛX´Å!t¸ˆje#ªU„ˆê…°Vi¤êBÇQ­¦#ª=ÕE£C—a«#–aO²#í^š`GQ­"ª2¢zYvu§)Ž6.Ž·5pÉÔr,(s25ŸÛ¶|>ê-`údj•@üñ¹æ<» §/˜L®„qÁ×Ê_«Á×K&¬ÞC„‚¯Õtðµ§ºì “v/M¦¾VÁ× |½,aÈ:‰ÕÌ Ãàò®å8ïZæ¼ëäÇpÏUŸw­rÞ5Slä¼k‹ ®y×*¥K.hZÙ i•^дJ>hZ AÓj:hÚS]öCð´{ibÞGM«€¨iÅ9j𮓏Í"¤²v <-ÇÁÓ2Oçùx€ßO«óL7Nbˆž] ñ¹O§0Äw‰ÏÊ&>«ôŸUò‰ÏjH|VӉϞê²â§ÝKCü>óYd>+ΙÏtÄî.{*c|\´'@Ëœüç¹ê UN€f:O z©§–y5E^àÕ.hZÙ i!hz飓šVCдššöTMe{é«Ä{i‚}Ô´ ˆšVȨi=^€®X€>‰4º)Æü÷÷²*\Ô´‹«5Íg¨Æ‹2}Ô´:Ï<ã$0“@×,…^M‘1Ã3.ÑZÙDk!ÑziÌ$Ÿh­†Dk5hí©.{̤ÝK˜é3­U@¦µBfZ/™4º)ÊL {Íà2­Õ¸…UδNm  À£Ï´V9Óš);r¦õÀ¾™Ö),jraÒʆI«ô¤UòaÒj“VÓaҞ게§ÝKƒñ>NZÄI+dœôÒƒñ4ºéÈ™…vžÒÊ&\¼täåŠyŽa=÷Ô¡~/­È0>×Á~]³Ô`Ÿo¼t ƒ}—ë¬l®³J/×Y%Ÿë¬†\g5ëì©.ûÁ~Ú½41Øï“U@²³B&;/=ØO£›Ž½óÎz´ïýB)=›1xñþ›9¿lú^ù¢»k¾Ð§öGÔUágàŸºs¬ýÈlÞÜÞ¼ÝÜ\ï6¯Ÿ|wuóꯛû›ÍýO7›w»[wn’Ft>yØœ<žÁì —è®Æ‰îŠ{¢;—¯ NTZDõºiÑ9 ¤mh,ó†OÓjÿÞ´Ú“fð†j¼j<À®b°¿yúÛS£}úÕìßd@ Þ Ð.ôDÛÐ!ôDoÍǼ6g‚/Ôij4”ÔC쉞Ž=ñT—wÖ"]1ÈZÔ}è‰=ÑœCOé$̤ßëï®þÚ0ÿâÕýæÍå=è q!'jüu®rz{ €>äD'¤Aÿe‹€ê€i¼jwZwêÀ ‰Ó þÕÀE†h¢#D†à•:â˜wêL€,é`¼‚&öû¾,†Ð=â©.s¤‘F1}dˆˆ Ñœ#Cé$4~½›v.éø¢ÆßæŠ{ zÿŸjüß§Žèn1gü¯ûñ?¦ñŠñÍÏ"ö½ÏvüÂc <ÅMcõ Ó$Ç”¸\f‰¶™%:BfÉò“iça´ƒÈ!µDO§–xªËÜiÄaÌ4@ŸY¢2K4çÌ’D:iþ$@·Åš?€¸|5þ6S9ï uTö{9žiÊppÒ£âtò¿@5EÆÁÇ8p/Ôö‚:Æ ÅøÅ6M!ÈÞz6^ û @‹ƒá‚zú‚žê2ÇYqÀAÿAðA|à²8H£“Ð8`þÔòµaã¯s÷Û<Û`56(¶ šìmÎÄAÓš­ PªõøŽiSß§=*fËQ‘—Ž|д_ïƒæºëIj“m-Òžbàƒ®rû¾;äƒîÇ´“^&ÒIó'ø¬#3¡½ô"˜ ÇMªiÓ̄ĆÅ-DÏÁŸ éL!4­Ù1Ó¨Zï6eL8éQqc¦12ÆLŽ Â2A$8ÐÖ"íi„¶– bš pu™3¬80AôLLœ™F'ÍžFࣄü´QŒ„Ÿg?môæòöî~ó®Í¼º¿ÙÜí^Ý\¿&iägŒøœ2žêí¸ s`Œ%½HlÛZe®ä!‹çEÿzÁó ¶•¶ÆÿP©ß>ë[ôàÝþ•„ífÌôuÿ®fÌÔnþ“3æŽ`Ìö5…w0§Áqñ9™Ú» ˜ÆÈK?¾·à^jXØ—^j¨·5ÌÚÞ] {›zÍÛLŠáņÅô‹ =f~!wæùúhâþBÿbÃ"àņç&ÒIˆû ϪNyn)èÀ½ÊZR÷«<£#±šzÑѿȰÀ¼ìm¼úu²ÄBGqýËÛ:ŠÃèðnæ‰Ìi´zt`#£ãct¸×'öõ‰E„×'ždï}[Ã+‹éW(z*Ìi¼o&:úW(¯P,8¿B1‘NZ :poSƒ(Å]Ÿ”Ñ±Ž‘Ñó¢ìÑQrŸéàµÒñ:ÊÃè({t”åatx7£÷Àõ )$@æ4"8.N­¨ÆÈèø¥CGiÑQFAÇøE£m!!:J2tŒ?=*ögÚ¢£ÐQN£®0stõt”=:Êt”œÑ‘F'áÑÁü• í•£à9Ð#ÍúX±>L¯Ã]éLy˜Ãú0½>ŒÕ‡9¬ïfÄ(Å÷yÛB}`N#‚ã ݵ,€‡¦ Š'¹0 BtT‰+Ä8…«“æÔ‡YÁÔ‡b¦W˜¹BÈz‰ƒBL¯ ÃY!itÒ …°y ˆÁdüŒ©>â!ÓL•ÞòoÌ}˜L‰ÕÈ«®¤:L>h¦ýѤ:LïfžÁœFgALƒd‚@q‘5…¬)"DÖœ‚ dY dˆ­)¦ck<fN4Qf¤­)bk α5‰tÒÊ‚ ­ÑÀõ5î{&ÈÝ퀹¬)0_à+Y,‚Ô‡ ÒÇÙ´?:‚Ô‡ âÝŒêC ·Ñž´ÁœFÇÅ ˜Éâ‚q ŒSDÆ9ÅZ,²D×b á8Åt8ާÂÌ ’FîÊL‚ôá8E@8NÁ9'‘N:b-k‹àBr4p¡û Ÿ-²‹”}DN‰‰ YikD²HÓ”‡,Röñ9íÖ"ÍÏCñoFŒUj9^¡ÙÆ·ê4"8.>'SãTcP NãþöýúúÏGûâÀÛ,€Pé€J›TFHRu \7…t*É¢KÔøûXžIÔC¨R€Êé O…ÑZbªé…èú‰þVÞ$…Ê>¨ È*9çÑuÒ©)tyýêjóz÷ãæ›æÿ˜R¨D/µ:îh5S(±šz)ÔÇ•˜8”¼2ì …Äa õAíŽBâ0…¼›yRs­žB˜ÆÈbI!—rTÚ”£2BÊÑ)(Dϲ …†¤£r:éÈSaö"ë'곎ʀ¬£’sÖ]'e P™‡2¬ê¸£ÕL¡uŒ×ž—²§ä>+”Ì µ¦)RHö’–Bò0…¼›Qä?4×Ps§Ö Tcd ±¤t’–B2 …ÆßÏm!!…dúOè´u°’Ó‚+ÌžBdýÄB²§  äL!²NbC¡ýJ9Æ&’(ãÆ-iÝl¢ÄnÏKÕ›óÑ]ikÄ2‘:l"Õ›HYÁ{LäÝŒø^ßJàvtSH`"Ôp*þq…îz±§vP ý¨²bÙH9)k#ÁF :ÿUôÏåC)ªáœNÝØŸ'k#5ØHMÛ®0ÞFÛñ÷OSF¨#²žžñZ\Gª×‘ БÂéHÖcÅ6e„BJ££ŽÒ¯.Þ¾ûlsÿóuG¤ ߊګ2†IcKq1‘™”XM½LÒ=“0±ßyÝA&éÃLÒ=“´eü»&y7ódæ4: &a$3‰-“´c’¶LÒi2‰,Œ|A&éIzšIp…`YOq`’˜¤¹3)Ž:&i“ƧJäHí̤5À Ô=“0Aå+Ec,&‡™TôL*,“ŠÃLònFqXÑÓ0 s?&a$3‰-“ ǤÂ2©ˆÂ$àüWÑ?—™DŸ.ǯz’±ß¨g™T L*¦™W8&‘õ&=“Š&Ü™”FGÅaÒ+ïØxÉûÝR'T{ÓÓG”öK÷K—ûUÚܯ2BîWszAoXiK ïvÅ-sÿÐÞﲿÊéì/O•y¿xŸ®Ÿ¼x¿ì³¿Ê€ì¯’söW"„¸ÏÑñ‘Ãý 5¸Ì¯bü}^Äý2ϨPS'še–ÛöYb%n˜þXÃôqrP£¦O+mš˜w‡51ÒÄE r|¹vÔ`š#£æcÔ¸$±Ò&‰•’ÄNƒ²¤EQ3¤‰•Óibž*3GMAU3QÓ§‰•ib%ç4±D:i%¨Á…‡ãׯq_¾–Q Æl“@Í"35¦%3¨4©µ"ïqr£Æô±dÆÆ’ywأƿGàMmi|Ô O%‚#ãô{jŽŒšPc\*˜±©`&F*XÃ`Yj[J‡Ce$ `ajA³2Õ É`f:ÌSeÞ¨¡ë'¨1}.˜ È3œsÁé$,j†Å§­µ=÷'µkúè0c£Ã¼;´®‰&5¸þFG_Óºw*¯u<­m0MBtT‰ûÆ%x›àeb$xbYš!‹ZrYšR¼ÌtŠ—§ÊÌ}“F<ÔLßô^& ÃËpÎðJ¤“Ð38ßpyc@d^×ø[ÄýjÏĈ£’ Î"Oܘ>Ì î®ù‰ãKÞŠGœ> ÌØ$0ï-qb$-J\´êY‡ÁH5q⸠.cƒ¸LŒ ®“‡.ŠkIâ a\f:ŒËSeæÄ!ë'Ä郸L@—Aq-Kœ4:iUÄAfmWq_Íœ‰G'Aœefqú /ƒ _Z+ù÷'5qú/cS¼¼;´Ä‰‘âÕzà@Gà %îT"82^c–8'MòZq\ˆ–±!Z&FˆÖIžÃ¡‹ÑZò9œ!HËLiyªÌœ8dýÄ8}ˆ– Ñ2È­e‰“F'¡‰³ wÕù™©Á]uï.Ìaµíñ¸?9¨ÝU÷»|;´îònÆ<™TWãÖëJ Ü…zl…âÈøœP­»0Í‘ÝÅÒ]µsWmÝUÇx`k+ÇC€®”Ð]5Ý[Àº‡êˆ›È‡ÜUÝW™½»ÈzŠƒ»êÞ]u€»jÎî"뤓?²•Ä,W×%eÜ$ø<Ëh«ÚæY.§­jÛi«ù‘Û£ÓVÓÄÚjöÐj«ýÑjË»Ã^[þ͈f ,^jKãk w*Q¯;ó¸PMý¨²º"©«ýÞèÔÕü¡SWwñÌ=ò¸)\Óùe"5ÎëWoß}¶¹ÿùº#؆ï+4Úë2†aã/Ü2î÷mfÄ0‘Ãy®«=ÃD~®Ë2L3Lô –a¾Z†y7seêT:†aš$3Œ-Äc˜° ©2L¬‚ab`˜˜f\åFÖW&z†‰† î K££ÎaǰñãeÜg2à †É$¶Ìl˜ì&ùφ-óÌWÓÔ “=äe˜o‡–aÞÍ8ìÏ—´¥ CJGÆa˜&É cË0é&-Ãd†AÏ~m£62LÒ1l\™¦Œ†ar`˜œf\åFÖW&{†É†Iî K££b0ìƒgÁ8{Lâ<6d?&—Çx?V©ü<Ø`2Õ› õ½»ÖöxÜŸÔ&S½É”5™o‡ÖdÞ͘§®¶@¾NWJ`2Ì©DrdüLvÒ7d“Å2™r&SÖd*Âsa”}Õ•šL‘ Íx\ӔјL &SÓ&ƒ«œ€ÉèúŠÉTo2`2ÅÝditÂdã§Ã˜ÎŽy¿WŠGϨx¾{sñþêÞÿoæü²é¯¾Ew'¶“û#êªðï7—W»ÍýíÅõÝeûqÙ<~s{óvó¦-Ý6ÿÛÜß´?Å'dÃO6§gPýE;øÂÜo©Ç÷[êµÜoa8ÿ]| }ë54îîÀ óýôxP©ãÞ,;á˜>߯¤­¶=÷'õ˜>߯²ù~ÞÚ;0Qòý ðö®”à .ßàÈøœPíÝ—“åûM|§&‘^^¹h½ÊFëUQ¢õ 0ÿÑ•Þô ŒÖ[î%¤Õ­WMGëyªÌ;½œ®Ÿ¤—W}°^¬WqÖK¤“°ÐnfÔÁ%é™qšEÚ0S 2_ïDCûežÃìóõ*L ˜»]Çuû)©Cž¯Wõùz•Í×óîÐR'J¾Þ’ÔÁåë­ž:'Ë×[u\´]e£íª(Ñv§ a´Ý‚Ô¢íªéh;O•™S'Ì´™Ôéƒíª€`»Šs°]"´êà’ìÌxÜjâ>Í“©Nd¾Ýjg1Zêôùv&…kµô{ÜŸÔÔéóí*›oçÝ¡¥N”|;#Ç+Ý»Rêàò펌Óìª92u>¦Ž‹–«l´\%ZΫ,ºRBêFË_™Ð”ÑPgˆ–«¦£å'Tëž“åË­Â=.Ú­²ÑnU„h·JÔÀ%¬+%tYU©Ç•iÊhÜ3D»UÓÑnž*3wO™a3ÝÓ»UÁnç`·D:)|¨.Ý£{LŃKr3ã Í"O@æYž±vjd¾Ûšgyê>ß­Æ$P­¶=÷'±vê>ß­¶ùnÞöÚñoÆÜÖÖà }O£Ô©Drd¼V5âA5 ÑQ¥­žÚE«Õ6Z­Ž­v‚…m5a´Úr Ûê!Z­žŽVóT™·zèú‰zê>V­ˆU«‘±j‹ª'‘NBÏö|Ãåµ$cøÔ¸ì43^®nâ®UÏð ‡2QmµË¹Zøô‰j5&óiÕOòÔä‰juŸ¨VÛD5ï-|b$ª- T¢ÚyÀ礉j«€ 3«m˜Y%Ììð! 3[>C˜Y=fæ©2søõøôAfu@Y 2[>itÒªàƒK+3ãe`&îÊõ Ÿpø 3ÌV;ÃѧÏ0«1)K«…àãþä †OŸaVÛ 3ï-|bd˜5€dÐÑdh჋Ã#82^#Å>'Í0[|\|XmãÃê(ña'x¾§&Œ[îùžzˆ«§ãÃ:¬ˆ«‘ÑaËÂ'NBÃgŸÆX@¸|0`{ä5ìù)œ‚°©ak}ª¥UPŸV£¾n×Úû“ƒZA}jXmSü;´ ŠV‰|¬¡ŒþXC« Ì©DrdüÄà.~â r]µ ìª#väiŸš,[hɧ}ê!°«žìòT™¹‚Èú‰ƒ‚ú°®: ¬«F†u-« 4:)|È><íÃÚ?¸¼3¾9cޏ7ÃÊ?éÍé<4ØG÷öÑüg€²왘öѽ}´µo‡Ö>Þ͘[ÝE=Pw¥öÁœJ$GÆç„j݃iêoÜœ”< ^ÚÁK[xéÓOUºRBxiºé'à&hûÅ‘^z€—ž†\e|Ròø=UŠ.~÷+ºž¢_H1M/ÝÓKÐKs¦Y'vjHnc‘¹Lã6³“ãžvÀŠ$¶Ì³GE°"?{dV¬èVX€ùvhæÝÌ`˜Sé †iŽ 0–+À °"U€«X1¬˜\eö#ë)+z€+8Œ¬“2À&Và6³š¸cÖ °p€•Il™°²XÉl¡g Jr€•=ÀJ 0ß-À¼›qÌOÁw¥ÜJ$GÆéöÕ`,V:€•`e€¯h])!ÀÊU¼y¢VN ®2{€‘õ€•=ÀÊ€•œFÖIL¶‹±ÄJ”Ä*`½÷"m›ŸÅòhÌäg±™^c&?‹e5fÈ5fz«1߭Ƽ›1@Ð])Æ0§É‘ñ9¡Zaš#kŒ¥ÆŒÓ˜±313õx€ß•jÌ=¦Æ÷››2™AcfZcp•ÙkŒ¬§8hÌô33œ5FÖI',‰1ƒsØøÌ¯Ž8õY9,½±*ψ «zƒUügÄ2XEn°ª7Xe æÛ¡5˜w3bü ÌXþÔ– s*‘¯»ø­Ã0Mý¨²ÅbY¬r«¬Åª3c[èЖZ¬¢›nÐD/娋UÓƒ«Œ·Ô[„}õ]_³Iˆ&©±ª×X ± §1 Üâ„78^&ÒQóçÇ~uñöÝg›ûŸ¯;˜m¿°£Âá ¸vÄy’qvÎê$p¶Ìóbu3ÌxbÝÏ‹Õä8«{œÕg¾Zœy7sÅjhz8Ã4IÆ[œÕgµÅY*ÎêUà¬pVOã ®r8#ë+8«{œÕ8«¹ã,Ž:œÕ8œÛ·Z¤3ÎÆ8Ûm:[dê¬mŒ–gíOö-²ÌãdmS­ÝE+´îgK4ÿ>{£ØŽ£ðüJ[i¸ŠäÐØ) ×&™i\™Ö}ƒtNkÿÔA­¿ŽFô€Ù6úôÔºÊPQm\›¦Œ„j]5z«µœÂš¯ÖüµFØ_ ¸Ö×îA[ÿ9ä,¶T:+Ù>xòŒ±ÝºK5ozŒ7½¼%ùø™ØŠüüÙpÂNä'Ðà=à„œp€óíÓλq%ÚV0Íß–RsB‘CÀ¡>dpl'À 8ã™4} ÚRJÀ º‡ÒÆcŸ¦Œpb88¸Ö)ެ¿XNXÀ‰À ö€K£³ÂU<¢ÆtÚÍûS¶çµ€J+ Ò èp˜ßíHdy¢]1…€0gÑÁqú¬½ÀµHÐH@å  Ò (B¢^Gñ:¸¾˜R@d)` vÆ_Àm!‘€Ê½€¦ƒõ|õæ. 42Ûæ ¨´ ˆÖë?‡L;êe*5C@îÙF®Bê‰Qqïufù„ŒÎ[7€Œ&âkµ-òØžÔ2@Æèp~Þí¨ž€ou‹è·º;aÎ(¢ƒ Ýý‚O/¢B*i+u™BÆA(B˜Ýɦ‚èòìž 2{MgÚùêÍBiĥͅ± Hµë?‡L;êe*uÌTÐæ. BBÛï~F¾ó™=äó2ÆnµÓ‡*ë!LÔÖÊ—ÄŸd7å¡Êz¨r:œew`ûÉÉá÷æŒ:4ÒnªUÎCåNæ!²ø«¥=Tí=4+ç«7w‘õ UÖC¹rýçiG½L¥£Öë!\–\=nÕz‘fÍÚ"“ãV;Òy¨¶ V+ÄÇöü öPm=T;Ž;°Ip!ˆ¾¨ór0Jqp =tÒ¹ux¨–³à[PçÔY¼5Õ"ù™¡)$†4áBÒD„´SM ²$§…§†Ä>(M¥yêÍœBt½ÅB†¤‰4 I[–B‰tÔ1SCL$p1h@òÂ1Á YA(!ÏN4æ_f™œ°‰gÆÜ=]âæýr :>ólJA6òL¸È3ï.‚"Dž-® Ì9u ´HVÐHACÒ˜pIc"BÒØÉD—5¶°‚öic" mÌSoî "ë- ²Ic"$iL “ÆVPµFáòƒêñ0²^$¶-+¨ãÊ$´Ð\´ ’üç‚Xg$É$­‚¤So—NAÞíHh,øþ8Ô9uïCµHVÐHArPt ’é.‹k«³ŠeqmEœ‚d€‚àzsWYo±P´ ’! ’¬”FG±,Ž+‚$Aã5õ·i2‚PRA{)‹ TðúZ[Ä!æNL)‹ åäÛ¥Cw;ò1œß™€:§Îæ ¨VÉÏR†”ÃJxJH­eJHí1¤0×›;†Èz‹†”Å Áb¡4:ê˜)!Ö µ—dŒ‰Æj¨0C6ÊD: -´‹¬Õ–Hg›ÚÚ+6Jmãó¦)Ëj;ÔìÑÔ&’PÛ2smý×׿µ?Ù·ÈB‹ ›¦ V›°jNm¾]:µy·#a­³j‹)Ô†9£ˆŽ¡Ú0­’ÕÆWmbP›pj‘Ô~ÊèÒÔ&Õ6~Þ£-$R›Ø«M¨ ®w j#ë1jVm"Dm‚½ÚÒè¬8jû`q$k´ $ÚÆÝ”­mÌ_~ß|ÚóËï¶IË6Éÿå÷‹‚5A 7iá&Ü|»tpónG> ¶à[EPçÔY¼UÕ"ùé5ž`“ؤ›L÷­"muVñV‘¶"l2lp½Ù?½F×_ž^ëj÷ sMr~z®£Ø<½–Æ[EÚÓ¥³ñUXs Î:CéL%¡³…–B*«3Ôœ•Ç5KØa1u¦¬Î”Ó™o—NgÞí'_g˜sêí½ÿvÌ-1#Çóm])ÁÔ Erh|N«öž ª=Éû§’¸•¡†BåbU„Âæ+Ç3])á E–’¶Ì=B{Cí#U@¡§ÖèÛK>4øa_Ñ×cú†²ñƒ*$~P!ã—}º3‘Ž O¿¾¸¿Øˆî‡{€M2VPŒ[²)Ë6É6mbÃÿTÿlr|øß¤MlúŸréÞ}:›DHÿk‡ú㥮”Â&˜ŠäÐ8}Ð^àÚ#Ûdd“!lO¹°=!l¯UÈøf^WJi²,0YŒŸjʈl²ÚSA{žZs· Y_±°‰ ÙS!!{ ²·°MÒè¨Y6ÙO¥>¾¿¸¼Úܽ»º¼ÿÌ•ÝümwÛÿµïnÞ_¿¾¸ý官AÆí `È'âù3fV„‰§0a]§Â tj“pæøH¼IÎØL<å2ñ¼ûtœ‰‰× ÒhEÂ\Ê"Á¡qú¨½ÀµGæÌˆ3CrQt*B]sŠ3†”3dyYËrfC§bè<µæÎ™4²ÍærƆЩ:Å:„.‘ŽšÍ™Þ.wÆ•&DdF/9nÊ2i2i`ÒØ9…z§ñ‰HœÚ±Ó÷¤9>Gn’46HN¹ 9ï>i"ɵBw]WJAÔã‡Æé£ö×™4#Ò ùmÊå·©ùmÍ)VTµ¥”¤! ™Z–4ûì6Ýæ©5wÒ¤6—46¹M…$·)ÖÉm‰tÔlÒÈI³{wqÛ^À7»Ÿ/^Ý_ý²¹¹¾¿Ù\7ÿ´ûlAƒŒuã'›² š 46zMaB¡NàÔŽýtšã£×&Ac³×”Ë^óîÓ&BöZëƒñp§+¥ .ÍàÐ8}Ô^àÚ#ƒfš!òL¹È3!ò¬¥Ëøk¥+¥ Y.Ó² ÙÇ©€¸3O­¹ƒ& ­¹ ±ag*$ìL±;K¤£fƒF9ÐtåÉ.:CF¡‰ñS²MYM W¦0!J«m‘ž3LJ•MrƦ•)—VæÝ§ãL„´2%5ø$³Žþ$sÇ\þÁ¡…—zk"ªMˆ+uÖ !aÊ…„©!a'zÊŸ,ÃhÙ§ü÷a* ÌSkî¬!ë+¬±á`*$L!ÃÁfM55ßpy!$dô—/2oʲT²T`©Øx.… Zù³þLJsMJŦs)—Îåݧ“J„t®và= £?×ÜI•÷Fqh¼FuTNšÌµ© ¡XÊ…b©¡X'zæŸ,³gÙ ˜} – ÄòÔš»TÈúŠ…Tl– ÃRÈ0¬…¥’FGÍž€A=óÏ›6Èl,hØyÜ™i³"ÚØü*…IÕYý“ÿÇçWMâÆX)`åݧÃM„«Æ àSÎ:úSÎnp‘h‡Æ7' ¯Zn†Ü(år£T„ܨ½€,ÖfYÜì3£T@f”§ÖÜqCÖW,pcó¢TH^”BæE-Œ›4:j6nÐoà d†Œ_ך² œ 8•N•p{@EœÊ§rÀñíÓÇ»¥ð™gý™ç8˜SŠäÐÓ&8 pª8•N•ìûªu§Ú§ \kîÀ!ë+À©,pªàT¬“FGÍò}¼yS!y3^ Ü”eÞdÞÀ¼©-o0CÕ¿ ¦çMmyS;ÞøöéxãÝŽ3ô´ŽþtÇÜè’àÐòÓ&™7 oê7µãMìÛêuð¦Þó¦à \kî¼!ë+¼©-oêÞÔ¬y“FGÍæ òí¼yS#y3žcoÊV›ñíû¸ï§æ4˜_†7zÛóFcÊWÛ"n𦠯M³‹7íÏ7Þ}ZÜø·‡÷ZYèzüêJ pƒ:¡HÏiÕÂÕù•g£Fojš?õ¨é.]G¢¦, àåá])!j4YXyÌpѧ¸-jÚjXÔèí4j<µfŽº¾â€š®vúï jºÏ ÓŽz™JG!Q#;«Ü1ÅI{ ÆàDŽ›²)Ë8É8q",N0 åë~-@Óä8'ÂáÄ·O‡ïvœÆ³W])N0'É¡qú ½ÀµGÆÉ'bÀ‰p8qp2ž£èJ)qB–V^óÓEô j‡±Ç‰À \kî8I#Y~.N„ʼnÁ‰`“4:jNÐïàªÔ 0æ“qWcdͬH3ÒjP¾ö74Aîi=#g|ûtžñnGݶšR Ï`N)’CãôQ{k왑gäàé<#£xFƒžÑ¤ž!‹+_Ö3rïà¸ÖÜ=“F´ü\ÏHëâÉÚ3itÔ|Ï _ÀÕ4iè é¸#ÿlš™FYÓ`>âkø_Ãz‰jeM£œi|ût¦ñnÇÁaœ@Õ•R˜sJ‘§Ú \{dÓŒL£Ó(gÇ4ãJ»RJÓå•/kµ7 0 \kî¦I#[~®i”5 1bmš4:j¾iOýsBŠx(ZƽәE³"Ñh+LDùÚŸ÷×°R¢ŠF[Ñh'ß>h¼Ûql'Uu¥¢ÁœR$‡Æé£ö×Y4#ÑèA4Ú‰FÇÍøÒ®”R4dåËŠFïE£Dך»hÒ—Ÿ+mE£CD£Y‹&Žš/äƒþ\E£‘¢ßélʲh²h`ÑV4˜„òÕ¶HÞ3…õLá<ãÛ§óŒw;fì&ÁÇ™eôÇ™;Ï`N(’C ¾¨,öþ2T›ä÷—®)×Î5E²ú“%–/û¨±wMà¸ÖÜ]“Fºü\×Ö5Eˆk Ö®I££fº†óÛÈÚ 1Š*ã[˜MY¦J¦ L•ÒRQ¾òþKzª”–*¥£ŠoŸŽ*Þí8@Èè7wTÁœP$‡Æ*˜6ÉT©RT)UÊdü'‹,_v ¦ÜS¥   \kîTI#^~.UJK•2„*%kª¤ÑQó§`Pþó¶M‰´ 0î”qžÙ6+²±¶Á$”¯þñC¯cucœn|ûtºñnG|ÔYFÔ¹Ó æ”"94†ºÁ´IÖ ¨3èÆ8ݘd_@–Y¾¬nÌ^7&@7p­¹ë&|ù¹º1V7&D7†µnÒè¨ùºA¿€·p R8ãËtS–…“… §²ÂÁD”¯þe½p*+œÊ Ç·O'ïv# ŸeôŸ;á`N)’Cc(L›dá€Â©áTN8U²/ -_V8Õ^8U€pàZsNós…SYáT!©X 'Žš/äKxû¦Búf|‘nʲo²o`ßÔÖ7˜±Àê_ PÓû¦¶¾©o|ût¾ñnÇ!z ZF ºó nxIph }ƒi“ìÐ7õà›Úù¦Nöd©åËú¦Þû¦ð \kî¾I#a~®ojë›:Ä75kߤÑQó}ƒ|EoßÔHߌ/ÒMÙJ|3~ ¯z]µoŠmï›U¾ÚétÓ4µnš]tºivºñîÓêÆ¿ÑkJl¡»]1op§ÍÁñ9µZÝ Z$¿üìcÙ['›æO½lºËב²iÏ1L wÅ„¶)ÈÂË›Ï ðº í1ï :¤›¶"V7ÅvZ7žz3× ]oqÐMW»ýwP7ÝçiG½L¥£ºÑ½n˜"¥½ c¢ÆMÙ”e¤d¤ÀH)˜Èòu¿" i r¤‹áâÛ§CŠw;ÒÀ-Œ®˜)¨SŠæà8}Ø^àZ$#e„1 E8¤ˆHHæwÛbJ¤…˜7ŸàEAÛcÞt)b€¸ÞÜ‘’Fäü\¤‹‚Á)itÔ<¤ _ÀU5©`ì§ŽýeÕ¬[5Òª\¾ö—4Aîi]#k|ût®ñnÇßLEâÔIEspœ>n/p-’]3r\#kd×T°k*R×…™/í¹w p \oî®I#z~®k¤u qdíš4:j¾kÐ/àj‰´ 4A÷á’l›ÙFYÛ ¾9WþZ€VLTÛ(kålãÛ§³w;n §¡ݶ˜Â6¨“Šæà8}Ü^àZ$Ûfd5ØF9Û¨H¶buÅ”¶! 5_Ú6jo`¸ÞÜm“Fý\Û(kbÅÚ6itÔ|Û _ ÀU6 )èîÓ·ž²lÖ-meƒ 2_û  X+Qe£­l´“oŸN6ÞíH<€³ºM1…lP'ÍÁqú¸½ÀµH–ÍH6zv²Ñ‘dH +¦” Y¸ùÒ²Ñ{ÙèÙÀõæ.›4¢èçÊF[ÙèÙhÖ²I££æËùB®²ÑHÙ@÷žâŽÿ³lV$›ÂÊg¾Úé]Sл¦°®)œk|ût®ñnG=€gÀGžÛb × N)šƒ Ýýr/;CµJ~Ùè›bðMá|S$üJ²ó¥_ Pì}Sø®7wߤI?×7…õM⛂µoÒ訙¾áüö²öbŒ" tSéˆ{0™,« wd)-Y0±æ+1@IO–Ò’¥tdñíÓ‘Å»©h>«-¦ ꔢ98†dÁ´J& H–r KéÈR&ü‚²°ó¥§dÊ=YʲÀõæN–4¢éç’¥´d)CÈR²&K5Jõ‚ÞÆ)‘ÆÆŸ*î4gEÆ1Ö8˜póÕ¿&ÀÐ+ÇXå§ß>r¼Ûq‡n‹)”ƒ:©hŽ¡r0­’•*Ç Ê1N9&á×…ž/­³WŽ P\oîÊI#¢~®rŒUŽ QŽa­œ4:j¾rЯ à-ƒ”Ž®Ô&K'K–Ne¥ƒ 9_ýK*zéTV:•“ŽoŸN:ÞíHL@ TÛb é N*šƒc(L«dé€Ò©éTN:UÂ/ ?_Z:Õ^:U€tàzs—NQõs¥SYéT!Ò©XK'Žš/äËx;§B: ®‹›Z—³"çÔÖ9¨ñÀÚ_!PÓ;§¶Î©s|ûtÎñnGRZÕÚS87È$98†ÎÁ´JvèœzpNíœS'ü*²ô¥SïS8®7wç¤Y?×9µuN✚µsÒè¨ùÎA¾J€·sj¤s Œˆ¸§sŽz:¾j7eÙ:GY§ÜöÖ)Qaðkm‘N:MSPK§ÙE'ög'ï>­tüÛ½&t©;Ö]1tp§ÍÁñ9µZå Z$¿,ícá”['œæO½pºË×±Âiα ¸CØ §$ B¯êñº)£ñM[ ë›r;íO­™û†®¯8ø¦«Ýƒþ;è›îSÈ´£^¦ÒQHß4ãÜ^8L¡Ò^†1P"§=d¨¬gXÞAEX¨ áWý*¦)È¡",T„ƒŠoŸ*ÞíH ·h»b ¨ N)šƒãôa{k‘ •TÄá ""A¸Å×SB…, ½ªÇ+Л2"¨ˆ=TDTàZs‡JÁõs¡",TDTk¨¤ÑQs¡2¼B€«XR,À˜.rŠCËÊÄ"­XPQïkm‘^,’^,ÒŠE:±øöéÄâÝŽ™)ø²Š~ ¹ ꔢ98~‹ÈP­’‘r‘ƒ\¤“‹LxŠ…,î|Ù)¹—‹ \kîrI#š~®\¤•‹ ‘‹d-—4:j¶\8¯k/Æ(¶Aë¸! ™-ë¤wlQ–-¨5Õëžh•-ʲE9¶øöéØâÝŽ”xCYE¿¡Ü±uJÑC¶`Z%³d‹Ø¢[TÂ.dIæËN¸¨=[T[àZsgK©ósÙ¢,[T[k¶¤ÑQGO¸ðö‹Búx¼YÇ}—ééüuôÊ|¤¾]´µ *§ý4-òdÜýOŽÖËÃOè«]h`Efüú-²g(êÃ&’¹¶\÷ië>L{(i€®®”Â%L-u›èÁ&ÚÙDo“¦išt¥”2!K"—ÅX&M‘Lô^&:@&p­¹Ë$Ôø¹2ÑV&:D&šµLÒè¨ð±óÏ—¯ŒtÚÿŸÿù¿¬K>Ût`[÷€~÷æ±Í›Ë{Þá·kY€§X#?šɲ"²–,¨öÕ´° úåÔ`)èÁ‚ivü?>U»R °09´ÔÁR `)XŠ(`©Ç®”,dÑâË‚¥Øƒ¥ \kî`I#~.X –",k°¤ÑQsÀ’¬R ¤R€GX#?¿š•’è˜RJi•‚J]_Í¢0þJAõË©•RÒ+Óí <–ëJ)”ÂäÐRWJ9(¥tJ)ã(e|éJ)•B–&¾¬RʽRÊ¥Àµæ®”4’ßç*¥´J)C”R²VJ5kZåשOª”H®CIw,y:®ŒŸÑ‰û„§ÁùB\1–+¨øô¼lA° zæÔ`1ô`Á´GYÛñÂØ®”,L-u°˜,ÆÅ–¦i!~WJ ²`ðH´*¢GZ9°˜=XLXàZsK!îsÁb,XLX k°¤ÑQ8°HûxÊ ‚¤YÆ—å¦,›%›6KeÍ‚ B_M‹ð ª_N-–Š^,˜öh0^Û•Rˆ…É¡¥.–jKåÄRÅËø]`])¥XȾ—KµK ¸ÖÜÅ’Fû\±TV,UˆX*ÖbI££f‰%Y¦TH¦ïeÔqß š™’è bJm™‚z‘N^ ¶SPýrj¦ÔôLAM·ã…±])S˜ZêL‚ïK|_F¾oA2~À¾+¥d Y>÷²LÙ‡Þ—¡÷žZsgJYês™bCïËÐû’uè}"5ob%ù¥`È{ ¼Q¯%ä~|#rðëU{ÅØ€{“@Àýy.Ãõ̉ÅÒ,µXpí¡ÄVWÄ®˜À,|.qµ˜!ÌÞ¸0{#̾iÛxifWLèC–»-¶xVµ)¤‘‹ÙÇÙ›€8{O½™Ë…®·8ÈÅØ8{goXÇÙ'ÒQ8¹èÕ, 3ÈÜû½kɽÏv‰n›yoȼ?Ç%a¸~9µ\½\PíÑ I5Ð&]1…\Ø\êrÒíK·71Òí[¢ïÎìŠ)åBĽ´\öùö& ßÞSoîrI#6}®\l¾½ É·7¬óíé¨YrI–+ÈÐûX@S¬%ô>s%:Wlà½I ðþ—†áúåÔ\‘ô\AµG;ôΚ®˜‚+l.u® ‘öÆEÚ›‘ö­K€á}WLɲôí¥¹²µ7¡öžzsçJYés¹bCíMH¨½ajŸHGÍ›hI}‰˜A¦Þ@ê}‘SïÜñºíbSïM©÷g9Õ‚ÉœØ.°R¢ÚÕB—@n|_La6—º]†\{ãríMœ\{}FºbJ»¬#×ÞìsíM@®½§ÖÜå’F\ú\¹Ø\{’koXçÚ'ÒQ8¹ ±öÉ’t_kv‹µÝg²Å†Ý›ÂîÏrº%¥¨{Cukvø¼@±+¦ ›ƒK,Cܽqq÷&BÜ}gà*ÒS’…,›{Y²ìïM@ཧÖÜÉ’FŽú\²ØÀ{xoXÞ'ÒQ3É’þt 2ñ¾Ví9ñ>»Åã›xoH¼?Ó'òSʼ7ô™÷¨öhÃÛm)…[N~hx“Üß¾“äO_ÿùh‘xé$Ó S1Щpt*¢DZVã—0t¥”p"Ë_4Ò²­†ƒS'¸Öh85½cÀ>3t}öÕúl Q…ET‚¨‡¨¶{ÀìXCw‡âeJ†•ºÜ¿%`Ó´Âfwýãæv÷ê ­^ï~üOÎÀ*ÀÖåq¿²3°V¬Ò  ¿šáÏ+T¿œšW%=¯0íÑje|ªv¥¼:ù¡e^EãU9ðªt¼*£ðªF»RJ^‘…›/Ë«rÏ«2€Wp­çðªû¬"ŸJ¿Ï¦xUZ^•!¼*ñ¼?ØÑ•Rò*N›Å«Õ˜ªDš ï³Þ<›j=‚€Le¬©P¹õy±Ýb¦BõË©MeèM…i–(ã”ÂT'?´lªh¦2ƒ©Œ3•‰c*ðNzMºÖ,}YS™½©L€©àZÏ2Øg]Ÿ}µ†>›2•±¦2!¦2xSŸ<éJ)M•N§Í2Õ¯W7ae¸Ç/Ó 9S4E\UW˜Üö¼"pI^¡zæÔ¼ªèy…i6¤sü%Û•Rðê䇖yWÕÀ«ÊñªŠm:þ†îJ)yE¿h´i[ Ç«*€Wp­Ñ¼jzGƒ}¦éúì«5ôÙ¯*Ë«*„WŽWM÷ùg])%¯Òé4¯ä(8h=ªªaUYXYX°°j+,Ô3׫iþ¾BõË©}UÓû 9œÝŽAíJ)|uòCË¾Šæ«zðUí|UÇñÕøû¹+¥ôYÊý²¾ª÷¾ª|×z–¯À>Ót}öÕúlÊWµõUâ«í+ ¥­+¥ôU:6ÏW«AUDU^!õŒªõ@UµíQÕüdªs\ˆë—£ª9XjT¡Ú£5Êø±Ó®”U§?´ŒªX¨júÒ¢ªùSªîJUà½ôc¾¡'QÕV&‘±Þ!TµÕ°¨jþ8‰*O­g¡ ì3Mù‹ôÙªº>è˃¨ê>ÈNºRBT%ÔióPµºEíE£«a•‹¼ž1§É¦¨+au%øëê<âzæÔ¾ô¾Bµ‡[`mH_L!,—ÍXb0–pÆ‘Bm$’®˜RYb%¡¶mEœ³D€³àz£ÕvP÷[E×o_­£ß¦¬%¬µDˆµÎZm8EÚÐ¥H¿L«ãpÞÒë]$Ø^âQâîú—qß‘ŵ"qI+.É_\ç¸H×/§ö–¤÷ª=š¯T`­H_Lá-—½Í[rð–tÞ’‘¼¤§tŔޒ ÿzKî½%¼×{ž·à~«èúí«uôÛ”·¤õ– ñ–œá-`ÄÒSz+¥Ž›ç­Õ K"‘5^X×”ededÁÈRY¨y^4¸²pß# æTTd¡Ú£ pÖtÅÈbppYÑ¥d)‡, Yð=öšîû»ê$3æ;ˆ,µG– @\ïyÈ‚û­¢ë·¯ÖÑoSÈRY*Yj²€7ÀuÅ”ÈJ©ãæ!k}‹R[À,w¹ÈZδ¥žéœOãždœ|±¸´—æ/®³œÖBõË©Å+*ª¸Pí!t EYtÅâbppY\ÑÄ¥qi'.A\BèƒÚSŠKS «z<žmʈ¼¥÷ÞÒÞ‚k÷VÓ=[¸×¶”±X+èµ)mi«-¢-ÔVs5­9ˆ®˜R[étÎZ͈{mSZ‰,`j»Œ;ñ“‘•0) dYdå´ª_N¬‚Y¨öhÁÜÐìŠ)Åàà2²¢!«U8d‘\ʺbJdÉŒû"«Ø#«@\ëyÈ‚{mK™“µ‚^›BVa‘U„ «˜,`ê¡+¦DV:Ý6Yë›Ò*Ú¦ËEfsqŠÒ*­´JþÒ:Ó÷b zæÔÖ*é¬UXkaÚ£önд¥Ò:ù¡egEsV98«tÎ*£dWãW¡t¥”Ê*“‰H=¨¬r¯¬2@Yp­çd°Ï é{Óï³)c•ÖXeˆ±J|1 mHß;˜N§á„e³ˆ?z Fƒ§ôU" ~ãŽ~3°V,ceøë,× ¢úåÔ¼2ô¼Â´G«•ñ©Ú•Rðê䇖yWfà•q¼2QxU?£])%¯L2£¾ƒ¼2{^™^ÁµžÃ« 쳊t`ú}6Å+cyeBxeð¼/ìJ)y•N§ÍâÕjLe¦Vœ”‹4~6wA@¦ª¬©*þ¦:Ëå¨~9µ©*zSaÚ£%ÊørØ•R˜ê䇖MÍTÕ`ªÊ™ªŠc*ðNzMº0°Jf¨wÐTÕÞTU€©àZÏ2Øgé²ÀôûlÊT•5Ubª oªñªÀ®”ÒTétÚ,SýzuVWã ò¦l%¸Ê9ÄÑqU[\¡È+äªgNÍ«šžWÈà×íøK¶+¥àÕÉ-ó*¯êWµãU'‰x° XÀÝÑc¢f`­X•VÅXg¹^Õ/§æUEÏ+L{´Z/¡îJ)xuòCË¼ŠÆ«jàUåxUÅáÕ8w©+¥äU•Ì¨ï ¯ª=¯ª^ÁµžÅ+°Ï*Ò¬¬ôûlŠW•åU« Í«jüeØ•Rò*N›Å«ÖT}ñݳ^®*$®Æ‚¦,ã*ã ÆUmq…Zê’× .†+T¿œW5=®cY ,.nK)puòCË¸Š†«zÀUípUÇÁxK½&Í̪“óÄU½ÇU€+¸Ö³pöYEš™•~ŸM᪶¸ªCpUãq5.t¥”¸J§ÓfáꃥëRVTÆrÌ´(+eådâÈÊ’Ûm§¬ö'{eåAdÏœÖYíÁ; ×eQO¾t¥ñÅàв³"9«íËÞYíŸ:gõ×Ê#Õôo &R—„/ì*“HdêguÕèÕþqÊY¾ZÏÉ&.À>+_.¸†>;쬾†úò³úÏ#2›x,“®”ÎY)uÎYÎÊZµºë;†ZÀ$©8fŠ4SkÝÔ–Z‚?µÎpµ ²_N -A-L{´n_2ºR hüÐ2´¢AK ÐZ"´Æ×ή”Z"™ñßAh‰=´D´àZÏ‚ØgÑ_Pú´Òï³)h --‡ÖxeWWJ ­t:m´Ö§+Ô°¨ê˜9Ò¬«õXÒ•´º’üuu†Ë‘ýrj]Iz]aÚ£ÅÊø[©+¥ÐÕÉ-ë*š®ä +ét%ãè ¼»^¾ê¢«L"ƒ¾ƒº’{]É]Áµž¥+°Ï¢/±û@Wé÷Ù”®¤Õ• Ñ•Äëj¼¢«+¥ÔU:6OW«]/Ø]ÝQ̪¸É¡Ä+b–²ÌB…âåõ‚ B WxbhÁ¤Š -T{´¿p eôu#µ\ÆV4l©[ÊaKE‰%ЛÀ»bJn©„RR‚KíÁ¥À×{V,q Ljׄop_G¿M¡KYt©t)|,q¼Ã½+¦dWJ‡ƒ—>‡õƒ I/à;æüÊôZ7½´¥—æO¯³\?ˆê—Sà ÆTTx¡Ú£E ð‚ ®˜^ .Ã+¼ô/íà¥#Á ¸?ÚSÂK'4</½‡—€\ïyðûmKtûÕ:úm ^ÚÂK‡ÀKÏ€×xäØSÂ+¥Ž›¯õiK#µœWÇœTY[뱤­Âj«à¯­³\Oˆê—Sk« ×ª=Z¹/ êŠ)´Åàಶ¢i«´U8m‘´ÞuoŠ)µU$4ø;¨­b¯­"@[p½çi ž-!L¼ýjý6¥­Âj«ÑV1C[@E»bJm¥Ôqó´µâõ…’]@‡sv±b—z ¼)ùiÜÚq‚ÆBô*-½Jþô:ˉ.T¿œš^%=½Pí!tY·¢ºb z18¸L¯hô*z•Ž^ez m0ÁÙSÒ«¤ V@s=‡ÙÁ«Üë €\k<¼šîÙ½¶%Œ×ZC¯M±«´ì*CØU"ÙÕöð­ØS²+ná«q¯v’«Dj Wq‡ÁY[ ÛÒ–±Ú2üµu–]¨~9µ¶ ½¶PíÑʸÕSh‹ÁÁemEÓ–´eœ¶L$mœ]1¥¶L2ÀƒÚ2{m™mÁµž§-¸×¶„y[kèµ)m«-¢-3C[À¸¡+¦ÔV:Ý6W[+™äò~Iž!FFÏwo.Þ_ÝûÿÍœ_6ý=ºèîšáåÔþˆº Ñ‹Ûïw›ÇÍçòþòÉÛÝ÷íù÷Éæíîî®;kŸl¾þíïÿò_ÿñ_þ÷æDýñWïnî>{ÝÄïïož4'çûÝûÝæóÏ7QÆÞÖÔùübs~yFô_t>Ôà Õ"çÔRÎ1¸t߬J>k™)¶ß~&P¡›ãG ¼pu™8ÂFø LúØj[ä±=?޽ QxîBÔý]ˆfOÚáhû³Ùå×þ]~ÒnÿÓ툋Kad%[<}9ÁÍ'Ô9Evx|ήöªMÉûí©’¸©"†`=á‚õD„`½î$ƒÖ.ôå„·UYæ—²æwÛRš[+b°'ö<5GßZ¡ÿÜ>¼¡B×_ôõ˜¾™"l¨ž ÕÈP½%;êe*>Bו½sòùFlÞ\^í6ÝM‘«_8Üì#°xÀÅê˜KU¶d‘í³·ÍÔ˜@°µÁ Úçød¸)ûkáìãÛ¥³w;@öåöAåR§ÏÛ \›dûŒì3dÝ —u'"dÝõÆVôå”ö!‹áj•̶¶¥DöÙgÞ‰€Ì;O͹ۇ¬¿XØÇæÜ‰œ;̹[Ø>itÔ û¸Yc®æÁÆÔ©c.QÙç\'!L›d $4äÒ —K'"äÒµ'Y \ël9¥„È"³„,40¯Õ–IhŸO'òé<5ç.!²þb!!›I'B2é2“na ¥ÑQ Õϵ$j‹¸’)WK@êEšöœH¤2‰ö$²©tóñ_ù42Y-”DrŠDÊ’H9ùđȻs¼ªåºÞ•S5‚£:{Md¯yjÎ]BdýÅBB6kM„d­ dÖÚÂJ££fHh˜ b- dF”žxLtb$ 2 h/ ±&0éP«m‘Çöü Y0WL-˜+­‹Jç"ß8y·£U5¼(¨-§p*‡ŒêðºÓ.ÙE ‹†P4áBÑDŒP´n0ŸÒ—Sºˆ,©©]"ÜnhK‰\´FÑhžšswY±p‘ C!ah†¶°‹Òè¨ çÆëåX kVóôuÜ@Ò $a2ö@²©hè´ò)"d¶W(Ê)  $ã€ä;$ïvÔàOW´<¨+§*:Œêð Ó.H †3árÌDŒ³¢*DÜ»ìË)D®$¤ª€©°¶”Hû43fæ©9w ‘õ Ùü2’_&ùe )Žš¤€'‹x“ ™CTW±:îÚúL&Qe2íÉTY2UüÉ´ÔœRE¾ª®²8ªŽ|»t8ònÇ­*¡e»}9Ž0çÙáñ9»:aÚ„úë7'LÏ“Y5Ȭr2«â,é3 ^WÚ”Sʬ¢\Ò½óA“®xPfÕ^fU€Ìàš£eùqÚft=F~_2Àf•µYb³ŠµÍÈ:êÔ‹ú.¯_]m^ï~žsjJÙŠ¬BŠ ¸×q×øg‘5_“YdƒÈj+2ÌØaå“X5¹Èj+²Ú‰Ì·K'2ïv,y [ñ]9…ȰãQ’Ããôy{k“,2ž"«‘ÕNdu,‘~éË)EV¯çuõ^du€Èàšó]qYmEV‡ˆ¬f-2²Žb#²ýÜg‰ÕH‰×áúˆkp– ±æ2›%æ$&·½ÄšŸ¹Ez‰5MA²œ°žXNØì·óYû³ó™÷@¬ÏüÛ1ÿºh~0üëÊ |†:ÓÈÏ9×ú Õ&Ùg,}Ö~­ô>kÇñ¿¶Ÿä>kÎtðu€]9¡ÏÚ ‘=ìU÷\ÚRŸµU±>kþ8é3OÍÙûŒ°Çø¬«Ýƒ<è³îÓÈÖgtuêÕŒã³ýs_Œ¡Ö^©QPn/Õq#$3ÔšËq†Ú5a¡&øCm¡)³¦)( Ö~͆š°Pj¾qPónG=ÓRo tŸ¾+§€æL#;ÛÜÿ| ?—¶aü¶öc¡5eµ¦B´¦Øk-Ί£µæÓX+M!•œ6Û#N›¬4Hi:+m¯4m•¦ù+m©95X^G/‡TSË!µµ›vv󈳛w;ƒ©,¿Ý0gÚ9Í´aÚ%Û¯Ýô`7í즓žiÓë™iÓ{»é»Á5OÁnd}ÆÂnÚÚM‡ØM³·[uÌâH`¦íƒçÙX#N#7¾¤5eqqWdÄíWXÄü·ÔT[Aƒ8=…¸Â"®pˆóˆCœw;ƒ.?â0gÚ9MÀaÚ%#Ž/âŠq…C\‘ô\±ž ¸b¸"qpÍS@YŸ±@\aW„ ®`¸4:+âBŸucͺɺq?4e+a@ÖÓ°®Ä±zª:nâÂ)YWZÖ•˜¯ãµ¶Èc{~P¯ ,-àJ8ß.à¼Û—x¡‹²¿œÎ–SsN‘Ÿ³«Ã¦M)ñêöÚmÍT:3•Ç›©=ÉŒß=°å”f*©†t²9ôñuº+%2S¹7S`&¸æÌC¨éú‹CuW»ýwØK%ë·¤ÑQá#ôº´Vú|#;ݹɸâ§Dâgüåß”eüÄÅÉøÙãÇXüþøYjNËãÇXü‡ß.~¼Û±ºO…Ør ü`Î)²Ããôy{k“ŒŸ~Ì€ãðcbág<ñ`Ë)ñc(ñ3žëJ‰ðcöø1økÎ?dýÅ?ÆâÇ„àǰÆO5?nꇫy Ò<ÀêŒmÜïûlYeóìÍSYóTüͳԄOE³ŽÏL­ã«¬„*'!ß8 y·£F{¥Ù‚·ºÛr aÎ4²ÃãsÎu´I–ÐHBÕ ¡ÊI¨Š"¡ÒÀšå¾œRB¡„€YÕ®”HBÕ^BU€„àšs—Y±Pe%T…H¨b-¡4: !!3šrë⸚¨Bš˜µÞÆmfÉ:›ho¢ÚšµÎ~Ýó@5‰ª)ÕÖDµ3‘ï@œ‰¼ÛL¿øM„½A³C˜6É&™¨LT;ÕIÏÕ뙪÷&ªLל»‰Èú‹…‰jk¢:ÄD5k¥ÑQ3Lôà™¡qÄ~æ¨F* ¸€m㾬*+Im³’%5Ñ)©ù™[¤WRÓÄ«åš=tjvòîÒzÈ¿7æÓž1ŸŽ>æk=„:§È/øò´Ø»PíBtX‰»¨½†÷.jþÔ»¨»’%ûÈP[¡•<2ÔVź¨ù㤋<5gî"ºþâࢮvúï ‹ºÏ"ÓŽz™JGÍX57zdhó ã·&´—g…ÆWay©H¦™B{ K!ÁŸB M5MAM!a)$…|»tònGZCÏ*[NA!Ì9Evx )„i—L!Bb p)OµZÉQ[G!@!¸æÜ)DÖ_,($,…D…k ¥ÑQ3(´wg ¤€€×FnãÞÍR2 h/ i$ù h©É I²d®ªv‘´.’ÎE¾q.òng0ãwæL;§)"L»d.’ƒ‹¤s‘LzŠH®gŠHî]$\ל»‹Èú‹…‹¤u‘ q‘dí¢4:jÆÒ9`Åk!IœÄ¸}›²,¤¸BRYH{!)+$TÐźçˆ` -$9%$e…¤œ|â„äÝÎ`jÆ/$TâÃÍ¡¸YHÔ $儤’ž9Rë™9R{!©!Á5ç.$²þb!$e…¤B„¤X )Žš!¤‡‹x›I!Íä[‰#†ÙL™t6ÓÞLÚšIó7ÓR³JðZ¹˜ëê´Õ‘v:òíÒéÈ»7ò+ŠqÏÙr aÎ)²Ããsvu2´ õ÷oŽ‘G3=ÐL;šé84+·ðý¦œ’fš’f€îÛR"šé=ÍtÍàš£i9v}gd=Fc2gÚâL‡àL³ÆYGzYß <êÔ³%™F’ `‹8â£I‘¬È$Û“¬°$+ø“l©i¬‚œd…%YáHæÛ¥#™w;Ö<ã[×¶œ‚d˜sŠìð8}Þ^àÚ$“Œ'ÉŠd…#Y‹d㋜-§$Y±žõ„ÅždEÉàšó'Y± YaIV„¬`M2²ŽbC²ýäg‰H‰)@b*K,®ÄÊ,±½ÄJ+±’¿Ä–š+iS Kë³ÒùÌw ÎgÞí æ¤ü>Üiç2e†i“ì3ž>+Ÿ•ÎgeÒSfåz¦ÌʽÏÊŸÁ5çï3²cá³Òú¬ ñYÉÚgduêõŒÀ”ÙþÑ/ÎR+‘RâïÄáwYjÔL–Ú^jÆJÍð—ÚRsf†Fjå”ÔŒ•šqR󈓚w;ƒ©*¿Ô0gڹ̤aÚ$K§ÔÌ 5ã¤f’žI3ë™I3{©™©Á5ç/5²c!5c¥fB¤fXK¬£ØH-ì4Îv3H»÷žDÜÄËl7Ue»ííVY»Uüí¶Ô,[E¾Þ±²J«œÒ|»tJónG uY‚ƒÀ¶œBi˜sŠìð‚/O˽ Ó.Ñ+k-šÖªAk•ÓZEkº.Áyµ¶œRk¥Ö€W’µ¥DZ«öZ«´×­5Y¯¦®éÞìòaŸW|±Þk®×*ëµ*ÄkÎk²ÞøPÓÝy™Jg³úñWoß}¶¹ÿùÚóhÚ†óC*$×€P‘™kªÎ\Ûs­¶\à (V>ÕV“s­¶\«×|»t\ónÇz¸ùÛ—Sp ;H%9<†\ôKæ_®Õ×jǵ:×àuôû*p­¦ä06nK‰¸Vï¹Vp ®y \#ë3\«-×ê®Õì¹–FgÅáÚj¬•V#•¼žVÄ]ö“•¦·YiƒÒšÆè”ÖüÌ-Ò+­i ’‘õÄ‚Èf¿ÝڟݼbíæßÎ`.Ëk7Ô™vFSm¨vÉvck·ö륷[ó§ÞnÝE6Ù©¶¶B+™jk«bíÖüqÒnžš'`7º>ã`·®vúð ÝºÏ#k»%ÒYÇ,„¦Ú>x¤³âÚË7Jqã`ɦ,+.®âDVÜ^qÂ*NðWÜBsmMSP(®ÓVœ°ŠNq¾qŠóng0ÅåWæL;£8T»dÅñUœ'œâDÊ3pm…V2×VÅ)N(®y Š#ë3ŠVq"Dq‚½âÒè¬8Š ~ܵëÒuÀç_ñùgåºñ«XŽxË1®“8×­WÚ§T´ª“ˆ‹ÅJÛã±=7ˆ×O6{èõ&Þ|»tzónï²²ùbÒ@~ˆ-§ÐæŒ";<>gW'7L›Râ9ÔíuÛ‚I:0ÉãÁÔdÀ»Km9%˜$ÕxN×¢œºR"0É=˜d˜àš3Ï¡¦ë/9Ô]íôßa,IÎoI¤£ÂG碖VJ£çȸÊGâä#Yz¹HÛž“|T–“²òÁ|úOÒKÍfÁƉ)e壜||»tòñnÇÒb|Ë–SÈsF‘§OÛ \›dùŒä£ù('K>ãl9¥|¥|Ƨ®”H>j/ ¸æÜåCÖ_,䣬|Tˆ|kù¤ÑQsäã&}¸‚G!ÁÌʸdðhÁãÀ£-x4wð,5Õ#æèå{jjùž¶ ÒŽA¾q òng0Ãâgæ<;— L›d¤iÇ ô^ÏÞ3H0®9w‘õ iË  ͚Ait†Aj4ä–Ãq‘F‚xîGÆýêÏ ÒE‘QaATpÑR3@ ˆôˆ ¢ÂÈw DÞí &^ü œgç2/„i“ ¢ˆŠD…Q‘ô¼P±žy¡b¢"Dp͹ƒˆ¬¿X€¨° *B@T°Q5D! q%R$Òø Ö”e"Å%R™‰äˆTZ"•܉´ÔœQI¾H®´*†|»tònÇ ø„gÀ'H2‹QgÙá_x—{¹¦]ˆ+u•ŠJ‡¢2éY¢r=³DåEeŠàšsGY±@QiQT† ¨d¢4:jÎb9 nŠókJ¤ƒÆVhʲƒâ:Èd9ë ÃÝAKMrë ãäÛ¥sw;㎳åœQd‡ÇÐA˜vÉdç “ôäYÏäÙ;È8®9w‘õ ë â ÃÚAitÔíßÇ™?ÉàëNÆ}§kæ®2*ËŸŠ;–šªhVÊ™©•r•EQåPä;‡"ïv³/~aγsš´KFˆ¢j@QåPT%=9T­gr¨Ú£¨ @\sî("ë/(ª,ŠªU¬Q”FGÍY1$qæQ…äÑø:Ü”eÅåQyäxT[aF «žªixTMñ¨¶<ª|âxäÝÎ`RÆÏ#ìÈô\æŒ0í’yò¨xT;ÕIÏÕë™3ª÷<ªxל;Èú‹jË£:„G5k¥ÑQsx–<ÄL5Lã+sS–ÁLÅ6ƒÉ‚©Øö`j~ž}{<¶çñrºfÚŸ¼»´4òoÇ ûd5þÚr¡Î(²Ããsvµ,Bµ õ—oΊå²öË£wYó§ÞeÝE4‚Ë”„o4å„.k+Dç²ñ}®®”ÆemU¬Ëš?NºÌSs´Ëv{bî¡ÌèzŒþ®ä´ÌºÚ=èÁƒ2ë>leF×Q'_Í7ÄÄ75Å\=Ö^ªQ_‘å1Wãì1Èc"{ÌyLX î[h«ij ë1á<æÛ¥ó˜w;<ãy[Ná1ÌEvxœ>m/pm’=ÆÓcbð˜p±<6΀·å”«YFØVÅyLx ®9‘õ ë1â1ÁÚcdÅÇcûi1Î H†/Äò˜‹pfÄ0™æ&-Ã$w†-5-&IÖ¶C¶Ã8“gÒáÌw gÞí f£ü8Ügç2Y†i“Œ3ž8“ΤÙLz²L®g²Lîq&pל?ÎÈzŒΤř Á™d3²Ž:ù2F`²lÿ¸g¦IÓÔ¸¥›²Ì´¸LS™iŽiÊ2 s5Xõl ²£™&§˜¦,Ó”cšï@Ó¼ÛLRù™†9ÏÎeÓ7qfÚ<¦©iÊ1M%=‡¦Ö3‡¦öLSLƒkΟid=Æ‚iÊ2M…0M±fYGñaZØcgœá¦pHš² ·¸pÓnnÚÂMs‡ÛRókðÒŘ˵%švDóíÒÍ»5ܪ ¶åDÜQd‡|á]ì¨v‰~X™jѨ¦ªiG5…jÛ²gÔÚrJªiBªéñûǺR"ªéÿËÞû/Écº¯’ÑŽ˜Ðx¬îÄäðèkÙ;«u{¬îqx7:&6ª¥ê±v¤ªnUiÜÞ¿ööyî?÷QöI.I€Ì’&q˜8ä9L8ÂS²U$pÀäùðë;¡šN@µxÍÁ¨&›Èꨯ_!Æ,<Æá—9XÓÖt ¬i¬É&’j7¹‰ô`㬋=þêæýO¿=<þr7±í@øˆîëÄjáÈŒ¼dh¦°ZŒÕªÂj«UžÕ*ꬶÖ$[…Îj•gµj`µ©K¬6ù9†š°Oùr Vƒô(´Û#Èjv)¬F—Õª‘ÕªÕª\¬µ0ÙU>aµ “Õ£ÏúR$V«N¬V%°Z¼æX -f$X­ò¬V¥°ZEžÕx+«}2•FÑ* ¢…­Ý–DË‹h¦ Ú€hÆ#š¡ŽhkM§œuÕÜ:HãÁÍ à6u#¸M~N`kÜ ýìš&Ù íRÀ.¸™ÜÌn†õ$›ÙÏ$›9›I·xÍ9€ZÌH€›ñàfRÀÍ7ÁºhUdl’í“ml¤Î.Äœ¶¬ \^„³á„³á,u„[k–Íâ œ™C8ëÎ7u#ÂM~N`rká ýìšæÞ íRŽ.ÂÙáì€p–õÜ›ÝÏÜ›=!œM@¸xÍ9 ZÌH œõgSÎ’G8ÁÊ„pÉ[ÜÈ@Ýä ©úâ+œ¼¸ýñæã»Çé³ä—Í¿3W½œ8Î^)Té½ówoÿôo‡¿t·~{x¼}x|hÿíáÃÍÝ›û÷‡÷·]G|w{÷§Ç{8>Þ?oûéÇÛmgþÛC–œb²euékdúÚDÿuŸèÆz"Y¼Ê›ÅO<ß0ÃõÔ‹VÇþÿ‚Kûò½ ÚoO4¯ÛÛ&úÅA)V×Å(>Lsn0ØÈ‘¬û9Ô·ÄÜænFk ¬&ƒ5PDA¨ Þ[ÍÉh,5&noÇ‹{»ñŽ@“â4¤Lv8¼ùáÝŸïo~¡0jÁtªp}K[¶<À›Š_t"VŒSàx™ŸhÆöØ6ð`Jêw@Õ‘ jžÂf.½}¨±3ƒÆÎdÐØ©”ªì “OͱµÊœWŸ]ž´u&A[7Qcê€ÀÃ…¶¼´Î¤Hë ii“@¥gµª„Ê„ÔÓ©pùD[¶Ba7+d-ÂÄó`_¾´©eoš3ÛÕÛ†#L¹åÎ2(°±Á‘{´aKJØLê¶J}jfð©™ >5!ëÈ—I[ˆÉ x^®¼/¬³¤pr¨™‡ÚD©“1×RRð5“bP3¤ jL˜J8ÒG¨"-œ°mËv‚ ÿ[fý[|Q~<fœ Ç@Á»Î ÈAµ»–ဠSn³³˜ µMˆ ]!ËÆ0aã,‚;&Œ>/3ø¼LŸ—ªcSXõ%sX󘀦Êü²:‹ '‡—IpxMÔ™:&ðC-Åoð2)/CÚàÅ$P€ ˜rE¶*ª¼gem9£¡Ycy<–|Óá(x·–v×2@aʰu aÒV± …¶”ePc¨i‚ *Œ>)3ø¤LŸT7yŸR@…4ÕMöWÖY\8y¤L‚Gj¢ÖÔq-V$pÁ;¤LŠCÊR+ã@AfðP ˜‘™×o9­]¿ÒÄB|ô|éSK Þîd@.žµ V˜2^Zg™á¤Ð1 ‰zSg´h‘`¯Ï1)úÔç¬Ì <š_` @Ž–h{†0†øÝã9 1dðbkì±m8`ƒýã_·ß^Ž ñÚlEþ¡‘}úºi[ìƒF‹|,&ƒEꈧM£žo„¦‰ˆ¼a‘òÿ“Å$øW&jL=ÿG‹‰üß»WLŠ{ÅÝ++çÿ<[_ÔéS¿¼}Ó!@'TËÞÜ<Þô…ßS«ÄèhPÐáÙlmÙNè ‘ŠHµ§ƒº‘Jžê'tŒ×6ë÷V¢ƒMÛbtPtPtPg°5FèÀàÒA•Ì„smL³¥ƒúDuÄkLÐâD‚jOu Ô¤é€G `+Š>§ÅŒj d£!EåxTT>h<€ì°m8ðAó„@ñÚnÍÞJ„°qk샚‘šš 3ÇØëéˆK ÚJ¢È’<,NhNœÐ$pB¼ÎÔ9-R$8¡ñœÐ¤pBCšx ¸Š(2À‹ )DLÌz/&ær:*'Xob¶MíþZ†%´)-55å³Jغ5vA v-ÛA´l3ˆ–MŒ .%X43¬!×¹¥ÑžìI¶ldËu&N x‘¢@ ÖË–mŠlÙ’–-3 h6!:ÀŠ,PȬ#Bf½!s9•¼’ÙBlµûkœ žp$Z">÷³N¬V"…ÍÛc¬0:—íà\¶œËí÷BtJ™Ð4±¢ õ¦m/œÜË6Á½°ÌÉmlÑ5³Œ «U¹Ï«ý„fxGjŽf¼/Ú¦ø¢-i_4^ ¶¢™^æàXåÍíÏdY¨Œ®"CÎÕn”Ñá—Ó* ±¦WO0ÍÇc¬â•ÑdÞaÛp`•%ÚhX`#êy•Y=¿%­l¦Œ.´’VFoµ¼Õ6·:ìù—pú<­à9«s¾/ÏÒÊÉWm|Õ5¦O+h‘"A+ÞXmSŒÕ–´±/P[­Ù:©çhã ÐZ]E¬ÕÕ~¬Õ‘9´.»ÃƒRcÀâ­Õ$8ÞaÛp–%æjX`¥Šy«Õn¬Õ Ö(ÈBYFu¶ÔÙ6“:;6©ͶˆÚì¬oͳØrRfÛeöDéc ^¬(`‹—fÛi¶%-ÍÆ ÔfKƸp Pœ]E´žÕ^ÄÙåH[Djñâl ‘ï¯e80‹]Â,°*Û„ÌÒ² iŒY ­Q˜…&³ŒŠo;(¾mÅ·ªc3Œõ%SŒóÌ‚æ$ÎüÆ<Ë,'Í·MÐ|OÔ™>³ ÅгxÑ·M}[Ò¢o¼@m¶=ž ³uÞUDç]íEç]ØE¥/ô¶)òþZ†µÔK¨V!m›jiKY5Æ-ö(ÜB“[Fù¸äã6ƒ|¼›V‰O¶ ’ š/9û{ó,»œ$ä6AB>Qkúì‚- ìâ5ä6ECnIkÈñµÝ}.ð´W‘ƒQª½ØÆËY¿ˆèâ]ãx|ç®Ú…¸4KÀ6ä¬"àÒ—² j \ íQÀ…&¸ŒFt;Ñm#z7·ŸqA4söwæYp9YÑm‚}¢ÖôÁÏ`O\¼ݦxÑ-i/:^ ¶;“˜ ¸åçUD~^íE~^ŽÆD—ÚëÏkˆBzoíÂ]ÚøÀÑTÑ~íÄ']šýÀ ¨E ¼„—zµ×ƒ¨½Î jïçW&¦]0ñ¥F3K#¼9ÏL}¶× Âö‰z“Äx˜Ú+Ûëe{MZÙŽ¨ GfB05PÌIw‰•ÂÅ´X­rõ®x%}¢¤¯JzÁ…‹+<‚[‘ҳà–¾Šhé«ýhé#TºÂewxÊo X¼–¾Mdï°m8Ë5=,^Û-±\ YèµF–eТFhQ´¨ s,ÇØ$Ë[ÐäÝ"2®.ê.*\âu†Ï³Äâ%0ãõ 1^¡ûûó÷RtQ]T º(àLË16ÕrÄ…á.ÿŠL¶ðÃÄ—°ÁÛ²àK9^´‡ˆ8|-Ã]ôtDKMMŒí]èµFA—eè¢GtѺè ó-±TØ £ š©[‡C•ú‚‘ʳè¢Oè¢Ð%^gøœK,^ ]Ðâ%ckׇíáE§À‹λÄàE!à —€æ^¢“/üðEÃðÅD¶J›U6 •3‘9¥é1€©<À@\âûkS=H´D|¦lX­„0Û£@Ì2ˆ©Fˆ©ˆ©rÌ¿Ä'`1ϵY!..Y!~dªÈT ¯õ‚9˜ø$ 2Ê Å̆ÃÌvƒy˜Ê£L•‚2t&>ƒ 3\B›‹‰OÆðÙ ˆ3aÒЖígÊ)Ɉ0c<Ì@,ã{k(cž  $Vjz—ÒnP†b{”Y†2fD3 ŒÉ1ŸAF4­·¶anËPÆœPÆ$ L¼Ö ædâ“2È(ƒ3©"'ª N„] 3ÆÃŒI—‰OÌ Ã Ÿ Áæfâ“3üpÆq&rr‚Yåì„rv2§Ä=4Ö DA¾·vá4ö Ð@b%&¶1­4“¶ÒÐl‘5Ë ÆŽPc¨±Yæg&&h±OÑ^™prWˆ6ö66lâõ^2G31IƒŒ6hq³u˜°µeë£õhcSÐÆ‚çi&&já†OØ€s5“5´ñfò]T}ñÕ‚´lòß,ùeó¯ËU/'޳×C UzGüîíwÊ×ß¾8üóßû¿ÿö›¯ÿéïþþðÿüý?ýÝï¾ùúÛÿòµ=òoÚÙýÿáß?¼m»åßôýòo¥?žIwô÷¯Ñ]:™N5‘­Ý's á0É‘—ä8´†7r &!ˆ)ÆG†7¯†j@¡khg2îbúõ9Sیϻ\ÔœíWøüŠÜÙî÷_þnãÛ¢ÛÎj „[š|!)cÍ(hjAS“AД]2ƒC¿ï+Âà¸æéXüc_?îÐ$H™&j wXÓôó 1Nøõ˜ih¼’©IQ25¤•LL•žä¶/½§#¤nï~NµKB/ ˆ¨f2‘a_³ÊŽÁB¬ó¨– ¼œ© nöß:ËBx‚5gdG½È¾¥¾'ˆMo+yy^ìtj †€´ÊMq§ˆÑ¦Ô 6¥&‡Mi}Šà!}9K'›R“`Sš¨1uŠ@‹ Šð&¥&ŤÔMJ+S@f(uý”"~s0úÉtåÚP±dZ¢ ”Ld©«Ye©k Æis^¡Ô@44WÑ:Ë Bz¨€4çŠÓ›ÞØöHi ìWYYŸ¹ŒjFéR3H—š Ò¥ ¨Í ³Õœ¤KM‚ti¢ÆðÍfaUî~Â5h‘Â_ žÀ5^¹Ô¤(— rie®A ½Ù‘nfÿ·Ã›ÛU¨¦ZB5@Ï’‰| › ¾„ Õ\EÞÞS÷,5PSÍî[gÕ(O5 æ\oªdÓÛ"7URr潩B6ÙÈf435ƒ™©ÉafZŸlÐÄ1ë‘ÍÉÊÔ$X™&j¼`·Yä›ê˜û ô¶ÁshQ`ïdjRœL ØÉ”ùäæy¾á,Œy›w:Þyò·Ç_î.£ŸOJ]É“‚áÕÒCз·7o¾ˆÀÑãŠjëùøéËãÓê_N‘ßñúÞ ¿ãÛ]Óßùó3[†R±°h¿¹FûÍ Úo>býf9ÿ›_ßJ:÷+dÂ~1ŒÍÿÖ‹k”õ~UÂoUàß*P~kJ àüVx»Võ®r¯ ~«œï¯çžÚÅ‹%>½m=û•eA»pûß8°Ä/öðÓÍÝÝ'_þ?h+ùäÓ0©úæáO‡‡·ÿó˜[½¾ÿËí‡Û7ÏgkB·æÿµÍçHÕ|¦)ÂmÙ¦ÃÏIµåj5oSÿ+­y‹9ÛÔ|mú€f Æ ou)s_p?nxñŸ¶¼øÍû» ¯Þf)oï^Øðþêýß¶l‡­[à›·[ö€on~)ußæÉÿøxÿ¼}oùµûáæîáííÝã†÷Ðþïí‡-{A?TºáõûØRÿRìy‚×÷ïß§?먹j9RŽÁé_é=ë?ß½}|{ó®%«ÃýÝ»ÿ@[‚PΆ#Ý;(-úüG´Nß·¼Ý¢='ç…ÈM±CR}–Ø]cTÂ¥u›µÂ¢'™ØŠ¶Átë=·,·‘“¸1OOdzۮ±6êû/ÿqôÚÎ[m39mW=q->ÎpÛ‡-Ð`»êŽJOÍþéþðþöááæO·‡ÿó¿þ÷áÜüpwóîðúÃm_¯Þ8Ñÿ‚Z –¤vÃïÞ¾ÿéÝíÐ}ñÃWÝ0Éoïú®÷ÐþºÃO·Þ><ÞÞ=~ÕŸÊ?Œ™~¼ýxûæ«»û»n·ÿË߸o^wÿöæ]ÿÙ'%Œ¯ NdÁ_Ã×­%ÆÃæøö:{üVaõºgõšÐšr¬.b{òÓ:(.yo‰R/} i‡ÂëŸñzíy½v¼^_ÎëUÈë"¯×Üy½x½žåõx]ió:Z|ðzÝóz=Ïë5a^ç Àþ²Ã_Äñy7O>Ð3ìvNFÎm°«dؤsÆ'D/úãþòÎÞOkpHܯ32aâi‡’¸–¸7>qo\âÞ°KÜî‰{3$îÍlâ¯+íÄ->÷¦OÜ›ùĽ!œ¸ó q?rÉÜPæÊDWQ‰–±qÔ$[8ѧy>wÒ’lX`vœeƒbã°Q’høm œVG¤-Î…¤ÂûGEýèÊL!ÐT‰kA…Õ£bÞ<:Q]Ú\#`!œvT$XGeé(“ ]4+pø~\õGšœ!`ÖÑÈBiâ A:qÞqRøB8ɨ€9F÷Ò,øå:CáHC~¹^~ԧ›OEñéÚü‚&i\_F驘wžNT—8¿ði.ä'< ¾SAYwÊ$H—MŽ0˜á4ô›®b7-3%¸¬áÜ£¤ÝIk° ˜vÒX§)^nÙ3_%,%‰AÔ)¼§SdÐt®aTzšƒó6?vIܨèó†Î‰êÏÁyHæàNÎ)Üœ‚²š“I.CøåŽjò q†Æ°U$œeö€zÉèwSÀ´›{i9=ƒÐ¬•ÓoØ»ÈéE¥ð†J‘AP¹vNÏC¢w.§å”bÞM9Q]â9=ZŒ(äôNJ)œ”¨¤\7§ç¤‹ÇÕé&õ ”Ô۠–xR?óÊ,#ê]þ­]þ­Ëˆ:Íì˜}gß´š¢¬ß¡Ëzà í9Cóã Íž3ôÈzž3âÕ%Îh1¢ÀÚq†Nà M™3xéò¹ƒù%<uPóš8pN ÷3j¡§ßÂ~ÚƒÇ0ÍZC«) ÇÐå˜A"¼Ed ¬Í1ì-(bÔ ˆyÊDu‰s ÑÆBŽq*‘àB”e(L‚”a¾„>ÈÀŒMPõ†8ȸ‚2sr–9ŒcSfNh(0»Áo@bã]Ïiþ¢ï2j `0 ¿ Ɇ=˜Ì<Ä« žÖ®.8®}xGiŒC“€†2  ‰Ò”ƛ۟©æÿf4CO.ÊTÆ^²µÂ:¬°W™½²‹ë M, QÀ‚$XØ,¬ Ë,,딵ËKív,âÕ%¼£4Ö…M K,ЂDjŽ0YXYDú~±%³Ï¬^§KÅ—LŠ0Ù#Ucò¢ìû: ÞfáÅÍ‚Ÿ¹Y°W7‹ÑÝ,æåÍÕ%¼£4ƒÎà,΂²Ã/HÄæè¯2‚YEø0‹âu.³ qÀpbgÐ;}?íÁ1®34Ä êv.ˆ‘ŒƒaZxÅ´àç˜ì%Ób´L‹yÍôDuÉ#ï(Í †sM‹Ù´ l›Æ µYúŒóO‹P@-Šš}ŽõB:µ,jš0 Ì®wOkвìh ÈA-½ ZfD¯¼±Y²DËQ-çÑÕÁºØöŠy”ÎtŠh™ ˆ–@E´Ïð×â?‡L”cÖìy°¦ˆáƒ,Š$šNE 0œ$Z^§‰˜b0ÍZˆA«) b,BŒÁá,½ÃYfp8¯ìÎrt8Ëy‡óDuÉ#ï(Í †³8Ë‹³Zœ×F aÊ2ë@—1`gjœEñ8sK#8à<βxœ‰Â@ñ80@×ã\ %ÃÀ “–^&-ùɤ%{™´eÒr^&=Q]ò0À;J30àtÒ2A'-:éµa€G˜.œoèÎRå° IÂôÒ"ì+¢¦é¤R´Pà ¦%‹ñµÂƒÐ¬tÓ6’ac°\Ko¹–ü,×’½åZŽ–k9o¹ž¨.yØà¥Øpžk™à¹–@ÏõÚ°Á#L—Î<°¡ ˜÷Z„âkQÌ×Üá8óµ,æk¢X@K÷¼ÝhPCß½_ŸÜ9$„ÔÒ ©e!µ“M‰ì…ÔrRËy!õDui‹ÜðbD@ä&Z&©%e!5“ ]8÷Ð~tûì2$˜‰Z„*jQ\Ôt†Sé<æW8µ¤eù-dñ„,®34²€4D! ’d1(¢¥WDË ŠèµÉ‚½"ZŽŠh9¯ˆž¨.q²àa^HN-Ñ’²"šI.h 07´åТء٧V/¤³CËb‡&ÊÅ퀑q%ùuOç`°CKo‡–ìÐk3oïp—DŽvh9o‡ž¨.qà¡^ÈÎ -ÜÐ’²šI.œ]×hÃL-Ã&–E]æâ„áDÑò:mÄ,ã:Ca F¢èÂqÆDÑÒ‹¢eQôÚŒÁ[AÜ%©£(Z΋¢'ªKœ1xˆ2†ÓDËM´¤¬‰f¤Kç¸@Ì-Ã/+YœÑ쓬Ò9£eqF¥Îèµ¶9oØ©øu®<¼ÑÒ{£%?o´äm$îRÊÑ-ç½ÑÕ%N<„Ä ‰ÀY£e‚5ZR¶F3 Ò¥³¿ÜG˜2Z†rFY”Ñtò':zÇN-¯ÓKÌ‚0„f-ÂØ°) aä"ŒA-½6ZòÓFKÞBâ.EµÑr^=Q]â„ÁÃG¼0œ4Z&H£%ei4“ ]<ç@1`Æh£e1FsË~CPέŠ1š& À³k ÖeýÑPƒ1Zyc´Ê`Œ^™ÉíP£1ZÍ£'ªK›ðbD€”óE«_´ú¢We&AÊ0ÛÀd ’‚©£eø½+‹:šN&Eç¡ïHé£-)oa'¬Á 4k±­¦(¬±ˆ5u´òêh•A½6kð°ÝžcQ­æÕÑÕ%Îh1¢ÀN­ÄÑ (Ž^—5x)ǼØ€9¤eè–Å!Í-ŽpsH«â&J Ò«ì~5ÄÆ»Ÿ÷+l#Ñç cPT+¯¨VÕ+o°V<¬ºç cTT«yEõDuÁСÂeØê‚eسØÁ;J3ØáÕ*AQ­€Šêu±-H”¦8:]mÖ€™©e(”ÅLMgؖΣÞŒ3S+úãkE˜ë Ma(½` ÂPE˜A|­¼øZe_¯0<\½çf_«yñõDuÉ# ï(Í Œ_«ñµН×E´ ‘š9¡Ï00ßµ }ײø®Ùçp/”ó]«â»& ÅwíaƒªïZ1Xº4ˆ¦•M+~¢iÅ^4­FÑ´šMOT—| Î;J3)¸SM«Õ´¢¬šÆ µY.k—`âiЧeO—ù„x‚ïÄÓê:íÆ,Rüë M$ŧ*žæâÆgåÏŠŸñY±7>«Ñø¬æÏÕ%ŸâóŽÒLŠïœÏ*Áù¬(;Ÿñ‚Dn”KŽ3@ËÐ-‹š}ŽóB9´*h¢É8ôZ»–i5EYàC=Ñ´ò¢i•A4½öÆhö¢i5Цռhz¢º`ôX7J¯˜Gi=œjZ%¨¦P5­Ãèú‚è³ðÁ#L9樟˪`ªi¾ÅUQMÓIÕh¡ŒSM«ëô³€¡Y fh5Eª03­•7Z« Fëµa†½ÑZFk5o´ž¨.y˜á¥˜qNk•à´V@§õÚ0Ã#LYfRÈÓ Ìi­ÂVÅiÍ-ÑŽ€‡sZ«â´&ŠÅi=b]§5‡EMƒLZy™´â'“VìeÒj”I«y™ôDuÉ'ã¼£4“Œ;´JÐI+ NzídœG˜.œYèòpN+›`zéÌËËÃ~ÆÔ#©¾ÓK+ãkMö„f­dŸ®^šC²?x•÷:+~^gÅÞë¬F¯³š÷:OT—|²Ï;J3ɾ3;«³³š×Nöy„éÒ‘wÒÙþä ¥úâ«ÉËä¿YòËæÇÊW½\ûBŸ»R¨Ò{àú>Ö=2‡?Ü¿?ÜßÝÞ<ÿáÝýë?ïÿ~øéöÃÐ7Ѳ]:™Î3‘Ì~­`FwÝu£;•×%TZ…굓´èâéÚ#–Ë<ƒOÛjÿ©mµçmòj¼:L°ëÜ ö÷_þnË{"Ð/_‚‚üIˆBÈèH€¤'ÚKOt鉈œš#.96g_~¯y 5Z”Ô£öDÏkO&ªKÛµˆ#®Eí¤':Az¢)KO˜ 2é÷æ‡wn1ÿæõãáÇ·@? “œ¨ðu®Š½½@œäD30ià¿lÁ @¯u§õºÓIØðž(uð— †(ð9 Êí•!:ƒ2DDŽÔ—œ©3 h¦ƒpMîó¾<ŒÒ=/ ™¨.qàa£XN¢”!š²2„IÀð›ƒ8tsDó˜ D…osE]¨^òÿ­ògÑEn±$ÿ×.ÿ‡4^~V¹Ç>»üÃ{JNžòÚX' Ò(÷Äœg‰öÎÁY²þ$oF—DŽÖ=o-™¨.qà¡ÃXÈÎY¢œ%𲳄I–Oô I¬ù‹ ÌO¢Â·™*¾ó‚qp~Í@‚Ap* r(i¼*­r‚v(°á=QCHSQ`p›hï6ÑÜ&ëO )V›í&zÞn2Q]â(ÀC›±œÛD'¸M4e· “ -ž Ce÷ƒ éý,¶ûç¡ìþ¹æÎ3‘²~­a¶"eCâ·…ø ñG‰ßß§œ‡ß±|Mýñ¯#¥ú¿>7P»‘H£jΆ´ec›Þ`wc¦( ? Ôþ@@ã@@t[†8€vŠYÄ׊30 ¨çœ¨.ñ‘´Q pê„5ð@ÀuGx 2ðÀagðÀ¿ð}®ò¾Î ì#îàÀø§9œ*‡>˜  ÷» çGÚ28Øô®(uþ— ¦(pð9 j€ Îq€ ¶iËáíÔ³p~î€Æ#õü‚Õ%h1¢îAp€  ¸.ðˆï¾ÎUÞ·yaƒÝ°AuìÙ ýAž øL´­Ù±¨QµGLÛ²ül°í][FjŒ²Œ0àƒ¶ý´èù ÿ>á6yÐÕ‚÷äAWÇíŸæø`¢º´ù/Fø ¯Ü)vçø âˆé;&AZ>y@gaˆ ÝW/tؤz•6-˜À,-î0A8Lô1ÏBÛš=&@Uëpì´-CÀ„MïŠ&@£`Bˆ bÀá1A0œFèjÁ{¡«Ç1 ñêÇ´QÀá0A$`‚ Œ <‚´x%”ÝF 6Œ¤÷³ÏwýøöÃÃãá§Îyõxx¸}}÷-Ó({Œèt™‰Dõë.¯‚Œ„°¤WѶíu \–¹“M/*w¼`9‚m§­áÿXiýǯ\ƒžp'v?Ú”éÛ*þ«Ú”©ûøÓ¢%t$`m!Âà¨å¿/:}©\€4FY¡øùÐÂp¦aåÏ4¬2œiX‹&œ‚ì Ðc3á$¤Aš…Ï5¬æÏ5œ¨0ñáGæMÅhfxÁkX%œkXQ>×Ià _Õ=äQQˆ0ì$ÃØŠ¢¼ Š s0«é$s¸s +ÈYoáâ×]ìdÉÄÕyæpGv?zæ¨Î3ÇäÇ4™Ò‹vÏÆ(Ìñ9s ‡'VþðÄ*Ãá‰[0Ú©o+2Çx€b5€âD…‰3³ù2‡;@±J8@±¢|€"“ í†9`g©Å–'å]T˜c™Ñ‹Ê8æ0Ôç9h­s<Ãæ ½á¾R/-«È†© c¤AîŠ9„ØB¬‡ËsâÃî`âÃŽbç!$^aâ‚% b„ر”!„G@™M\±0 GFô##…@v:àß%ÌN$SA”eÉÕ9©ÏˆsÌt?z©ÏÈäÇ4 Ò‹®‚@ R$F ƒ­¦ò¶š*ƒ­f AÓl¬H £±¦š7ÖLT˜8ð¡,$g¬©Œ5ec “ íŒ@`¾¾Êôo²B ;õïfg«©@/ð}òX&iΈÙt?ziÎÈäÇ L?\uÒ"¤!Ü=4H! JœÊ+qª Jœ-b¡¹F¦/ºB<2hÎ"É tŽƒÌ¨ÿ1óúŸ‰ ƒ9H…sf oÆì^œðòfIÈ8IÊ ¼ mMBoï^¿;¼¹ýùð}û?DIÈ @a®šù€›BBÌj:IBÎÿc ”²,ì ‰ó$äÔ@Ýž„Äyšü˜& AzÑîIÒ…„H’Ð`72Þnd2ض !4-ËŠ$4ŽÌ¼áh¢ÂäI-NHÈ9ŽL‚ãÈPvá©P =(a®šùØBBûÈ×^éHHRŸâ²<­mɳ$$ IOBò< M~ "ްíºBõ¢ü÷EéÉz jŒBB$IH$$= É,$.(ë IHòßÓÕÁ“œ'¡x…É“Zœ(t$$HHR&!´ ‘!¡Ó29ÂH$AHT…[­Òº‰˜%n/ŒrHytwÚ™HG"åHy$Š_`D¢É;c„ 󖮉 ½á¾R/½ÚŽPƒd¿«‚F¹ÐH h¤<©ËÑÈêXÿ×ٟ˧h¤°²¹*|'T¹=PÔˆFj↣Ñ1|ý´eˆp„©p6a2oŽ”ƒ#•G G2rˆD< ä;&º~uóþ§ß¹ë é@w3Q÷­ ¡¤%ª¼,Q(‰YM')I;J‚ؾ˺s”¤ÏS’v”¤=%ÅÕHI“Ó¤$H/º J‚4H¡$²”¤JÒž’4OJBS¯HIz¤$=OIñ 3 $´HQ $í(I'P’¦NI<u”¤a”v•*ï±®…’öÀ]R_9JÉÚ÷ÉŒ™(©:OI•£¤ÊSRuž’&?ÑH¸œ§+D $H/B¸/z”iBId)©(©ò”Te¡¤Hÿ×ٟ˧”„&M¯Â‘³*·ß×SR5RR5OIñ 3 $´HQ ¤ÊQR•@IuJâ¨<”ôɲ;2¸4ùn©¾øjæŸ>½Ò‹Ûo>¾{œþ7K~ÙüëoÕˉãìõB•Þÿ¹ë|¯o^ÿÛíáÃý»w÷¹ýpxöã‡û÷®[ søáæõŸ÷îïÇ_£å&ºô2ýg"ÃþºËÄ ##á·jµÊ×*·‘‘0<¯oå3¯Ãð…×tLüw¦þãoà-.By9hÃ'ü23ÑnÛã™ëñ1ÈÈËÙ!§ü2^ù5y=?ä’Aùe*ÉCûR„AHOB¹3:ý©r4Âñ>lß º/ãu_&ƒîËT• ¿ÁúRı4KQ%"Ó+"÷“íG;Få—™W~MT™öûxq"pà¾qÊ/“ ü2”•_L‚åèá‘ÂèEi`ª¯*\kSå]iV&‚4 ¤Yg¥­3ˆHN B+„Èk…Øi¦¤]ÙÆ9ÄŒwˆM^Ï#M‡ØºHË.w4æ(Hó9Ò þ0ãýa&ƒ?l¤AÓ­Š4£CÌÌ;Ä&ªLixè©"sˆ™‡˜¡ìc¤ LV… cª¼Ë ҄Hc,f•YëLd¢MÚ-â=si¬s‘Yï"›¼žCšéaà9'»+Í4 ž„rg”ž¯— æ(HóÒØAf½ ÌfPuðÊô¥xHcÑüE•ˆ,õ8‡àÛQfçu`U¦4xq"€4ÖÉÀl‚ ÌR–1 iÆe§4ÙÆÂ$`‘M™÷xlÈ6_†àÖ–Qáä›Hf½“³¿_Xç³ÒnÛã™ëÈ|ã cÖÆ&¯çù&ƒa¬©¥ ¯/Eà©ãÎèô§Žo6³Œí‚oÁ—õ‚/›AðÕÔê.•èKùÍJdM¸ »-Ãá›Qòeç%_U&Î7<ìQ ùÆ)¾l‚âËRV|1 €o¾¬zÂy J60©WŽUy—ô”Y›ÕÈ2k3Rs…YˆØh·íñÌudªq¶0ëma“×óT“Áf*]y#³¯¼é¨Ò“Pî,õâ«ij¤»bN7ƒ´Ëzi—Í íÚdAšE3 ­¹ ÍŽâ.;/2qºáa„ZH7NÛe´]–²¶‹IÀ³7ßS9'$8@EWÞ£` ऎb8«ì´±ÎüeAfÅ=ï´±S²­l€ãÜ_Ö»¿&¯ç'ƒûk]À¹T¯p6õípõ–õê-›A½µ ààÉ·ÖœQ¿eçõ[U&8hq¢8N½eÔ[¨ÞZpxiW€´kå=ŵNàh€³Î Ž“vYˆ]h·À÷ÌudÀqÚ.ëµ]“×ó€“AÛÕCl£Ì¾Ñ HOB¹3z€³©ºk€3X³¬·fÙ Ö¬mößày³ÖÜ3š³ì¼9k¢ÊÄ-NÇY³l‚5Ë­Yë çtì;aÒ²"ËÏ3¯=/»p&h§*»pFÚqò- ±í¶=ž¹ÎL;N¿e½~kòzžv2è·šúÝvpÌ¾í £HOB¹3z´³©‚k´3د¬·_Ù ö«mvã ÉzVÝ3°ì¼k¢ÊÄi-NhÇÙ¯l‚ýÊíWëÒ hgØCšs`¾f‹&o¶Xft"ŒcÊŒÎÈ8Æ1Ž¡?£³ãlÆ1ŽqŒgœ©ëyÆ™ü2t­L˜Ìõ¥ŒéI(wF§?u|iì7kÑ /,3–ñ€erL'écÈ$})"`¼é¤ðu_]2ðu°ÌXf°âU†+†Ãõ€ Ï[û /Røë"æË8Ä2 ˆe(#Z¶PÕÂG˜*hhEyý´" eY€Ö:{ƒ¬-[öyвؠehYZS×ó 5ù1UЂô¤+-HsÐ" Zv-ëAËr-» в#hÙyЊW™…0‰aaFÓ–á@˜!LÎCX¼Ê -V L:“ &©C@倰Oö€Q¦1 £±ÈhXÞÁà²l‚ÈTÙ6™rDZJ¾×öxæ:2‘)GdÊÙÔõ<‘M~ Øsb¥Šì9éJˆ Ò“PAš¤Y"S‘)Od*Ã~0[EŸ€*û³ù”ÈÞ~°0¶—œÜ|ŽÈÔHdjžÈâUf@dh±¢@dÊ™J 2EÈx @dá®0¢3c“ï•ꋯHñâöÇ›ï§ÿÍ’_6ÿê[õrâ8{=¤P¥w¼xûîöðøáæîám÷¸žýøáþýáÇ®ôØþßáñ¾û)~–ŽèÒmÈt›‰¤úë.ù‚Œ¶D†½óÎú”¹ï§ï·ê±·^íÆ_€>?ž˜$wr þ‹Úùüjˆvl·íñÌuäñçó«½Ïoòz~ü%‹ÏOG’ѾaüæóC¸3:ý©{ÙÌç7óFeá*¯•^íUzu•^lÈ£/Eò@Té­wðh=ªôêy•ÞD•i»ÊñâDÀU^;‘^ Ò«)‹ô˜ :ùLa(#:0sž ›Ð®Ò†tBÐúô6Jì×Ùé|z5Dû%ÃUrÇò?stœO¯ö>½ÉëyÐÉâÓ[t`>½ÝƒÎf>½]€Î ²«½Ê®Î¢²ÛtUv+‚Ψ²«çUvU&:<i Aljìê‘]MYdÇ$H;˜¹Î†i«Í» ±€N2è}v»Áè@Çùìjˆvk·à÷ÌudÐq>»Úûì&¯çA'‹ÏN7qfWŠ:0ŸÂQz¾^‚š£€Îç 3¨äj¯’«³¨ä*9[ +ED•\øµe8 3ªäêy•ÜD•‰ƒGÙBÐq"¹:A$WSÉ1 tƽƒD‰f޳‘µõy‡/·#Ù1( ìGö«›÷?ýöðøË]eÂÇuÔ04 ;J}A?)hv š5,ÐlýbC3H:±ïýb 6š5ÍfS×óh6ù1U4ƒ%¦Wf°‡« Q4k4k<š5\ѬÙš5#š5óh¯24C‹4kš5 hÖPG3º4k`h¶o½J4 ÐL,Øl•i³®1:8ë~’o‘u¶“uM‹gÝ:>ëv€6}IGhg>‡PdÿJWšÑ`ý åÖÈ1ð+FÒúHOiÝŸzLs_£8-öØìèNë+ƒjáùÑm ¨õÕp¤ÖýqÕ¦jMŸÕãEÖ\ížÄð®¹ç2¯q V`ûdçar뿪!èæºuÞ\·l?ƒá›(ûÏNø&<¾‰²mÀ7ŽoÂã›ðmê’¾M~Ùî"­mwéŠ1Ô£pnŽ ÂAZ¥ ]„#‰áD–=iÇèsÐcBœ@Ü”&@]!ƉƉŒ‹×›Æ¡EŒÆ q"ãyŒã¬t¶ˆlQ#:õ6ùž©¾ø €!/n¼ùøîqúß,ùeó¯ÂU/צõs×C UzÇû‡·ïnîß½»ÿËí‡Ã³?Ü¿?üØ•G{x¼ï~}x~øöwßü÷ýö÷ÿôÿíðáöç_ÝütÿðÛ7‡ö=Ü||¼ÞvÑ·oû·‡,YÂd£êÒÍÈt³‰¬üë>uƒ é„£uÞÅ¥Û é#C:GC:ÆÞ5†"$(‘ ¶¼Ñ~ðâ[Ûm‹<óýãÒÁ›jfðFûÁ= Þœ—žù<=h¶6ªŽé|ºbŒÁHBº9:=«¸ÙÌi8ó:earï¿·ýx‰ÆK2Û>¦#g¸bÌñ4 š:b§i ‘ÆKôi¼d^,8UoÚBwÄh0º»Ú=‰ßù±ÊnA.LwK?Lò·‡ÿïÿíA•˜HŒ€`jÁ:L"ë¼Id! )šw›ï÷Ty‚(ÏvÛ"Ï|ÿÀ& ÊP5ÐyŸà™ÏA™žˆgz"{¦×¤G!ÝÁék˜³嶸“P5’P5P¹ßf$„&$[›„ª Í;þ¦êM„xèã–’PåI(ÁòçžC¢úŽK ¸“P™f0m—Þɾi+òöy¿ÂüýíÃCßßž½»ùðäïÝÜðãíÃãÃá/7ÞÞ|h»åã¿uë·w¯ïßÜ~ø²Ì_A§šHq¿î“1ÈHH¸G©¾`‹©‘¬ûe“4lÏ=°y?ZQûÑ È9¨;m7V}Æpw7VQcç>ó9€\*YbØ—bŒT€Ƹ5:½ª£€´G™© Æ'êq|¢Æ'rœï[U*êK1G'ðø]QÙWc›˜?âwªÖÔÇ&ÐbEbl¢öc Güºgh ¾ã¨ôTúÍÍãÍÁíL¦0(ƒàI¾‘¤1oÖX déR %‡ÈÛ|“öXëø&ìÓu»+8(i(9Àî™Ïa™¸[¨/Å€ =ÿ­QzÊ^ÂÚ£@I%Í%Í%9N¶mñ#¿ëK1¡ïh[>Fm”4'(™?ÜvªÖÔ¡-V$ ¤ñP’p¸­{‰ê;.Z%§³‘ž=Þ¼}wxøéÝÛÇßeýNÈþ?ûáþãÝ››ÿñkª<ô6<Ì®Î{žc¡˜}äW/„ðÇÐ È™ÛPL$ô(#СþZ1C;yIÏ1ÓŸƒr¶c ÚRŽõ'”[£ôœ½„µGá˜Ï9FŒ'¿ŠáäW‘ãäתÒQŽÑ˜#ðN~]“cÄéäW‘pòëD­‰s ^¬(pŒð§¾Š”S_ðÔ×u9†I sŒƒ–‡ÏHf(åÃ2v l¶~³Jó–a–cµ,ãÏd³"·a™ÈQ…¹—@8–A?“Uø3YÅp&ëä%–Ép&k‡‘=.])Ë@úÊ­QzÎ^ÂÚ£°LÀ2ã¨b8Ud8µ£–ˆm°+Åd´×e™Óñ§"áøÓ‰ZSg´X‘`ô©H9úT>]™exj1ËÈOYæö§›Ýøáö—›×ïþãp÷x¸kÿiÿ/È’ ìðÃ&L÷›¼ù~!™}dX-ÉHO2’<É„/éìïhG2d¤'9ÌÔ%’™ü† á±9})É@úÊ­QzÎ^ÂÚ£L@2r$9ŒÌC2_WŠI2r$#O$#H&^kê$ƒ+$#=ÉÈ’‘¤I†G “ŒH¦/纾¬ûÒ†L8zyl!™}dX-É(O2g{§íá8&N,99FyŽQÇL]rà˜ÉÏ!9›ŒnS–Ù·)÷: ãÖR/¾Þƒ 6)G FyF<£žQ\·ðw•ÙÁþ®Ϩž‰×š:Ï ÅŠÏ(Ï3*…gižá¨e<ó=÷\ Q Q—Ì{b ¢ì!%ïÅÛ ÄT³çüârÔ¢x”\P“—%ƒ ªËøc‹ýeöMË=¢@zÊ­DH›D‰"Ê胃JdðAm³¡_ ikÖr9¹ D‚ j¢ÖÔ…‡bh)¢x”HqA Ò.(&Z<åÚÐO›i`î¨&\VÞä]S^˜f7LãýNâÙù¶þËýNsTãýNbð;M^r š ~§¢[˜eö-Ì=Õ@úÊ­¤šâvº˜jF·“ÜN"ƒÛi£íýh šu©æäu ^§‰ZS§º ¥Tã½N"Åë$H{˜j1Õ€·÷Ó&˜ ¦ —™7yט²Ù ÙO6†<Ù¬¶Éß “ñdc²™ºä@6“ŸÃð!¶¡YfßÐÜ“ ¤?¡ÜA²´I!›(Ù˜‘lÌ@6†íf³²1'²1 d¯5u²A‹ ²1žlL ÙÒdÃ#P‹É¸ÙŸ6×ׄoÄ&ïšóÂ5»áë¹Æ’çšÕ¶ü[t®±žkìÀ5S—¸fòs<Ķ7ËìÛ›{®ô'”[#È56)\å;r¸Æ²Ýúo÷Á5öÄ56kⵦÎ5h±"Á5ÖsMáKškxj1×·þÓæ ãšpR½É»¤c;® íW|ï™kjÏ5»øNÛÃQMN5µ§šz š©KT3ùyzÈ„GÆÍcp ¤?!ÝžÕS ¤EÊafÑÔ#ÑÔÑÔ—M×Çld&¸/Æd4ÙxûÌ„+Ñ»B$ª©OTS'PM¼ÞÔ©†‡~)ÕÔžjꪩIS @©Fö°ò@•Nj„/ú&ïK¾ÐÉ>r¦–NO'Wú®÷þ7ètÒx:i:™ºä@'“Ÿ XvÅtMnŽÒ“öÖ"…N:iF:i:i2ÑIl>·+Ƥ4ëxûÌ„«É»B$:iNtÒ$ÐI¼ÞÔ鄇#~)4žNš:iHÓ @-£ðæª8ÓÀp&Ì š¼YAÁ™}$Y/„<:œ‘Ûø¾·ý·M 4íz é~ö@3yI4ÓŸÃ7g…4 …ts”žµ—°)@ó9ÐÈã4]òûÿeš:45&ÐH4ýøÊ@ÓUÄ<ÎÍD½‰ ^´(M_»'ñ; 4ýsH4Pßq Ôr ïû' 5Ýw7jÄ1lÿ¶¬`MÁšÖ5ñø¾÷ü·M5Âc°fê’ÖL~Kßtä«¡/ÆÀHBº9JÏÚKX‹¬ °FŒX#¬™°&²êª/ÆÄ4ùÚX#NX#°&^oêXÃÿk„Ç‘‚5‚4ÖðÔr¬nú§ 55‘AícÞmñjö‘hµP#=Ô@äûÞðß66ÔH5r€š©KP3ù9"_ }1Ô@zÒÍQzÖ^ÂZ¤@M5r„9@Ì5è‹1¡MJ¾6ÔÈÔȨ‰×›:ÔðPÈ/…é¡F¦@$ 5<µj€;þ©BBMdHû˜7õ/P³D«…å¡òxï´=ÒÄá%'Ò(4j@š©KH3ù9$u;Úè®æ®i@YÎÍ%§¬vލUÊ9fQ´Q#Ú¨mß]ÿÍO¾ò®ÿ®"Ú¨´‰×›:Úð°É/EåÑF¥ "6<µm(ŸLÖ}ƒh%2V}¼`°ºÐÊN³óžV´§ˆ—|Ï»ÿeœ@rÒŠö´¢Z™ºä@+“Ÿ 6‰ÕcÐ ¤?!ÝAZ´J¡•(­è‘Vô@+šï)MU¾öDŒ>ÑŠN •x½©Ó ±üRZÑžVt ­hÒ´Â#PË'b@§ÐÆ Ä›ØÌC0oöŒ7•Lj |ç§Tè€SyÀ©À™ºä8“ŸÃ"ºë¹+ÆHBº9‚€i•8QÀ©FÀ©À©Ÿ €f-_pªàT €¯7uÀáá˜_ 8•œ*p*Ò€Ã#PË|*mÈ©€ûf»à‹­@Ξ!ÇxȸÊw~6€A‡ã!Ç 3uÉr&?rDltWŒ9…ts!Ò*r¢cFÈ1äÆg  Ì׆s‚“9ñzS‡ºù¥c<ä˜È1¤!‡G –CðŒÚˆc€ˆ¾Û²‚8qbˆc=â@´å;?)À¢#ŽõˆcÄ™ºä€8“Ÿ)"¶#º+Æ@HBº9‚ˆi•‚8Qı#âØq,ãÐ\æk#Ž=!ŽM@œx½©#óüRıql âXÒˆÃ#PËxbmı@ı‘ï5»Ä ‡ÙVäÝ3âÔq 󶇜pj8õ8S—gòs@¶ldìèwWŒ8þ„tstzV7)G¡`S`S`S_6]ÓáKÅc‚ šÎ\uøýÜ"M}›:lâõ¦6<äóKÁ¦ö`S§€MMlx 6Ú U>©|~=µe…O ŸÄø¤ñ|y©ïú¤€OÏ'ÍÀ'S—ødòs „MèŠ1øš$"Ü¥'í%¬E Ÿ|ÒŒ|Ò |Òdâ“p¢Âcò šÐ¼E‘ÐPÛ"ñIsâ“&Oâõ¦Î'<ôóKù¤ñ|Ò¤ðICšOxjŸ€O  4 h"Æíc^Ývš}¤Y/„:: Q‹ù¾Ïh›iÚ+ôHÓýì‘fò’i¦?‡¥n‘6tÅHêQH7GéY{ k‘‚4Ÿ#:HÓþÉ!Mÿí•iª8ÒT˜H£Ð”æ+#MW4ê84õ&Ž4xÑ¢€4}ížÄï,ÒôÏ!Ñ@}Ç%PË‘|&Q¬é¾»!X#"Æm±J Ö0KµZ¬k ó}ŸÐ66Ö5bÀš©KX3ù9";Íúb ¬ô(¤›£ô¬½„µHÁškĈ5bÀ‘ k"[’úbL¬ASš¯5â„5"kâõ¦Ž5<ôK±Fx¬)X#Hc @-Çà)T¡F¡&6¨wÀ³@Í>­j¤‡ˆÅ|ßûÿÛ¦À†é¡FP3uÉj&?rC؆®j = éæ(=k/a-R &€9B Ff‚šp?©+Æ„4¥ùÚP#OP# &^oêPÃC@¿j¤‡™5’4ÔðÔr¨îû§ 55‘!íÌÃjö‘hµP£<Ô@ﶇCš8¼äDå‘F H3uÉi&?‡¤n¢ŽîlîŠ1”…áÜ\òåW;Ò Ö*åH³Ú¨mÔ€6ŠïÎ…¦4_yçW‘mTÚÄëMmxè—¢òh£RÐF‘FZˆ6”Ï'뾌A´«Î<ŽYheÙyO+ÚÓ Äa¾çýÿ*N 9iE{ZÑ­L]r •ÉÏ@ÛÐcÐ ¤?!ÝAZµJ¡•­è‘Vô@+šï9 Mi¾öDŒ>ÑŠN •x½©Ó ýRZÑžVt ­hÒ´Â#PË'b@çÐÆ Ä›ˆ Lä…€‚7»Á›Êã Ä`¾óÓ*tÀ©<àTàL]rœÉÏa ÝõÜc¤G!ÝAÀµJœàT#àTàTŒO@Sš¯ 8Õ pªÀ‰×›:àðÐ/œÊN•8iÀá¨å€>€6äT@ȉ¸³DÞL¡@În ÇxÈÌw~6€A‡ã!Ç 3uÉr&?rDltWŒ9…ts!Ô*rbcFÈ1äÆg )Í׆s‚“9ñzS‡ú¥c<ä˜È1¤!‡G –CðŒÚˆc€ˆ~O·eq âÄÇzÄÌw~R€EGëLj3uÉq&?RDlGtWŒ8…tsÔ*qbˆcGıâXÆ' )Í×F{B›€8ñzSGú¥ˆc=âØı¤‡G –#ðÄÚˆcˆ~O·e;Aõe8GÕ–̹sj9—ùNÛÃAN9µ‡œz€œ©K3ù9 å‘R‘c”ûb Èô'¤›£Ó³zÀ´H9-€›z„›z€›:Ü´}ÌF`²/Æ„4­¹°Mdλ-D‚›ú7uÜÄëMnxHè—ÂMíá¦N›š4ÜðnÚ$×á UJ©”&mY¡”Ë.¼Ó¬¼§”ÆS äÕ¾ë3tJi<¥4¥L]r ”ÉÏ 9¹/Æ hªˆps”ž´—°)”PJ3RJ3PJ“‰R"S0}1&¥ ÉÍ[ ‰L[·…H”Òœ(¥I ”x½©S ýRJi<¥4)”Ò¦ZJ)ãiTq¥âJ8YÜ–\¹ìÂ;Æ}t¸¢!¶ò¶G+mC ãJ{…WºŸ=®L^ÒãÊôç ´ì<>f<îpÔŸnŽÞÊ1X«”•clÑÇ[Ú?9lé¿Ã˜N®h4ùÊ“+]E<¶èã<¶LÔ›8¶àE‹¶ôµ{¿³ØÒ?‡Dõ—@-ÆÊ Áº¯c³È°IÛ²Â,—]x§9zÏ,Â3 DE¾ç)–¶!°™Exf³L]r`–ÉÏXJ>fJî™ÒŸnŽ ³€Z¥0KŒYÄÈ,b`ÁwªE£ÙÉWžjé*20‹H`–x½©3 —üRfžYD ³ÒÌÂ#POµÐ†„—HŠ'/HòHÁKÖô•t¢¾¸H.Ýø&íñ< ýó‹ÑåéÓùú¶í¡Ò¦ªŽaîÖ—b@ ‘[ã&r9€‰¼LÚ¦ìÂïK1±Í/^EÎ-©²\2@‰Ü¾>‹Uonþ›e¶R@¶Šù¼V‰Ca+~l¥=[A´â{iúd‰Êæd§¥œdiŽTÂ%b})Ym~k…¬²‘•ÉJd¥óUøÍÙ—b’šÆ|]²Ò'²Ò d¯õ"²ŠÆLãÅìÕb6GVÚ“•N!+ &«ˆ‘«/Å$+>A[DV»Á) Ä©˜i,ï{ºàK|ˆáTåq â=/ëëÖÂ)~kœªÐq Ò„+ØúR œÚüÖ NeéjÄ©jÀ©*NEÇÏ+Ôå}h†õuqª:áT•€SñZ/©h̲Ÿñ NñÙNU§ªœªà8f })&Nñ Ú"œúÍ* Wżhy_ØEº®2ž« jö²p=²‚Äes²2èdj%Ž‘• ®ƒ­Ü\¡«lteFº2]™LæÒØB’®“¯Ðdð+›K»Š „e+^o0auªãq«ñâöjq›£,ã)ˤP–QV!W[<ëÖw¼#-H‚ö[[¡¡-+°U`+[ÖÃÄ.¿—ö Z¨lŽZµ@ÍѾM#KC\1j¸¹‚ZÙPËŽ¨eÔ²™P+¶È¤+ÆD-4›ýÚ¨eO¨eP+^ïe¨[·WûˆÛjYZ6µìÔŠ%Ä]1&jq Ü2ÔÚ _Y _…Û/Û²ÂW…¯b|U{¾ª©óÕU.„Des¾ªÑù Ô«DÏ$=f_,Òó›+|•¯ê‘¯ê¯êL|YoðFÖßW‡Mºw–¯ê_Õ |¯÷2¾ŠÇ­Æ‹Û«}ÄmޝjÏWu _Õ ø*–wŘ|Å)pËøj«k h…yp[¶Ð*RÙÌ ÕxЂ¤ eÕàz¨Jã¶F­µ QöWŒZn® V6ÔjFÔjÔj2¹m#çÜ÷بÕìÅmÛœP«I@­x½Á¨Õ¨ŠÇ­BE­=ÄmµZM j50Ôê"9úÑc¢§ÀÁPKïxÕ`„­ØÂÿUQ`‹l™£ƒ­ö'qغÆUƒ ¨lZíÍ"£¨9zl‰L‚÷ŨEáæ jåB­6šµÚ?9Ôê¿*s V¸ˆÉ#¢VW6™ß9Ôê*âQ«ýã,jMÔ{jÅãVáÅíÕ>â6ƒZ}ŸÄó,jõÏ%µ"ì¾µXnjí…¯ºïu_ÅVûg^ê_øŠ%OÄøJx¾ÔùêW ‚¢²9_ t¾‚4GÏ*ážuWŒÁWn®ðU6¾#_‰¯D&¾Š¬LVW6éÞY¾'¾ |¯÷2¾ŠÇ Ñ`öjq›ã+áùJ¤ð•XÀW‘ŠöŘ|Å)pËøjw«»/xhERÒÌkþ·-õe8sß–غ¶¤‡-I¶®r2 •ÍaK¢Ã¤9¤l”Çf\1l¸¹[Ù`Kް%Ø’`« p[ï‹1aK¢í±±Ómö³Ø’'Ø’ °¯7¶ÚÕñ¸Õ˜‚¬]Äm¶¤‡-™[[]„£+Æ„-NƒÁV›rïm:K)+\†Ò–ʺìÂ{¡Še)OY½Li­EY°²SVœ§rRhè¯#–È†Š¾ƒ²Ü\¡¬l”¥FÊRe©L”‘î‹1)K1JúÎR–:Q–J ¬x½—QVo_Œ€Zn® V.Ôj£éQ«ý“C­þËòÒSÚ;Ãpätþ¾µºê°ñ žC­®"µÚ?΢ÖD½—‰‡ãq«0Qkq›A­¾ŽOâyµúç*Ž£Ï~üŽWà`¨¥÷»j°ûЇÀVI¹ª¼KQ lí¶„‡-A¶®qÕ (*›£–@G-HsôØ™ï‹1P‹ÀÍÔʆZbD-1 –È„Zá"&WŒ‰Z‚QæwµÄ µDjÅë½ µâq«ðâöjq›C-áQK¤ –X€Z_t_Œ‰Zœ· µvÃWÈW‘Õ(UÞñÑÂW,y"ÆWÒó•¤ÎW׸j•ÍùJ¢ó¤9zV ÷¬»b ¾"ps…¯²ñ•ùJ|%3ñU|dÝ`š°ºê°I÷Îò•<ñ•Là«x½—ñUQ–N ¬x½—QVûb ê"ps…º²Q—©KÔ¥3P—lô12ÚÞcR—FÛUbcÛ›löÍMuéuéêŠ×N]íƒÙDãÖcR×â6G]ÚS—N¡. ¤®6Bu˜¯¸bLêâ8uµ)÷n'¸4·"'±\2Ý]pkOxíÊãVE·®r’ •Íq«BÇ-HsôèÙtÑcà›+¸• ·ª·ª·ªL¸sï‹1q«b”ýÅ­ê„[UnÅë½ ·¢qk‹1qkq›Ã­ÊãV•‚[ÕÜŠL õظÅ)pKqkëi®OJ]É“‚áEÔç.ßÞÞ¼ù×oo¿ˆ$67b(þ©­öã§ï›OÛ«ŸÅ’£{3üŽ7·?Þ||÷Øßþó§ÿÍ ÷Á_Á~±°X¿YJ´{nÐ~ó­5æs—“¹_1Fï\²…P#¼^ƒs¿*á·*ðo(¿5¥p~+¼]«zW¹×¿UÎ÷×sOíâQÍOo[Ï~eÙáòé¿q:‰_ìá§›»»O¾ýÐVòɧáˆÓ7:<¼ýŸ·ÀTëõý_n?ܾy>[º5ÿ¯mâGªæ3½HnËÛ»ŸŸ“jËÕjÞ¦ýWZó–u¶©ùð]Ž9qhÖ`¨ñöáñðWyG©A7ðøã†ÿiˋ߼¿Ûðêm–òöîõ‡ ïà¯Þ?üéaËxغ¾y»eøææ—R÷mžü÷ÏÛwñ–_»nîÞÞÞ=nxíÿÞ~زô£^ÿÍïþ¼åõKýY×?ýB¯ïß¿OÖQsÕIb¬@ôðÙPÉdÞ úe󣹫^N·ÂŠôžõŸïÞ>¾½y×’ÕáþîÝ ­Z€±eé+÷üt«¬„œir•>K¼ÊêA·v²î ;$[lÔJ±ùÝ5F%\•·Y+,z’‰-‚–Àùp–¿éÈEˆ‡èá­ÁÉ¿9ºÜm\ì6¿Ô-ÓB7ü§òÉò6´øà×bvY›[Ô–°¤ ¸ mÍ}Ç#@é©Ù?ÝÞß><ô‹×žÆkÿãþㇻ›w‡×nûÚ|õv Šþ¯²(ìZè$µ7~÷öýOïn‡yꎯïßÿðö®ïzí¯;ütûááíÃãíÝãWon>ŒC§o?Þ¾ùêîþ®Ëíÿò7îÛ×Ý¿½y×öIIáã+èÉð×l/a·=Ç7žlR÷í½î‘½Þ*×åŠì+møÅ%ï-Qê¥/!íP°ý3l¯=¶×Ûë˱½ ±½BÄöš;¶×¶×³Ø¯+mlG‹l¯{l¯ç±½&Œí<”žŠÃ_Äñy¿ÕÌÓ3ì”aGŽÛ"ža“Îwœ½hú´òÎÞOkpHܯ32aâi‡’¸–¸7>qo\âÞ°KÜî‰{3$îÍlâ¯+íÄ->÷¦OÜ›ùĽ!œ¸ó q?rÉÜPæÀAýHó™÷cïÃ;º³ðŽetœd’ ÌŽ³lPC`õo‘DÃoáœ;"mq(Ú~â±;ú3ìŽì˜¢«o¨èjà¬;ΟX¯.m®À‹°è+wŠÝÙÓ鎄قI.š8|?.Ž¢z¢÷ØN×2E°—¤ð…Ž^ÄU&É,øå:CáHC~¹^~¿Ï/‚¿öü"F~óü¯.q~A‹~Ž_D¿ÊüÂ#H—MŽ0˜ÐØ“××SfJ6a éXC–™š¤ Ì6¤±NS¼Ü²)f¾JX,JjÛÏçàÒçàòò<¢©»ÄR7›ƒKö9¸sp9ŸƒÇ«K<G‹…\º\&äà’rÎ#H—Î!ÐõJPòú/P—ÙƒɾÊeô —ßnÚƒENÏ 4kåô6Å.rz5äôÊçôŠ_N¯ØçôjÌéÕ|N¯.ñœ-Frzårz•Ó+Ê9= ]<®N7©W ¤>´Ð^  -#êžÆ.ÿÖ.ÿÖeDfö ̾³oZMQÖïÐå =p†öœ¡ùq†fÏzä =Ïñêç ´Qà í8C'p†¦Ì<‚tùÜÁü Ž:¨yM8H'Ðû5PŒ3‚ˆva?íÁ‚c„f-ޡՅcèrÌ`B^…"2¸PÖæö21ÚPļe¢ºÄ9†‡oc!Ç8#ŠHP¢ÊN&AÊ0_Bd`Fƒ&¨zCd\A™99ËÆ1‡)3'4‰˜ÝÀ`„7 ±ñ®g„4Ñw50†ß†dÃÌfâÕƒ€ OkW×>‹¼£4ƒÆ¡€I@CЂDiJãÍíÏTó3š¡'e*c/ÙÚ aVØ«Ì^Y€Åu†&†(`A,ìÖƒ…å–uÊÚå¥v ;ñê’ ÞQš ëÀÂ&€…¥ hA"5Ç@˜,,Œ,"}¿Ø’ÙgV/„Ó%‹âK&ŠE˜ì€ª1yQö}‹Œo³ðâfÁÏÜ,Ø«›Åènóòæ‰ê’GÞQšAgp gAÙáŒ$bs ôWÁ¬Î"|˜Eñ:—Y†8`8±3辟ö`ךbPu;ÄHFŒÁ0-¼bZðsL ö’i1Z¦Å¼fz¢ºäƒw”fù¦E‚lZP¶Mã‰Ú,}Æ€ù§E( Å@Í>Çz!Z5M€f×»§‰5EYv´ä ˆ–^-3¢WÞØ,Ù ¢å(ˆ–ó‚è‰ê‚`]l{Å祙ܩ¦U‚jZQVMã‰Ú,—µK0ñ´ ÅÓ²ˆ§Ë|B<Áwâiuvc)þu†&’âSOsHñã³òÆgÅÏø¬ØŸÕh|VóÆç‰ê’OñyGi&ÅwÎg•à|V”ÏxA"7ÊÎ%LJ eh€–ÅÍ>Çy¡œZ4Ñdœz­]Ë´š¢,𡊃hZyÑ´Ê š^{c4{Ñ´EÓj^4=Q]0z¬¥WÌ£4ƒN5­TÓ ¨šÖát}ÁôYøà¦ó ÔÏeU0Õ´ ß⪨¦é¤j´PÆ©¦ÕuúŒYÀ ƒÐ¬3´š¢À U˜ŒÖÊ­U£õÚ0ÃÞh­F£µš7ZOT—<ÌðŽÒ Ì8§µJpZ+ Ózm˜á¦,3)äiæ´Va «â´æ–hGÀÃ9­UqZÅŽâ´±ƒ®ÓšÃ¢¦A&­¼LZñ“I+ö2i5ʤռLz¢ºä“qÞQšIÆNZ%è¤P'½v2Î#LÎ,ty8§•M0½tæåŠeŽa?cê‘Tß饇ñµ&û B³V²OW/Í!Ù¼ÎÊ{?¯³bïuV£×YÍ{'ªK>Ù祙dß™U‚ÙYÍÎk'û<ÂtéÈ;élò…R}ñÕ‚äeòß,ùeócå«^®}¡Ï])Té=ð}ë™ÃîßîïnožÿðîþõŸ÷‡Ç¿?ütûaè›hÙˆ.‡Lç™Hf¿V0£» ÑÊë‚*­BõÚIZtqtíËežÇÁ§mµÿÔ¶Úó6y5^&Øuîûû/·å= è—/A Aþ$D!dt$@Òí¥':ƒôDDNÍ—›3ƒ/¿×¼…-JêQ{¢çµ'Õ¥íZċעvÒ =Ñ”¥'L‚™ô{óû?·˜óúñðãÛG  €†INTø:WÅÞ^ Nr¢˜4ð_¶`P W‡ºÓúÝé$lxO”:øKPCøeˆöÊA""GêˆKÎÔ™4ÓA¸‚&÷y_Fiˆž—†LT—8ð°Q,§ Ñ ÊMYÂ$H`øÍAº¹@¢ù?L¢Â·¹¢.T/ùÿVù¿³Žè"·X’ÿk—ÿC¯ ?«ÜcŸ]þ¿á=%'Oym¬“i ”{b΃³D{g‰Îà,Y€·£K"Gk‰ž·–LT—8ðÐa,dç,Ñ ÎMÙYÂ$HË'ú…€$ÖüEPæ'QáÛLßyA8 8?‰f Á 8P9€4^ƒV¹GA;Øðž¨¡¤) „(0¸M´w›è n“õ§Д «MŒv=o7™¨.qà¡ÍXˆÎm¢Ü&š²Û„IOÐ!²û‡ÁŽô~ÛýóPvÿ\sç™HY¿Ö0[‘²!ñÛBü…ø£ÄïŽïÓ N‰ÃoX¾¦þø×‘RýÇ_Ÿ ¨ÝH¤Qµ gCÚ2„±€MïŠN°»±HS”…Ÿ j  Îq  º-C @;Å,âkÅ ÔóGNT—øHZŒ(Œ¸u€x àº#<‚ xà°3xà_ø>Wy_çö‘ wpàüÓN•CŸÌƒûÝ„ó#mlzW”:ÿKPS8ø†µ?@Pç8@P„Û´eˆp€vêY¸@?÷@ã‚zþÁ‰ê‡´Q€w€ N8@P\x Äw  _ç*ïÛ¼°ÁnØ :ölÐþ Ï|&ÚÖìØÔ¨Z‡#¦mY~6Øö®ˆ-#5FYFðAÛ~ŽÚ?ô|Пp›<èjÁ{ò «ãƒöOs|0Q]Ú|€#|ÐWî»s|Ð?qDƒô“ -Ÿ< ³Ž0Ą :lR½J›L`–w˜ &ú˜Àg ¡mÍ ªu8vÚ–!`¦wE  Q0!Ä1`‚ð˜ N#tµà=ÐÕÀc‚˜Ç„xu‰cZŒ(`‚p˜ 0APÆAZ<@‡Ên#FÒûÙç»~|ûááñðSç¼z¼?<ܾ¾¿{ƒ–i”=FtºÌD¢úu—WAÆBXÒ«hÛö:.ËÜÉ&‹•;^°‚Á¶ÓÖ€ð¬Tˆ?~åZôìè€;’°ûÑæLßVñßÕæLÝǘþ2"XEfûÚB„ÑH7B¸/:©]€4FY¢øùØÂp¨aå5¬2j¨MDÌÚ"Ž. Æ¦Â§GáœfRVóNT˜øø3ó¦b43¾à6¬6¬(lÈ$H€ñ…¯êžò( )D v”alIQÞWyf5„wa9ì-\ýº‹­,¹ £:îðÃîGÕyè˜ü˜&t@ºÑî¡Ò:>‡ŽáøÄÊŸXe8>q è@;÷mEèP¬æPœ¨0qèàq:ßBèpG(V G(V”Pd¤Ý@ì4µØ¥¼ë“ tì#3zQ†úL­•Žç Ãœ‡ã Ãxè0ç¡còcPrù>h  Òî‹Ò£õÔ:>‡3@‡ñÐa²@GxÐhWˆ :§Gå~¦=t˜:ÌvLÖч¥N|¦<ìyú°Ž>¬§{ž>&?d)62ÎÛ"Ф!ÜWê¥eÙ4Uaìä‚4Ò]1§;PˆõbyN}ØL}Ø‘Bì<…Ä+LœBТDB¬£›@!–2…ðÒ !³‘+‚ † áS}Á&Ó‚ ;òïf'“© Z²êê,‚Ôçĉfº=‚ÔçdòcšéFW )CAYSyeM•AY³‚ ¹6VDQ[SÍkk&*LAxQ"ˆÓÖT ÚšŠ²¶†Iv† 0iŽ|¿æ}±ÙCÒÝ%ÌNYSA^à;²\ÒœG§³é~ôÒœGÉA©~Ìp›}áI‡ n„p_ôÒ Ab2ˆq*/Æ©2ˆq¶X‹…fôXq-Ö(Ç©æå8&Ž <¼+ ÄÉqª9NEYŽÃ$H¬Å"Í"0IŽŽ|Ñæ}ÃÙ ‹§È1UÈN[#‹´MyŽEŒÓçt?:ižc‘é¹J#Ú]a~u#„û¢Ó™Z5ö[p†0?| ãßþËÅ|qæ4K dñ “Á¤\·…x dÐÔ%*|«ÜƒD„Ìh2ó ‰ ƒAh©¦'(„'ü¡¼Y2Îd<@†²/H[£ÐÛ»×ïon>|ßþQ2@PøU«óf«…˜Õt…œÈ@t(eeØYçQÈ)‚º= ‰ó(4ù1M‚t£Ý£¤1 ‘D¡Árd¼åÈd°mBhz–Qh4™yÓÑD…É£Zœ( s™ב¡ì: RA¡úPÂdUçÍV í#_{a¤C!I}VˆÍ µ¶)Ï¢t($= Éó(4ù19ÂMs]! AºÂ}Qz´^‚£ I’ IB2 …ïç®…$ÿ:]< ÉyŠW˜< ¡Å‰ I‡B2…$eB :­”#ÌDÄDUظÕ*­[˜ˆYâöÂ(ÇDGw§­‘‹‰Ôy&Rމ”g¢øF&šüð^?ÊÈpt[ˆÀD t*ÿ}¥^zµ]; É~W…r±‘ØHy6RØHÅú¿Êþ\>e#…•ÎÉH×Íýx/NÞ7ÎýeÜ_†²û‹Iã=>R¿ˆ@ ÌùU…ïó*ï˼@M jP³Îr[ç3° ÈôÇrÐg®s`C³‰o›¼ ‡š6±U¡˜_îj ÍQ æs¨LbÆ›ÄL“Ø6Pƒ&@ZjF›˜™·‰MT™8ÔðU-„g3 61CÙ&Æ$H;˜<¬ _«ò¾V &5öÈjV™©±NJfA6©½BÞ3×9¡Æ:-™õZ²É :¨™þ†‘“»ÒüPìJwFé{ jŽ5ŸA¬`Ö[Ál+X‹/‘e©])ÔX4•‘¬" S+œ•©v4ƒÙy3ØD•iC ^œ@u^0›à³”½`L‚…šqñ)Mº±0Xd¬%óN éæË0kiËÈŽ€ŽŒ¬(ß á8ט…x‘vÛÏ\çÀ&g³Þ66yAO8lcºLj~°+E HWB¹3:ª#œÍŒc» œAöe½ìËf}µÝ+²A¾/E$<ÝW™ø¬sOÈzÂ…_v^ø5Qeâ„ÃÃ$µpœîË&è¾,eÝ“ ç˪gœ¢l|E†\.q¡Å6„gnd™¹¹Æ‰Ã,Èø´×öxæ:6×8u˜õê°É z®É¡“:ºþFg_Óq ¬+!Ü­u<Û@š鮘óÍ`ð²Þàes¼¶X–fÑ´Ck.K³£ÅËÎ[¼&ªLœoxè¡òsxÙ‡—¥ìðb$ð Î÷TN ‰ Ð×~ÁVy_íqbˆ£X Î*;n¬ó€YÐXážwÜØ)óV>Äq&0ëM`“ôˆ“ö*âÀÔªW82Uæˆ3ˆ¸¬qÙ"®MOŵ&âŒ2.;/㚨2qÄA‹Äq".› â²@׺ˆÃ#H»B k+\\å=š¹ N q4 ÄYgÇ9¼,H¾´Wä{æ:6â8‹—õ¯É zÄÉañj‘!¶á@gßpÐ!¬+!Ü­œ°CœMM^»@œA¢e½DËæhm²O£µæ>œQ¤eçEZU&Ž8hq¢€8N¢e$Z(ÑZqx Œ8§Cà ³L˜eÂ5«4iÙc«²gä'ã² iÐ^Ûã™ëؼãt\Öë¸&/èy'ƒŽK×ulûAWŠÀ;®„rgôxgS%×.xg°aYoòlXÛìÊÁ“÷¬¹+g4bÙy#ÖD•‰óZœþÿöîownËø}žâ‡¾x2éî’(ñ:3ÀÅÂØÍÀã]Á ¼í_c ¸íûçAÏ]Þ!Ï“›Õ”#ëSY9rRŽäÿTç åHråÈI9Ò)'´C§œàfLÁz³¾t¶­ÊÁJGÆç„…ƒêßÖ8¼‹XÒK:bÉ”dÄØŽ­„Ä’t”Ö76Û[–ˆ½F,9Kn î2>t8íÞ&²È*EÿrÄ6²ä„,,ÉYdE:÷¡Ò6<& s¥–ÄQkýñ•i¯Y+µ j©,¨uÌ!5QKÕ9BŽZŠœZj¢–rÔ íÐQ+¸™+µP§RùÔ G¥Kj)O-娥r¥–*‚Zj¦–Ú¦ÜeöÔ"«j©‰Z*‚ZŠ3µÈŠT©µI-…£ÖúšU¦½f­Ô‚¨¥³ Ö1OµôD-Lö|±ô|6ÔÔÒµ´£Vh‡ŽZÁÍ8Ьç1ØVjáN%‚#ãô{ŽŽJ-–ÔÒžZÚQK§¡ÖúͶRK±„ž©¥·©w™=µÈ*ÅZz¢–Ž –æL-²"1¡Ö2gб¹4Î\ëœeÚ°õ:g*ä.SçLÍî2“»0— Ŏdzéä v—™Üeœ»B;tî nÆÌL2z=z¶•À]¨i+GÆç„Ý…Žê.–î2Þ]ƹˤ˜°ui×—¶•Ð]†nÂðÞƒ¾á&ò5w™Ù]fÛ]p—Ù»‹¬RÜe&w™wÎî"+ÒéS¶²xÊepâZ£D¦M‚¯O¹méK}Êåµ¥/V[Ãu<¬¶†‘ ÖÖ°‡Q[㣶‚;œ´ÞŒ(X£.ÀËKckzmáN%Š#ãug~jH’UUW"u¿V]Ã_¬ºì—çÍO».Ð'`l¥Sרª§]ëÎ m$ê{1©køÛ–º]Æ« ªa­^ÐÕ ðñÑî²[êwÍ]öÓ‡¹Gn€›Â†È/3)Ôþg^¿yõÓÏß=<ýòÎìïã÷2†aë\™ö÷¶2 bX“Ùץ›‰aM×åÖ3¬™Ö8†…vèÜÌ•a¨Sé>†’Ê0¶ k<Ãǰ&W†5E0¬™Öl3 îr #«†5Ú†5Ü–G¡îa Žaëé2í\‡Ê0ˆam ;æiX;1¬åÿ4ì˜9_ÃHP3¬Ö:†…vèÜŒÃ0¿dl%`êT¢82~ à Ie[†µža­cX›†aÐܯKòÏæç ké¶îÌÐFðvfX»Í0¸Ë0Œ¬VÖN k#ÖrgX…JÁ°/æ‚qöX‹óØ:ý–4v^ã=L‹:l6™˜L†úÝ-u<žM'µÉÄd2áLÚ¡3Yp3fÖÕÈ×±­&ÜJ$GÆÏd§®hPM–Êd›L8“‰óÂ4”}e[ M&È.Õúºfh£1™˜M&¶Mw9“ÑÕŠÉÄd2a2ÁÝdy a²õì0¦OÇ‚¿+ýWß"Pñýã¯>½} ÿ›=ÿ±íŸ¾Cw×\6÷GTªøïÞ¼}|xúðêÝÇ7ãÇåáÙÞÿôðãØzþïáéýøgó[²Ë‘®ž6lN›ÀEõÇ‹/Ìý³¾ßbJ¹ßÂðùwÿ? _½Æö 2߯[_Tvio–xfÊ÷Ó˜²bÇãÙtrPß™òý´Ë÷ îÐÝI’ï§€Õ?l+Á\¾Á‘ñ9¡Æ»/§åûmü¦f‘^®}´žvÑz:I´žžØV›„ÑzÇ-Bªçh=½­è2ïôrº:1H/×S°žŽÖÓœƒõ2)ö4‡›upIzj=„ê1¬Ô¨ƒÌ×;éÒþ˜y˜S¾žÆ¤€uk·wiÝ~&uÈóõô”¯§]¾^p‡Ž:Iòõޤ._¯x꜖¯Wu|´vÑv:I´ÝÔ!Œ¶;:s´Þ޶ t™9uòÈLÛI)ØNGÛiÎÁv™©êà’ìÔúºU¥ÍS©Od¾]±O1FêLùv“ÂU,ýžM'5u¦|;íòí‚;tÔI’o§Úõ›î¶•€:¸|;‚#ãô{ŽŽJ_SÇGËi-§“DË)à- ÛJHÂh¹õ’ C uæh9½-è2sêä‘Y¶“:S°œŽ–Óœƒå2)–:óB¦æÁ%É©õ{Í*íô«óÌÓ3[3¯»›/¬VHüÕ÷zÊ—Ó¨|¹RÇãÙtrP»gÊ—Ó._.¸CçžùrºÑj}ض¸s*‘ŸjtÏiùrE¸ÇG»ií¦D»éÆ_a¶•Ð=dYT²[wfh£qÏí¦·£Ý]fîž<2Ãvºg vÓÁnšs°[&EŠ¿ToýÔ=¦âÁ%¹©õHuÈ Èú”g­ƒÌw+ù)™òÝ &ªØñx6ÄÚ1S¾›qùnÁNÚ oÆÜÖîÀ7xºäoð ÚAJ$GÆëM A<¨!!:ª¼Õc|´šqÑj&I´Ú /¶Âhµã^l3s´šÙŽV t™·zèêÄ@=fŠU3±j«v¨z2)úiÏ_¸,K²†Áe§©õëê*í»ê>ñðA&ªû:ן)QÍ`2ŸŠžÉcÈÕÌ”¨f\¢Zp‡>)Õ…*Qí>àsj¢ZðñafÆ…™™$afgÀ‡0Ìì@øÌaff;Ì,Ðeæð!«øLAf&"ÈÌ ƒÌŽ…OE* >¸´2µ~ L¥}s½Â'>È ³bŸpŒð™2Ì &e©X>›NjøLfÆe˜wèà“"Ãl€4‘¡K>‘a„.àÈx])Žð95ìøøø0ãâÃL’ø°æ÷Âø°ãæ÷˜9>ÌlLJºÌ>duâŸ):ÌDD‡dtرðÉ£Hhø,aŒ„Ë^cOü{åƒS65¬ÔY-£‚¦Ô0ƒú¹-u<žM'µ‚¦Ô0ãRÂ;t J¦ Nkɧ5Œ œJ$GÆOA îâg® Øe\`—IØuÊlC–-täl3v™íÀ®@—™+ˆ¬N4…u™ˆ°.ƒ ë:VAy)þ’}žíÃÚ?¸¼µ¾9£n¸7ÃÊ?ù=êê Ù>ÝdŸŽÿ ƒì{&¥}ºÉ>³Oh‡Î>Á͘[ݽY_PÛVû`N%’#ãsBîÁ õ/nMJÞ¯ÎësðêR<~‚¬b[ áÕÑ=~n‚ö©ŽtðêfxuÛð‚»ŒOJ^¯S%èâw_ÐUŠþEŠmzu½ºzuœéEV¤s@ÍÉc,2W€u8€­¯YoÉq¯» `};fîQ?¬¯sÀzr€õÀz°ÐÀ‚›¹ s*ÝÀ0ÃQÆ`½XïÖç °¾€õ3Àúm€Á]f0²JqX?¬XÏ`dEªÛXØúšU¥½f­‹˜Ì`Ç<“À$ÿ'`Í’ä“À¤Xh‡`ÁÍ8æ¬ÁÛV€aN%’#ãô{ŽŽ 0–“`ÒL¦ØúͶL±ò„œ&·w™=ÀÈ*Å`r˜Œ˜ä 0²"1Ø2‹±Ä$Jbxßû±­s±Su.Ö¬15iLÕ¹XNcŠ\cjÒ˜r íÐi,¸3éD‚¶­ÜJ$GÆç„5†Žª1–S^cÊiL¥˜¦Ìúß¶jL‘MëûÍCÆÔ¬1µ­1¸Ëì5FV)S“ÆT„Æg‘éô©`Y<S8‡­Ï|}éÏÊaù=Óõ‰Øl0=Ló"vÁ4¹Áôd0í Ú¡3Xp3âú¡Qàõ§±•À`˜S‰äÈxÝņ’äGU-–ÊbÚ[L;‹éOÆ.Ð'`l%´˜¦{2Ü ë‰åгÅô¶Åà.ã-U‹°V/èjµÎ&½!št§Æô¤1¡1ÓX Üâh op¼Ì¤PûŸýæÕO?÷ðôË; ³Æ vh΀{a7œ'g7áÌd³cæ‹™ g˜ë‰²ç‹rœ™ gÆá,´C‡³àf®8C]šÞÎ0CRqÆgÆãÌ8œ™\qfŠÀ™™qf¶qw9œ‘ÕŠÎÌ„33Ãgyêpfp8[¯>d€+ÎÖ8k.—,tvÈ£³q0Fž²‘c¦“CA ´q£ÐìŸ#ÑÂûœŒve;ŽBÀü•±5=Òp'É¡±SnL*Ó¸2Íþ‚X§³P›¾GH š`vIþýLj¶3TT[÷fh#¡šíÆdµñ¯[X õš¿ÖëÅ€kSï>«á5°MŸCÎbË¥X)ÈöÅÌ3Æv³_Õ¼uk¼u…à-ËégÍ¥©óÏÀ5pMæ×Юq€k<àBûô€ nG|]t<æ[)‡9¡H!àP² 8¶€kfÀ5pMŠ9i ô)[)×ÐMJ[_û mD€kÀ5€ƒ{àÈêÅp\¸†=àò(V¼*€)jL»cú¯¾EäûÇ_}zûþ7{þcÛ?ƒ‡î®¹lî¨Tñ'Þ?¼yûøðáýÛ·ïÿöøááÙÞÿôðãØv¹\ÔÃÓûñÏËo¾~øó?þóÿüoþÓý§ÿþðáñ¯¿yõóûß½~>£¯>=½ÿz8E?=~z|øýï’\!µ«§›Ó,pEþG{Ù†¹™³¾ß¡o¸ÝÁêfθ™sap3Gº»6ÈÃf­§¦5þ¿cºkƒI[+vDž¹óãÖ»6ýÆM›ÎÝ´éüM›ëQ†W¶ÇMi)4æ36SܶÁœQDÇç̲7mNK4Üø9Í"ÅÝ~o»{%¿W’ Op8Ç:`M˜©™òn Yšl:àvÉÐHt¿¤[î—lÇ †úÍ;аZ ݧÞ}V¿ë÷J8' æR(Ä}’ÎÝ&ùýÃÿý?ªî‰@ êõE¤N{Y2Fð¤ëýC¦…Žƒ1 wÖ¬“š"bž¹óƒZ@½Pït=KðÊöÓ‘挺)X†€úY@½P‚@¿ÓDBv´€úE@Û¹~¡~sP‘q{Ô;E$ûMŸC¦…z™K¡J.ÙO¯/"uÚ‹È* €9~Å>ñ°’N@˜¼±bMøÌÔ’N@Ò èz˜ß•íHdy¢¶™B@˜3Šèà8}ÖžãF¤ h% 9 Hz%HÔ³ÔY¿75S ˆ,lÀÎúxl$\´¬ê7wå‘Ù¶W@Ò ("Zoú2-ÔË\ µC@~n#W!õ€Ä¨´÷:+€BBFç• å„‰ø*vDž¹óƒ@ÊHy]ÏÏ»²u¡×À·º›ä·º-€0gÑÁÅîþÀÙ‹¨JšÃÊBj†òJfwÚ£ º<»ƒ©BÛ™v¡~s‡Pqi{!¤„"Rí¦Ï!ÓB½Ì¥P·< zø —i‚‡Áv뻟‰ï|V…<„Œ±+öñ‡õvÂDmþJÜíIv[ÒÎCÚ{èz–Ý•í§“#ì!Ìu?:5Ò® éÙCÚ{(A Üi"‹¿:ÚCzñÐv®\¨ßÜ=DV-ÒÎC¹rÓçi¡^æR¨r=„Ë’3ëQ5‡ kõЙWìÓë!ã<„¹t(VˆÏÜùAí!ã\†‘ê2ž.S³Ë”w™ÊØeª—©Åe*Âep¿ù»Œ¬^,\¦œËTŒËk—‘ªº ç2…sÙza)“vQ©ê²Ët.;èy™v.ÓüŸ—E; µË´s™ö. íÒ»,¸IŸõNÍ.ÜQDÇé³ö7"Õe<]¦g—iï2Èeë_Í©™Òeº”ù\zq™ŽpÜoþ.#« —iç2ã2ÍÚed…bã²å5FÎ,Ó(–5—õðmf׆=ÌL…Ù3ã`†¹Œ(vDž¹óƒfÆÁÌx˜…véaÜŽ¹öë…„îÉÍ0Ã]˜’\ìîœ×…•ä‡U– hfšñ@3)€ÖKøs “H¿š¡šX¿46Í,@3@ƒûpA'n¹ Û&YÅ€¥¬Ž'šqD31D38¢µ@k{Kë6Óò(Ö-LûÍ«Ÿ~þîáé—w¿zöÀyò™Aª ¸{qI{߸ª- ¶á+9µóšã0Vmßõ5ÇImÃP«m؃UÛø§U[p—Nmáí§Ã(¨6Ôu7jCJU[µ?"“ÚÆ+üß¹Os¶j»S„ÚÆŽ8µ ÝT[ ß¨®bÔf{÷Y ¯ªÍ~Y«-“bÝ›ÚÆol”ÚÖçÍÐVÕvmØ“©­ÉBmÇtÆéG¸êlŸÎĬ3áu&2Ö™(EgbÑ™ˆÐÜoþ:#« §3£3ÁZgd…ª:ÃéL u¶æKsËòNUg(uYèì ggÓYÇÿÙÙaÁÑ-,®”:ëœÎ:¯³Ð.½Î‚Û‘:påGÔ9u+?¢F¤êŒ§ÎºYg×Y—ïÊcwŠXùqìˆ×Y¡3¸ßüuFV/:ëœÎºu¬uFV(6:ËbåÇñË…3`>Ï%í4ƒŠ³0Îúгg½ÃYÏgÇ=:ëÉqÖ;œõg¡]zœ·#'}¹Ž机ŸuD0£Rg¤ñEZ?#­÷Hë3^G¤/e‘~AZ4¸ß9ÌH#«‹i½cZôžýŒ´<Šu Õ²\G¤GÊ X?ï’v½*·°Üdr;è¥Géä&ëK‹Ü$¹Ü¤“›ôr íÒË-¸ýt…å†9§îGn˜Q©rã+79ËMz¹ÉŒå&K‘›\ä&#ä÷;¹‘UŒ…ܤ“›Œ‘›d/·<Šuwr“H¹­¿É†¶*·ƒä¦²ÛAÏÜ”“›âÿÌí¸"¹Ü”“›òr íÒË-¸‰£#WÄœS÷³ $fTªÜøÊMÍrS^n*ãU U)«@ªEn*Bnp¿sYÅXÈM9¹©¹)örË£XiäÆsÈàïMÿÕ·z|ÿøã«OoŸÂÿfÏlû'ñÐÝ5—Íý•*þüç¡#o¾ÏÁ‡Ÿ?~´çâ³·¯>|ö¿ûðõÃÓãǧ{õáÍûO‡SöéßÇSöáñÝï_?~ø†ì"¦«'›“*p-þG{Á†ºq³¾Ú ¹q“”×Ñ;-9`C¸XDŠE,uDlÀ†¸=±i®ß[.Qø\Äà>ݽ•ðvÌ-1Õ®Ÿ·ÙV‚;+¨ŠäÐøœVã=ÔxRð7Odq+CÌ1„ÂÇŠ1„Ã)&×O:l+á A–’vÌ=BwC,„""‚0ÐkômŒ#' ¾ ¬}?¶oa?(bâ2~ðØÙ™*þzúõ«§W½…ÁáÞ`ŒlÖ#9´U›T›À6qᢆÿÍ6¹=üoÓ&.ýOøô¿à>½M¤ÿ—úë'J¶•Â&˜ŠäÐ8}ÐžãÆ£Úde“9lOø°=‘ loTÈúfžm¥´ YXÛ¯g mD6Y‚öDDÐ^ ×ÜmBV+6q!{"&dO Cö¶I…Úe“åQê³§WoÞ>|üùí›§ï|Ûû¿=~˜þßþíý§w¯_}øûo¹b·×—|MÚKþŠ™‚0ã"ñ&¬ë,Ì@§6 gnÄÛäŒËÄ>/¸OÏ™™xÃEšÍ H8ƒKY$84Nµç¸ñ¨œYqfŽ¢>ŠN$ˆ¢N1rF‘r†,/ëXÎ,1t""†.ÐkîœÉ#Ûl/g\ˆ ¡¬Cè2)ÔnÎLvùø+ÐøÖŒHƒÌ¨kÖ¯m•4•40i\Žœ@­i|i€S;õËôinÏ‘Û$ ’>H.¸OOšAr£Ö¥³­¤AM/ 84Nµç¸ñ¨¤Y‘fÎo>¿M$ÈoN1 L©[)IC2u,i–ì6‘Ýè5wÒä¶—4.¹MÄ$· ÖÉm™j7iÚ/Ióøó«ãøÃã/¯~xzû÷‡÷ïžÞ?¼þ©ýlAƒŒukÖ3‡¶ š 4.zM`B¡Î pj§ž=æöèµMиì5á³×‚ûô I½6ú`}¹c[)@ƒKó#84Nµç¸ñ¨ YfŽ<>òL$ˆ<é²þY±­” !Ëe:4KÜ™ˆˆ; ôš;hòÈÐÚ v&bÂÎë°³L µ4ƒƶgûÒ2 ­YÏ’Ú*h*h`и¸2 Q*vD&ÎÜV¶É—V&|ZYpŸž3 ÒÊDÛ3™»ä3™-gpùw‡ xÔª‰¨1!:¬ÜY3‡„ &„„4ËŸ,ÃèØYþK@˜ˆ ôš;kÈjÅ‚5.LÄ„ƒ d8ØÁ¬É£PûXó.ëBRAF5ë—̇¶*•*X*.žK`Bƒ Ÿë{8צT\:—ðé\Á}z©$Hç/ü¡‰]òyÍV*¨¼7ŠCãuUg¥rj2WR™C±„Å B±NšóO–Ùsì˜%KDbzÍ]*dµb!†%b°2 ë`©äQ¨Ý`PsþyÓ™]v&¾î¬´)ˆ6.¿J`RuŠŸù{~Õ&n\€•ðVÁ}zÜ$°¬Îrî’Ïr¶¸ÁE¢CÜœ^UnæÜ(ás£D‚ܨ“V ‹µ97Kf”ˆÈŒ ôš;nÈjÅ7./JÄäE d^ÔÁ¸É£P»qƒ^€7p2Íú{mh«À©À£ptÀ9lM퀣=pBûôÀ nG)œóÜ%Ÿólƒ9¥H!p0cRGÏÀÑ8:ÛõtÀÑ ptpà^sY­XG;àèàhÖÀÉ£P»ƒ\€7o4’7ëw‡¶Ê›Ê˜7Æñs)Püª†ž7ÆñÆxÞ„öéyÜŽ34ºK>ÚòwuIph yƒ“Ê7fæñ¼1Ù®`ÊàYxc"x÷š;oÈjÅ‚7ÆñÆÄðÆ°æM…ÚÍäê¼yc¼Y?cÚ áÍúö}Úõ©9]ÌÛî2ñ¦Ã”;"7ÃPPãfØ…ÅÍø§ÅMpŸ7áíñU“}gÖ ÛJ€Ô Erh|N«6¨ñ¨Kžý5ÝÅ£føÛ„ûÕu#jdß‹‡ÛVBÔtdaå=ð„»OþˆÛ¡fì†CMwÙFM ×ÌQCW+¨±½û¬~WQc?ƒL õ2—B!QÓZ«|dŠ“ñ+ƒ“v=”C[ÅIÅ Œ“Æá“P^ö²ÃPã¤q8i½i‚ÛqpX'PÙV Ó`N)’CãôQ{Žjš•iÄláM#Ò˜f=¡Ô¶Rš†,¯üXÓˆÅ4"Â4p¯¹›&lù½¦Î4"Æ4‚µiò(Ô~Ó gýs@Š˜ݦ½ÓYESh:'LDyéóý;X)IEÓ9Ñt^4¡}zÑ·ãذNª²­¢ÁœR$‡Æé£ö7U4+Ñt³h:/š.hÖsHm+¥hÈËM·ˆ¦‹ Ükî¢É#\~¯h:'š.F4kÑäQ¨ý¢ANôç*š)šõΡ­Š¦ŠMïDƒI(/vD&Ïôôžégzï™Ð>½g‚Û1×n-8¹M>ÙzsB‘Zô—Êa뗡Ƥ®_º¦Ÿ]Ó{×ôÙNõ'K,?vª¿¸¦p Ükî®É#]~¯kzçš>Æ5=k×äQ¨®á¼ÙøEŒ¢ÊúæÐV©R©SE:ª`"Ê Ÿð/é©"U¤§JhŸž*Áí8@“Úä“›-U0'É¡1¤ fL*U@ªÈ™*ÒSEf;ñŸ,²üØG0r¡ŠŒ  ÜkîTÉ#^~/U¤£ŠŒ¡ŠdM•< µÿ jâ?oÛH¤m€ëÎ6í…gµMA¶QÎ6˜„òâ§ÿ+zÝ(§åuÚ§×Mp;ŠàTç6ùTg«Ì)Erh uƒ“ªP7jÖòºQÙ.@–Y~¬nÔ¢¡¸×Üu“G¾ü^Ý(§£ÅZ7yj¿nÐËðŽB gý5=´UáTáÀÂÑN8˜ˆòâÐôÂÑN8Ú '´O/œàv# ‰Ïmò‰ÏV8˜SŠäÐ 3&U8 pô,í…£³]€,´üXáèE8:B8p¯¹ '€ù½ÂÑN8:F8šµpò(Ô~á àíôÍúKzh«¾©¾}cœo0×Å/ `è}cœoŒ÷MhŸÞ7Áí8D@Ó ÛäÓ ­op——‡ÆÐ7˜1©¾}cfßï“íd©åÇúÆ,¾1¾{ÍÝ7y$Ìïõq¾11¾1¬}“G¡öû¹DoߤoÖ_ÒC[!¾Y/‚pÀR¯Eû¦¿L¾é1QåÅŽˆÕÍ0Ôºvau3þiuܧÓMx;¢j¢¹@w?l3op§ÍÁñ9µFÝ F¤.~ökÙô/›áo“lì×ײÏ1<¶Í„¶éÉÂË‡Ï °\Ðå–õ‚®éfìˆÓMÙÖM ßÌuCW-º±½û¬~Wuc?‡L õ2—B!uÓMºaŠ”ñkƒ±Ê¡­"¥"FJら,/{‰€a(È‘Ò8¤4)¡}z¤·#ܰÍHAR4ÇéÃö7")+¤43R”&R ç»c3%RÈBÌ‡Ï °PÐå–•‚®"¥YÒD î7w¤ä9¿)CJƒ”†5Rò(Ô>¤ —ચ©àÚOÜpõWUS¶jZ§Lpyé‹ ƒAîšÖ¹¦õ® íÓ»&¸wý&`:× N*šƒãôq{Ž‘êš•kÚÙ5­wM›Æ5v&u Y˜ùÑ®i×´®ûÍÝ5yDÏïuMë\ÓÆ¸¦eíš< µß5èŸڦEÚz@žvrIµMA¶Î6¨_ΗèaÅ$µp¶Þ6¡}zÛ·ã®á:è‰îØLaÔIEspœ>nÏq#Rm³²˜m#¼mD"Û/bÙfJÛ…šm±ØFDØî7wÛäA¿×6ÂÙFÄØF°¶M…Úoä²\e#²î>Ýpë©Ê¦lÙtN6˜ óÒèa­$•MçdÓyÙ„öéeÜŽÄøTwh¦ ꤢ98N·ç¸©²Yɦ›eÓyÙt‰dHÀ6Sʆ,ÜühÙt‹lºÙÀýæ.›<¢è÷ʦs²ébdÓ±–M…Ú/ä‚\eÓ!eÝ{J{ý_eSlz'Lœy±#2¹¦§wMï\Ó{ׄöé]ÜŽš€§À)Ïc3…kP§ÍÁÅîþ¸ÅÎP£R;}ÓϾé½oúŒ— 9?zI€~ñMá¸ßÜ}“G$ý^ßôÎ7}Œoz־ɣP;}Ãyõ²ñËEè¦Ò ÷`*Yн@·d‘Ž,˜XóÂôd‘Ž,Ò“%´OO–àv¤  çYc3YP§ÍÁ1$ fT*Y@²È™,Ò“Ef¼@YØùÑdäBA¸ßÜÉ’G4ý^²HGCÉš,yjÿ#Ô¼#‘Æ®?EÚ Ðjœ‚Œ£œq0áæÅ/ è•£œr”WNhŸ^9Áí8H€Ó¡Çf å N*šƒc¨̨Tå€ÊQ³r”WŽÊx¹²Ð󣕣å¨åÀý殜<"ê÷*G9å¨å(ÖÊÉ£Pû•ƒ^.€·tR: ø¦VU:U:°t´“&ä¼øE4½t´“ŽöÒ íÓK'¸‰ èÕ±™B:¨“ŠæàJ3*U: tô,í¥£3^<€,üühéèE::B:p¿¹K'¨ú½ÒÑN::F:šµtò(Ô~é àít\—6µ®:§ ççÔõ@éKzççãÚ§wNp;’Ð[­c3…sp™$ÇÐ9˜Q©Îcfçï“ñRd!èG;Ç,Î1ÎûÍÝ9yDÖïuŽqÎ11Î1¬“G¡ö;¹”oç¤s Œˆ´ç9G|³þÖÚªun²Ž¼LÖ‘¨0øRGÄJg jé »°Òÿ´Ò îÓI'¼Qµ¦“pÇÚ6HwJÑŸSkTjDêbi¿޼xá ›„c¿¾nÎpŽià¡m&Ž$ B×fý=´Ñøfì†ó¼lû&Ðk澡«ߨÞ}V¿«¾±ŸB¦…z™K¡¾®s'á0…Êø5Œ ø8í¡B¥œËr •ÆA_ôRÃPC¥qPi´A¥Y ÒD@î5w¨ä\¿*ƒJ•†5Tò(Ô^¨ÌKpKƒ pM—8šХ0±´N,¨¨÷RGdKK/–Ö‰¥õb íÓ‹%¸ue&à[È"ù-d+Ô)Espü^"CJ}‰ ”K;Ë¥õri3~ÄBw~ì#–v‘K!¸×Üå’G4ý^¹´N.mŒ\ZÖrÉ£P»åÂù°ñËÅ`t—6¤¡²¥œ‹tËáØ‚z§ºì-0P’²E8¶Ï–Ð>=[‚Û‘2o(‹ä7”-[P§ÍÁ1d fT*[@¶ˆ™-³EdüÀ…,ÉüØ.ba‹ˆ` ÜkîlÉ#u~/[„c‹ˆa‹`Í–< uóÞ~H¿Ó›»´k™žç—¤W¯Ì¯Ô²Kçì‚Êi?gD¾^—ÿë›õòù'ô‡Ç1À™é+ôÈÊPÔ—CÔ~uÍ}°å’¸¯sîÃŒ‡hÀe[)\ÂäÐr·I7Û¤ó6én·É0´Ml+¥LÈ’ÈÛ~-“¡H&Ý"“.B&p¯¹Ë$Ôø½2éœLº™t¬e’G¡â¯yózÀˆhÿÿþ×ÿv.ùîÁ‚¥¹ø úv屇ß<ñž„?~Y£ÈÌbM<…µ’¥ ²ôŽ,¨öbF„?XPu9,==X0ã1^ÿ¯OUÛJ&‡–;Xú,½KŸ,fýñ°­”`!‹?,ý–>,p¯¹ƒ%ø½`éXú°ô¬Á’G¡ö€%[¥ôH¥SXÏ_­JÉôšRŠtJA¥®óR¥ êr¶R$½R0ã1^ô¯¯ål+…R˜ZîJ‘³R¤WŠL£”õˆm¥T Yšø±J‘‹Rd„Rà^sWJÉï{•"RdŒR$k¥äQ¨]U~—ûC‰ä p)Ù¥½–<+ë9:igèpº8?ˆ+ÊqŸ^ß;,¨Êœ EÌxȾ¿¬_Œµ­`arh¹ƒEÍ`Q,êv° C \âÛVJ°ƒ÷@¢UŸ<ÒʃE-`Q`{Í,y„¸ï‹r`Q1`Q¬Á’G¡p`iÝô”^SH³¬¿–‡¶j–jØ,Ú™„^̈ð ª.g‹EÓ‹3#ÖïÅÚV ±09´ÜÅ¢g±h/F,ëµÀl+¥XȾ‹^Ä¢#Ä÷š»Xòcß+íÄ¢cÄ¢Y‹%BíK¶LÑH¦ë2vi×­LÉô¢bŠqLA-¤Sß;Œ)¨ºœÍCÏä¥éeýb¬m¥` “CË)sð½ôÁ÷2Aðý’õ{ÛJɲ|îc™²„ÞˈÐû@¯¹3%,õ½Lq¡÷2&ô^²½Ï¤Pû¬dÿ*2ä¾dìJ ¹_߈< üºh¯(p¯2¸¿ÏWÁp•9Y,ÃÁR‹7¢¹àÑ6˜…ÏÁe®5‡Ù+f¯R„Ùck€E3m3¡[Yîvsi¹ªC#\Ôg¯"âìýf.ºjq‹rqö*&Î^±Ž³Ï¤P8¹tż¦¹÷=põÒ—’{_í’Ü..ó^ey¯„áêr¶\z¹ Æc¸$í€1±Írasp¹ËeN·W>Ý^¥H·‰¬i›)åBÄ}´\–|{‘oè7w¹ä›¾W..ß^ÅäÛ+Öùö™j—\²å 2ô¾^ éK ½¯\IÎx¯2¼¿ÇWÃpu9›+-=WPã1^úgm¦à ›ƒË+s¤½ò‘ö*E¤ýèàòÞ6Sr…,}ûh®,¡ö*"Ô>Ðoî\É#+}/W\¨½Š µW¬Cí3)Ô¾-¹¿"¦©÷=zß×Ôûw\¶]\ê½Ê õþ.µà®dN¶ ¬”¤vAGÓI 7~j¦° ›ƒËÝ.s®½ò¹ö*M®½‚>#¶™Ò.eäÚ«%×^EäÚzÍ].yĥ˵W1¹öŠu®}&…ÂÉeŽµÏ–,È ûxg·/%è¾’…„,.ì^ev—[rŠºWôQ÷¸ñ/ÿm3YØ\îd™ãî•»W âî­M€oÛLI²lîcɲÞ«ˆÀû@¯¹“%õ½dq÷*&ð^±¼Ï¤P;É’ÿãdâ}¼µÛ×Äûê–€[\â½Ê ñþNgäç”y¯è3ïQã1†3·;ÆV ·œ~hx“<}ø´&É¿üù_oINmtêg:õžN}’HK½^„Á¶R‰,#üÐH˱N}œà^£á4TG5St5{QBͶÕ;Dõ1ˆêqˆËfÇ*º;/s*T.êrY%àa…‡Çw}øðøÃU[½~üëzà ¬ ,à½Ü>íOvVAÀ’X¨PøbF„?¯Pu9›W’žW˜ñµ²>Um+¯N?´Ê«d¼’3¯¤ç•LÂ+³þŒÚVJ^‘…›Ë+¹ðJFð îõ^i°fšôùTþ5Ûâ•t¼’1¼’x^­'vØVJ^åS´]¼*ÆTi*àz÷–÷Í«©Êd*åL…Ê­¯/Ûf*T]Î6•¢7fEÛç«bPe¨²Êû¤^QU!TéË„ªáOö¨ºÇwqu9UÃÁR£ 5£QÖÓNm+ªÎ?´ŠªT¨jéP5ümB•ý¦L€*ð^ú-¿Ð›¨;“ɵÞ5TÝp¨þº‰ª@¯w¡ ¬YG¹ŒE5Û@•íágµ¼Š*ûyDm}¹`[ Q•QÑö¡ª¸—Ç/uŒ®$p…%Yž±¦É樫Æéªá¯«û|)W™³}ÕÐû 5¢¹ï†LÍÂbppÕXÉŒÕÌÆj¼±šD¡¶@‰m¦TVSH¨íØï¬&ÂYp¿ÑÎ ¤áºiºº½(£n[Öjœµšk58kRpŠ´¢K‘~™WápÞêÊ}IpüŠG‰ ¸ë/Ó®QÅU¸Z'®–¿¸îñ%A\]ÎöVKï-Ôx ?©À»"S3…·\õV2oµ³·Zï­6‘·€ôÛLé­6£Ë¿«ÞjoµÞ‚û½Ï[pÝ4]Ý^”Q·-oµÎ[mŒ·ÚÞ®Xl3¥·r*Ü>oƒ¬‰¬õ‹uC[EVEŒ,á…ú×—CîË÷ddÁœJŠ,ÔxŒ`ÎÛL,W‘• YbF–ðȉßc7t÷Øÿd»“Í5ßUd‰Y"Yp¿÷! ®›¦«Û‹2ê¶…,á%b%v XÎ6S"+§ÂíCVy/ ¤¶€§Üòw9ЖøHçü&íIÆÉ‰«sâêø‹ë.k¡êr¶¸`E%j<šNBQ¶™B\ ®Š+™¸ºY\W—@\M§ ªm¦WGu¨Íúzvh#òV·x«‹ðÜk¼·†ò\àª](c± ¨Ú–¶:§­.F[R[÷©žAØfJmåS6œµ†+îÒiuHd¶eÚ?Y“BVïÕóGÖ]>ÖBÕåldõôÈBÇà†¦m¦@ƒƒ«ÈJ†¬~FVï‘Õ'BðUf›)‘ÕgsÝwYý‚¬>Yp¯÷! ®Ú…2'«€ªm!«wÈêcÕï@ðèÁ6S"+Ÿ²íEVy´z¤¶€G‹ò'‹5‰8GiI'-É_Zwº.ª2g[KÒY«wÖÂŒÇí Ü [)¤uú¡Ug%s–œ%½³d’,b½^ ŶR*Kf‘zUYrQ–ŒPÜë=YÄ ¬™"]w0ÿšmK:cÉcI|1 ­H×̧h8a¹,â_-ƒ1à)`I$° ‹ß´W¿XK9`)þÀºË÷Qu9›WŠžW˜ñµ²>Um+¯N?´Ê«d¼R3¯”ç•JÂ+³þŒÚVJ^©l®ú®òJ-¼R¼‚{½‡W¬™&}O0ÿšmñJ9^©^)<¯Ö¯ ÚVJ^åS´]¼*ÆT i*àyÈàWSqd*íL¥ù›ê._DÕålSizSaÆc$ÊúëжR˜êôC«¦Jf*=›J{Sé4¦ï¤Òu6—zWM¥SéSÁ½Þe*°fšôµÀük¶e*íL¥cL¥ñ¦Z¿h[)M•OÑv™êwÅ=°ÒH\­ƒÈ‡¶BpUsˆ“ãÊ8\¡¦Ô7äª2góÊÐó üzYÿÈÚV ^~h•WÉxef^Ï+“&‰x}d[)ye² H½Ê+³ðÊDð îõ®$b°f]Í^”P³-^Ç+Ã+ƒN"Rm+%¯ò)ŽW-œŒU„° RX@¸¥L›lY…UްÌeÖð'{aÝã+¸ºœì«á`©}…‘+ël+¯Î?´ê«T¾jé|5ümò•ý¦Là«õPÛJ諱3™\ö]óÕØ ç«á¯›¾ ôz—¯Àšut5{QBÍ6|e{øY-¯úÊ~‘E[ÿÚVB_eT´}¾*Uãw9U ¸°Riã++ª2%„ªÆ¡ªáª{|'W—³QÕУ 3£QÖkEØV T~hUÉPǪ̃j<ªš4¨ï¥÷”ËXŒÉäZï*ªšUMªà^ïBX³Žr‹j¶…ªÆ¡ª‰AUƒGÕúrÁ¶R¢*Ÿ¢íCUq/Ž_ê(]× ·¬ŒÂJW5g8¹®Z§«–¿®îó¥@\eÎöUKï+Ôxˆæ¼25S‹ÁÁUc%3V;«õÆj% «DÛfJeµŸ^uV»8«pÜï}IÃpÝ4]Ý^”Q·-kµÎZmŒµÚIÃpD´¢‹ˆ~™WápÞêÊ}IpüŠG‰ HU»e±”*®²Å%œ¸PŸ÷bF„¿·pßÃ'{ –URo¡ÆcøIÞ™š)¼Åàષ’yKÌÞÞ["‘·€¬JÛLé-‘ÑåßUo‰Å["Â[p¿÷y ®›¦«Û‹2ê¶å-á¼%b¼%vx ¸b±Í”ÞÊ©pû¼U ²YÀ}—[VO©È*‡²:‡¬Ž?²îò¥AT]ÎF §¤ÈBÇ଱ÍÈbppYÉÕÍÈê<²ºDȂﱺ{ì²ÝÉæšï*²ºY]²à~ïC\7MW·eÔm YCVƒ¬n²€åµm3%²r*Ü>d•÷a‡Ôp×E¥5ÉyÚßéœß¤=É8ùâ qõN\=qÝåc-T]ÎWO/.Ôx4ìsÕ6Sˆ‹ÁÁUq%W?‹«÷âꈫéôAµÍ”âê©.µY_ÏmDÞêoõÞ‚{÷ÖPž \µ e,VUÛÒVï´ÕÇh«Gjkø65À‹^¶™R[ù” g­áŠ»´GZ=Yvoyµ"«,R@È’Y’?²îò±ª.g#KÒ# 5#X€š¶™Y ®"+²äŒ,é‘%! ø*³Í”È’Ù\÷]E–\%#÷z²àª](s² ¨Ú²¤C–ŒA–Ü,àE/ÛL‰¬|ʶYå=Ò’HmWÁ·¼ŒÊJ[5‰8¹´”“–â/­;]U™³­¥è¬%µ0ã!ZÕ‰”EXÖù‡V•ÌYjv–òÎRi²ˆÁêä׸_(Ke‘zUYjQ–ŠPÜë]YÄ`Í4eXV5Û2–rÆR1ÆRø,âõšC¶•RXù ',—Eü«e07å,…pwô–Q+°Ê–vÀÒüu—ï ¢êr6¯4=¯0ã1jeý µm¥àÕé‡Vy•ŒWzæ•ö¼ÒixµÎ]²­”¼ÒÙ\õ]å•^x¥#x÷z¯ÀšiÒ¬¬ük¶Å+íx¥cx¥Ñ¼ÒëCÛJÉ«|ж‹W£©¦æßþ¡\i$®Ö_C[ÅUÅŒ+ãp…zÕ¥¾'x®Pu9W†WÈkÙx¹xl¥ÀÕé‡Vq• WfÆ•ñ¸2ipÞR7¤™Y&›k¾«¸2 ®L®à^ïÂX3Mš™•Ͷpe®L ® WëËÛJ‰«|ж W_¼X–² RY@Ë-EY)«&'VV{¹Xe²WÖ]¾#ˆ¬Ì¹Î–ØY¸ñ}Ì|±­éÅàЪ³9k¬åä¬ñoÖYÓwåÎê+ÁDjI¸À íL&‘©Wœe»19küë–³B½Þ“M܃5ë ,¡f×5õð³Z^sÖôyDf¯eb[霕SÑpÎjᬬ’¨e¿ß1Ô’6·<"­Ô*›Z£VßZwø¶ ².gC«¡‡fEÛ§«bß´ßî(f:-Fj(qAÌŽY¨P¼ú¾àÐÂÅž -˜TI¡…1â¤LþÞˆ¥ƒƒ«ØJ†-1cKxl‰$±Ä ´¸m¦ä–È(%õ*¸Ä..¸ß»b‰ #nWp/£n[è]"]K¬5Üm3%»r*^Ý=¼?(ôN±[ίJ¯²éÕ9zuüéu—ï¢êr6¼`L%…jxu»ݾ(£n[ð꼺xu;ൾrœš)á•SáöÁ«mü~adP[Î.Vìß+%“¶wœ q½¤£—äO¯»|Ð…ªËÙô’ôôBGÓI ÜŠ²Íôbpp•^Éè%gzIO/™€^M§à§m¦¤—¤ºÔ@³NžÃìá%xÉxÁ½ÆÃk(Ϯڅ0^«„ªm±K:vÉvI$»Æú¿Š¶™’]ù” ‡®áŠ»Ø‡\©-à*X§½ ®ÚÊØ¶”Ӗ⯭»|Ð…ªËÙÚRôÚBÇ(à”m¦Ðƒƒ«ÚJ¦-5kKym©DÚpÚfJm©l.¯jK-ÚRÚ‚{½O[pÕ.„y[%TmK[ÊiKÅhKíÐpÝ`›)µ•OÙöj+—‡\_þ ÿ_üé-–Äú¯¾E\9}ÿøã«OoŸÂÿfÏlûw6éî¶þMð8Ä®¡Úú¯ÊÍÞQž8õ¼I¶»-gVRÕRÖ¯øÄ©'Æ_´Ž-ÿù[w}ôñç¯^ü÷Çǧ¡}nþ·÷¯ÿþ‡ÿ0ÿÏ×ïøôÓã»§áÞ= þáÿPKVϕқFÆ: PK¦ªl7 styles.xmlÝYßoÛ6~ï_!¨C±“%9é»±ó°bÛC;íºwZ¢d®)Tï¯ß‘Ñ–mµ(¶µ DäwÇ»ïŽw'çæö¾¡Ñ’p¶ŠóYG˜¼$¬^Åÿø%yß®=ºáUE ¼,yÑ5˜©Dª=Å2i&—vsw‚-9’D.j°\ªbÉ[ÌœÐrˆ^š³ìŠQ6UÜ€‡Ò ß«©ÂÈ¢Íô“ x(] ´›*¬±@êP¼âS…ï%M*ž¼i‘"VÜSÂ>­â­Rí2Mw»Ýlw5ã¢NóÅb‘š]opáqm'¨A•EŠ)Ö‡É4Ÿå©Ã6X¡©öiìÐ$Ö5,&Sƒ:Šª¼«'gÄ]}‚šb‹ÄäÜ0à0¼Wåôð^•C٩퉘¼LߦùñöÍC.ˆfêYPUÒNvÓ¢‡òœsoª°Ô˜;ϲëÔ>л³ð ‹¼8 /-<ã¼# py ˆßé4£¾„uký(rUªâP¡*Tà¤Ä•°ÝØó;‘}ÖD­âßI³édô1AD Ÿ°!t¿ŠŸ –ËWÊ.ÅQ V£“3,ø,wDÊÑU@VÜ!At)‰Ó‹–½Æ¡?»èͯ?›sOÚv€›bÝ^*Ü\4ï&=É©cÛ¶‚+%®PGûáèÍ554)0¥:`^äa9id‰Pº‹SWÁ…Óå*ž[Þœh‹ªj·Ç’ ŒàmR©Ó-*›=#,T {±¬ñw4=*¾¤ˆÕªa3³PðŽ)þ}ü*HàJ"6KƒsºLk´;½R·1¦Z—=Šï/+÷Àcõ~ °™ŽÄÑlغޗ÷ ¾=SYÕõO a a × [’š(fîŽQ=<²è„€Ád?vhž]¿ËœWwœBÐM]‰‡æ<(Ù7N]a4vÑõÞ¾g¼=NÛó<¸ÝZð®5ã—q ¤'¤á3ŠÏe¼I`Ê¡a?®ª ¾Üéukü:ñF˜Çï‚WGHƒZ)XI”i*wˆvøÇ§OjõjåÓ µ-í9N‚ô»'+×ö–ŧ ezVþ=–çÅ#_'ýàž„G_È Snú(DIátpÇJ,`ÊÄ#9%å ÈŽ”z0Bâ'}&šCÍß±·a‡I½…A¤ KÕeš-Mó/â)¤X«Gp!H¿a¤_©¾^”ÎôKÃ'D©f‰äÐ]¯"÷î[Œ4³pQpY!*ñ„Ž 1xP¼Š †!ïl#õ©Cþòç­ŠO¥Ó׈pÏqþE$»M%Yp…t™H ÆiGÙ¸¹~~òS’›ô€·R$GT«;6E{Þ©ÀöÉã0Pwl¡ÃÁ£¤á%ˆR‘¨M®-¸ OpenOffice.org/2.0$Linux OpenOffice.org_project/680m5$Build-9073 Kim van der Riet 2007-10-05T09:57:46 Kim van der Riet 2007-11-12T16:21:11 en-US 56 P1DT4H18M42S PK¦ªl7Thumbnails/thumbnail.pngë ðsçå’âb``àõôp ÒM@¼€ƒ HHßr€•ËÓÅ1¤bÎÛ¹o‹ð¸Îž¹§<}¾Íî¼¥¹ù5jÎÍ_æ9A´({Âúß?þÈZ`oÅhÁÎÁÈâÄÐ(à À"cþ8ô¶¬úÏc±}š_kÞÔžŸ¯ú>·³Þdñ“KMž¿èÙP·°zý×7SîH\+ôú|æhTò”=ËUëúbbVÏá·5±™¼AÈE'ÎûÏ$‰×Å·ec»oì•®Ž™\­æ{ÎSñjÉ2ÍKQ¦=Ò·ÏoMZqSïÛ-~Å5ñy¯ÒX<é(nŽY·ÕòíÑŽ·íÓ~ì¼sèóš½?Â,åË2,.÷‰õ͹©!ï,°^‹»ï¾º÷Ƭò5™ïîŠüi¯¼$lKÒùëkš•Û <5}S»³zZÚVgžçË_È(¦]wû¶Ø¹“{¯döüužs8Ç€-„WáNë ÃשckQdÝðډóe+[þ¿Ùó1wvñçžµB?:'û¼ŸçÉ?é×"ë{“ŒŸs(0u¨)¾1íùöHôzqOÿ“=sÄ#æÈkœSKOC¸Pydï± ;ïí'ïzíÙ½.ùîý•6‡žë¾ ½º¹6Gú,ÇF—béuýº¹×#¼kû>¼SR}ïÝ;ûÕ•u'·û¾êÓXk9=GþµñÙ§åüÖýÉÒÛLV|3Ûš—¾|ÿ´îí⫟W¼™>É^£_íá[³ïWú2}³ju·>ÎÞw@Ì+úSö±Þ­ÏÚçš×­ú¸ýééõ‚Iž=_¿ßÕ{ö–½¯vÇ•²•ß×¶tN9â·7?Í^›bËi=±¿¢²ö¸­»9]õ£·ÝKÕŸ›¾êŠˆló^¼¸¨åüEÛcßOê5ý=ÒÄ_ӷΙ9i_l{Û•÷b\gùª'¿ý9K w$|§¼æKöÉt®®ý¹Øx)o¯Ä:×Ï cºƒïùœÖ^ö\âÆ…N •š»gl\8}†íÓÇOùr2륭j Ërþ0£Ìñ)±gn\ð–9åÎáÈ¡P _(Ïjܤxygxfz»ùŽ~ PoòC»C€vOëßÑïísê¸øŽþ¶³«Ã8nô±U>0[ó´ûãª|O•[PV|7yŽ wÖd½´|jgWØ©ÊÉ%8VuàPX¦#rØ”~ý ­Oe `Þó@†‘ÅÌÄÌä©òG°Ûc3.ÕÖ-ºüìlÇï%·keÞ2;Ë¥]#—üÀîȤ]ï÷X%ýìø½ãÈ’ššÆßŸµX€N9û=éžÜÚš#µ1ìœ8ÀiÓd­rK6ûâµÊ=†kyy÷Kæ¶–²ü`tb¸¸§ïA`÷ñcY¤g•²pݰpÿ†…ó}þÊÇlXØ»kÆN†oV%±š$Ö9Oç[½N<ç…‚™I S CÒ©O“{4ïºçÍN÷ú.hÃýOXô¶ÇÔ¶x78øÜÜÓtà]ywí‰Aì¤L×ÙÆ°‹MÉ,¬i' mÇÚÄÎp´ù¤;Ç/+{/Á¹ù@›Y_+/¯ógñò¬¿†*Sø^ GOW?—uN MPKh}“!CPK¦ªl7 settings.xmlíZ]sÚ8}ï¯`xÝI1Nš&¡cœ’&)µù~“m´È’W’1ôׯŒ!Ó88àØf»3ä%ؒιWçJW¾ü¶pqiG”\•«Ÿ•r ›:ˆL®Ê½nëä¢ü­ñéÓ%}~F6¬;Ôö]HÄ ‡BÈ:¼$Û^НÊ>#u 8âu\Èë®S’M³úïµë+¶èÍ#2»*O…ðê•JŸƒÓÏ”M*ÕZ­VY•nªÚ”<£É¾TQíß©(¥/DaƒÈ˜™ª(g•è¹\ZùªoŸJ›žØt€|Uº\³DÿN€nØA¥õëо«²ä­Ï ^º.DÛÚöu»>âÈÂPct©WÞŠ¥' å†rYy ’ü>‹âÐÈÓmðgªzq– E¢Ét« ÕóSõk*?N\à âÀtâ„0X]bC9ýØ2ª²0¸sbfsÁä)7ÂÂj¢áÉ„Ð1û@öÓÆò¶¿n©¯|„\/»=Ó}Æ)ëPŽÂ†Ãt“ë £Ú”¡_”€M#ñHª)eÙyú dÍófÓqôÛ+ äÑlæpÅb2Iè65ÉÆþô™ ÞHÒ¯œðS‡TèM*uSl+{#¢iÔÊ€À9ŠÔ)RWÛ(R=ÚO¤rZ‡G‘úH½;Ä…ˆÔ€ÉG¥:*ÕQ©ŽJõÇ*U*uzT©£JýïU*ã,>ªÔ.•J¬´Jní›^‹æÊ*ù•c[)\º[â³ÅØ,9ÝYýš…jL©Û•hégü^Ð}€ý8öÊì‹LÐ0a&ó]ŠóLæ”!M“A0ë0fÚc,¥Rn<Ì3 xH6†Œ®|áI4‚ù™Yž¨(–à–¡xî97ü[§˜²¦á²«ªç§ªúå<·Q/¬ŸÚ€K7|—4h˃dER™S…Ô«Â8îø_`D ¹t-й ãñ'G*“¯K ÀŒOƒüÖcÇ×—AóËÙ¸»­¾wÕô’­Ü|HLßrÐñDWr¥ØîH¶é‘h ÄÍ%±§Œô ~PÐöØ”$VY_®.ƒ·ÕáP¤º(Ž^¬¯ÜÒÞçž Nņ¦¢Ó!ÂÒNŽDf=Ï&“(‹yÅG¼ƒÅ»ƒD»ÃĺDºĹ£\á1î°n÷YÙTØö±”•b(:òÌ-ýy’ Çñ[H Cvi]33•\î~<bÏÏšˆ¶,7F“ö_ÅÁ–Û_‚Áã¤×¾÷,b`{¢ý‘=ÅiuqÓìï®:дGíŸõC¥rÑÒZãžü9³~jšéÖqÛRF¦¶ÐISúþEïj†Ú÷ÇÃ{o´lþ´]ì;·ý¥îÖdy_þn)`Pó;ýæÜ&Ær4ÀŠî>Íí[Œí_ŠÄyú{4XàN÷&xÔ<1Þ‰; ¼ª}jˆ±äèt5þ¨Áõö}¤Ö¸uz?› ¸V–OšØê;nŸËz¿•½‘Ú«IÌŸ½ãÚRC[§7£¡1Õqs:Vû]0ôð™lÛŽÆ.ÃBð<¼ìqÈ®y-„äoÊZ”YÈq ѧ€[„Á6Ç/Ì™øàmšt½à ɲשÚ’€Ý3³`÷C€Ä£õ»²/JN‘Û¥ÿ÷ƹ!ÎN”œÎ{tûf¦èÔõäaðùHbpô2¢q9ŸØÂ[’úym;L0‡ýèƒÛDÇ”µ¿‰vÎ-FÝ.t½÷Â]F" cy#Ñ=µt@lˆ :Pà„_P‚ã‹q?_Þ9·^VÞ|ôüòêÍáPKHŠ´ÓOT.PK¦ªl7META-INF/manifest.xmlµ•KjÃ0@÷=…ÑÞVÛU1q-ôé&òØè‡f’ÛWäÓ6”¦X; HïFi±Ú[Sí0’ö®OÍ£¨Ð)ßk7vâcý^¿ˆÕòaaÁé‰ÛÓ ÊëÃN¤èZ¤©u`‘ZV­èz¯’EÇí×ùídZ>Tð  Öybì„`´ÎyÊ뛣«¹V4"BO[DÈŒ©ð¶RÈ»œ·)oÞ zLñ˜ =KbàDˆeð Ì¡R¥§æbw Æc!xð!…|R!|ôcD*wÒSêÅàì½)×F$ùªÙB ¢Ž;Ùߟ Jnêž&éF] þ–ÃrÆ=Ë©Wo³Ÿÿ×Ì¿s‰iv¬E†Ùžõ6ÙmHòiØ7Î Ÿ·°ÈœÿÅsiòÇ·¸üPKÌMfBQPK¦ªl7…l9Š..mimetypePK¦ªl7TConfigurations2/statusbar/PK¦ªl7'ŒConfigurations2/accelerator/current.xmlPK¦ªl7ãConfigurations2/floater/PK¦ªl7Configurations2/popupmenu/PK¦ªl7QConfigurations2/progressbar/PK¦ªl7‹Configurations2/menubar/PK¦ªl7ÁConfigurations2/toolbar/PK¦ªl7÷Configurations2/images/Bitmaps/PK¦ªl7VϕқFÆ: 4content.xmlPK¦ªl7N7\,7 Istyles.xmlPK¦ªl7cÏIIÐNmeta.xmlPK¦ªl7h}“!C?SThumbnails/thumbnail.pngPK¦ªl7HŠ´ÓOT. ¦Xsettings.xmlPK¦ªl7ÌMfBQ/^META-INF/manifest.xmlPKî´_qpid-cpp-store-debian-0.16/tests/jrnl/_ut_jdir.cpp0000644000176300017630000003516211222162407021151 0ustar cajuscajus/* * Copyright (c) 2007, 2008 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #include "../unit_test.h" #include #include #include #include #include #include #include "jrnl/file_hdr.hpp" #include "jrnl/jcfg.hpp" #include "jrnl/jdir.hpp" #include "jrnl/jerrno.hpp" #include "jrnl/jexception.hpp" #include #define NUM_JFILES 4 #define JFSIZE_SBLKS 128 #define ERRORSTR(e) std::strerror(e) << " (" << e << ")" #define NUM_CLEAR_OPS 20 using namespace boost::unit_test; using namespace mrg::journal; using namespace std; QPID_AUTO_TEST_SUITE(jdir_suite) const string test_filename("_ut_jdir"); const char* tdp = getenv("TMP_DATA_DIR"); const string test_dir(tdp && strlen(tdp) > 0 ? string(tdp) + "/_ut_jdir" : "/tmp/_ut_jdir"); // === Helper functions === void create_file(const char* filename, mode_t fmode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) { ofstream of(filename, ofstream::out | ofstream::trunc); if (!of.good()) BOOST_FAIL("Unable to open file " << filename << " for writing."); of.write(filename, std::strlen(filename)); of.close(); ::chmod(filename, fmode); } void create_file(const string filename, mode_t fmode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) { create_file(filename.c_str(), fmode); } void create_jdat_file(const char* dirname, const char* base_filename, u_int32_t fid, u_int64_t first_rid) { stringstream fn; fn << dirname << "/" << base_filename << "."; fn << setfill('0') << hex << setw(4) << fid << ".jdat"; file_hdr fh(RHM_JDAT_FILE_MAGIC, RHM_JDAT_VERSION, 0, first_rid, fid, 0x200, true); ofstream of(fn.str().c_str(), ofstream::out | ofstream::trunc); if (!of.good()) BOOST_FAIL("Unable to open journal data file " << fn << " for writing."); of.write((const char*)&fh, sizeof(file_hdr)); of.close(); } void create_jinf_file(const char* dirname, const char* base_filename) { timespec ts; ::clock_gettime(CLOCK_REALTIME, &ts); jinf ji("test journal id", dirname, base_filename, NUM_JFILES, false, 0, JFSIZE_SBLKS, JRNL_WMGR_DEF_PAGE_SIZE, JRNL_WMGR_DEF_PAGES, ts); ji.write(); } void create_jrnl_fileset(const char* dirname, const char* base_filename) { create_jinf_file(dirname, base_filename); for (u_int32_t fid = 0; fid < NUM_JFILES; fid++) { u_int64_t rid = 0x12340000 + (fid * 0x25); create_jdat_file(dirname, base_filename, fid, rid); } } unsigned count_dir_contents(const char* dirname, bool incl_files, bool incl_dirs = true) { struct dirent* entry; struct stat s; unsigned file_cnt = 0; unsigned dir_cnt = 0; unsigned other_cnt = 0; DIR* dir = ::opendir(dirname); if (!dir) BOOST_FAIL("Unable to open directory " << dirname); while ((entry = ::readdir(dir)) != NULL) { // Ignore . and .. if (std::strcmp(entry->d_name, ".") != 0 && std::strcmp(entry->d_name, "..") != 0) { stringstream fn; fn << dirname << "/" << entry->d_name; if (::stat(fn.str().c_str(), &s)) BOOST_FAIL("Unable to stat dir entry " << entry->d_name << "; err=" << ERRORSTR(errno)); if (S_ISREG(s.st_mode)) file_cnt++; else if (S_ISDIR(s.st_mode)) dir_cnt++; else other_cnt++; } } ::closedir(dir); if (incl_files) { if (incl_dirs) return file_cnt + dir_cnt; return file_cnt; } else if (incl_dirs) return dir_cnt; return other_cnt; } void check_dir_contents(const char* dirname, const char* base_filename, unsigned num_subdirs, bool jrnl_present) { if (jdir::is_dir(dirname)) { // Subdir count BOOST_CHECK_EQUAL(count_dir_contents(dirname, false, true), num_subdirs); // Journal file count unsigned num_jrnl_files = jrnl_present ? NUM_JFILES + 1 : 0; BOOST_CHECK_EQUAL(count_dir_contents(dirname, true, false), num_jrnl_files); // Check journal files are present if (jrnl_present) try { jdir::verify_dir(dirname, base_filename); } catch(const jexception& e) { BOOST_ERROR(e); } for (unsigned subdir_num = 1; subdir_num <= num_subdirs; subdir_num++) { stringstream subdir_name; subdir_name << dirname << "/_" << base_filename << ".bak."; subdir_name << hex << setfill('0') << setw(4) << subdir_num; try { jdir::verify_dir(subdir_name.str().c_str(), base_filename); } catch(const jexception& e) { BOOST_ERROR(e); } } } else BOOST_ERROR(dirname << " is not a directory"); } void check_dir_not_existing(const char* dirname) { if (jdir::exists(dirname) && jdir::is_dir(dirname)) jdir::delete_dir(dirname); if (jdir::exists(dirname)) BOOST_FAIL("Unable to remove directory " << dirname); } void check_dir_not_existing(const string dirname) { check_dir_not_existing(dirname.c_str()); } // === Test suite === QPID_AUTO_TEST_CASE(constructor) { cout << test_filename << ".constructor: " << flush; string dir(test_dir + "/A/B/C/D/E/F"); string bfn("test_base"); jdir dir1(dir, bfn); BOOST_CHECK(dir1.dirname().compare(dir) == 0); BOOST_CHECK(dir1.base_filename().compare(bfn) == 0); cout << "ok" << endl; } QPID_AUTO_TEST_CASE(create_delete_dir) { cout << test_filename << ".create_delete_dir: " << flush; // Use instance string dir_A(test_dir + "/A"); string dir_Ats(test_dir + "/A/"); // trailing '/' check_dir_not_existing(test_dir + "/A"); jdir dir1(dir_A, "test_base"); dir1.create_dir(); // check all combos of jdir::exists and jdir::is_dir() BOOST_CHECK(jdir::exists(dir_A)); BOOST_CHECK(jdir::exists(dir_Ats)); BOOST_CHECK(jdir::exists(dir_A.c_str())); BOOST_CHECK(jdir::exists(dir_Ats.c_str())); BOOST_CHECK(jdir::is_dir(dir_A)); BOOST_CHECK(jdir::is_dir(dir_Ats)); BOOST_CHECK(jdir::is_dir(dir_Ats.c_str())); BOOST_CHECK(jdir::is_dir(dir_Ats.c_str())); // do it a second time when dir exists dir1.create_dir(); BOOST_CHECK(jdir::is_dir(dir_A)); dir1.delete_dir(); BOOST_CHECK(!jdir::exists(dir_A)); // Use static fn check_dir_not_existing(test_dir + "/B"); jdir::create_dir(test_dir + "/B"); BOOST_CHECK(jdir::is_dir(test_dir + "/B")); jdir::create_dir(test_dir + "/B"); BOOST_CHECK(jdir::is_dir(test_dir + "/B")); jdir::delete_dir(test_dir + "/B"); BOOST_CHECK(!jdir::exists(test_dir + "/B")); // Non-empty dirs check_dir_not_existing(test_dir + "/C"); jdir::create_dir(test_dir + "/C"); BOOST_CHECK(jdir::is_dir(test_dir + "/C")); create_file(test_dir + "/C/test_file_1.txt"); // mode 644 (default) create_file(test_dir + "/C/test_file_2.txt", S_IRWXU | S_IRWXG | S_IRWXO); // mode 777 create_file(test_dir + "/C/test_file_3.txt", S_IRUSR | S_IRGRP | S_IROTH); // mode 444 (read-only) create_file(test_dir + "/C/test_file_4.txt", 0); // mode 000 (no permissions) BOOST_CHECK(jdir::is_dir(test_dir + "/C")); jdir::create_dir(test_dir + "/C"); BOOST_CHECK(jdir::is_dir(test_dir + "/C")); jdir::delete_dir(test_dir + "/C"); BOOST_CHECK(!jdir::exists(test_dir + "/C")); // Check non-existent dirs fail check_dir_not_existing(test_dir + "/D"); try { jdir::is_dir(test_dir + "/D"); BOOST_ERROR("jdir::is_dir() failed to throw jexeption for non-existent directory."); } catch(const jexception& e) { BOOST_CHECK_EQUAL(e.err_code(), jerrno::JERR_JDIR_STAT); } cout << "ok" << endl; } QPID_AUTO_TEST_CASE(create_delete_dir_recursive) { cout << test_filename << ".create_delete_dir_recursive: " << flush; // Use instances check_dir_not_existing(test_dir + "/E"); jdir dir1(test_dir + "/E/F/G/H", "test_base"); dir1.create_dir(); BOOST_CHECK(jdir::is_dir(test_dir + "/E/F/G/H")); dir1.delete_dir(); BOOST_CHECK(!jdir::exists(test_dir + "/E/F/G/H")); // only H deleted, E/F/G remain BOOST_CHECK(jdir::exists(test_dir + "/E/F/G")); jdir::delete_dir(test_dir + "/E"); // delete remaining dirs BOOST_CHECK(!jdir::exists(test_dir + "/E")); check_dir_not_existing(test_dir + "/F"); jdir dir2(test_dir + "/F/G/H/I/", "test_base"); // trailing '/' dir2.create_dir(); BOOST_CHECK(jdir::is_dir(test_dir + "/F/G/H/I/")); dir2.delete_dir(); BOOST_CHECK(!jdir::exists(test_dir + "/F/G/H/I/")); BOOST_CHECK(jdir::exists(test_dir + "/F/G/H/")); jdir::delete_dir(test_dir + "/F"); BOOST_CHECK(!jdir::exists(test_dir + "/F")); check_dir_not_existing(test_dir + "/G"); jdir dir3(test_dir + "/G/H//I//J", "test_base"); // extra '/' in path dir3.create_dir(); BOOST_CHECK(jdir::is_dir(test_dir + "/G/H//I//J")); dir3.delete_dir(); BOOST_CHECK(!jdir::exists(test_dir + "/G/H//I//J")); BOOST_CHECK(jdir::exists(test_dir + "/G/H//I")); jdir::delete_dir(test_dir + "/F"); BOOST_CHECK(!jdir::exists(test_dir + "/F")); // Use static fn check_dir_not_existing(test_dir + "/H"); jdir::create_dir(test_dir + "/H/I/J/K"); BOOST_CHECK(jdir::is_dir(test_dir + "/H/I/J/K")); jdir::delete_dir(test_dir + "/H/I/J/K"); BOOST_CHECK(!jdir::exists(test_dir + "/H/I/J/K")); // only J deleted, H/I/J remain BOOST_CHECK(jdir::exists(test_dir + "/H/I/J")); jdir::delete_dir(test_dir + "/H"); BOOST_CHECK(!jdir::exists(test_dir + "/H")); check_dir_not_existing(test_dir + "/I"); jdir::create_dir(test_dir + "/I/J/K/L/"); // trailing '/' BOOST_CHECK(jdir::is_dir(test_dir + "/I/J/K/L/")); jdir::delete_dir(test_dir + "/I/J/K/L/"); BOOST_CHECK(!jdir::exists(test_dir + "/I/J/K/L/")); BOOST_CHECK(jdir::exists(test_dir + "/I/J/K/")); jdir::delete_dir(test_dir + "/I"); BOOST_CHECK(!jdir::exists(test_dir + "/I")); check_dir_not_existing(test_dir + "//J"); jdir::create_dir(test_dir + "//J//K//L//M"); // extra '/' in path BOOST_CHECK(jdir::is_dir(test_dir + "//J//K//L//M")); jdir::delete_dir(test_dir + "//J//K//L//M"); BOOST_CHECK(!jdir::exists(test_dir + "//J//K//L//M")); BOOST_CHECK(jdir::exists(test_dir + "//J//K//L")); jdir::delete_dir(test_dir + "//J"); BOOST_CHECK(!jdir::exists(test_dir + "//J")); // Non-empty dirs check_dir_not_existing(test_dir + "/K"); jdir::create_dir(test_dir + "/K/L/M1/N1"); jdir::create_dir(test_dir + "/K/L/M1/N2"); jdir::create_dir(test_dir + "/K/L/M1/N3"); jdir::create_dir(test_dir + "/K/L/M1/N4"); create_file(test_dir + "/K/L/M1/N4/test_file_1.txt"); // mode 644 (default) create_file(test_dir + "/K/L/M1/N4/test_file_2.txt", S_IRWXU | S_IRWXG | S_IRWXO); // mode 777 create_file(test_dir + "/K/L/M1/N4/test_file_3.txt", S_IRUSR | S_IRGRP | S_IROTH); // mode 444 create_file(test_dir + "/K/L/M1/N4/test_file_4.txt", 0); // mode 000 (no permissions) jdir::create_dir(test_dir + "/K/L/M2"); jdir::create_dir(test_dir + "/K/L/M3/N5"); jdir::create_dir(test_dir + "/K/L/M3/N6"); BOOST_CHECK(jdir::is_dir(test_dir + "/K/L/M1/N1")); BOOST_CHECK(jdir::is_dir(test_dir + "/K/L/M1/N2")); BOOST_CHECK(jdir::is_dir(test_dir + "/K/L/M1/N3")); BOOST_CHECK(jdir::is_dir(test_dir + "/K/L/M1/N4")); BOOST_CHECK(jdir::is_dir(test_dir + "/K/L/M2")); BOOST_CHECK(jdir::is_dir(test_dir + "/K/L/M3/N5")); BOOST_CHECK(jdir::is_dir(test_dir + "/K/L/M3/N6")); jdir::delete_dir(test_dir + "/K"); BOOST_CHECK(!jdir::exists(test_dir + "/K")); cout << "ok" << endl; } QPID_AUTO_TEST_CASE(clear_verify_dir) { cout << test_filename << ".clear_verify_dir: " << flush; // Use instances const char* jrnl_dir = "/tmp/test_dir_1"; const char* bfn = "test_base"; check_dir_not_existing(jrnl_dir); jdir test_dir_1(jrnl_dir, bfn); test_dir_1.create_dir(); BOOST_CHECK(jdir::is_dir(jrnl_dir)); // add journal files, check they exist, then clear them unsigned cnt = 0; while (cnt < NUM_CLEAR_OPS) { create_jrnl_fileset(jrnl_dir, bfn); check_dir_contents(jrnl_dir, bfn, cnt, true); test_dir_1.clear_dir(); check_dir_contents(jrnl_dir, bfn, ++cnt, false); } // clean up test_dir_1.delete_dir(); BOOST_CHECK(!jdir::exists(jrnl_dir)); // Non-existent dir with auto-create true jrnl_dir = "/tmp/test_dir_2"; check_dir_not_existing(jrnl_dir); jdir test_dir_2(jrnl_dir, bfn); // clear dir test_dir_2.clear_dir(); // create flag is true by default check_dir_contents(jrnl_dir, bfn, 0, false); // clear empty dir, should not create subdir test_dir_2.clear_dir(); // create flag is true by default check_dir_contents(jrnl_dir, bfn, 0, false); // clean up test_dir_2.delete_dir(); BOOST_CHECK(!jdir::exists(jrnl_dir)); // non-existent dir with auto-create false jrnl_dir = "/tmp/test_dir_3"; check_dir_not_existing(jrnl_dir); jdir test_dir_3(jrnl_dir, bfn); try { test_dir_3.clear_dir(false); BOOST_ERROR("jdir::clear_dir(flase) failed to throw jexeption for non-existent directory."); } catch(const jexception& e) { BOOST_CHECK_EQUAL(e.err_code(), jerrno::JERR_JDIR_OPENDIR); } // Use static fn jrnl_dir = "/tmp/test_dir_4"; check_dir_not_existing(jrnl_dir); jdir::clear_dir(jrnl_dir, bfn); // should create dir if it does not exist // add journal files, check they exist, then clear them cnt = 0; while (cnt < NUM_CLEAR_OPS) { create_jrnl_fileset(jrnl_dir, bfn); check_dir_contents(jrnl_dir, bfn, cnt, true); jdir::clear_dir(jrnl_dir, bfn); check_dir_contents(jrnl_dir, bfn, ++cnt, false); } // clean up jdir::delete_dir(jrnl_dir); BOOST_CHECK(!jdir::exists(jrnl_dir)); cout << "ok" << endl; } QPID_AUTO_TEST_SUITE_END() qpid-cpp-store-debian-0.16/tests/jrnl/_ut_jerrno.cpp0000644000176300017630000000314211142067437021521 0ustar cajuscajus/* * Copyright (c) 2007, 2008 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #include "../unit_test.h" #include #include #include "jrnl/jerrno.hpp" using namespace boost::unit_test; using namespace mrg::journal; using namespace std; QPID_AUTO_TEST_SUITE(jerrno_suite) using namespace mrg::journal; const string test_filename("_ut_jerrno"); QPID_AUTO_TEST_CASE(jerrno_val) { cout << test_filename << ".jerrno_val: " << flush; const char* m = "JERR__MALLOC"; string malloc_msg = string(jerrno::err_msg(jerrno::JERR__MALLOC)); BOOST_CHECK(malloc_msg.substr(0, std::strlen(m)).compare(m) == 0); BOOST_CHECK(std::strcmp(jerrno::err_msg(0), "") == 0); cout << "ok" << endl; } QPID_AUTO_TEST_SUITE_END() qpid-cpp-store-debian-0.16/tests/jrnl/_ut_jexception.cpp0000644000176300017630000002606411142067437022402 0ustar cajuscajus/* * Copyright (c) 2007, 2008 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #include "../unit_test.h" #include #include #include "jrnl/jerrno.hpp" #include "jrnl/jexception.hpp" using namespace boost::unit_test; using namespace mrg::journal; using namespace std; QPID_AUTO_TEST_SUITE(jexception_suite) const string test_filename("_ut_jexception"); // === Helper functions === void throw_exception(const jexception& e, std::size_t what_len, std::size_t ai_len, std::size_t tc_len, std::size_t tf_len) { try { throw e; } catch (const jexception& e) { BOOST_CHECK_EQUAL(std::strlen(e.what()), what_len); BOOST_CHECK_EQUAL(e.additional_info().size(), ai_len); BOOST_CHECK_EQUAL(e.throwing_class().size(), tc_len); BOOST_CHECK_EQUAL(e.throwing_fn().size(), tf_len); } } void throw_exception(const jexception& e, std::size_t what_len, std::size_t ai_len) { throw_exception(e, what_len, ai_len, 0, 0); } void throw_exception(const jexception& e, std::size_t what_len, std::size_t tc_len, std::size_t tf_len) { throw_exception(e, what_len, 0, tc_len, tf_len); } // === Test suite === QPID_AUTO_TEST_CASE(constructor_1) { cout << test_filename << ".constructor_1: " << flush; try { jexception e1; BOOST_CHECK_EQUAL(e1.err_code(), (u_int32_t)0); BOOST_CHECK(e1.additional_info().size() == 0); BOOST_CHECK(e1.throwing_class().size() == 0); BOOST_CHECK(e1.throwing_fn().size() == 0); BOOST_CHECK(std::strlen(e1.what()) > 0); throw e1; } catch (const jexception& e) { BOOST_CHECK_EQUAL(e.err_code(), (u_int32_t)0); BOOST_CHECK(e.additional_info().size() == 0); BOOST_CHECK(e.throwing_class().size() == 0); BOOST_CHECK(e.throwing_fn().size() == 0); BOOST_CHECK(std::strlen(e.what()) > 0); } cout << "ok" << endl; } QPID_AUTO_TEST_CASE(constructor_2) { cout << test_filename << ".constructor_2: " << flush; const u_int32_t err_code = 2; try { jexception e2(err_code); BOOST_CHECK_EQUAL(e2.err_code(), err_code); BOOST_CHECK(e2.additional_info().size() == 0); BOOST_CHECK(e2.throwing_class().size() == 0); BOOST_CHECK(e2.throwing_fn().size() == 0); BOOST_CHECK(std::strlen(e2.what()) > 0); throw e2; } catch (const jexception& e) { BOOST_CHECK_EQUAL(e.err_code(), err_code); BOOST_CHECK(e.additional_info().size() == 0); BOOST_CHECK(e.throwing_class().size() == 0); BOOST_CHECK(e.throwing_fn().size() == 0); BOOST_CHECK(std::strlen(e.what()) > 0); } cout << "ok" << endl; } QPID_AUTO_TEST_CASE(constructor_3a) { cout << test_filename << ".constructor_3a: " << flush; const char* err_msg = "exception3"; try { jexception e3(err_msg); BOOST_CHECK_EQUAL(e3.err_code(), (u_int32_t)0); BOOST_CHECK(e3.additional_info().compare(err_msg) == 0); BOOST_CHECK(e3.throwing_class().size() == 0); BOOST_CHECK(e3.throwing_fn().size() == 0); BOOST_CHECK(std::strlen(e3.what()) > 0); throw e3; } catch (const jexception& e) { BOOST_CHECK_EQUAL(e.err_code(), (u_int32_t)0); BOOST_CHECK(e.additional_info().compare(err_msg) == 0); BOOST_CHECK(e.throwing_class().size() == 0); BOOST_CHECK(e.throwing_fn().size() == 0); BOOST_CHECK(std::strlen(e.what()) > 0); } cout << "ok" << endl; } QPID_AUTO_TEST_CASE(constructor_3b) { cout << test_filename << ".constructor_3b: " << flush; const string err_msg("exception3"); try { jexception e3(err_msg); BOOST_CHECK_EQUAL(e3.err_code(), (u_int32_t)0); BOOST_CHECK(e3.additional_info().compare(err_msg) == 0); BOOST_CHECK(e3.throwing_class().size() == 0); BOOST_CHECK(e3.throwing_fn().size() == 0); BOOST_CHECK(std::strlen(e3.what()) > 0); throw e3; } catch (const jexception& e) { BOOST_CHECK_EQUAL(e.err_code(), (u_int32_t)0); BOOST_CHECK(e.additional_info().compare(err_msg) == 0); BOOST_CHECK(e.throwing_class().size() == 0); BOOST_CHECK(e.throwing_fn().size() == 0); BOOST_CHECK(std::strlen(e.what()) > 0); } cout << "ok" << endl; } QPID_AUTO_TEST_CASE(constructor_4a) { cout << test_filename << ".constructor_4a: " << flush; const u_int32_t err_code = 4; const char* err_msg = "exception4"; try { jexception e4(err_code, err_msg); BOOST_CHECK_EQUAL(e4.err_code(), err_code); BOOST_CHECK(e4.additional_info().compare(err_msg) == 0); BOOST_CHECK(e4.throwing_class().size() == 0); BOOST_CHECK(e4.throwing_fn().size() == 0); BOOST_CHECK(std::strlen(e4.what()) > 0); throw e4; } catch (const jexception& e) { BOOST_CHECK_EQUAL(e.err_code(), err_code); BOOST_CHECK(e.additional_info().compare(err_msg) == 0); BOOST_CHECK(e.throwing_class().size() == 0); BOOST_CHECK(e.throwing_fn().size() == 0); BOOST_CHECK(std::strlen(e.what()) > 0); } cout << "ok" << endl; } QPID_AUTO_TEST_CASE(constructor_4b) { cout << test_filename << ".constructor_4b: " << flush; const u_int32_t err_code = 4; const string err_msg("exception4"); try { jexception e4(err_code, err_msg); BOOST_CHECK_EQUAL(e4.err_code(), err_code); BOOST_CHECK(e4.additional_info().compare(err_msg) == 0); BOOST_CHECK(e4.throwing_class().size() == 0); BOOST_CHECK(e4.throwing_fn().size() == 0); BOOST_CHECK(std::strlen(e4.what()) > 0); throw e4; } catch (const jexception& e) { BOOST_CHECK_EQUAL(e.err_code(), err_code); BOOST_CHECK(e.additional_info().compare(err_msg) == 0); BOOST_CHECK(e.throwing_class().size() == 0); BOOST_CHECK(e.throwing_fn().size() == 0); BOOST_CHECK(std::strlen(e.what()) > 0); } cout << "ok" << endl; } QPID_AUTO_TEST_CASE(constructor_5a) { cout << test_filename << ".constructor_5a: " << flush; const u_int32_t err_code = 5; const char* err_class = "class5"; const char* err_fn = "fn5"; try { jexception e5(err_code, err_class, err_fn); BOOST_CHECK_EQUAL(e5.err_code(), err_code); BOOST_CHECK(e5.additional_info().size() == 0); BOOST_CHECK(e5.throwing_class().compare(err_class) == 0); BOOST_CHECK(e5.throwing_fn().compare(err_fn) == 0); BOOST_CHECK(std::strlen(e5.what()) > 0); throw e5; } catch (const jexception& e) { BOOST_CHECK_EQUAL(e.err_code(), err_code); BOOST_CHECK(e.additional_info().size() == 0); BOOST_CHECK(e.throwing_class().compare(err_class) == 0); BOOST_CHECK(e.throwing_fn().compare(err_fn) == 0); BOOST_CHECK(std::strlen(e.what()) > 0); } cout << "ok" << endl; } QPID_AUTO_TEST_CASE(constructor_5b) { cout << test_filename << ".constructor_5b: " << flush; const u_int32_t err_code = 5; const string err_class("class5"); const string err_fn("fn5"); try { jexception e5(err_code, err_class, err_fn); BOOST_CHECK_EQUAL(e5.err_code(), err_code); BOOST_CHECK(e5.additional_info().size() == 0); BOOST_CHECK(e5.throwing_class().compare(err_class) == 0); BOOST_CHECK(e5.throwing_fn().compare(err_fn) == 0); BOOST_CHECK(std::strlen(e5.what()) > 0); throw e5; } catch (const jexception& e) { BOOST_CHECK_EQUAL(e.err_code(), err_code); BOOST_CHECK(e.additional_info().size() == 0); BOOST_CHECK(e.throwing_class().compare(err_class) == 0); BOOST_CHECK(e.throwing_fn().compare(err_fn) == 0); BOOST_CHECK(std::strlen(e.what()) > 0); } cout << "ok" << endl; } QPID_AUTO_TEST_CASE(constructor_6a) { cout << test_filename << ".constructor_6a: " << flush; const u_int32_t err_code = 6; const char* err_msg = "exception6"; const char* err_class = "class6"; const char* err_fn = "fn6"; try { jexception e6(err_code, err_msg, err_class, err_fn); BOOST_CHECK_EQUAL(e6.err_code(), err_code); BOOST_CHECK(e6.additional_info().compare(err_msg) == 0); BOOST_CHECK(e6.throwing_class().compare(err_class) == 0); BOOST_CHECK(e6.throwing_fn().compare(err_fn) == 0); BOOST_CHECK(std::strlen(e6.what()) > 0); throw e6; } catch (const jexception& e) { BOOST_CHECK_EQUAL(e.err_code(), err_code); BOOST_CHECK(e.additional_info().compare(err_msg) == 0); BOOST_CHECK(e.throwing_class().compare(err_class) == 0); BOOST_CHECK(e.throwing_fn().compare(err_fn) == 0); BOOST_CHECK(std::strlen(e.what()) > 0); } cout << "ok" << endl; } QPID_AUTO_TEST_CASE(constructor_6b) { cout << test_filename << ".constructor_6b: " << flush; const u_int32_t err_code = 6; const string err_msg("exception6"); const string err_class("class6"); const string err_fn("fn6"); try { jexception e6(err_code, err_msg, err_class, err_fn); BOOST_CHECK_EQUAL(e6.err_code(), err_code); BOOST_CHECK(e6.additional_info().compare(err_msg) == 0); BOOST_CHECK(e6.throwing_class().compare(err_class) == 0); BOOST_CHECK(e6.throwing_fn().compare(err_fn) == 0); BOOST_CHECK(std::strlen(e6.what()) > 0); throw e6; } catch (const jexception& e) { BOOST_CHECK_EQUAL(e.err_code(), err_code); BOOST_CHECK(e.additional_info().compare(err_msg) == 0); BOOST_CHECK(e.throwing_class().compare(err_class) == 0); BOOST_CHECK(e.throwing_fn().compare(err_fn) == 0); BOOST_CHECK(std::strlen(e.what()) > 0); } cout << "ok" << endl; } QPID_AUTO_TEST_CASE(msg_scope) { cout << test_filename << ".msg_scope: " << flush; try { // These will go out of scope as soon as jexception is thrown... const string msg("Error message"); const string cls("class"); const string fn("function"); throw jexception(100, msg, cls, fn); } catch (const jexception& e) { stringstream ss; ss << e; BOOST_CHECK(ss.str().size() > 0); } cout << "ok" << endl; } QPID_AUTO_TEST_SUITE_END() qpid-cpp-store-debian-0.16/tests/jrnl/_st_read_txn.cpp0000644000176300017630000002721611232164074022027 0ustar cajuscajus/* * Copyright (c) 2008 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #include "../unit_test.h" #include #include #include "jrnl/jcntl.hpp" using namespace boost::unit_test; using namespace mrg::journal; using namespace std; QPID_AUTO_TEST_SUITE(journal_read_txn) const string test_filename("_st_read_txn"); #include "_st_helper_fns.hpp" // === Test suite === QPID_AUTO_TEST_CASE(tx_enqueue_commit_block) { string test_name = get_test_name(test_filename, "tx_enqueue_commit_block"); try { string msg; string xid; string rmsg; string rxid; bool transientFlag; bool externalFlag; test_jrnl_cb cb; test_jrnl jc(test_name, test_dir, test_name, cb); jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); create_xid(xid, 0, XID_SIZE); for (int m=0; m #include #include "jrnl/jcntl.hpp" #include "jrnl/lpmgr.hpp" using namespace boost::unit_test; using namespace mrg::journal; using namespace std; QPID_AUTO_TEST_SUITE(arr_cnt_suite) const string test_filename("_ut_lpmgr"); #include "_st_helper_fns.hpp" // === Helper functions and definitions === typedef vector flist; typedef flist::const_iterator flist_citr; class lpmgr_test_helper { lpmgr_test_helper() {} virtual ~lpmgr_test_helper() {} public: static void check_pfids_lfids(const lpmgr& lm, const u_int16_t pfids[], const u_int16_t lfids[], const size_t pfid_lfid_size) { vector res; lm.get_pfid_list(res); vectors_equal(lm, pfids, pfid_lfid_size, res, true); lm.get_lfid_list(res); vectors_equal(lm, lfids, pfid_lfid_size, res, false); } static void check_pfids_lfids(const lpmgr& lm, const flist& pfids, const flist lfids) { vector res; lm.get_pfid_list(res); vectors_equal(lm, pfids, res, true); lm.get_lfid_list(res); vectors_equal(lm, lfids, res, false); } static void check_linear_pfids_lfids(const lpmgr& lm, const size_t pfid_lfid_size) { vector res; lm.get_pfid_list(res); linear_vectors_equal(lm, pfid_lfid_size, res, true); lm.get_lfid_list(res); linear_vectors_equal(lm, pfid_lfid_size, res, false); } static void rcvdat_init(rcvdat& rd, const u_int16_t num_jfiles, const bool ae, const u_int16_t ae_max_jfiles, const u_int16_t pfids[]) { rd.reset(num_jfiles, ae, ae_max_jfiles); load_vector(pfids, num_jfiles, rd._fid_list); rd._jempty = false; rd._lfid = pfids[num_jfiles - 1]; rd._eo = 100 * JRNL_DBLK_SIZE * JRNL_SBLK_SIZE; } static void rcvdat_init(rcvdat& rd, const flist& pfidl, const bool ae, const u_int16_t ae_max_jfiles) { const u_int16_t num_jfiles = pfidl.size(); rd.reset(num_jfiles, ae, ae_max_jfiles); load_vector(pfidl, rd._fid_list); rd._jempty = false; rd._lfid = pfidl[num_jfiles - 1]; rd._eo = 100 * JRNL_DBLK_SIZE * JRNL_SBLK_SIZE; } static void initialize(lpmgr& lm, test_jrnl& jc, const u_int16_t num_jfiles, const bool ae, const u_int16_t ae_max_jfiles) { lm.initialize(num_jfiles, ae, ae_max_jfiles, &jc, &jc.new_fcntl); BOOST_CHECK_EQUAL(lm.is_init(), true); BOOST_CHECK_EQUAL(lm.is_ae(), ae); BOOST_CHECK_EQUAL(lm.ae_max_jfiles(), ae_max_jfiles); BOOST_CHECK_EQUAL(lm.num_jfiles(), num_jfiles); if (num_jfiles) check_linear_pfids_lfids(lm, num_jfiles); else BOOST_CHECK_EQUAL(lm.get_fcntlp(0), (void*)0); } // version which sets up the lfid_pfid_map for later manipulation by insert tests static void initialize(lfid_pfid_map& lfm, lpmgr& lm, test_jrnl& jc, const u_int16_t num_jfiles, const bool ae, const u_int16_t ae_max_jfiles) { lfm.journal_create(num_jfiles, num_jfiles); initialize(lm, jc, num_jfiles, ae, ae_max_jfiles); } static void prepare_recover(lfid_pfid_map& lfm, const u_int16_t size) { if (size < 4) BOOST_FAIL("prepare_recover(): size parameter (" << size << ") too small."); lfm.journal_create(4, 4); // initial journal of size 4 u_int16_t s = 4; // cumulative size while (s < size) { const u_int16_t ins_posn = u_int16_t(s * ::drand48()); // this insert posn if (3.0 * ::drand48() > 1.0 || size - s < 2) // 2:1 chance of single insert when >= 2 still to insert { lfm.journal_insert(ins_posn); // single insert s++; } else { // multiple insert, either 2 - 5 const u_int16_t max_ins_size = size - s >5 ? 5 : size - s; const u_int16_t ins_size = 2 + u_int16_t((max_ins_size - 2) * ::drand48()); // this insert size lfm.journal_insert(ins_posn, ins_size); s += ins_size; } } } static void recover(lfid_pfid_map& lfm, lpmgr& lm, test_jrnl& jc, const bool ae, const u_int16_t ae_max_jfiles) { flist pfidl; flist lfidl; rcvdat rd; const u_int16_t num_jfiles = lfm.size(); lfm.get_pfid_list(pfidl); lfm.get_lfid_list(lfidl); lm.finalize(); // clear all file handles before erasing old journal files lfm.write_journal(ae, ae_max_jfiles, JFSIZE_SBLKS); lpmgr_test_helper::rcvdat_init(rd, pfidl, ae, ae_max_jfiles); lm.recover(rd, &jc, &jc.new_fcntl); BOOST_CHECK_EQUAL(lm.is_init(), true); BOOST_CHECK_EQUAL(lm.is_ae(), ae); BOOST_CHECK_EQUAL(lm.ae_max_jfiles(), ae_max_jfiles); BOOST_CHECK_EQUAL(lm.num_jfiles(), num_jfiles); if (num_jfiles) check_pfids_lfids(lm, pfidl, lfidl); else BOOST_CHECK_EQUAL(lm.get_fcntlp(0), (void*)0); } static void finalize(lpmgr& lm) { lm.finalize(); BOOST_CHECK_EQUAL(lm.is_init(), false); BOOST_CHECK_EQUAL(lm.is_ae(), false); BOOST_CHECK_EQUAL(lm.ae_max_jfiles(), u_int16_t(0)); BOOST_CHECK_EQUAL(lm.num_jfiles(), u_int16_t(0)); BOOST_CHECK_EQUAL(lm.get_fcntlp(0), (void*)0); vector res; lm.get_pfid_list(res); BOOST_CHECK_EQUAL(res.size(), u_int16_t(0)); lm.get_lfid_list(res); BOOST_CHECK_EQUAL(res.size(), u_int16_t(0)); } static void insert(lfid_pfid_map& lfm, lpmgr& lm, test_jrnl& jc, const u_int16_t after_lfid, const u_int16_t incr = 1) { flist pfidl; flist lfidl; const u_int16_t num_jfiles = lm.num_jfiles(); lfm.journal_insert(after_lfid, incr); lfm.get_pfid_list(pfidl); lfm.get_lfid_list(lfidl); lm.insert(after_lfid, &jc, &jc.new_fcntl, incr); BOOST_CHECK_EQUAL(lm.num_jfiles(), num_jfiles + incr); lpmgr_test_helper::check_pfids_lfids(lm, pfidl, lfidl); } static void check_ae_max_jfiles(lpmgr& lm, const u_int16_t num_jfiles, const u_int16_t ae_max_jfiles) { bool legal = ae_max_jfiles > num_jfiles || ae_max_jfiles == 0; lm.set_ae(false); BOOST_CHECK(!lm.is_ae()); if (legal) { lm.set_ae_max_jfiles(ae_max_jfiles); BOOST_CHECK_EQUAL(lm.ae_max_jfiles(), ae_max_jfiles); lm.set_ae(true); BOOST_CHECK(lm.is_ae()); BOOST_CHECK_EQUAL(lm.ae_jfiles_rem(), ae_max_jfiles ? ae_max_jfiles - num_jfiles : JRNL_MAX_NUM_FILES - num_jfiles); } else { lm.set_ae_max_jfiles(ae_max_jfiles); BOOST_CHECK_EQUAL(lm.ae_max_jfiles(), ae_max_jfiles); try { lm.set_ae(true); // should raise exception BOOST_ERROR("Auto-expand enabled with out-of-range ae_max_jfiles"); } catch (const jexception& e) { BOOST_CHECK_EQUAL(e.err_code(), jerrno::JERR_LFMGR_BADAEFNUMLIM); } BOOST_CHECK(!lm.is_ae()); BOOST_CHECK_EQUAL(lm.ae_jfiles_rem(), 0); } BOOST_CHECK_EQUAL(lm.ae_max_jfiles(), ae_max_jfiles); } static void check_multiple_initialization_recover(lfid_pfid_map& lfm, test_jrnl& jc, const u_int16_t num_jfiles_arr[][2], const bool init_flag_0, const bool finalize_flag, const bool init_flag_1) { unsigned i_njf = 0; while (num_jfiles_arr[i_njf][0] && num_jfiles_arr[i_njf][1]) // cycle through each entry in num_jfiles_arr { for (unsigned i1_njf = 0; i1_njf <= 1; i1_njf++) // cycle through the two numbers in each entry of num_jfiles_arr { const u_int16_t num_jfiles_0 = num_jfiles_arr[i_njf][i1_njf == 0]; // first number in pair const u_int16_t num_jfiles_1 = num_jfiles_arr[i_njf][i1_njf != 0]; // second number in pair for (unsigned i_ae = 0; i_ae < 4; i_ae++) // cycle through combinations of enabling AE { const bool ae_0 = i_ae & 0x1; // first bit: enable AE on first init const bool ae_1 = i_ae & 0x2; // second bit: enable AE on second init for (unsigned i_aemjf = 0; i_aemjf < 4; i_aemjf++) // cycle through combinations of enabling/disabling ae limit { const u_int16_t ae_max_jfiles_0 = i_aemjf & 0x1 ? 3 * num_jfiles_0 : 0; // max ae files, 0 = disable max const u_int16_t ae_max_jfiles_1 = i_aemjf & 0x2 ? 4 * num_jfiles_1 : 0; // max ae files, 0 = disable max lpmgr lm; // DUT if (init_flag_0) initialize(lm, jc, num_jfiles_0, ae_0, ae_max_jfiles_0); else { prepare_recover(lfm, num_jfiles_0); recover(lfm, lm, jc, ae_1, ae_max_jfiles_0); lfm.destroy_journal(); } if (finalize_flag) finalize(lm); if (init_flag_1) initialize(lm, jc, num_jfiles_1, ae_1, ae_max_jfiles_1); else { prepare_recover(lfm, num_jfiles_1); recover(lfm, lm, jc, ae_1, ae_max_jfiles_1); lfm.destroy_journal(); } } } } i_njf++; } } static void check_insert(lfid_pfid_map& lfm, lpmgr& lm, test_jrnl& jc, const u_int16_t after_lfid, const u_int16_t incr = 1) { const u_int16_t num_jfiles = lm.num_jfiles(); const u_int16_t ae_max_jfiles = lm.ae_max_jfiles(); const u_int16_t effective_ae_max_jfiles = ae_max_jfiles ? ae_max_jfiles : JRNL_MAX_NUM_FILES; BOOST_CHECK_EQUAL(lm.ae_jfiles_rem(), effective_ae_max_jfiles - num_jfiles); bool legal = lm.is_ae() && num_jfiles + incr <= effective_ae_max_jfiles; if (legal) { insert(lfm, lm, jc, after_lfid, incr); BOOST_CHECK_EQUAL(lm.num_jfiles(), num_jfiles + incr); BOOST_CHECK_EQUAL(lm.ae_jfiles_rem(), effective_ae_max_jfiles - num_jfiles - incr); } else { try { insert(lfm, lm, jc, after_lfid, incr); if (lm.is_ae()) BOOST_ERROR("lpmgr::insert() succeeded and exceeded limit"); else BOOST_ERROR("lpmgr::insert() succeeded with auto-expand disabled"); } catch (const jexception& e) { if (lm.is_ae()) BOOST_CHECK_EQUAL(e.err_code(), jerrno::JERR_LFMGR_AEFNUMLIMIT); else BOOST_CHECK_EQUAL(e.err_code(), jerrno::JERR_LFMGR_AEDISABLED); } BOOST_CHECK_EQUAL(lm.num_jfiles(), num_jfiles); BOOST_CHECK_EQUAL(lm.ae_jfiles_rem(), effective_ae_max_jfiles - num_jfiles); } } static void check_limit(lfid_pfid_map& lfm, test_jrnl& jc, const bool ae, const u_int16_t num_jfiles, const u_int16_t ae_max_jfiles) { lpmgr lm; for (unsigned i = 0; i < 2; i++) { if (i) initialize(lfm, lm, jc, num_jfiles, ae, ae_max_jfiles); else { prepare_recover(lfm, num_jfiles); recover(lfm, lm, jc, ae, ae_max_jfiles); } // use up all available files unsigned j = ae_max_jfiles ? ae_max_jfiles : JRNL_MAX_NUM_FILES; while (ae && j > num_jfiles) { const u_int16_t posn = static_cast((lm.num_jfiles() - 1) * ::drand48()); const u_int16_t incr = 1 + static_cast((lm.ae_jfiles_rem() > 4 ? 3 : lm.ae_jfiles_rem() - 1) * ::drand48()); check_insert(lfm, lm, jc, posn, incr); j -= incr; } // these should be over the limit or illegal check_insert(lfm, lm, jc, 0); check_insert(lfm, lm, jc, 2, 2); lfm.destroy_journal(); } } private: static void load_vector(const u_int16_t a[], const size_t n, flist& v) { for (size_t i = 0; i < n; i++) v.push_back(a[i]); } static void load_vector(const flist& a, flist& b) { for (flist_citr i = a.begin(); i < a.end(); i++) b.push_back(*i); } static void vectors_equal(const lpmgr& lm, const u_int16_t a[], const size_t n, const flist& b, const bool pfid_check) { BOOST_CHECK_EQUAL(n, b.size()); for (size_t i = 0; i < n; i++) { BOOST_CHECK_EQUAL(a[i], b[i]); fcntl* fp = lm.get_fcntlp(i); BOOST_CHECK_MESSAGE(fp != (void*)0, "Unexpected void pointer returned by lpmgr::get_fcntlp()"); if (fp) BOOST_CHECK_EQUAL(pfid_check ? fp->pfid() : fp->lfid(), pfid_check ? a[i] : i); } } static void vectors_equal(const lpmgr& lm, const flist& a, const flist& b, const bool pfid_check) { BOOST_CHECK_EQUAL(a.size(), b.size()); for (size_t i = 0; i < a.size(); i++) { BOOST_CHECK_EQUAL(a[i], b[i]); fcntl* fp = lm.get_fcntlp(i); BOOST_CHECK_MESSAGE(fp != (void*)0, "Unexpected void pointer returned by lpmgr::get_fcntlp()"); if (fp) BOOST_CHECK_EQUAL(pfid_check ? fp->pfid() : fp->lfid(), pfid_check ? a[i] : i); } } static void linear_vectors_equal(const lpmgr& lm, const size_t n, const flist& f, const bool pfid_check) { BOOST_CHECK_EQUAL(n, f.size()); for (size_t i = 0; i < n; i++) { BOOST_CHECK_EQUAL(i, f[i]); fcntl* fp = lm.get_fcntlp(i); BOOST_CHECK_MESSAGE(fp != (void*)0, "Unexpected void pointer returned by lpmgr::get_fcntlp()"); if (fp) BOOST_CHECK_EQUAL(pfid_check ? fp->pfid() : fp->lfid(), i); } } }; // === Tests === #ifndef LONG_TEST /* * ============================================== * NORMAL TESTS * This section contains normal "make check" tests * for building/packaging. These are built when * LONG_TEST is _not_ defined. * ============================================== */ /* * Check that after construction, the fcntl array _fcntl_arr is empty and the is_init() function returns false. */ QPID_AUTO_TEST_CASE(default_constructor) { string test_name = get_test_name(test_filename, "default_constructor"); try { lpmgr lm; BOOST_CHECK_EQUAL(lm.is_init(), false); BOOST_CHECK_EQUAL(lm.is_ae(), false); BOOST_CHECK_EQUAL(lm.ae_max_jfiles(), u_int16_t(0)); BOOST_CHECK_EQUAL(lm.num_jfiles(), u_int16_t(0)); BOOST_CHECK_EQUAL(lm.get_fcntlp(0), (void*)0); } catch(const exception& e) { BOOST_FAIL(e.what()); } cout << "done" << endl; } /* * Check that initialize() correctly creates an ordered fcntl array _fcntl_arr. */ QPID_AUTO_TEST_CASE(initialize) { string test_name = get_test_name(test_filename, "initialize"); const u_int16_t num_jfiles = 8; try { jdir::create_dir(test_dir); // Check test dir exists; create it if not test_jrnl_cb cb; test_jrnl jc(test_name, test_dir, test_name, cb); { lpmgr lm; lpmgr_test_helper::initialize(lm, jc, num_jfiles, false, 0); } { lpmgr lm; lpmgr_test_helper::initialize(lm, jc, num_jfiles, true, 0); } { lpmgr lm; lpmgr_test_helper::initialize(lm, jc, num_jfiles, true, 5 * num_jfiles); } } catch(const exception& e) { BOOST_FAIL(e.what()); } cout << "done" << endl; } /* * Check that recover() correctly sets up the specified pfid list order. */ QPID_AUTO_TEST_CASE(recover) { string test_name = get_test_name(test_filename, "recover"); ::srand48(1); // init random gen for repeatable tests when using lpmgr_test_helper::prepare_recover() try { test_jrnl_cb cb; test_jrnl jc(test_name, test_dir, test_name, cb); lfid_pfid_map lfm(test_name, test_name); { lpmgr lm; lpmgr_test_helper::prepare_recover(lfm, 8); lpmgr_test_helper::recover(lfm, lm, jc, false, 0); lfm.destroy_journal(); } { lpmgr lm; lpmgr_test_helper::prepare_recover(lfm, 8); lpmgr_test_helper::recover(lfm, lm, jc, true, 0); lfm.destroy_journal(); } { lpmgr lm; lpmgr_test_helper::prepare_recover(lfm, 8); lpmgr_test_helper::recover(lfm, lm, jc, true, 5 * lfm.size()); lfm.destroy_journal(); } } catch(const exception& e) { BOOST_FAIL(e.what()); } cout << "done" << endl; } /* * Check that finalize() after an initialize() empties _fcntl_arr and that afterwards is_init() returns false. */ QPID_AUTO_TEST_CASE(initialize_finalize) { string test_name = get_test_name(test_filename, "initialize_finalize"); const u_int16_t num_jfiles = 8; try { jdir::create_dir(test_dir); // Check test dir exists; create it if not test_jrnl_cb cb; test_jrnl jc(test_name, test_dir, test_name, cb); { lpmgr lm; lpmgr_test_helper::initialize(lm, jc, num_jfiles, false, 0); lpmgr_test_helper::finalize(lm); } { lpmgr lm; lpmgr_test_helper::initialize(lm, jc, num_jfiles, true, 0); lpmgr_test_helper::finalize(lm); } { lpmgr lm; lpmgr_test_helper::initialize(lm, jc, num_jfiles, true, 5 * num_jfiles); lpmgr_test_helper::finalize(lm); } } catch(const exception& e) { BOOST_FAIL(e.what()); } cout << "done" << endl; } /* * Check that finalize() after a recover() empties _fcntl_arr and that afterwards is_init() returns false. */ QPID_AUTO_TEST_CASE(recover_finalize) { string test_name = get_test_name(test_filename, "recover_finalize"); const u_int16_t num_jfiles = 8; ::srand48(1); // init random gen for repeatable tests when using lpmgr_test_helper::prepare_recover() try { test_jrnl_cb cb; test_jrnl jc(test_name, test_dir, test_name, cb); lfid_pfid_map lfm(test_name, test_name); { lpmgr lm; lpmgr_test_helper::prepare_recover(lfm, num_jfiles); lpmgr_test_helper::recover(lfm, lm, jc, false, 0); lpmgr_test_helper::finalize(lm); lfm.destroy_journal(); } { lpmgr lm; lpmgr_test_helper::prepare_recover(lfm, num_jfiles); lpmgr_test_helper::recover(lfm, lm, jc, true, 0); lpmgr_test_helper::finalize(lm); lfm.destroy_journal(); } { lpmgr lm; lpmgr_test_helper::prepare_recover(lfm, num_jfiles); lpmgr_test_helper::recover(lfm, lm, jc, true, 5 * lfm.size()); lpmgr_test_helper::finalize(lm); lfm.destroy_journal(); } } catch(const exception& e) { BOOST_FAIL(e.what()); } cout << "done" << endl; } /* * Check that 0 and/or null and other extreme/boundary parameters behave as expected. */ QPID_AUTO_TEST_CASE(zero_null_params) { string test_name = get_test_name(test_filename, "zero_null_params"); const u_int16_t num_jfiles = 8; try { test_jrnl_cb cb; test_jrnl jc(test_name, test_dir, test_name, cb); lfid_pfid_map lfm(test_name, test_name); lpmgr lm; lpmgr_test_helper::initialize(lfm, lm, jc, num_jfiles, true, 0); // Check that inserting 0 files works ok lpmgr_test_helper::insert(lfm, lm, jc, 0, 0); lpmgr_test_helper::insert(lfm, lm, jc, 2, 0); lpmgr_test_helper::insert(lfm, lm, jc, num_jfiles - 1, 0); } catch(const exception& e) { BOOST_FAIL(e.what()); } cout << "done" << endl; } /* * Check that initialize()/recover() works correctly after a previous initialize()/recover() with/without an intervening * finalize(). */ QPID_AUTO_TEST_CASE(multiple_initialization_recover) { string test_name = get_test_name(test_filename, "multiple_initialization_recover"); ::srand48(1); // init random gen for repeatable tests when using lpmgr_test_helper::prepare_recover() // Set combinations of value pairs to be used for number of journal files in first and second init u_int16_t num_jfiles_arr[][2] = {{8, 12}, {4, 7}, {0, 0}}; // end with zeros try { test_jrnl_cb cb; test_jrnl jc(test_name, test_dir, test_name, cb); lfid_pfid_map lfm(test_name, test_name); for (unsigned p = 0; p < 8; p++) { const bool i_0 = p & 0x01; // first bit const bool i_1 = p & 0x02; // second bit const bool f = p & 0x04; // third bit lpmgr_test_helper::check_multiple_initialization_recover(lfm, jc, num_jfiles_arr, i_0, f, i_1); } } catch(const exception& e) { BOOST_FAIL(e.what()); } cout << "done" << endl; } /* * Check that insert() works correctly after initialize() and shifts the pfid sequence beyond the insert point correctly: * * The following sequence is tested: * initialize 4 pfids=[0,1,2,3] lfids=[0,1,2,3] * insert 1 after lfid 0 pfids=[0,4,1,2,3] lfids=[0,2,3,4,1] * insert 2 after lfid 2 pfids=[0,4,1,5,6,2,3] lfids=[0,2,5,6,1,3,4] * insert 1 after lfid 6 pfids=[0,4,1,5,6,2,3,7] lfids=[0,2,5,6,1,3,4,7] * issert 1 after lfid 3 pfids=[0,4,1,5,8,6,2,3,7] lfids=[0,2,6,7,1,3,5,8,4] */ QPID_AUTO_TEST_CASE(initialize_insert) { string test_name = get_test_name(test_filename, "initialize_insert"); const u_int16_t initial_num_jfiles = 8; try { jdir::create_dir(test_dir); // Check test dir exists; create it if not test_jrnl_cb cb; test_jrnl jc(test_name, test_dir, test_name, cb); lfid_pfid_map lfm(test_name, test_name); lpmgr lm; lpmgr_test_helper::initialize(lfm, lm, jc, initial_num_jfiles, true, 0); lpmgr_test_helper::insert(lfm, lm, jc, 0); lpmgr_test_helper::insert(lfm, lm, jc, 2, 2); lpmgr_test_helper::insert(lfm, lm, jc, 6); lpmgr_test_helper::insert(lfm, lm, jc, 3); } catch(const exception& e) { BOOST_FAIL(e.what()); } cout << "done" << endl; } /* * Check that insert() works correctly after recover() and shifts the pfid sequence beyond the insert point correctly: * * The following sequence is tested: * recover 4 pfids=[0,2,3,1] lfids=[0,3,1,2] * insert 1 after lfid 0 pfids=[0,4,2,3,1] lfids=[0,4,2,3,1] * insert 2 after lfid 2 pfids=[0,4,2,5,6,3,1] lfids=[0,6,2,5,1,3,4] * insert 1 after lfid 6 pfids=[0,4,2,5,6,3,1,7] lfids=[0,6,2,5,1,3,4,7] * issert 1 after lfid 3 pfids=[0,4,2,5,8,6,3,1,7] lfids=[0,7,2,6,1,3,5,8,4] */ QPID_AUTO_TEST_CASE(recover_insert) { string test_name = get_test_name(test_filename, "recover_insert"); const u_int16_t initial_num_jfiles = 4; ::srand48(1); // init random gen for repeatable tests when using lpmgr_test_helper::prepare_recover() try { jdir::create_dir(test_dir); // Check test dir exists; create it if not test_jrnl_cb cb; test_jrnl jc(test_name, test_dir, test_name, cb); lfid_pfid_map lfm(test_name, test_name); lpmgr lm; lpmgr_test_helper::prepare_recover(lfm, initial_num_jfiles); lpmgr_test_helper::recover(lfm, lm, jc, true, 0); lpmgr_test_helper::insert(lfm, lm, jc, 0); lpmgr_test_helper::insert(lfm, lm, jc, 2, 2); lpmgr_test_helper::insert(lfm, lm, jc, 6); lpmgr_test_helper::insert(lfm, lm, jc, 3); } catch(const exception& e) { BOOST_FAIL(e.what()); } cout << "done" << endl; } /* * Check that illegal ae parameter combinations are caught and result in an exception being thrown. */ QPID_AUTO_TEST_CASE(ae_parameters) { string test_name = get_test_name(test_filename, "ae_parameters"); ::srand48(1); // init random gen for repeatable tests when using lpmgr_test_helper::prepare_recover() try { jdir::create_dir(test_dir); // Check test dir exists; create it if not test_jrnl_cb cb; test_jrnl jc(test_name, test_dir, test_name, cb); lfid_pfid_map lfm(test_name, test_name); const u_int16_t num_jfiles = 8; lpmgr lm; for (unsigned i = 0; i < 2; i++) { if (i) lpmgr_test_helper::initialize(lfm, lm, jc, num_jfiles, false, 0); else { lpmgr_test_helper::prepare_recover(lfm, num_jfiles); lpmgr_test_helper::recover(lfm, lm, jc, false, 0); } lpmgr_test_helper::check_ae_max_jfiles(lm, num_jfiles, num_jfiles - 2); lpmgr_test_helper::check_ae_max_jfiles(lm, num_jfiles, 0); lpmgr_test_helper::check_ae_max_jfiles(lm, num_jfiles, 2 * num_jfiles); lpmgr_test_helper::check_ae_max_jfiles(lm, num_jfiles, num_jfiles); lfm.destroy_journal(); } } catch(const exception& e) { BOOST_FAIL(e.what()); } cout << "done" << endl; } /* * Check that initialized or recovered journals with auto-expand disabled will not allow either inserts or appends. */ QPID_AUTO_TEST_CASE(ae_disabled) { string test_name = get_test_name(test_filename, "ae_disabled"); ::srand48(1); // init random gen for repeatable tests when using lpmgr_test_helper::prepare_recover() try { jdir::create_dir(test_dir); // Check test dir exists; create it if not test_jrnl_cb cb; test_jrnl jc(test_name, test_dir, test_name, cb); lfid_pfid_map lfm(test_name, test_name); lpmgr_test_helper::check_limit(lfm, jc, false, 8, 0); } catch(const exception& e) { BOOST_FAIL(e.what()); } cout << "done" << endl; } /* * Check that initialized or recovered journals with auto-expand enabled and a file limit set will enforce the correct * limits on inserts and appends. */ QPID_AUTO_TEST_CASE(ae_enabled_limit) { string test_name = get_test_name(test_filename, "ae_enabled_limit"); ::srand48(1); // init random gen for repeatable tests when using lpmgr_test_helper::prepare_recover() try { jdir::create_dir(test_dir); // Check test dir exists; create it if not test_jrnl_cb cb; test_jrnl jc(test_name, test_dir, test_name, cb); lfid_pfid_map lfm(test_name, test_name); lpmgr_test_helper::check_limit(lfm, jc, true, 8, 32); } catch(const exception& e) { BOOST_FAIL(e.what()); } cout << "done" << endl; } /* * Check that initialized or recovered journals with auto-expand enabled and no file limit set (0) will allow inserts and * appends up to the file limit JRNL_MAX_NUM_FILES. */ QPID_AUTO_TEST_CASE(ae_enabled_unlimited) { string test_name = get_test_name(test_filename, "ae_enabled_unlimited"); ::srand48(1); // init random gen for repeatable tests when using lpmgr_test_helper::prepare_recover() try { jdir::create_dir(test_dir); // Check test dir exists; create it if not test_jrnl_cb cb; test_jrnl jc(test_name, test_dir, test_name, cb); lfid_pfid_map lfm(test_name, test_name); lpmgr_test_helper::check_limit(lfm, jc, true, 8, 0); } catch(const exception& e) { BOOST_FAIL(e.what()); } cout << "done" << endl; } #else /* * ============================================== * LONG TESTS * This section contains long tests and soak tests, * and are run using target check-long (ie "make * check-long"). These are built when LONG_TEST is * defined. * ============================================== */ /* * Tests randomized combinations of initialization/recovery, initial size, number, size and location of inserts. * * To reproduce a specific test, comment out the get_seed() statement and uncomment the literal below, adjusting the seed * value to that required. */ QPID_AUTO_TEST_CASE(randomized_tests) { string test_name = get_test_name(test_filename, "randomized_tests"); const long seed = get_seed(); // const long seed = 0x2d9b69d32; cout << "seed=0x" << hex << seed << dec << " " << flush; ::srand48(seed); lfid_pfid_map lfm(test_name, test_name); flist pfidl; flist lfidl; rcvdat rd; u_int16_t curr_ae_max_jfiles = 0; jdir::create_dir(test_dir); // Check test dir exists; create it if not for (int test_num = 0; test_num < 250; test_num++) { test_jrnl_cb cb; test_jrnl jc(test_name, test_dir, test_name, cb); lpmgr lm; // 50% chance of recovery except first run and if there is still ae space left const bool recover_flag = test_num > 0 && curr_ae_max_jfiles > lfm.size() && 2.0 * ::drand48() < 1.0; if (recover_flag) { // Recover from previous iteration lfm.get_pfid_list(pfidl); lfm.get_lfid_list(lfidl); lfm.write_journal(true, curr_ae_max_jfiles, JFSIZE_SBLKS); lpmgr_test_helper::rcvdat_init(rd, pfidl, true, curr_ae_max_jfiles); lm.recover(rd, &jc, &jc.new_fcntl); lpmgr_test_helper::check_pfids_lfids(lm, pfidl, lfidl); } else { // Initialize from scratch const u_int16_t num_jfiles = 4 + u_int16_t(21.0 * ::drand48()); // size: 4 - 25 files curr_ae_max_jfiles = u_int16_t(4 * num_jfiles * ::drand48()); // size: 0 - 100 files if (curr_ae_max_jfiles > JRNL_MAX_NUM_FILES) curr_ae_max_jfiles = JRNL_MAX_NUM_FILES; else if (curr_ae_max_jfiles <= num_jfiles) curr_ae_max_jfiles = 0; lfm.destroy_journal(); lfm.journal_create(num_jfiles, num_jfiles); lfm.get_pfid_list(pfidl); lfm.get_lfid_list(lfidl); lm.initialize(num_jfiles, true, curr_ae_max_jfiles, &jc, &jc.new_fcntl); lpmgr_test_helper::check_linear_pfids_lfids(lm, num_jfiles); } // Loop to insert pfids const int num_inserts = 1 + int(lfm.size() * ::drand48()); for (int i = 0; i < num_inserts; i++) { const u_int16_t size = lm.num_jfiles(); const u_int16_t after_lfid = u_int16_t(1.0 * size * ::drand48()); const u_int16_t num_jfiles = 1 + u_int16_t(4.0 * ::drand48()); const bool legal = lm.ae_max_jfiles() ? size + num_jfiles <= lm.ae_max_jfiles() : size + num_jfiles <= JRNL_MAX_NUM_FILES; if (legal) { lfm.journal_insert(after_lfid, num_jfiles); lfm.get_pfid_list(pfidl); lfm.get_lfid_list(lfidl); lm.insert(after_lfid, &jc, &jc.new_fcntl, num_jfiles); lpmgr_test_helper::check_pfids_lfids(lm, pfidl, lfidl); } else { try { lm.insert(after_lfid, &jc, &jc.new_fcntl, num_jfiles); BOOST_FAIL("lpmgr::insert() succeeded and exceeded limit"); } catch (const jexception& e) { BOOST_CHECK_EQUAL(e.err_code(), jerrno::JERR_LFMGR_AEFNUMLIMIT); break; // no more inserts... } } } lm.finalize(); BOOST_CHECK_EQUAL(lm.is_init(), false); BOOST_CHECK_EQUAL(lm.num_jfiles(), u_int16_t(0)); BOOST_CHECK_EQUAL(lm.get_fcntlp(0), (void*)0); } cout << "done" << endl; } #endif QPID_AUTO_TEST_SUITE_END() qpid-cpp-store-debian-0.16/tests/jrnl/run-journal-tests0000755000176300017630000000405511307246366022220 0ustar cajuscajus#!/bin/bash # Copyright (c) 2007, 2008 Red Hat, Inc. # # This file is part of the Qpid async store library msgstore.so. # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 # USA # # The GNU Lesser General Public License is available in the file COPYING. if test x${TMP_DATA_DIR} == x; then export TMP_DATA_DIR=/tmp fi fail=0 num_jrnls=3 # Run jtt using default test set echo echo "===== Mode 1: New journal instance, no recover =====" jtt/jtt --analyzer ../../tools/store_chk --jrnl-dir ${TMP_DATA_DIR} --csv jtt/jtt.csv --format-chk --num-jrnls ${num_jrnls} || fail=1 rm -rf ${TMP_DATA_DIR}/test_0* echo echo "===== Mode 2: Re-use journal instance, no recover =====" jtt/jtt --analyzer ../../tools/store_chk --jrnl-dir ${TMP_DATA_DIR} --csv jtt/jtt.csv --reuse-instance --format-chk --num-jrnls ${num_jrnls} || fail=1 rm -rf ${TMP_DATA_DIR}/test_0* echo echo "===== Mode 3: New journal instance, recover previous test journal =====" jtt/jtt --analyzer ../../tools/store_chk --jrnl-dir ${TMP_DATA_DIR} --csv jtt/jtt.csv --recover-mode --format-chk --num-jrnls ${num_jrnls} || fail=1 rm -rf ${TMP_DATA_DIR}/test_0* echo echo "===== Mode 4: Re-use journal instance, recover previous test journal =====" jtt/jtt --analyzer ../../tools/store_chk --jrnl-dir ${TMP_DATA_DIR} --csv jtt/jtt.csv --reuse-instance --recover-mode --format-chk --num-jrnls ${num_jrnls} || fail=1 rm -rf ${TMP_DATA_DIR}/test_0* echo exit $fail qpid-cpp-store-debian-0.16/tests/jrnl/_st_helper_fns.hpp0000644000176300017630000011067411650071721022356 0ustar cajuscajus/* * Copyright (c) 2008, 2009 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ // NOTE: This file is included in _st_*.cpp files inside the QPID_AUTO_TEST_SUITE() // definition. #define MAX_AIO_SLEEPS 500 #define AIO_SLEEP_TIME 1000 #define NUM_TEST_JFILES 4 #define NUM_DEFAULT_JFILES 8 #define JRNL_DEFAULT_FSIZE 24 // Multiples of JRNL_RMGR_PAGE_SIZE #define TEST_JFSIZE_SBLKS 128 #define DEFAULT_JFSIZE_SBLKS (JRNL_DEFAULT_FSIZE * JRNL_RMGR_PAGE_SIZE) #define NUM_MSGS 5 #define MSG_REC_SIZE_DBLKS 2 #define MSG_SIZE (MSG_REC_SIZE_DBLKS * JRNL_DBLK_SIZE) - sizeof(enq_hdr) - sizeof(rec_tail) #define LARGE_MSG_REC_SIZE_DBLKS (JRNL_SBLK_SIZE * JRNL_RMGR_PAGE_SIZE) #define LARGE_MSG_SIZE (LARGE_MSG_REC_SIZE_DBLKS * JRNL_DBLK_SIZE) - sizeof(enq_hdr) - sizeof(rec_tail) #define XID_SIZE 64 #define XLARGE_MSG_RATIO (1.0 * LARGE_MSG_REC_SIZE / JRNL_DBLK_SIZE / JRNL_SBLK_SIZE / JRNL_RMGR_PAGE_SIZE) #define XLARGE_MSG_THRESHOLD (int)(JRNL_DEFAULT_FSIZE * NUM_DEFAULT_JFILES * JRNL_ENQ_THRESHOLD / 100 / LARGE_MSG_RATIO) #define NUM_JFILES 4 #define JFSIZE_SBLKS 128 const char* tdp = getenv("TMP_DATA_DIR"); const string test_dir(tdp && strlen(tdp) > 0 ? string(tdp) + "/" + test_filename : "/tmp/jrnl_test"); class test_dtok : public data_tok { private: bool flag; public: test_dtok() : data_tok(), flag(false) {} virtual ~test_dtok() {} bool done() { if (flag || _wstate == NONE) return true; else { flag = true; return false; } } }; class test_jrnl_cb : public aio_callback { virtual void wr_aio_cb(std::vector& dtokl) { for (std::vector::const_iterator i=dtokl.begin(); i!=dtokl.end(); i++) { test_dtok* dtp = static_cast(*i); if (dtp->done()) delete dtp; } } virtual void rd_aio_cb(std::vector& /*pil*/) {} }; class test_jrnl : public jcntl { test_jrnl_cb* cb; public: test_jrnl(const std::string& jid, const std::string& jdir, const std::string& base_filename, test_jrnl_cb& cb0) : jcntl(jid, jdir, base_filename), cb(&cb0) {} virtual ~test_jrnl() {} void initialize(const u_int16_t num_jfiles, const bool ae, const u_int16_t ae_max_jfiles, const u_int32_t jfsize_sblks) { jcntl::initialize(num_jfiles, ae, ae_max_jfiles, jfsize_sblks, JRNL_WMGR_DEF_PAGES, JRNL_WMGR_DEF_PAGE_SIZE, cb); _jdir.create_dir(); } void recover(const u_int16_t num_jfiles, const bool ae, const u_int16_t ae_max_jfiles, const u_int32_t jfsize_sblks, vector* txn_list, u_int64_t& highest_rid) { jcntl::recover(num_jfiles, ae, ae_max_jfiles, jfsize_sblks, JRNL_WMGR_DEF_PAGES, JRNL_WMGR_DEF_PAGE_SIZE, cb, txn_list, highest_rid); } }; /* * This class is for testing recover functionality by maintaining an internal lfid-pfid map, then creating physical * journal file stubs (just the fhdr section of the journal) and jinf file. This allows the recover functionality (which * analyzes these components to determine recover order). * * First set up a map or "blueprint" of what the journal should look like for recovery, then have the class create the * physical files. The jinf object under test then reads and analyzes the created journal, and it's analysis is checked * against what is expected. * * General usage pattern: * 1. Create instance of lfid_pfid_map. * 2. Call lfid_pfid_map::journal_create() to simulate initial journal creation. * 3. (optional) Call lfid_pfid_map::journal_insert() one or more times to simulate the addition of journal files. * 4. Call lfid_pfid_map::write_journal() to create dummy journal files (files containing only file headers) * 5. Create and initialize the jinf object under test * 6. Call jinf::analyze() to determine the pfid order - and thus also first and last lids * 7. Call lfid_pfid_map::check_analysis() to check the conclusions of the analysis * 8. Call lfid_pfid_map::destroy_journal() to delete the journal files and reset the lfid_pfid_map object. * 9. (optional) Back to step 2 for more tests * * See the individual methods below for more details. */ class lfid_pfid_map { public: typedef pair lppair; // Used for loading the map typedef multimap lpmap; // Stores the journal "plan" before it is created on-disk typedef lpmap::const_iterator lpmap_citr; // General purpose iterator typedef pair lpmap_range; // Range of values returned by multimap's equal_range() fn private: string _jid; // Journal id string _base_filename; // Base filename lpmap _map; // Stores the journal "blueprint" before it is created on-disk u_int16_t _num_used_files; // number of files which contain jorunals u_int16_t _oldest_lfid; // lfid where owi flips; always 0 if !_full u_int16_t _last_pfid; // last pfid (ie last file added) public: lfid_pfid_map(const string& jid, const string& base_filename) : _jid(jid), _base_filename(base_filename), _num_used_files(0), _oldest_lfid(0), _last_pfid(0) {} virtual ~lfid_pfid_map() {} // Mainly used for debugging void print() { int cnt = 0; for (lpmap_citr i=_map.begin(); i!=_map.end(); i++, cnt++) { const file_hdr fh = i->second; cout << " " << cnt << ": owi=" << (fh.get_owi()?"t":"f") << hex << " frid=0x" << fh._rid; cout << " pfid=0x" << fh._pfid << " lfid=0x" << fh._lfid << " fro=0x" << fh._fro << dec << endl; } } std::size_t size() { return _map.size(); } /* * Method journal_create(): Used to simulate the initial creation of a journal before file insertions * take place. * * num_jfiles: The initial journal file count. * num_used_jfiles: If this number is less than num_jfiles, it indicates a clean journal that has not yet * completed its first rotation, and some files are empty (ie all null). The first * num_used_jfiles will contain file headers, the remainder will be blank. * oldest_lfid: The lfid (==pfid, see note 1 below) at which the owi flag flips. During normal operation, * each time the journal rotates back to file 0, a flag (called the overwrite indicator or owi) * is flipped. This flag is saved in the file header. During recovery, if scanning from logical * file 0 upwards, the file at which this flag reverses from its value in file 0 is the file * that was to have been overwritten next, and is thus the "oldest" file. Recovery analysis must * start with this file. oldest_lfid sets the file at which this flag will flip value for the * simulated recovery analysis. Note that this will be ignored if num_used_jfiles < num_jfiles, * as it is not possible for an overwrite to have occurred if not all the files have been used. * first_owi: Sets the value of the owi flag in file 0. If set to false, then the flip will be found with * a true flag (and visa versa). * * NOTES: * 1. By definition, the lfids and pfids coincide for a journal containing no inserted files. Thus pfid == lfid * for all journals created after using initial_journal_create() alone. * 2. By definition, if a journal is not full (num_used_jfiles < num_jfiles), then all owi flags for those files * that are used must be the same. It is not possible for an overwrite situation to arise if a journal is not * full. * 3. This function acts on map _map only, and does not create any test files. Call write_journal() to do that. * 4. This function must be called on a clean test object or on one where the previous test data has been * cleared by calling journal_destroy(). Running this function more than once on existing data will * result in invalid journals which cannot be recovered. */ void journal_create(const u_int16_t num_jfiles, // Total number of files const u_int16_t num_used_jfiles, // Number of used files, rest empty at end const u_int16_t oldest_lfid = 0, // Fid where owi reverses const u_int16_t bad_lfid = 0, // Fid where owi reverses again (must be > oldest_lifd), // used for testing bad owi detection const bool first_owi = false) // Value of first owi flag (ie pfid=0) { const bool full = num_used_jfiles == num_jfiles; bool owi = first_owi; _oldest_lfid = full ? oldest_lfid : 0; for (u_int16_t lfid = 0; lfid < num_jfiles; lfid++) { const u_int16_t pfid = lfid; file_hdr fh; if (pfid < num_used_jfiles) { _num_used_files = num_used_jfiles; /* * Invert the owi flag from its current value (initially given by first_owi param) only if: * 1. The journal is full (ie all files are used) * AND * 2. oldest_lfid param is non-zero (this is default, but lfid 0 being inverted is logically * inconsistent with first_owi parameter being present) * AND * 3. Either: * * current lfid == oldest_lfid (ie we are preparing the oldest lfid) * OR * * current lfid == bad_lfid AND bad_lfid > oldest (ie we are past the oldest and preparing the * bad lfid) */ if (full && oldest_lfid > 0 && (lfid == oldest_lfid || (bad_lfid > oldest_lfid && lfid == bad_lfid))) owi = !owi; const u_int64_t frid = u_int64_t(random()); init_fhdr(fh, frid, pfid, lfid, owi); } _map.insert(lppair(lfid, fh)); } } /* * Method journal_insert(): Used to simulate the insertion of journal files into an existing journal. * * after_lfid: The logical file id (lfid) after which the new file is to be inserted. * num_files: The number of files to be inserted. * adjust_lids: Flag indicating that the lids of files _following_ the inserted files are to be adjusted upwards * by the number of inserted files. Not doing so simulates a recovery immediately after insertion * but before the following files are overwritten with their new lids. If this is set false, then: * a) after_lfid MUST be the most recent file (_oldest_lfid-1 ie last lfid before owi changes). * b) This call must be the last insert call. * * NOTES: * 1. It is not possible to insert before lfid/pfid 0; thus these are always coincidental. This operation is * logically equivalent to inserting after the last lfid, which is possible. * 2. It is not possible to insert into a journal that is not full. Doing so will result in an unrecoverable * journal (one that is logically inconsistent that can never occur in reality). * 3. If a journal is stopped/interrupted immediately after a file insertion, there could be duplicate lids in * play at recovery, as the following file lids in their headers are only overwritten when the file is * eventually written to during normal operation. The owi flags, however, are used to determine which of the * ambiguous lids are the inserted files. * 4. This function acts on map _map only, and does not create any test files. Call write_journal() to do that. */ void journal_insert(const u_int16_t after_lfid, // Insert files after this lfid const u_int16_t num_files = 1, // Number of files to insert const bool adjust_lids = true) // Adjust lids following inserted files { if (num_files == 0) return; _num_used_files += num_files; const u_int16_t num_jfiles_before_append = _map.size(); lpmap_citr i = _map.find(after_lfid); if (i == _map.end()) BOOST_FAIL("Unable to find lfid=" << after_lfid << " in map."); const file_hdr fh_before = (*i).second; // Move overlapping lids (if req'd) if (adjust_lids && after_lfid < num_jfiles_before_append - 1) { for (u_int16_t lfid = num_jfiles_before_append - 1; lfid > after_lfid; lfid--) { lpmap_citr itr = _map.find(lfid); if (itr == _map.end()) BOOST_FAIL("Unable to find lfid=" << after_lfid << " in map."); file_hdr fh = itr->second; _map.erase(lfid); fh._lfid += num_files; if (lfid == _oldest_lfid) _oldest_lfid += num_files; _map.insert(lppair(fh._lfid, fh)); } } // Add new file headers u_int16_t pfid = num_jfiles_before_append; u_int16_t lfid = after_lfid + 1; while (pfid < num_jfiles_before_append + num_files) { const u_int64_t frid = u_int64_t(random()); const size_t fro = 0x200; const file_hdr fh(RHM_JDAT_FILE_MAGIC, RHM_JDAT_VERSION, frid, pfid, lfid, fro, fh_before.get_owi(), true); _map.insert(lppair(lfid, fh)); _last_pfid = pfid; pfid++; lfid++; } } /* * Get the list of pfids in the map in order of lfid. The pfids are appended to the supplied vector. Only * as many headers as are in the map are appended. * NOTE: will clear any contents from supplied vector before appending pfid list. */ void get_pfid_list(vector& pfid_list) { pfid_list.clear(); for (lpmap_citr i = _map.begin(); i != _map.end(); i++) pfid_list.push_back(i->second._pfid); } /* * Get the list of lfids in the map. The lfids are appended to the supplied vector in the order they appear * in the map (which is not necessarily the natural or sorted order). * NOTE: will clear any contents from supplied vector before appending lfid list. */ void get_lfid_list(vector& lfid_list) { lfid_list.clear(); lfid_list.assign(_map.size(), 0); for (lpmap_citr i = _map.begin(); i != _map.end(); i++) lfid_list[i->second._pfid] = i->first; } /* * Method check_analysis(): Used to check the result of the test jinf object analysis by comparing the pfid order * array it produces against the internal map. * * ji: A ref to the jinf object under test. */ void check_analysis(jinf& ji) // jinf object under test after analyze() has been called { BOOST_CHECK_EQUAL(ji.get_first_pfid(), get_first_pfid()); BOOST_CHECK_EQUAL(ji.get_last_pfid(), get_last_pfid()); jinf::pfid_list& pfidl = ji.get_pfid_list(); const u_int16_t num_jfiles = _map.size(); const bool all_used = _num_used_files == num_jfiles; BOOST_CHECK_EQUAL(pfidl.size(), _num_used_files); const u_int16_t lfid_start = all_used ? _oldest_lfid : 0; // Because a simulated failure would leave lfid dups in map and last_fid would not exist in map in this // case, we must find lfid_stop via pfid instead. Search for pfid == num_files. lpmap_citr itr = _map.begin(); while (itr != _map.end() && itr->second._pfid != _num_used_files - 1) itr++; if (itr == _map.end()) BOOST_FAIL("check(): Unable to find pfid=" << (_num_used_files - 1) << " in map."); const u_int16_t lfid_stop = itr->second._lfid; std::size_t fidl_index = 0; for (u_int16_t lfid_cnt = lfid_start; lfid_cnt < lfid_stop; lfid_cnt++, fidl_index++) { const u_int16_t lfid = lfid_cnt % num_jfiles; lpmap_citr itr = _map.find(lfid); if (itr == _map.end()) BOOST_FAIL("check(): Unable to find lfid=" << lfid << " in map."); BOOST_CHECK_EQUAL(itr->second._pfid, pfidl[fidl_index]); } } /* * Method get_pfid(): Look up a pfid from a known lfid. */ u_int16_t get_pfid(const u_int16_t lfid, const bool initial_owi = false) { switch (_map.count(lfid)) { case 1: return _map.find(lfid)->second._pfid; case 2: for (lpmap_citr itr = _map.lower_bound(lfid); itr != _map.upper_bound(lfid); itr++) { if (itr->second.get_owi() != initial_owi) return itr->second._pfid; } default:; } BOOST_FAIL("get_pfid(): lfid=" << lfid << " not found in map."); return 0xffff; } /* * Method get_first_pfid(): Look up the first (oldest, or next-to-be-overwritten) pfid in the analysis sequence. */ u_int16_t get_first_pfid() { return get_pfid(_oldest_lfid); } /* * Method get_last_pfid(): Look up the last (newest, or most recently written) pfid in the analysis sequence. */ u_int16_t get_last_pfid() { u_int16_t flfid = 0; if (_num_used_files == _map.size()) // journal full? { if (_oldest_lfid) { // if failed insert, cycle past duplicate lids while (_map.count(_oldest_lfid) == 2) _oldest_lfid++; while (_map.find(_oldest_lfid) != _map.end() && _map.find(_oldest_lfid)->second.get_owi() == false) _oldest_lfid++; flfid = _oldest_lfid - 1; } else flfid = _map.size() - 1; } else flfid = _num_used_files - 1; return get_pfid(flfid, true); } /* * Method write_journal(): Used to create the dummy journal files from the built-up map created by calling * initial_journal_create() and optionally journal_append() one or more times. Since the jinf object reads the * jinf file and the file headers only, the create object creates a dummy journal file containing only a file * header (512 bytes each) and a single jinf file which contains the journal metadata required for recovery * analysis. */ void write_journal(const bool ae, const u_int16_t ae_max_jfiles, const u_int32_t fsize_sblks = JFSIZE_SBLKS) { create_jinf(ae, ae_max_jfiles); u_int16_t pfid = 0; for (lpmap_citr itr = _map.begin(); itr != _map.end(); itr++, pfid++) { if (itr->second._pfid == 0 && itr->second._magic == 0) // empty header, use pfid counter instead create_journal_file(pfid, itr->second, _base_filename, fsize_sblks); else create_journal_file(itr->second._pfid, itr->second, _base_filename, fsize_sblks); } } /* * Method destroy_journal(): Destroy the files created by create_journal() and reset the lfid_pfid_map test * object. A new test may be started using the same lfid_pfid_map test object once this call has been made. */ void destroy_journal() { for (u_int16_t pfid = 0; pfid < _map.size(); pfid++) { string fn = create_journal_filename(pfid, _base_filename); BOOST_WARN_MESSAGE(::unlink(fn.c_str()) == 0, "destroy_journal(): Failed to remove file " << fn); } clean_journal_info_file(_base_filename); _map.clear(); _num_used_files = 0; _oldest_lfid = 0; _last_pfid = 0; } /* * Method create_new_jinf(): This static call creates a default jinf file only. This is used to test the read * constructor of a jinf test object which reads a jinf file at instantiation. */ static void create_new_jinf(const string jid, const string base_filename, const bool ae) { if (jdir::exists(test_dir)) jdir::delete_dir(test_dir); create_jinf(NUM_JFILES, ae, (ae ? 5 * NUM_JFILES : 0), jid, base_filename); } /* * Method clean_journal_info_file(): This static method deletes only a jinf file without harming any other * journal file or its directory. This is used to clear those tests which rely only on the existence of a * jinf file. */ static void clean_journal_info_file(const string base_filename) { stringstream fn; fn << test_dir << "/" << base_filename << "." << JRNL_INFO_EXTENSION; BOOST_WARN_MESSAGE(::unlink(fn.str().c_str()) == 0, "clean_journal_info_file(): Failed to remove file " << fn.str()); } static string create_journal_filename(const u_int16_t pfid, const string base_filename) { stringstream fn; fn << test_dir << "/" << base_filename << "."; fn << setfill('0') << hex << setw(4) << pfid << "." << JRNL_DATA_EXTENSION; return fn.str(); } private: static void init_fhdr(file_hdr& fh, const u_int64_t frid, const u_int16_t pfid, const u_int16_t lfid, const bool owi, const bool no_enq = false) { fh._magic = RHM_JDAT_FILE_MAGIC; fh._version = RHM_JDAT_VERSION; #if defined(JRNL_BIG_ENDIAN) fh._eflag = RHM_BENDIAN_FLAG; #else fh._eflag = RHM_LENDIAN_FLAG; #endif fh._uflag = owi ? rec_hdr::HDR_OVERWRITE_INDICATOR_MASK : 0; fh._rid = frid; fh._pfid = pfid; fh._lfid = lfid; fh._fro = no_enq ? 0 : 0x200; timespec ts; ::clock_gettime(CLOCK_REALTIME, &ts); fh._ts_sec = ts.tv_sec; fh._ts_nsec = ts.tv_nsec; } void create_jinf(const bool ae, const u_int16_t ae_max_jfiles) { if (jdir::exists(test_dir)) jdir::delete_dir(test_dir); create_jinf(_map.size(), ae, ae_max_jfiles, _jid, _base_filename); } static void create_jinf(u_int16_t num_files, const bool ae, const u_int16_t ae_max_jfiles, const string jid, const string base_filename) { jdir::create_dir(test_dir); // Check test dir exists; create it if not timespec ts; ::clock_gettime(CLOCK_REALTIME, &ts); jinf ji(jid, test_dir, base_filename, num_files, ae, ae_max_jfiles, JFSIZE_SBLKS, JRNL_WMGR_DEF_PAGE_SIZE, JRNL_WMGR_DEF_PAGES, ts); ji.write(); } static void create_journal_file(const u_int16_t pfid, const file_hdr& fh, const string base_filename, const u_int32_t fsize_sblks = JFSIZE_SBLKS, const char fill_char = 0) { const std::string filename = create_journal_filename(pfid, base_filename); ofstream of(filename.c_str(), ofstream::out | ofstream::trunc); if (!of.good()) BOOST_FAIL("Unable to open test journal file \"" << filename << "\" for writing."); write_file_header(filename, of, fh, fill_char); write_file_body(of, fsize_sblks, fill_char); of.close(); if (of.fail() || of.bad()) BOOST_FAIL("Error closing test journal file \"" << filename << "\"."); } static void write_file_header(const std::string& filename, ofstream& of, const file_hdr& fh, const char fill_char) { // write file header u_int32_t cnt = sizeof(file_hdr); of.write((const char*)&fh, cnt); if (of.fail() || of.bad()) BOOST_FAIL("Error writing file header to test journal file \"" << filename << "\"."); // fill remaining sblk with fill char while (cnt++ < JRNL_DBLK_SIZE * JRNL_SBLK_SIZE) { of.put(fill_char); if (of.fail() || of.bad()) BOOST_FAIL("Error writing filler to test journal file \"" << filename << "\"."); } } static void write_file_body(ofstream& of, const u_int32_t fsize_sblks, const char fill_char) { if (fsize_sblks > 1) { std::vector sblk_buffer(JRNL_DBLK_SIZE * JRNL_SBLK_SIZE, fill_char); u_int32_t fwritten_sblks = 0; // hdr while (fwritten_sblks++ < fsize_sblks) of.write(&sblk_buffer[0], JRNL_DBLK_SIZE * JRNL_SBLK_SIZE); } } }; const string get_test_name(const string& file, const string& test_name) { cout << test_filename << "." << test_name << ": " << flush; return file + "." + test_name; } bool check_iores(const string& ctxt, const iores ret, const iores exp_ret, test_dtok* dtp) { if (ret != exp_ret) { delete dtp; BOOST_FAIL(ctxt << ": Expected " << iores_str(exp_ret) << "; got " << iores_str(ret)); } return false; } bool handle_jcntl_response(const iores res, jcntl& jc, unsigned& aio_sleep_cnt, const std::string& ctxt, const iores exp_ret, test_dtok* dtp) { if (res == RHM_IORES_PAGE_AIOWAIT) { if (++aio_sleep_cnt <= MAX_AIO_SLEEPS) { jc.get_wr_events(0); // *** GEV2 usleep(AIO_SLEEP_TIME); } else return check_iores(ctxt, res, exp_ret, dtp); } else return check_iores(ctxt, res, exp_ret, dtp); return true; } u_int64_t enq_msg(jcntl& jc, const u_int64_t rid, const string& msg, const bool transient, const iores exp_ret = RHM_IORES_SUCCESS) { ostringstream ctxt; ctxt << "enq_msg(" << rid << ")"; test_dtok* dtp = new test_dtok; BOOST_CHECK_MESSAGE(dtp != 0, "Data token allocation failed (dtp == 0)."); dtp->set_rid(rid); dtp->set_external_rid(true); try { iores res = jc.enqueue_data_record(msg.c_str(), msg.size(), msg.size(), dtp, transient); check_iores(ctxt.str(), res, exp_ret, dtp); u_int64_t dtok_rid = dtp->rid(); if (dtp->done()) delete dtp; return dtok_rid; } catch (exception& e) { delete dtp; throw; } } u_int64_t enq_extern_msg(jcntl& jc, const u_int64_t rid, const std::size_t msg_size, const bool transient, const iores exp_ret = RHM_IORES_SUCCESS) { ostringstream ctxt; ctxt << "enq_extern_msg(" << rid << ")"; test_dtok* dtp = new test_dtok; BOOST_CHECK_MESSAGE(dtp != 0, "Data token allocation failed (dtp == 0)."); dtp->set_rid(rid); dtp->set_external_rid(true); try { iores res = jc.enqueue_extern_data_record(msg_size, dtp, transient); check_iores(ctxt.str(), res, exp_ret, dtp); u_int64_t dtok_rid = dtp->rid(); if (dtp->done()) delete dtp; return dtok_rid; } catch (exception& e) { delete dtp; throw; } } u_int64_t enq_txn_msg(jcntl& jc, const u_int64_t rid, const string& msg, const string& xid, const bool transient, const iores exp_ret = RHM_IORES_SUCCESS) { ostringstream ctxt; ctxt << "enq_txn_msg(" << rid << ")"; test_dtok* dtp = new test_dtok; BOOST_CHECK_MESSAGE(dtp != 0, "Data token allocation failed (dtp == 0)."); dtp->set_rid(rid); dtp->set_external_rid(true); try { iores res = jc.enqueue_txn_data_record(msg.c_str(), msg.size(), msg.size(), dtp, xid, transient); check_iores(ctxt.str(), res, exp_ret, dtp); u_int64_t dtok_rid = dtp->rid(); if (dtp->done()) delete dtp; return dtok_rid; } catch (exception& e) { delete dtp; throw; } } u_int64_t enq_extern_txn_msg(jcntl& jc, const u_int64_t rid, const std::size_t msg_size, const string& xid, const bool transient, const iores exp_ret = RHM_IORES_SUCCESS) { ostringstream ctxt; ctxt << "enq_extern_txn_msg(" << rid << ")"; test_dtok* dtp = new test_dtok; BOOST_CHECK_MESSAGE(dtp != 0, "Data token allocation failed (dtp == 0)."); dtp->set_rid(rid); dtp->set_external_rid(true); try { iores res = jc.enqueue_extern_txn_data_record(msg_size, dtp, xid, transient); check_iores(ctxt.str(), res, exp_ret, dtp); u_int64_t dtok_rid = dtp->rid(); if (dtp->done()) delete dtp; return dtok_rid; } catch (exception& e) { delete dtp; throw; } } u_int64_t deq_msg(jcntl& jc, const u_int64_t drid, const u_int64_t rid, const iores exp_ret = RHM_IORES_SUCCESS) { ostringstream ctxt; ctxt << "deq_msg(" << drid << ")"; test_dtok* dtp = new test_dtok; BOOST_CHECK_MESSAGE(dtp != 0, "Data token allocation failed (dtp == 0)."); dtp->set_rid(rid); dtp->set_dequeue_rid(drid); dtp->set_external_rid(true); dtp->set_wstate(data_tok::ENQ); try { iores res = jc.dequeue_data_record(dtp); check_iores(ctxt.str(), res, exp_ret, dtp); u_int64_t dtok_rid = dtp->rid(); if (dtp->done()) delete dtp; return dtok_rid; } catch (exception& e) { delete dtp; throw; } } u_int64_t deq_txn_msg(jcntl& jc, const u_int64_t drid, const u_int64_t rid, const string& xid, const iores exp_ret = RHM_IORES_SUCCESS) { ostringstream ctxt; ctxt << "deq_txn_msg(" << drid << ")"; test_dtok* dtp = new test_dtok; BOOST_CHECK_MESSAGE(dtp != 0, "Data token allocation failed (dtp == 0)."); dtp->set_rid(rid); dtp->set_dequeue_rid(drid); dtp->set_external_rid(true); dtp->set_wstate(data_tok::ENQ); try { iores res = jc.dequeue_txn_data_record(dtp, xid); check_iores(ctxt.str(), res, exp_ret, dtp); u_int64_t dtok_rid = dtp->rid(); if (dtp->done()) delete dtp; return dtok_rid; } catch (exception& e) { delete dtp; throw; } } u_int64_t txn_abort(jcntl& jc, const u_int64_t rid, const string& xid, const iores exp_ret = RHM_IORES_SUCCESS) { test_dtok* dtp = new test_dtok; BOOST_CHECK_MESSAGE(dtp != 0, "Data token allocation failed (dtp == 0)."); dtp->set_rid(rid); dtp->set_external_rid(true); try { iores res = jc.txn_abort(dtp, xid); check_iores("txn_abort", res, exp_ret, dtp); u_int64_t dtok_rid = dtp->rid(); if (dtp->done()) delete dtp; return dtok_rid; } catch (exception& e) { delete dtp; throw; } } u_int64_t txn_commit(jcntl& jc, const u_int64_t rid, const string& xid, const iores exp_ret = RHM_IORES_SUCCESS) { test_dtok* dtp = new test_dtok; BOOST_CHECK_MESSAGE(dtp != 0, "Data token allocation failed (dtp == 0)."); dtp->set_rid(rid); dtp->set_external_rid(true); try { iores res = jc.txn_commit(dtp, xid); check_iores("txn_commit", res, exp_ret, dtp); u_int64_t dtok_rid = dtp->rid(); if (dtp->done()) delete dtp; return dtok_rid; } catch (exception& e) { delete dtp; throw; } } void read_msg(jcntl& jc, string& msg, string& xid, bool& transient, bool& external, const iores exp_ret = RHM_IORES_SUCCESS) { void* mp = 0; std::size_t msize = 0; void* xp = 0; std::size_t xsize = 0; test_dtok* dtp = new test_dtok; BOOST_CHECK_MESSAGE(dtp != 0, "Data token allocation failed (dtp == 0)."); dtp->set_wstate(data_tok::ENQ); unsigned aio_sleep_cnt = 0; try { iores res = jc.read_data_record(&mp, msize, &xp, xsize, transient, external, dtp); while (handle_jcntl_response(res, jc, aio_sleep_cnt, "read_msg", exp_ret, dtp)) res = jc.read_data_record(&mp, msize, &xp, xsize, transient, external, dtp); } catch (exception& e) { delete dtp; throw; } if (mp) msg.assign((char*)mp, msize); if (xp) { xid.assign((char*)xp, xsize); std::free(xp); xp = 0; } else if (mp) { std::free(mp); mp = 0; } delete dtp; } /* * Returns the number of messages of size msg_rec_size_dblks that will fit into an empty journal with or without * corresponding dequeues (controlled by include_deq) without a threshold - ie until the journal is full. Assumes * that dequeue records fit into one dblk. */ u_int32_t num_msgs_to_full(const u_int16_t num_files, const u_int32_t file_size_dblks, const u_int32_t msg_rec_size_dblks, bool include_deq) { u_int32_t rec_size_dblks = msg_rec_size_dblks; if (include_deq) rec_size_dblks++; return u_int32_t(::floor(1.0 * num_files * file_size_dblks / rec_size_dblks)); } /* * Returns the number of messages of size msg_rec_size_dblks that will fit into an empty journal before the enqueue * threshold of JRNL_ENQ_THRESHOLD (%). */ u_int32_t num_msgs_to_threshold(const u_int16_t num_files, const u_int32_t file_size_dblks, const u_int32_t msg_rec_size_dblks) { return u_int32_t(::floor(1.0 * num_files * file_size_dblks * JRNL_ENQ_THRESHOLD / msg_rec_size_dblks / 100)); } /* * Returns the amount of space reserved in dblks (== num dequeues assuming dequeue size of 1 dblk) for the enqueue * threshold of JRNL_ENQ_THRESHOLD (%). */ u_int32_t num_dequeues_rem(const u_int16_t num_files, const u_int32_t file_size_dblks) { /* * Fraction of journal remaining after threshold is used --------------+ * Total no. dblks in journal ------+ | * | | * +------------+------------+ +-----------------+---------------------+ */ return u_int32_t(::ceil(num_files * file_size_dblks * (1.0 - (1.0 * JRNL_ENQ_THRESHOLD / 100)))); } string& create_msg(string& s, const int msg_num, const int len) { ostringstream oss; oss << "MSG_" << setfill('0') << setw(6) << msg_num << "_"; for (int i=12; i<=len; i++) oss << (char)('0' + i%10); s.assign(oss.str()); return s; } string& create_xid(string& s, const int msg_num, const int len) { ostringstream oss; oss << "XID_" << setfill('0') << setw(6) << msg_num << "_"; for (int i=11; i #include #include "jrnl/deq_hdr.hpp" #include "jrnl/enq_hdr.hpp" #include "jrnl/file_hdr.hpp" #include "jrnl/jcfg.hpp" #include "jrnl/rec_tail.hpp" #include "jrnl/txn_hdr.hpp" using namespace boost::unit_test; using namespace mrg::journal; using namespace std; QPID_AUTO_TEST_SUITE(rec_hdr_suite) const string test_filename("_ut_rec_hdr"); QPID_AUTO_TEST_CASE(hdr_class) { cout << test_filename << ".hdr_class: " << flush; rec_hdr h1; BOOST_CHECK_EQUAL(h1._magic, 0UL); BOOST_CHECK_EQUAL(h1._version, 0); BOOST_CHECK_EQUAL(h1._eflag, 0); BOOST_CHECK_EQUAL(h1._uflag, 0); BOOST_CHECK_EQUAL(h1._rid, 0ULL); BOOST_CHECK(!h1.get_owi()); const u_int32_t magic = 0x89abcdefUL; const u_int16_t uflag = 0x5537; const u_int8_t version = 0xef; const u_int64_t rid = 0x123456789abcdef0ULL; const bool owi = true; rec_hdr h2(magic, version, rid, owi); BOOST_CHECK_EQUAL(h2._magic, magic); BOOST_CHECK_EQUAL(h2._version, version); #ifdef JRNL_LITTLE_ENDIAN BOOST_CHECK_EQUAL(h2._eflag, RHM_LENDIAN_FLAG); #else BOOST_CHECK_EQUAL(h2._eflag, RHM_BENDIAN_FLAG); #endif BOOST_CHECK_EQUAL(h2._uflag, (const u_int16_t)rec_hdr::HDR_OVERWRITE_INDICATOR_MASK); BOOST_CHECK_EQUAL(h2._rid, rid); BOOST_CHECK_EQUAL(h2.get_owi(), owi); h2._uflag = uflag; BOOST_CHECK(h2.get_owi()); h2.set_owi(true); BOOST_CHECK(h2.get_owi()); BOOST_CHECK_EQUAL(h2._uflag, uflag); h2.set_owi(false); BOOST_CHECK(!h2.get_owi()); BOOST_CHECK_EQUAL(h2._uflag, (uflag & ~(const u_int16_t)rec_hdr::HDR_OVERWRITE_INDICATOR_MASK)); h2.set_owi(true); BOOST_CHECK(h2.get_owi()); BOOST_CHECK_EQUAL(h2._uflag, uflag); h1.hdr_copy(h2); BOOST_CHECK_EQUAL(h1._magic, magic); BOOST_CHECK_EQUAL(h1._version, version); #ifdef JRNL_LITTLE_ENDIAN BOOST_CHECK_EQUAL(h1._eflag, RHM_LENDIAN_FLAG); #else BOOST_CHECK_EQUAL(h1._eflag, RHM_BENDIAN_FLAG); #endif BOOST_CHECK_EQUAL(h1._uflag, uflag); BOOST_CHECK_EQUAL(h1._rid, rid); BOOST_CHECK(h1.get_owi()); BOOST_CHECK_EQUAL(h1._uflag, uflag); h1.reset(); BOOST_CHECK_EQUAL(h1._magic, 0UL); BOOST_CHECK_EQUAL(h1._version, 0); BOOST_CHECK_EQUAL(h1._eflag, 0); BOOST_CHECK_EQUAL(h1._uflag, 0); BOOST_CHECK_EQUAL(h1._rid, 0ULL); BOOST_CHECK(!h1.get_owi()); cout << "ok" << endl; } QPID_AUTO_TEST_CASE(rec_tail_class) { cout << test_filename << ".rec_tail_class: " << flush; const u_int32_t magic = 0xfedcba98; const u_int64_t rid = 0xfedcba9876543210ULL; const u_int32_t xmagic = ~magic; { rec_tail rt1; BOOST_CHECK_EQUAL(rt1._xmagic, 0xffffffffUL); BOOST_CHECK_EQUAL(rt1._rid, 0ULL); } { rec_tail rt2(magic, rid); BOOST_CHECK_EQUAL(rt2._xmagic, magic); BOOST_CHECK_EQUAL(rt2._rid, rid); } { rec_hdr h(magic, RHM_JDAT_VERSION, rid, true); rec_tail rt3(h); BOOST_CHECK_EQUAL(rt3._xmagic, xmagic); BOOST_CHECK_EQUAL(rt3._rid, rid); } cout << "ok" << endl; } QPID_AUTO_TEST_CASE(file_hdr_class) { cout << test_filename << ".file_hdr_class: " << flush; const u_int32_t magic = 0xfedcba98UL; const u_int8_t version = 0xa5; const u_int16_t uflag = 0x5537; const u_int64_t rid = 0xfedcba9876543210ULL; const u_int16_t pfid = 0xfedcU; const u_int16_t lfid = 0xf0e1U; #ifdef JRNL_32_BIT const std::size_t fro = 0xfedcba98UL; #else const std::size_t fro = 0xfedcba9876543210ULL; #endif timespec ts; const bool owi = true; { file_hdr fh1; BOOST_CHECK_EQUAL(fh1._magic, 0UL); BOOST_CHECK_EQUAL(fh1._version, 0); BOOST_CHECK_EQUAL(fh1._eflag, 0); BOOST_CHECK_EQUAL(fh1._uflag, 0); BOOST_CHECK_EQUAL(fh1._rid, 0ULL); BOOST_CHECK_EQUAL(fh1._pfid, 0UL); BOOST_CHECK_EQUAL(fh1._lfid, 0U); BOOST_CHECK_EQUAL(fh1._fro, std::size_t(0)); BOOST_CHECK_EQUAL(fh1._ts_sec, std::time_t(0)); BOOST_CHECK_EQUAL(fh1._ts_nsec, u_int32_t(0)); BOOST_CHECK(!fh1.get_owi()); } { file_hdr fh2(magic, version, rid, pfid, lfid, fro, owi, false); BOOST_CHECK_EQUAL(fh2._magic, magic); BOOST_CHECK_EQUAL(fh2._version, version); #ifdef JRNL_LITTLE_ENDIAN BOOST_CHECK_EQUAL(fh2._eflag, RHM_LENDIAN_FLAG); #else BOOST_CHECK_EQUAL(fh2._eflag, RHM_BENDIAN_FLAG); #endif BOOST_CHECK_EQUAL(fh2._uflag, (const u_int16_t)rec_hdr::HDR_OVERWRITE_INDICATOR_MASK); BOOST_CHECK_EQUAL(fh2._rid, rid); BOOST_CHECK_EQUAL(fh2._pfid, pfid ); BOOST_CHECK_EQUAL(fh2._lfid, lfid); BOOST_CHECK_EQUAL(fh2._fro, fro); BOOST_CHECK_EQUAL(fh2._ts_sec, std::time_t(0)); BOOST_CHECK_EQUAL(fh2._ts_nsec, u_int32_t(0)); ::clock_gettime(CLOCK_REALTIME, &ts); fh2.set_time(ts); BOOST_CHECK_EQUAL(fh2._ts_sec, ts.tv_sec); BOOST_CHECK_EQUAL(fh2._ts_nsec, u_int32_t(ts.tv_nsec)); BOOST_CHECK(fh2.get_owi()); fh2._uflag = uflag; BOOST_CHECK(fh2.get_owi()); fh2.set_owi(false); BOOST_CHECK(!fh2.get_owi()); BOOST_CHECK_EQUAL(fh2._uflag, (uflag & ~(const u_int16_t)rec_hdr::HDR_OVERWRITE_INDICATOR_MASK)); fh2.set_owi(true); BOOST_CHECK(fh2.get_owi()); BOOST_CHECK_EQUAL(fh2._uflag, uflag); } { file_hdr fh3(magic, version, rid, pfid, lfid, fro, owi, true); BOOST_CHECK_EQUAL(fh3._magic, magic); BOOST_CHECK_EQUAL(fh3._version, version); #ifdef JRNL_LITTLE_ENDIAN BOOST_CHECK_EQUAL(fh3._eflag, RHM_LENDIAN_FLAG); #else BOOST_CHECK_EQUAL(fh3._eflag, RHM_BENDIAN_FLAG); #endif BOOST_CHECK_EQUAL(fh3._uflag, (const u_int16_t)rec_hdr::HDR_OVERWRITE_INDICATOR_MASK); BOOST_CHECK_EQUAL(fh3._rid, rid); BOOST_CHECK_EQUAL(fh3._pfid, pfid); BOOST_CHECK_EQUAL(fh3._lfid, lfid); BOOST_CHECK_EQUAL(fh3._fro, fro); BOOST_CHECK(fh3._ts_sec - ts.tv_sec <= 1); // No more than 1 sec difference } cout << "ok" << endl; } QPID_AUTO_TEST_CASE(enq_hdr_class) { cout << test_filename << ".enq_hdr_class: " << flush; const u_int32_t magic = 0xfedcba98UL; const u_int8_t version = 0xa5; const u_int64_t rid = 0xfedcba9876543210ULL; const u_int16_t uflag = 0x5537; #ifdef JRNL_32_BIT const std::size_t xidsize = 0xfedcba98UL; const std::size_t dsize = 0x76543210UL; #else const std::size_t xidsize = 0xfedcba9876543210ULL; const std::size_t dsize = 0x76543210fedcba98ULL; #endif const bool owi = true; { enq_hdr eh1; BOOST_CHECK_EQUAL(eh1._magic, 0UL); BOOST_CHECK_EQUAL(eh1._version, 0); BOOST_CHECK_EQUAL(eh1._eflag, 0); BOOST_CHECK_EQUAL(eh1._uflag, 0); BOOST_CHECK_EQUAL(eh1._rid, 0ULL); BOOST_CHECK_EQUAL(eh1._xidsize, std::size_t(0)); BOOST_CHECK_EQUAL(eh1._dsize, std::size_t(0)); BOOST_CHECK(!eh1.get_owi()); } { enq_hdr eh2(magic, version, rid, xidsize, dsize, owi, false); BOOST_CHECK_EQUAL(eh2._magic, magic); BOOST_CHECK_EQUAL(eh2._version, version); #ifdef JRNL_LITTLE_ENDIAN BOOST_CHECK_EQUAL(eh2._eflag, RHM_LENDIAN_FLAG); #else BOOST_CHECK_EQUAL(eh2._eflag, RHM_BENDIAN_FLAG); #endif BOOST_CHECK_EQUAL(eh2._uflag, (const u_int16_t)rec_hdr::HDR_OVERWRITE_INDICATOR_MASK); BOOST_CHECK_EQUAL(eh2._rid, rid); BOOST_CHECK_EQUAL(eh2._xidsize, xidsize); BOOST_CHECK_EQUAL(eh2._dsize, dsize); BOOST_CHECK(eh2.get_owi()); BOOST_CHECK(!eh2.is_transient()); BOOST_CHECK(!eh2.is_external()); eh2._uflag = uflag; BOOST_CHECK(eh2.get_owi()); BOOST_CHECK(eh2.is_transient()); BOOST_CHECK(eh2.is_external()); eh2.set_owi(false); BOOST_CHECK(!eh2.get_owi()); BOOST_CHECK(eh2.is_transient()); BOOST_CHECK(eh2.is_external()); BOOST_CHECK_EQUAL(eh2._uflag, (uflag & ~(const u_int16_t)rec_hdr::HDR_OVERWRITE_INDICATOR_MASK)); eh2.set_owi(true); BOOST_CHECK(eh2.get_owi()); BOOST_CHECK(eh2.is_transient()); BOOST_CHECK(eh2.is_external()); BOOST_CHECK_EQUAL(eh2._uflag, uflag); eh2.set_transient(false); BOOST_CHECK(eh2.get_owi()); BOOST_CHECK(!eh2.is_transient()); BOOST_CHECK(eh2.is_external()); BOOST_CHECK_EQUAL(eh2._uflag, uflag & ~(const u_int16_t)enq_hdr::ENQ_HDR_TRANSIENT_MASK); eh2.set_transient(true); BOOST_CHECK(eh2.get_owi()); BOOST_CHECK(eh2.is_transient()); BOOST_CHECK(eh2.is_external()); BOOST_CHECK_EQUAL(eh2._uflag, uflag); eh2.set_external(false); BOOST_CHECK(eh2.get_owi()); BOOST_CHECK(eh2.is_transient()); BOOST_CHECK(!eh2.is_external()); BOOST_CHECK_EQUAL(eh2._uflag, uflag & ~(const u_int16_t)enq_hdr::ENQ_HDR_EXTERNAL_MASK); eh2.set_external(true); BOOST_CHECK(eh2.get_owi()); BOOST_CHECK(eh2.is_transient()); BOOST_CHECK(eh2.is_external()); BOOST_CHECK_EQUAL(eh2._uflag, uflag); } { enq_hdr eh3(magic, version, rid, xidsize, dsize, owi, true); BOOST_CHECK_EQUAL(eh3._magic, magic); BOOST_CHECK_EQUAL(eh3._version, version); #ifdef JRNL_LITTLE_ENDIAN BOOST_CHECK_EQUAL(eh3._eflag, RHM_LENDIAN_FLAG); #else BOOST_CHECK_EQUAL(eh3._eflag, RHM_BENDIAN_FLAG); #endif BOOST_CHECK_EQUAL(eh3._uflag, (const u_int16_t)enq_hdr::ENQ_HDR_TRANSIENT_MASK | (const u_int16_t)rec_hdr::HDR_OVERWRITE_INDICATOR_MASK); BOOST_CHECK_EQUAL(eh3._rid, rid); BOOST_CHECK_EQUAL(eh3._xidsize, xidsize); BOOST_CHECK_EQUAL(eh3._dsize, dsize); BOOST_CHECK(eh3.get_owi()); BOOST_CHECK(eh3.is_transient()); BOOST_CHECK(!eh3.is_external()); } cout << "ok" << endl; } QPID_AUTO_TEST_CASE(deq_hdr_class) { cout << test_filename << ".deq_hdr_class: " << flush; const u_int32_t magic = 0xfedcba98UL; const u_int8_t version = 0xa5; const u_int16_t uflag = 0x5537; const u_int64_t rid = 0xfedcba9876543210ULL; const u_int64_t drid = 0x76543210fedcba98ULL; #ifdef JRNL_32_BIT const std::size_t xidsize = 0xfedcba98UL; #else const std::size_t xidsize = 0xfedcba9876543210ULL; #endif const bool owi = true; { deq_hdr dh1; BOOST_CHECK_EQUAL(dh1._magic, 0UL); BOOST_CHECK_EQUAL(dh1._version, 0); BOOST_CHECK_EQUAL(dh1._eflag, 0); BOOST_CHECK_EQUAL(dh1._uflag, 0); BOOST_CHECK_EQUAL(dh1._rid, 0ULL); BOOST_CHECK_EQUAL(dh1._deq_rid, 0ULL); BOOST_CHECK_EQUAL(dh1._xidsize, std::size_t(0)); BOOST_CHECK(!dh1.get_owi()); } { deq_hdr dh2(magic, version, rid, drid, xidsize, owi); BOOST_CHECK_EQUAL(dh2._magic, magic); BOOST_CHECK_EQUAL(dh2._version, version); #ifdef JRNL_LITTLE_ENDIAN BOOST_CHECK_EQUAL(dh2._eflag, RHM_LENDIAN_FLAG); #else BOOST_CHECK_EQUAL(dh2._eflag, RHM_BENDIAN_FLAG); #endif BOOST_CHECK_EQUAL(dh2._uflag, (const u_int16_t)rec_hdr::HDR_OVERWRITE_INDICATOR_MASK); BOOST_CHECK_EQUAL(dh2._rid, rid); BOOST_CHECK_EQUAL(dh2._deq_rid, drid); BOOST_CHECK_EQUAL(dh2._xidsize, xidsize); BOOST_CHECK(dh2.get_owi()); dh2._uflag = uflag; BOOST_CHECK(dh2.get_owi()); dh2.set_owi(false); BOOST_CHECK(!dh2.get_owi()); BOOST_CHECK_EQUAL(dh2._uflag, (uflag & ~(const u_int16_t)rec_hdr::HDR_OVERWRITE_INDICATOR_MASK)); dh2.set_owi(true); BOOST_CHECK(dh2.get_owi()); BOOST_CHECK_EQUAL(dh2._uflag, uflag); } cout << "ok" << endl; } QPID_AUTO_TEST_CASE(txn_hdr_class) { cout << test_filename << ".txn_hdr_class: " << flush; const u_int32_t magic = 0xfedcba98UL; const u_int8_t version = 0xa5; const u_int16_t uflag = 0x5537; const u_int64_t rid = 0xfedcba9876543210ULL; #ifdef JRNL_32_BIT const std::size_t xidsize = 0xfedcba98UL; #else const std::size_t xidsize = 0xfedcba9876543210ULL; #endif const bool owi = true; { txn_hdr th1; BOOST_CHECK_EQUAL(th1._magic, 0UL); BOOST_CHECK_EQUAL(th1._version, 0); BOOST_CHECK_EQUAL(th1._eflag, 0); BOOST_CHECK_EQUAL(th1._uflag, 0); BOOST_CHECK_EQUAL(th1._rid, 0ULL); BOOST_CHECK_EQUAL(th1._xidsize, std::size_t(0)); BOOST_CHECK(!th1.get_owi()); } { txn_hdr th2(magic, version, rid, xidsize, owi); BOOST_CHECK_EQUAL(th2._magic, magic); BOOST_CHECK_EQUAL(th2._version, version); #ifdef JRNL_LITTLE_ENDIAN BOOST_CHECK_EQUAL(th2._eflag, RHM_LENDIAN_FLAG); #else BOOST_CHECK_EQUAL(th2._eflag, RHM_BENDIAN_FLAG); #endif BOOST_CHECK_EQUAL(th2._uflag, (const u_int16_t)rec_hdr::HDR_OVERWRITE_INDICATOR_MASK); BOOST_CHECK_EQUAL(th2._rid, rid); BOOST_CHECK_EQUAL(th2._xidsize, xidsize); BOOST_CHECK(th2.get_owi()); th2._uflag = uflag; BOOST_CHECK(th2.get_owi()); th2.set_owi(false); BOOST_CHECK(!th2.get_owi()); BOOST_CHECK_EQUAL(th2._uflag, (uflag & ~(const u_int16_t)rec_hdr::HDR_OVERWRITE_INDICATOR_MASK)); th2.set_owi(true); BOOST_CHECK(th2.get_owi()); BOOST_CHECK_EQUAL(th2._uflag, uflag); } cout << "ok" << endl; } QPID_AUTO_TEST_SUITE_END() qpid-cpp-store-debian-0.16/tests/jrnl/chk_jdata0000755000176300017630000000235611142067437020513 0ustar cajuscajus#!/bin/bash # Copyright (c) 2007 Red Hat, Inc. # # This file is part of the Qpid async store library msgstore.so. # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 # USA # # The GNU Lesser General Public License is available in the file COPYING. JRNL_BLK_SIZE=512 # Block size in bytes JRNL_PAGE_SIZE=256 # Journal page size in blocks JRNL_FILE_SIZE=12 # Journal file size in pages let END_OFFSET=${JRNL_BLK_SIZE}*${JRNL_PAGE_SIZE}*${JRNL_FILE_SIZE} for f in jdata/test.*.jdat; do echo $f hexdump -C -n 1024 $f hexdump -C -s ${END_OFFSET} $f echo "============" done qpid-cpp-store-debian-0.16/tests/jrnl/jhexdump0000755000176300017630000000235311346200757020424 0ustar cajuscajus#!/bin/bash # Copyright (c) 2008 Red Hat, Inc. # # This file is part of the Qpid async store library msgstore.so. # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 # USA # # The GNU Lesser General Public License is available in the file COPYING. if [ -z "$1" ]; then echo "No directory specified." exit fi JDIR=$1 echo "Target directory: ${JDIR}" rm -f j*.txt if [ -d "${JDIR}" ]; then n=0 for f in "${JDIR}"/*.jdat; do echo "$f -> j$n.txt" hexdump -C "$f" > j$n.txt (( n += 1 )) done else echo "This directory does not exist." fi qpid-cpp-store-debian-0.16/tests/jrnl/jtt/0000755000176300017630000000000011761423010017436 5ustar cajuscajusqpid-cpp-store-debian-0.16/tests/jrnl/jtt/args.cpp0000644000176300017630000001571311301004173021100 0ustar cajuscajus/* * Copyright (c) 2008 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #include "args.hpp" #include #include namespace po = boost::program_options; namespace mrg { namespace jtt { args::args(std::string opt_title): _options_descr(opt_title), format_chk(false), keep_jrnls(false), lld_rd_num(10), lld_skip_num(100), num_jrnls(1), pause_secs(0), randomize(false), read_mode(), read_prob(50), recover_mode(false), repeat_flag(false), reuse_instance(false), seed(0) { _options_descr.add_options() ("csv-file,c", po::value(&test_case_csv_file_name)->default_value("jtt.csv"), "CSV file containing test cases.") ("format-chk", po::value(&format_chk)->zero_tokens(), "Check the format of each journal file.") ("help,h", "This help message.") ("jrnl-dir", po::value(&journal_dir)->default_value("/tmp/jtt"), "Directory in which journal files will be placed.") ("keep-jrnls", po::value(&keep_jrnls)->zero_tokens(), "Keep all test journals.") ("lld-rd-num", po::value(&lld_rd_num)->default_value(10), "Number of consecutive messages to read after only dequeueing lld-skip-num " "messages during lazy-loading. Ignored if read-mode is not set to LAZYLOAD.") ("lld-skip-num", po::value(&lld_skip_num)->default_value(100), "Number of consecutive messages to dequeue only (without reading) prior to " "reading lld-rd-num messages. Ignored if read-mode is not set to LAZYLOAD.") ("num-jrnls", po::value(&num_jrnls)->default_value(1), "Number of simultaneous journal instances to test.") ("pause", po::value(&pause_secs)->default_value(0), "Pause in seconds between test cases (allows disk to catch up).") ("randomize", po::value(&randomize)->zero_tokens(), "Randomize the order of the tests.") ("read-mode", po::value(&read_mode)->default_value(read_arg::NONE), read_arg::descr().c_str()) ("read-prob", po::value(&read_prob)->default_value(50), "Read probability (percent) for each message when read-mode is set to RANDOM.") ("recover-mode", po::value(&recover_mode)->zero_tokens(), "Recover journal from the previous test for each test case.") ("repeat", po::value(&repeat_flag)->zero_tokens(), "Repeat all test cases indefinitely.") ("reuse-instance", po::value(&reuse_instance)->zero_tokens(), "Reuse journal instance for all test cases.") ("seed", po::value(&seed)->default_value(0), "Seed for use in random number generator.") ("analyzer", po::value(&jfile_analyzer)->default_value("./file_chk.py"), "Journal file analyzer program to use when the --format-chk option is used, ignored otherwise.") ; } bool args::parse(int argc, char** argv) // return true if error, false if ok { try { po::store(po::parse_command_line(argc, argv, _options_descr), _vmap); po::notify(_vmap); } catch (const std::exception& e) { std::cout << "ERROR: " << e.what() << std::endl; return usage(); } if (_vmap.count("help")) return usage(); if (num_jrnls == 0) { std::cout << "ERROR: num-jrnls must be 1 or more." << std::endl; return usage(); } if (read_prob > 100) // read_prob is unsigned, so no need to check < 0 { std::cout << "ERROR: read-prob must be between 0 and 100 inclusive." << std::endl; return usage(); } if (repeat_flag && keep_jrnls) { std::string resp; std::cout << "WARNING: repeat and keep-jrnls: Monitor disk usage as test journals will" " accumulate." << std::endl; std::cout << "Continue? "; std::cin >> resp; if (resp.size() == 1) { if (resp[0] != 'y' && resp[0] != 'Y') return true; } else if (resp.size() == 3) // any combo of lower- and upper-case { if (resp[0] != 'y' && resp[0] != 'Y') return true; if (resp[1] != 'e' && resp[1] != 'E') return true; if (resp[2] != 's' && resp[2] != 'S') return true; } else return true; } return false; } bool args::usage() const { std::cout << _options_descr << std::endl; return true; } void args::print_args() const { std::cout << "Number of journals: " << num_jrnls << std::endl; std::cout << "Read mode: " << read_mode << std::endl; if (read_mode.val() == read_arg::RANDOM) std::cout << "Read probability: " << read_prob << " %" << std::endl; if (read_mode.val() == read_arg::LAZYLOAD) { std::cout << "Lazy-load skips: " << lld_skip_num << std::endl; std::cout << "Lazy-load reads: " << lld_rd_num << std::endl; } if (pause_secs) std::cout << "Pause between test cases: " << pause_secs << " sec." << std::endl; if (seed) std::cout << "Randomize seed: " << seed << std::endl; print_flags(); } void args::print_flags() const { if (format_chk || keep_jrnls || randomize || recover_mode || repeat_flag || reuse_instance) { std::cout << "Flag options:"; // TODO: Get flag args and their strings directly from _options_descr. if (format_chk) std::cout << " format-chk"; if (keep_jrnls) std::cout << " keep-jrnls"; if (randomize) std::cout << " randomize"; if (recover_mode) std::cout << " recover-mode"; if (repeat_flag) std::cout << " repeat-flag"; if (reuse_instance) std::cout << " reuse-instance"; std::cout << std::endl; } std::cout << std::endl; } } // namespace jtt } // namespace mrg qpid-cpp-store-debian-0.16/tests/jrnl/jtt/test_mgr.hpp0000644000176300017630000000414411301004173021771 0ustar cajuscajus/* * Copyright (c) 2008 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #ifndef mrg_jtt_test_mgr_hpp #define mrg_jtt_test_mgr_hpp #include "args.hpp" #include #include #include "jrnl_instance.hpp" namespace mrg { namespace jtt { class test_mgr { public: typedef std::vector ji_list; typedef ji_list::iterator ji_list_itr; typedef ji_list::const_iterator ji_list_citr; private: ji_list _ji_list; args& _args; bool _err_flag; ptrdiff_t (*_random_fn_ptr)(const ptrdiff_t i); static volatile std::sig_atomic_t _signal; static volatile bool _abort; public: test_mgr(args& args); virtual ~test_mgr(); void run(); inline bool error() const { return _err_flag; } static void signal_handler(int signal); private: static bool exists(std::string file_name); void initialize_jrnls(); void print_results(test_case::shared_ptr tcp, const bool summary); inline static ptrdiff_t random_fn(const ptrdiff_t i) { return static_cast(1.0 * i * std::rand() / RAND_MAX); } }; } // namespace jtt } // namespace mrg #endif // ifndef mrg_jtt_test_mgr_hpp qpid-cpp-store-debian-0.16/tests/jrnl/jtt/_ut_data_src.cpp0000644000176300017630000001633011225714776022615 0ustar cajuscajus/* * Copyright (c) 2008 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #include "../../unit_test.h" #include #include "data_src.hpp" #include #include using namespace boost::unit_test; using namespace mrg::jtt; using namespace std; QPID_AUTO_TEST_SUITE(jtt_data_src) const string test_filename("_ut_data_src"); long get_seed() { timespec ts; if (::clock_gettime(CLOCK_REALTIME, &ts)) BOOST_FAIL("Unable to read clock to generate seed."); long tenths = ts.tv_nsec / 100000000; return long(10 * ts.tv_sec + tenths); // time in tenths of a second } #ifndef LONG_TEST /* * ============================================== * NORMAL TESTS * This section contains normal "make check" tests * for building/packaging. These are built when * LONG_TEST is _not_ defined. * ============================================== */ QPID_AUTO_TEST_CASE(data) { cout << test_filename << ".data: " << flush; BOOST_CHECK(data_src::max_dsize > 0); for (std::size_t i=0; i<1024; i++) { const char* dp = data_src::get_data(i); BOOST_CHECK_EQUAL(*dp, static_cast('0' + ((i + 1) % 10))); } for (std::size_t i=data_src::max_dsize-1024; i('0' + ((i + 1) % 10))); } const char* dp1 = data_src::get_data(data_src::max_dsize); BOOST_CHECK_EQUAL(dp1,(char*) 0); const char* dp2 = data_src::get_data(data_src::max_dsize + 0x1000); BOOST_CHECK_EQUAL(dp2, (char*)0); cout << "ok" << endl; } // There is a long version of this test in _ut_long_data_src.cpp QPID_AUTO_TEST_CASE(xid_data_xid) { const std::size_t num = 64; cout << test_filename << ".xid_data_xid: " << flush; BOOST_CHECK_EQUAL(data_src::get_xid(1), "0"); BOOST_CHECK_EQUAL(data_src::get_xid(2), "01"); BOOST_CHECK_EQUAL(data_src::get_xid(3), "002"); BOOST_CHECK_EQUAL(data_src::get_xid(4), "0003"); BOOST_CHECK_EQUAL(data_src::get_xid(5), "00004"); BOOST_CHECK_EQUAL(data_src::get_xid(6), "000005"); BOOST_CHECK_EQUAL(data_src::get_xid(7), "0000006"); BOOST_CHECK_EQUAL(data_src::get_xid(8), "00000007"); BOOST_CHECK_EQUAL(data_src::get_xid(9), "xid:00008"); BOOST_CHECK_EQUAL(data_src::get_xid(10), "xid:000009"); BOOST_CHECK_EQUAL(data_src::get_xid(11), "xid:0000010"); BOOST_CHECK_EQUAL(data_src::get_xid(12), "xid:00000011"); BOOST_CHECK_EQUAL(data_src::get_xid(13), "xid:00000012:"); BOOST_CHECK_EQUAL(data_src::get_xid(14), "xid:00000013:n"); BOOST_CHECK_EQUAL(data_src::get_xid(15), "xid:00000014:no"); std::size_t i = 15; for (; i #include "jrnl/slock.hpp" #include "jrnl/smutex.hpp" #include #include #include #define DATA_SIZE 1024 * 1024 #define XID_SIZE 1024 * 1024 namespace mrg { namespace jtt { class data_src { public: static const std::size_t max_dsize = DATA_SIZE; static const std::size_t max_xsize = XID_SIZE; private: static char _data_src[]; static char _xid_src[]; static u_int64_t _xid_cnt; static bool _initialized; static mrg::journal::smutex _sm; public: static const char* get_data(const std::size_t offs); static std::string get_xid(const std::size_t xid_size); private: data_src(); static u_int64_t get_xid_cnt() { mrg::journal::slock s(_sm); return _xid_cnt++; } static const char* get_xid_content(const std::size_t offs); static bool __init(); }; } // namespace jtt } // namespace mrg #endif // ifndef mrg_jtt_data_src_hpp qpid-cpp-store-debian-0.16/tests/jrnl/jtt/jtt.csv0000644000176300017630000006052011142067437020771 0ustar cajuscajus# Copyright (c) 2008 Red Hat Inc. # # This file is part of the Qpid async store library msgstore.so. # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 # USA # # The GNU Lesser General Public License is available in the file COPYING. ,,,,,,,"Msg size",,"Xid size",,,,,"enq-size",,"deq-size",,"txn-size",, "Test #","tf","pf","amn","mn incr","#msgs","ms incr","Min","Max","Min","Max","auto-deq","transient","extern","bytes","dblks","bytes","dblks","bytes","dblks","comment" ,,,,,,,,,,,,,,,,,,,, "Initialize only",,,,,,,,,,,,,,,,,,,, 0,"L",0,0,0,0,0,0,0,0,0,FALSE,FALSE,FALSE,44,1,0,0,0,0,"No messages - journal creation/initialization only" ,,,,,,,,,,,,,,,,,,,, "Simple message combinations of persistent/deq transientueued/non-dequeued, transactional/non-transactional",,,,,,,,,,,,,,,,,,,, 1,"L",1,1,0,1,0,10,10,0,0,FALSE,FALSE,FALSE,54,1,0,0,0,0,"1 * 10-byte message" 2,"L",1,10,0,10,0,10,10,0,0,FALSE,FALSE,FALSE,54,1,0,0,0,0,"10 * 10-byte message" 3,"L",1,1,0,1,0,10,10,0,0,FALSE,TRUE,FALSE,54,1,0,0,0,0,"1 * 10-byte message [transient]" 4,"L",1,10,0,10,0,10,10,0,0,FALSE,TRUE,FALSE,54,1,0,0,0,0,"10 * 10-byte message [transient]" 5,"L",1,1,0,1,0,10,10,10,10,FALSE,FALSE,FALSE,64,1,0,0,0,0,"1 * 10-byte message [txn]" 6,"L",1,10,0,10,0,10,10,10,10,FALSE,FALSE,FALSE,64,1,0,0,0,0,"10 * 10-byte message [txn]" 7,"L",1,1,0,1,0,10,10,10,10,FALSE,TRUE,FALSE,64,1,0,0,0,0,"1 * 10-byte message [txn transient]" 8,"L",1,10,0,10,0,10,10,10,10,FALSE,TRUE,FALSE,64,1,0,0,0,0,"10 * 10-byte message [txn transient]" 9,"L",1,1,0,1,0,10,10,0,0,TRUE,FALSE,FALSE,54,1,32,1,0,0,"1 * 10-byte message [deq]" 10,"L",1,10,0,10,0,10,10,0,0,TRUE,FALSE,FALSE,54,1,32,1,0,0,"10 * 10-byte message [deq]" 11,"L",1,1,0,1,0,10,10,0,0,TRUE,TRUE,FALSE,54,1,32,1,0,0,"1 * 10-byte message [deq transient]" 12,"L",1,10,0,10,0,10,10,0,0,TRUE,TRUE,FALSE,54,1,32,1,0,0,"10 * 10-byte message [deq transient]" 13,"L",1,1,0,1,0,10,10,10,10,TRUE,FALSE,FALSE,64,1,54,1,46,1,"1 * 10-byte message [deq txn]" 14,"L",1,10,0,10,0,10,10,10,10,TRUE,FALSE,FALSE,64,1,54,1,46,1,"10 * 10-byte message [deq txn]" 15,"L",1,1,0,1,0,10,10,10,10,TRUE,TRUE,FALSE,64,1,54,1,46,1,"1 * 10-byte message [txn deq transient]" 16,"L",1,10,0,10,0,10,10,10,10,TRUE,TRUE,FALSE,64,1,54,1,46,1,"10 * 10-byte message [txn deq transient]" 17,"L",1,1,0,1,0,10,10,0,0,FALSE,FALSE,TRUE,54,1,0,0,0,0,"1 * 10-byte message [extern]" 18,"L",1,10,0,10,0,10,10,0,0,FALSE,FALSE,TRUE,54,1,0,0,0,0,"10 * 10-byte message [extern]" 19,"L",1,1,0,1,0,10,10,0,0,FALSE,TRUE,TRUE,54,1,0,0,0,0,"1 * 10-byte message [transient extern]" 20,"L",1,10,0,10,0,10,10,0,0,FALSE,TRUE,TRUE,54,1,0,0,0,0,"10 * 10-byte message [transient extern]" 21,"L",1,1,0,1,0,10,10,10,10,FALSE,FALSE,TRUE,64,1,0,0,0,0,"1 * 10-byte message [txn extern]" 22,"L",1,10,0,10,0,10,10,10,10,FALSE,FALSE,TRUE,64,1,0,0,0,0,"10 * 10-byte message [txn extern]" 23,"L",1,1,0,1,0,10,10,10,10,FALSE,TRUE,TRUE,64,1,0,0,0,0,"1 * 10-byte message [txn transient extern]" 24,"L",1,10,0,10,0,10,10,10,10,FALSE,TRUE,TRUE,64,1,0,0,0,0,"10 * 10-byte message [txn transient extern]" 25,"L",1,1,0,1,0,10,10,0,0,TRUE,FALSE,TRUE,54,1,32,1,0,0,"1 * 10-byte message [deq extern]" 26,"L",1,10,0,10,0,10,10,0,0,TRUE,FALSE,TRUE,54,1,32,1,0,0,"10 * 10-byte message [deq extern]" 27,"L",1,1,0,1,0,10,10,0,0,TRUE,TRUE,TRUE,54,1,32,1,0,0,"1 * 10-byte message [deq transient extern]" 28,"L",1,10,0,10,0,10,10,0,0,TRUE,TRUE,TRUE,54,1,32,1,0,0,"10 * 10-byte message [deq transient extern]" 29,"L",1,1,0,1,0,10,10,10,10,TRUE,FALSE,TRUE,64,1,54,1,46,1,"1 * 10-byte message [deq txn extern]" 30,"L",1,10,0,10,0,10,10,10,10,TRUE,FALSE,TRUE,64,1,54,1,46,1,"10 * 10-byte message [deq txn extern]" 31,"L",1,1,0,1,0,10,10,10,10,TRUE,TRUE,TRUE,64,1,54,1,46,1,"1 * 10-byte message [txn deq transient extern]" 32,"L",1,10,0,10,0,10,10,10,10,TRUE,TRUE,TRUE,64,1,54,1,46,1,"10 * 10-byte message [txn deq transient extern]" ,,,,,,,,,,,,,,,,,,,, "Transition from one d-block to two per message",,,,,,,,,,,,,,,,,,,, 33,"L",1,10,0,10,0,84,84,0,0,FALSE,FALSE,FALSE,128,1,0,0,0,0,"1 dblk exact fit" 34,"L",1,10,0,10,1,85,85,0,0,FALSE,FALSE,FALSE,129,2,0,0,0,0,"1 dblk + 1 byte" 35,"L",1,10,0,10,0,58,58,26,26,FALSE,FALSE,FALSE,128,1,0,0,0,0,"1 dblk exact fit [txn]" 36,"L",1,10,0,10,1,59,59,26,26,FALSE,FALSE,FALSE,129,2,0,0,0,0,"1 dblk + 1 byte [txn]" ,,,,,,,,,,,,,,,,,,,, "Transition from one s-block to two per message",,,,,,,,,,,,,,,,,,,, 37,"L",1,10,0,10,0,468,468,0,0,FALSE,FALSE,FALSE,512,4,0,0,0,0,"1 sblk exact fit" 38,"L",1,10,0,10,1,469,469,0,0,FALSE,FALSE,FALSE,513,5,0,0,0,0,"1 sblk + 1 byte" 39,"L",1,10,0,10,0,442,442,26,26,FALSE,FALSE,FALSE,512,4,0,0,0,0,"1 sblk exact fit [txn]" 40,"L",1,10,0,10,1,443,443,26,26,FALSE,FALSE,FALSE,513,5,0,0,0,0,"1 sblk + 1 byte [txn]" ,,,,,,,,,,,,,,,,,,,, "Transition from first page to second",,,,,,,,,,,,,,,,,,,, 41,"L",1,8,0,8,0,4052,4052,0,0,FALSE,FALSE,FALSE,4096,32,0,0,0,0,"1/8 page" 42,"L",1,8,1,9,0,4052,4052,0,0,FALSE,FALSE,FALSE,4096,32,0,0,0,0,"1/8 page" 43,"L",1,8,0,8,1,4053,4053,0,0,FALSE,FALSE,FALSE,4097,33,0,0,0,0,"1/8 page + 1 byte" 44,"L",1,8,0,8,0,3796,3796,256,256,FALSE,FALSE,FALSE,4096,32,0,0,0,0,"1/8 page [txn]" 45,"L",1,8,1,9,0,3796,3796,256,256,FALSE,FALSE,FALSE,4096,32,0,0,0,0,"1/8 page [txn]" 46,"L",1,8,0,8,1,3797,3797,256,256,FALSE,FALSE,FALSE,4097,33,0,0,0,0,"1/8 page + 1 byte [txn]" 47,"L",1,8,0,8,0,3924,3924,0,0,TRUE,FALSE,FALSE,3968,31,32,1,0,0,"1/8 page incl deq [deq]" 48,"L",1,8,1,9,0,3924,3924,0,0,TRUE,FALSE,FALSE,3968,31,32,1,0,0,"1/8 page incl deq [deq]" 49,"L",1,8,0,8,1,3925,3925,0,0,TRUE,FALSE,FALSE,3969,32,32,1,0,0,"1/8 page incl deq + 1 byte [deq]" 50,"L",1,8,0,8,0,3028,3028,256,256,TRUE,FALSE,FALSE,3328,26,300,3,292,3,"1/8 page incl deq & txn [deq txn]" 51,"L",1,8,1,9,0,3028,3028,256,256,TRUE,FALSE,FALSE,3328,26,300,3,292,3,"1/8 page incl deq & txn [deq txn]" 52,"L",1,8,0,8,1,3029,3029,256,256,TRUE,FALSE,FALSE,3329,27,300,3,292,3,"1/8 page incl deq & txn + 1 byte [deq txn]" ,,,,,,,,,,,,,,,,,,,, "Page cache rollover (from page 32 back to page 0)",,,,,,,,,,,,,,,,,,,, 53,"L",1,32,0,32,0,32724,32724,0,0,FALSE,FALSE,FALSE,32768,256,0,0,0,0,"1 page" 54,"L",1,32,1,33,0,32724,32724,0,0,FALSE,FALSE,FALSE,32768,256,0,0,0,0,"1 page" 55,"L",1,32,0,32,1,32725,32725,0,0,FALSE,FALSE,FALSE,32769,257,0,0,0,0,"1 page + 1 byte" 56,"L",1.5,22,0,22,0,49108,49108,0,0,FALSE,FALSE,FALSE,49152,384,0,0,0,0,"1.5 pages" 57,"L",1,32,0,32,0,32468,32468,256,256,FALSE,FALSE,FALSE,32768,256,0,0,0,0,"1 page [txn]" 58,"L",1,32,1,33,0,32468,32468,256,256,FALSE,FALSE,FALSE,32768,256,0,0,0,0,"1 page [txn]" 59,"L",1,32,0,32,1,32469,32469,256,256,FALSE,FALSE,FALSE,32769,257,0,0,0,0,"1 page + 1 byte [txn]" 60,"L",1.5,22,0,22,0,48852,48852,256,256,FALSE,FALSE,FALSE,49152,384,0,0,0,0,"1.5 pages [txn]" 61,"L",1,32,0,32,0,32596,32596,0,0,TRUE,FALSE,FALSE,32640,255,32,1,0,0,"1 page incl deq [deq]" 62,"L",1,32,1,33,0,32596,32596,0,0,TRUE,FALSE,FALSE,32640,255,32,1,0,0,"1 page incl deq [deq]" 63,"L",1,32,0,32,1,32597,32597,0,0,TRUE,FALSE,FALSE,32641,256,32,1,0,0,"1 page incl deq + 1 byte [deq]" 64,"L",1.5,22,0,22,0,48980,48980,0,0,TRUE,FALSE,FALSE,49024,383,32,1,0,0,"1.5 pages incl deq [deq]" 65,"L",1,32,0,32,0,31700,31700,256,256,TRUE,FALSE,FALSE,32000,250,300,3,292,3,"1 page incl deq & txn [deq txn]" 66,"L",1,32,1,33,0,31700,31700,256,256,TRUE,FALSE,FALSE,32000,250,300,3,292,3,"1 page incl deq & txn [deq txn]" 67,"L",1,32,0,32,1,31701,31701,256,256,TRUE,FALSE,FALSE,32001,251,300,3,292,3,"1 page incl deq & txn + 1 byte [deq txn]" 68,"L",1.5,22,0,22,0,48084,48084,256,256,TRUE,FALSE,FALSE,48384,378,300,3,292,3,"1.5 pages incl deq & txn [deq txn]" ,,,,,,,,,,,,,,,,,,,, "File transition (from file 0000 to 0001)",,,,,,,,,,,,,,,,,,,, 69,"L",1,48,0,48,0,32724,32724,0,0,FALSE,FALSE,FALSE,32768,256,0,0,0,0,"1 page" 70,"L",1,48,1,49,0,32724,32724,0,0,FALSE,FALSE,FALSE,32768,256,0,0,0,0,"1 page" 71,"L",1,48,0,48,1,32725,32725,0,0,FALSE,FALSE,FALSE,32769,257,0,0,0,0,"1 page + 1 byte" 72,"L",2.5,20,0,20,0,81876,81876,0,0,FALSE,FALSE,FALSE,81920,640,0,0,0,0,"2.5 pages" 73,"L",1,48,0,48,0,32468,32468,256,256,FALSE,FALSE,FALSE,32768,256,0,0,0,0,"1 page [txn]" 74,"L",1,48,1,49,0,32468,32468,256,256,FALSE,FALSE,FALSE,32768,256,0,0,0,0,"1 page [txn]" 75,"L",1,48,0,48,1,32469,32469,256,256,FALSE,FALSE,FALSE,32769,257,0,0,0,0,"1 page + 1 byte [txn]" 76,"L",2.5,20,0,20,0,81620,81620,256,256,FALSE,FALSE,FALSE,81920,640,0,0,0,0,"2.5 pages [txn]" 77,"L",1,48,0,48,0,32596,32596,0,0,TRUE,FALSE,FALSE,32640,255,32,1,0,0,"1 page incl deq [deq]" 78,"L",1,48,1,49,0,32596,32596,0,0,TRUE,FALSE,FALSE,32640,255,32,1,0,0,"1 page incl deq [deq]" 79,"L",1,48,0,48,1,32597,32597,0,0,TRUE,FALSE,FALSE,32641,256,32,1,0,0,"1 page incl deq + 1 byte [deq]" 80,"L",2.5,20,0,20,0,81748,81748,0,0,TRUE,FALSE,FALSE,81792,639,32,1,0,0,"2.5 pages incl deq [deq]" 81,"L",1,48,0,48,0,31700,31700,256,256,TRUE,FALSE,FALSE,32000,250,300,3,292,3,"1 page incl deq & txn [deq txn]" 82,"L",1,48,1,49,0,31700,31700,256,256,TRUE,FALSE,FALSE,32000,250,300,3,292,3,"1 page incl deq & txn [deq txn]" 83,"L",1,48,0,48,1,31701,31701,256,256,TRUE,FALSE,FALSE,32001,251,300,3,292,3,"1 page incl deq & txn + 1 byte [deq txn]" 84,"L",2.5,20,0,20,0,80852,80852,256,256,TRUE,FALSE,FALSE,81152,634,300,3,292,3,"2.5 pages incl deq & txn [deq txn]" ,,,,,,,,,,,,,,,,,,,, "File rollover (from file 0007 to 0000) - RHM_WRONLY req'd for auto-dequeue == FALSE",,,,,,,,,,,,,,,,,,,, 85,"L",0.5,16,0,16,0,786260,786260,0,0,TRUE,FALSE,FALSE,786304,6143,32,1,0,0,"24 pages incl deq = ½ file [deq]" 86,"L",0.5,16,1,17,0,786260,786260,0,0,TRUE,FALSE,FALSE,786304,6143,32,1,0,0,"24 pages incl deq = ½ file [deq]" 87,"L",0.5,16,0,16,1,786261,786261,0,0,TRUE,FALSE,FALSE,786305,6144,32,1,0,0,"24 pages incl deq + 1 byte [deq]" 88,"L",0.5,16,0,16,0,785364,785364,256,256,TRUE,FALSE,FALSE,785664,6138,300,3,292,3,"24 pages incl deq & txn = ½ file [deq txn]" 89,"L",0.5,16,1,17,0,785364,785364,256,256,TRUE,FALSE,FALSE,785664,6138,300,3,292,3,"24 pages incl deq & txn = ½ file [deq txn]" 90,"L",0.5,16,0,16,1,785365,785365,256,256,TRUE,FALSE,FALSE,785665,6139,300,3,292,3,"24 pages incl deq & txn + 1 byte [deq txn]" 91,"L",0.25,32,0,32,0,786260,786260,0,0,TRUE,FALSE,FALSE,786304,6143,32,1,0,0,"24 pages incl deq = ½ file [deq]" 92,"L",0.25,32,1,33,0,786260,786260,0,0,TRUE,FALSE,FALSE,786304,6143,32,1,0,0,"24 pages incl deq = ½ file [deq]" 93,"L",0.25,32,0,32,1,786261,786261,0,0,TRUE,FALSE,FALSE,786305,6144,32,1,0,0,"24 pages incl deq + 1 byte [deq]" 94,"L",0.25,32,0,32,0,785364,785364,256,256,TRUE,FALSE,FALSE,785664,6138,300,3,292,3,"24 pages incl deq & txn = ½ file [deq txn]" 95,"L",0.25,32,1,33,0,785364,785364,256,256,TRUE,FALSE,FALSE,785664,6138,300,3,292,3,"24 pages incl deq & txn = ½ file [deq txn]" 96,"L",0.25,32,0,32,1,785365,785365,256,256,TRUE,FALSE,FALSE,785665,6139,300,3,292,3,"24 pages incl deq & txn + 1 byte [deq txn]" ,,,,,,,,,,,,,,,,,,,, "Multi-page messages (large messages) - tests various paths in encoder.",,,,,,,,,,,,,,,,,,,, 97,"L",1,16,0,16,0,32724,32724,0,0,FALSE,FALSE,FALSE,32768,256,0,0,0,0,"data 1 page" 98,"L",1,16,0,16,1,32725,32725,0,0,FALSE,FALSE,FALSE,32769,257,0,0,0,0,"data 1 page + 1 byte (tail split; 1 byte over page boundary)" 99,"L",1,16,0,16,11,32735,32735,0,0,FALSE,FALSE,FALSE,32779,257,0,0,0,0,"data 1 page + 11 bytes (tail split; 11 bytes over page boundary)" 100,"L",1,16,0,16,12,32736,32736,0,0,FALSE,FALSE,FALSE,32780,257,0,0,0,0,"data 1 page + 12 bytes (tail separated exactly onto next page)" 101,"L",1,16,0,16,13,32737,32737,0,0,FALSE,FALSE,FALSE,32781,257,0,0,0,0,"data 1 page + 13 bytes (data split; 1 byte over page boundary)" 102,"L",1,16,0,16,0,32468,32468,256,256,FALSE,FALSE,FALSE,32768,256,0,0,0,0,"data 1 page [txn]" 103,"L",1,16,0,16,1,32469,32469,256,256,FALSE,FALSE,FALSE,32769,257,0,0,0,0,"data 1 page + 1 byte (tail split; 1 byte over page boundary) [txn]" 104,"L",1,16,0,16,11,32479,32479,256,256,FALSE,FALSE,FALSE,32779,257,0,0,0,0,"data 1 page + 11 bytes (tail split; 11 bytes over page boundary) [txn]" 105,"L",1,16,0,16,12,32480,32480,256,256,FALSE,FALSE,FALSE,32780,257,0,0,0,0,"data 1 page + 12 bytes (tail separated exactly onto next page) [txn]" 106,"L",1,16,0,16,13,32481,32481,256,256,FALSE,FALSE,FALSE,32781,257,0,0,0,0,"data 1 page + 13 bytes (data split; 1 byte over page boundary) [txn]" 107,"L",2,16,0,16,0,65492,65492,0,0,FALSE,FALSE,FALSE,65536,512,0,0,0,0,"data 2 pages" 108,"L",2,16,0,16,1,65493,65493,0,0,FALSE,FALSE,FALSE,65537,513,0,0,0,0,"data 2 pages + 1 byte (tail split; 1 byte over page boundary)" 109,"L",2,16,0,16,11,65503,65503,0,0,FALSE,FALSE,FALSE,65547,513,0,0,0,0,"data 2 pages + 11 bytes (tail split; 11 bytes over page boundary)" 110,"L",2,16,0,16,12,65504,65504,0,0,FALSE,FALSE,FALSE,65548,513,0,0,0,0,"data 2 pages + 12 bytes (tail separated exactly onto next page)" 111,"L",2,16,0,16,13,65505,65505,0,0,FALSE,FALSE,FALSE,65549,513,0,0,0,0,"data 2 pages + 13 bytes (data split; 1 byte over page boundary)" 112,"L",2,16,0,16,0,65236,65236,256,256,FALSE,FALSE,FALSE,65536,512,0,0,0,0,"data 2 pages [txn]" 113,"L",2,16,0,16,1,65237,65237,256,256,FALSE,FALSE,FALSE,65537,513,0,0,0,0,"data 2 pages + 1 byte (tail split; 1 byte over page boundary) [txn]" 114,"L",2,16,0,16,11,65247,65247,256,256,FALSE,FALSE,FALSE,65547,513,0,0,0,0,"data 2 pages + 11 bytes (tail split; 11 bytes over page boundary) [txn]" 115,"L",2,16,0,16,12,65248,65248,256,256,FALSE,FALSE,FALSE,65548,513,0,0,0,0,"data 2 pages + 12 bytes (tail separated exactly onto next page) [txn]" 116,"L",2,16,0,16,13,65249,65249,256,256,FALSE,FALSE,FALSE,65549,513,0,0,0,0,"data 2 pages + 13 bytes (data split; 1 byte over page boundary) [txn]" 117,"L",4,16,0,16,0,131028,131028,0,0,FALSE,FALSE,FALSE,131072,1024,0,0,0,0,"data 4 pages" 118,"L",4,16,0,16,1,131029,131029,0,0,FALSE,FALSE,FALSE,131073,1025,0,0,0,0,"data 4 pages + 1 byte (tail split; 1 byte over page boundary)" 119,"L",4,16,0,16,11,131039,131039,0,0,FALSE,FALSE,FALSE,131083,1025,0,0,0,0,"data 4 pages + 11 bytes (tail split; 11 bytes over page boundary)" 120,"L",4,16,0,16,12,131040,131040,0,0,FALSE,FALSE,FALSE,131084,1025,0,0,0,0,"data 4 pages + 12 bytes (tail separated exactly onto next page)" 121,"L",4,16,0,16,13,131041,131041,0,0,FALSE,FALSE,FALSE,131085,1025,0,0,0,0,"data 4 pages + 13 bytes (data split; 1 byte over page boundary)" 122,"L",4,16,0,16,0,130772,130772,256,256,FALSE,FALSE,FALSE,131072,1024,0,0,0,0,"data 4 pages [txn]" 123,"L",4,16,0,16,1,130773,130773,256,256,FALSE,FALSE,FALSE,131073,1025,0,0,0,0,"data 4 pages + 1 byte (tail split; 1 byte over page boundary) [txn]" 124,"L",4,16,0,16,11,130783,130783,256,256,FALSE,FALSE,FALSE,131083,1025,0,0,0,0,"data 4 pages + 11 bytes (tail split; 11 bytes over page boundary) [txn]" 125,"L",4,16,0,16,12,130784,130784,256,256,FALSE,FALSE,FALSE,131084,1025,0,0,0,0,"data 4 pages + 12 bytes (tail separated exactly onto next page) [txn]" 126,"L",4,16,0,16,13,130785,130785,256,256,FALSE,FALSE,FALSE,131085,1025,0,0,0,0,"data 4 pages + 13 bytes (data split; 1 byte over page boundary) [txn]" 127,"L",3.5,16,0,16,0,114644,114644,0,0,FALSE,FALSE,FALSE,114688,896,0,0,0,0,"data 3.5 pages" 128,"L",3.5,16,0,16,1,114645,114645,0,0,FALSE,FALSE,FALSE,114689,897,0,0,0,0,"data 3.5 pages + 1 byte" 129,"L",3.5,16,0,16,0,114388,114388,256,256,FALSE,FALSE,FALSE,114688,896,0,0,0,0,"data 3.5 pages [txn]" 130,"L",3.5,16,0,16,1,114389,114389,256,256,FALSE,FALSE,FALSE,114689,897,0,0,0,0,"data 3.5 pages + 1 byte [txn]" 131,"L",1,16,0,16,-1,10,10,32735,32735,FALSE,FALSE,FALSE,32789,257,0,0,0,0,"xid 1 page – 1 byte; data 10 bytes (exact fit) [txn]" 132,"L",1,16,0,16,0,10,10,32736,32736,FALSE,FALSE,FALSE,32790,257,0,0,0,0,"xid 1 page; data 10 bytes (exact fit) [txn]" 133,"L",1,16,0,16,1,10,10,32737,32737,FALSE,FALSE,FALSE,32791,257,0,0,0,0,"xid 1 page + 1 byte; data 10 bytes (exact fit) [txn]" 134,"L",2,16,0,16,-1,10,10,65503,65503,FALSE,FALSE,FALSE,65557,513,0,0,0,0,"xid 2 pages – 1 byte; data 10 bytes (exact fit) [txn]" 135,"L",2,16,0,16,0,10,10,65504,65504,FALSE,FALSE,FALSE,65558,513,0,0,0,0,"xid 2 pages; data 10 bytes (exact fit) [txn]" 136,"L",2,16,0,16,1,10,10,65505,65505,FALSE,FALSE,FALSE,65559,513,0,0,0,0,"xid 2 pages + 1 byte; data 10 bytes (exact fit) [txn]" 137,"L",4,16,0,16,-1,10,10,131039,131039,FALSE,FALSE,FALSE,131093,1025,0,0,0,0,"xid 4 pages – 1 byte; data 10 bytes (exact fit) [txn]" 138,"L",4,16,0,16,0,10,10,131040,131040,FALSE,FALSE,FALSE,131094,1025,0,0,0,0,"xid 4 pages; data 10 bytes (exact fit) [txn]" 139,"L",4,16,0,16,1,10,10,131041,131041,FALSE,FALSE,FALSE,131095,1025,0,0,0,0,"xid 4 pages + 1 byte; data 10 bytes (exact fit) [txn]" 140,"L",3.5,16,0,16,0,10,10,114656,114656,FALSE,FALSE,FALSE,114710,897,0,0,0,0,"xid 3.5 pages; data 10 bytes (exact fit) [txn]" 141,"L",3.5,16,0,16,1,10,10,114657,114657,FALSE,FALSE,FALSE,114711,897,0,0,0,0,"xid 3.5 pages + 1 byte; data 10 bytes (exact fit) [txn]" 142,"L",1,16,0,16,-1,10,10,32735,32735,TRUE,FALSE,FALSE,32789,257,32779,257,32771,257,"xid 1 page – 1 byte for enq rec; data 10 bytes (exact fit) [deq, txn]" 143,"L",1,16,0,16,0,10,10,32736,32736,TRUE,FALSE,FALSE,32790,257,32780,257,32772,257,"xid 1 page for enq rec; data 10 bytes (exact fit) [deq, txn]" 144,"L",1,16,0,16,1,10,10,32737,32737,TRUE,FALSE,FALSE,32791,257,32781,257,32773,257,"xid 1 page + 1 byte for enq rec; data 10 bytes (exact fit) [deq, txn]" 145,"L",2,16,0,16,-1,10,10,65503,65503,TRUE,FALSE,FALSE,65557,513,65547,513,65539,513,"xid 2 pages – 1 byte for enq rec; data 10 bytes (exact fit) [deq, txn]" 146,"L",2,16,0,16,0,10,10,65504,65504,TRUE,FALSE,FALSE,65558,513,65548,513,65540,513,"xid 2 pages for enq rec; data 10 bytes (exact fit) [deq, txn]" 147,"L",2,16,0,16,1,10,10,65505,65505,TRUE,FALSE,FALSE,65559,513,65549,513,65541,513,"xid 2 pages + 1 byte for enq rec; data 10 bytes (exact fit) [deq, txn]" 148,"L",4,16,0,16,-1,10,10,131039,131039,TRUE,FALSE,FALSE,131093,1025,131083,1025,131075,1025,"xid 4 pages – 1 byte for enq rec; data 10 bytes (exact fit) [deq, txn]" 149,"L",4,16,0,16,0,10,10,131040,131040,TRUE,FALSE,FALSE,131094,1025,131084,1025,131076,1025,"xid 4 pages for enq rec; data 10 bytes (exact fit) [deq, txn]" 150,"L",4,16,0,16,1,10,10,131041,131041,TRUE,FALSE,FALSE,131095,1025,131085,1025,131077,1025,"xid 4 pages + 1 byte for enq rec; data 10 bytes (exact fit) [deq, txn]" 151,"L",3.5,16,0,16,0,10,10,114656,114656,TRUE,FALSE,FALSE,114710,897,114700,897,114692,897,"xid 3.5 pages for enq rec; data 10 bytes (exact fit) [deq, txn]" 152,"L",3.5,16,0,16,1,10,10,114657,114657,TRUE,FALSE,FALSE,114711,897,114701,897,114693,897,"xid 3.5 pages + 1 byte for enq rec; data 10 bytes (exact fit) [deq, txn]" 153,"L",1,16,0,16,-1,10,10,32735,32735,TRUE,FALSE,FALSE,32789,257,32779,257,32771,257,"xid 1 page – 1 byte for deq rec; data 10 bytes (exact fit) [deq, txn]" 154,"L",1,16,0,16,0,10,10,32736,32736,TRUE,FALSE,FALSE,32790,257,32780,257,32772,257,"xid 1 page for deq rec; data 10 bytes (exact fit) [deq, txn]" 155,"L",1,16,0,16,1,10,10,32737,32737,TRUE,FALSE,FALSE,32791,257,32781,257,32773,257,"xid 1 page + 1 byte for deq rec; data 10 bytes (exact fit) [deq, txn]" 156,"L",2,16,0,16,-1,10,10,65503,65503,TRUE,FALSE,FALSE,65557,513,65547,513,65539,513,"xid 2 pages – 1 byte for deq rec; data 10 bytes (exact fit) [deq, txn]" 157,"L",2,16,0,16,0,10,10,65504,65504,TRUE,FALSE,FALSE,65558,513,65548,513,65540,513,"xid 2 pages for deq rec; data 10 bytes (exact fit) [deq, txn]" 158,"L",2,16,0,16,1,10,10,65505,65505,TRUE,FALSE,FALSE,65559,513,65549,513,65541,513,"xid 2 pages + 1 byte for deq rec; data 10 bytes (exact fit) [deq, txn]" 159,"L",4,16,0,16,-1,10,10,131039,131039,TRUE,FALSE,FALSE,131093,1025,131083,1025,131075,1025,"xid 4 pages – 1 byte for deq rec; data 10 bytes (exact fit) [deq, txn]" 160,"L",4,16,0,16,0,10,10,131040,131040,TRUE,FALSE,FALSE,131094,1025,131084,1025,131076,1025,"xid 4 pages for deq rec; data 10 bytes (exact fit) [deq, txn]" 161,"L",4,16,0,16,1,10,10,131041,131041,TRUE,FALSE,FALSE,131095,1025,131085,1025,131077,1025,"xid 4 pages + 1 byte for deq rec; data 10 bytes (exact fit) [deq, txn]" 162,"L",3.5,16,0,16,0,10,10,114656,114656,TRUE,FALSE,FALSE,114710,897,114700,897,114692,897,"xid 3.5 pages for deq rec; data 10 bytes (exact fit) [deq, txn]" 163,"L",3.5,16,0,16,1,10,10,114657,114657,TRUE,FALSE,FALSE,114711,897,114701,897,114693,897,"xid 3.5 pages + 1 byte for deq rec; data 10 bytes (exact fit) [deq, txn]" 164,"L",1,16,0,16,-1,10,10,32743,32743,TRUE,FALSE,FALSE,32797,257,32787,257,32779,257,"xid 1 page – 1 byte for txn rec; data 10 bytes (exact fit) [deq, txn]" 165,"L",1,16,0,16,0,10,10,32744,32744,TRUE,FALSE,FALSE,32798,257,32788,257,32780,257,"xid 1 page for txn rec; data 10 bytes (exact fit) [deq, txn]" 166,"L",1,16,0,16,1,10,10,32745,32745,TRUE,FALSE,FALSE,32799,257,32789,257,32781,257,"xid 1 page + 1 byte for txn rec; data 10 bytes (exact fit) [deq, txn]" 167,"L",2,16,0,16,-1,10,10,65511,65511,TRUE,FALSE,FALSE,65565,513,65555,513,65547,513,"xid 2 pages – 1 byte for txn rec; data 10 bytes (exact fit) [deq, txn]" 168,"L",2,16,0,16,0,10,10,65512,65512,TRUE,FALSE,FALSE,65566,513,65556,513,65548,513,"xid 2 pages for txn rec; data 10 bytes (exact fit) [deq, txn]" 169,"L",2,16,0,16,1,10,10,65513,65513,TRUE,FALSE,FALSE,65567,513,65557,513,65549,513,"xid 2 pages + 1 byte for txn rec; data 10 bytes (exact fit) [deq, txn]" 170,"L",4,16,0,16,-1,10,10,131047,131047,TRUE,FALSE,FALSE,131101,1025,131091,1025,131083,1025,"xid 4 pages – 1 byte for txn rec; data 10 bytes (exact fit) [deq, txn]" 171,"L",4,16,0,16,0,10,10,131048,131048,TRUE,FALSE,FALSE,131102,1025,131092,1025,131084,1025,"xid 4 pages for txn rec; data 10 bytes (exact fit) [deq, txn]" 172,"L",4,16,0,16,1,10,10,131049,131049,TRUE,FALSE,FALSE,131103,1025,131093,1025,131085,1025,"xid 4 pages + 1 byte for txn rec; data 10 bytes (exact fit) [deq, txn]" 173,"L",3.5,16,0,16,0,10,10,114664,114664,TRUE,FALSE,FALSE,114718,897,114708,897,114700,897,"xid 3.5 pages for txn rec; data 10 bytes (exact fit) [deq, txn]" 174,"L",3.5,16,0,16,1,10,10,114665,114665,TRUE,FALSE,FALSE,114719,897,114709,897,114701,897,"xid 3.5 pages + 1 byte for txn rec; data 10 bytes (exact fit) [deq, txn]" ,,,,,,,,,,,,,,,,,,,, "High volume tests of random message lengths - RHM_WRONLY req'd for auto-dequeue == FALSE",,,,,,,,,,,,,,,,,,,, #175,"M",1,5000000,0,5000000,0,0,84,0,0,TRUE,FALSE,FALSE,128,1,32,1,0,0,"1 dblk max [deq]" #176,"M",3,3000000,0,3000000,0,0,340,0,0,TRUE,FALSE,FALSE,384,3,32,1,0,0,"3 dblks max [deq]" #177,"M",10,1600000,0,1600000,0,0,1236,0,0,TRUE,FALSE,FALSE,1280,10,32,1,0,0,"10 dblks max [deq]" #178,"M",30,600000,0,600000,0,0,3796,0,0,TRUE,FALSE,FALSE,3840,30,32,1,0,0,"30 dblks max [deq]" #179,"M",100,200000,0,200000,0,0,12756,0,0,TRUE,FALSE,FALSE,12800,100,32,1,0,0,"100 dblks max [deq]" #180,"M",300,60000,0,60000,0,0,38356,0,0,TRUE,FALSE,FALSE,38400,300,32,1,0,0,"300 dblks max [deq]" #181,"M",1000,20000,0,20000,0,0,127956,0,0,TRUE,FALSE,FALSE,128000,1000,32,1,0,0,"1000 dblks max [deq]" #182,"M",1,5000000,0,5000000,0,0,100,1,100,TRUE,FALSE,FALSE,244,2,144,2,136,2,"100 bytes xid max + 100 bytes data max [deq txn]" #183,"M",3,3000000,0,3000000,0,0,300,1,300,TRUE,FALSE,FALSE,644,6,344,3,336,3,"300 bytes xid max + 300 bytes data max [deq txn]" #184,"M",10,1600000,0,1600000,0,0,1000,1,1000,TRUE,FALSE,FALSE,2044,16,1044,9,1036,9,"1000 bytes xid max + 1000 bytes data max [deq txn]" #185,"M",30,600000,0,600000,0,0,3000,1,3000,TRUE,FALSE,FALSE,6044,48,3044,24,3036,24,"3000 bytes xid max + 3000 bytes data max [deq txn]" #186,"M",100,200000,0,200000,0,0,10000,1,10000,TRUE,FALSE,FALSE,20044,157,10044,79,10036,79,"10000 bytes xid max + 10000 bytes data max [deq txn]" #187,"M",300,60000,0,60000,0,0,30000,1,30000,TRUE,FALSE,FALSE,60044,470,30044,235,30036,235,"30000 bytes xid max + 30000 bytes data max [deq txn]" #188,"M",1000,20000,0,20000,0,0,100000,1,100000,TRUE,FALSE,FALSE,200044,1563,100044,782,100036,782,"100000 bytes xid max + 100000 bytes data max [deq txn]" ,,,,,,,,,,,,,,,,,,,, "STANDARD PERFORMANCE BENCHMARK: 10,000,000 writes, data=212b (2 dblks)",,,,,,,,,,,,,,,,,,,, #189,"M",1,10000000,0,10000000,0,212,212,0,0,TRUE,FALSE,FALSE,256,2,32,1,0,0,"212 bytes data (2 dblks enq + 1 dblk deq)" #190,"M",1,10000000,0,10000000,0,148,148,64,64,TRUE,FALSE,FALSE,256,2,108,1,100,1,"148 bytes data + 64 bytes xid (2 dblks enq + 1 dblks deq + 1 dblks txn)" qpid-cpp-store-debian-0.16/tests/jrnl/jtt/_ut_test_case_set.cpp0000644000176300017630000001227011142067437023652 0ustar cajuscajus/* * Copyright (c) 2008 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #include "../../unit_test.h" #include #include #include #include "test_case.hpp" #include "test_case_set.hpp" using namespace boost::unit_test; using namespace mrg::jtt; using namespace std; QPID_AUTO_TEST_SUITE(jtt_test_case_set) const string csv_file("_ut_test_case_set.csv"); const string test_filename("_ut_test_case_set"); // === Helper functions === bool check_csv_file(const char* filename) { struct stat s; if (::stat(filename, &s)) return false; if (S_ISREG(s.st_mode)) return true; return false; } // === Test suite === QPID_AUTO_TEST_CASE(constructor) { cout << test_filename << ".constructor: " << flush; test_case_set tcs; BOOST_CHECK(tcs.empty()); BOOST_CHECK_EQUAL(tcs.size(), unsigned(0)); cout << "ok" << endl; } QPID_AUTO_TEST_CASE(append_1) { cout << test_filename << ".append_1: " << flush; const unsigned test_case_num = 0x12345; const u_int32_t num_msgs = 0x100; const std::size_t min_data_size = 0x1000; const std::size_t max_data_size = 0; const bool auto_deq = true; const std::size_t min_xid_size = 0x200; const std::size_t max_xid_size = 0x200; using mrg::jtt::test_case; const test_case::transient_t transient = test_case::JTT_PERSISTNET; const test_case::external_t external = test_case::JDL_INTERNAL; const string comment = "This is a test"; test_case_set tcs; tcs.append(test_case_num, num_msgs, min_data_size, max_data_size, auto_deq, min_xid_size, max_xid_size, transient, external, comment); BOOST_CHECK(!tcs.empty()); BOOST_CHECK_EQUAL(tcs.size(), unsigned(1)); test_case::shared_ptr tcp = tcs[0]; BOOST_CHECK_EQUAL(tcp->test_case_num(), test_case_num); BOOST_CHECK_EQUAL(tcp->num_msgs(), num_msgs); BOOST_CHECK_EQUAL(tcp->min_data_size(), min_data_size); BOOST_CHECK_EQUAL(tcp->max_data_size(), max_data_size); BOOST_CHECK_EQUAL(tcp->min_xid_size(), min_xid_size); BOOST_CHECK_EQUAL(tcp->max_xid_size(), max_xid_size); BOOST_CHECK_EQUAL(tcp->transient(), transient); BOOST_CHECK_EQUAL(tcp->external(), external); BOOST_CHECK_EQUAL(tcp->comment(), comment); cout << "ok" << endl; } QPID_AUTO_TEST_CASE(append_2) { cout << test_filename << ".append_2: " << flush; const unsigned test_case_num = 0x12345; const u_int32_t num_msgs = 0x100; const std::size_t min_data_size = 0x1000; const std::size_t max_data_size = 0; const bool auto_deq = true; const std::size_t min_xid_size = 0x200; const std::size_t max_xid_size = 0x200; using mrg::jtt::test_case; const test_case::transient_t transient = test_case::JTT_PERSISTNET; const test_case::external_t external = test_case::JDL_INTERNAL; const string comment = "This is a test"; test_case::shared_ptr tcp(new test_case(test_case_num, num_msgs, min_data_size, max_data_size, auto_deq, min_xid_size, max_xid_size, transient, external, comment)); test_case_set tcs; tcs.append(tcp); BOOST_CHECK(!tcs.empty()); BOOST_CHECK_EQUAL(tcs.size(), unsigned(1)); tcp = tcs[0]; BOOST_CHECK_EQUAL(tcp->test_case_num(), test_case_num); BOOST_CHECK_EQUAL(tcp->num_msgs(), num_msgs); BOOST_CHECK_EQUAL(tcp->min_data_size(), min_data_size); BOOST_CHECK_EQUAL(tcp->max_data_size(), max_data_size); BOOST_CHECK_EQUAL(tcp->min_xid_size(), min_xid_size); BOOST_CHECK_EQUAL(tcp->max_xid_size(), max_xid_size); BOOST_CHECK_EQUAL(tcp->transient(), transient); BOOST_CHECK_EQUAL(tcp->external(), external); BOOST_CHECK_EQUAL(tcp->comment(), comment); cout << "ok" << endl; } QPID_AUTO_TEST_CASE(append_from_csv) { cout << test_filename << ".append_from_csv: " << flush; test_case_set tcs; BOOST_REQUIRE_MESSAGE(check_csv_file(csv_file.c_str()), "Test CSV file \"" << csv_file << "\" is missing."); tcs.append_from_csv(csv_file, false); BOOST_CHECK(!tcs.empty()); BOOST_CHECK_EQUAL(tcs.size(), unsigned(44)); BOOST_CHECK_EQUAL(tcs.ignored(), unsigned(0)); tcs.clear(); BOOST_CHECK(tcs.empty()); tcs.append_from_csv(csv_file, true); BOOST_CHECK(!tcs.empty()); BOOST_CHECK_EQUAL(tcs.size(), unsigned(18)); BOOST_CHECK_EQUAL(tcs.ignored(), unsigned(26)); cout << "ok" << endl; } QPID_AUTO_TEST_SUITE_END() qpid-cpp-store-debian-0.16/tests/jrnl/jtt/test_case_set.hpp0000644000176300017630000000713011142067437023007 0ustar cajuscajus/* * Copyright (c) 2008 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #ifndef mrg_jtt_test_case_set_hpp #define mrg_jtt_test_case_set_hpp #include "test_case.hpp" #include #include #include #include namespace mrg { namespace jtt { class test_case_set { public: enum csv_col_enum { CSV_TC_NUM = 0, CSV_TC_NUM_MSGS, CSV_TC_MIN_DATA_SIZE, CSV_TC_MAX_DATA_SIZE, CSV_TC_AUTO_DEQ, CSV_TC_MIN_XID_SIZE, CSV_TC_MAX_XID_SIZE, CSV_TC_TRANSIENT, CSV_TC_EXTERNAL, CSV_TC_COMMENT }; typedef std::pair csv_pair; typedef std::map csv_map; typedef csv_map::const_iterator csv_map_citr; static csv_map std_csv_map; typedef std::vector tcl; typedef tcl::iterator tcl_itr; typedef tcl::const_iterator tcl_citr; typedef boost::tokenizer > csv_tok; typedef csv_tok::const_iterator csv_tok_citr; private: tcl _tc_list; static const bool _map_init; unsigned _csv_ignored; public: test_case_set(); test_case_set(const std::string& csv_filename, const bool recover_mode, const csv_map& cols = std_csv_map); virtual ~test_case_set(); inline unsigned size() const { return _tc_list.size(); } inline unsigned ignored() const { return _csv_ignored; } inline bool empty() const { return _tc_list.empty(); } inline void append(const test_case::shared_ptr& tc) { _tc_list.push_back(tc); } void append(const unsigned test_case_num, const u_int32_t num_msgs, const std::size_t min_data_size, const std::size_t max_data_size, const bool auto_deq, const std::size_t min_xid_size, const std::size_t max_xid_size, const test_case::transient_t transient, const test_case::external_t external, const std::string& comment); void append_from_csv(const std::string& csv_filename, const bool recover_mode, const csv_map& cols = std_csv_map); inline tcl_itr begin() { return _tc_list.begin(); } inline tcl_itr end() { return _tc_list.end(); } inline const test_case::shared_ptr& operator[](unsigned i) { return _tc_list[i]; } inline void clear() { _tc_list.clear(); } private: test_case::shared_ptr get_tc_from_csv(const std::string& csv_line, const csv_map& cols); static bool __init(); }; } // namespace jtt } // namespace mrg #endif // ifndef mrg_jtt_test_case_set_hpp qpid-cpp-store-debian-0.16/tests/jrnl/jtt/_ut_jrnl_init_params.cpp0000644000176300017630000000724511142067437024366 0ustar cajuscajus/* * Copyright (c) 2008 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #include "../../unit_test.h" #include "jrnl_init_params.hpp" #include using namespace boost::unit_test; using namespace mrg::jtt; using namespace std; QPID_AUTO_TEST_SUITE(jtt_jrnl_init_params) const string test_filename("_ut_jrnl_init_params"); QPID_AUTO_TEST_CASE(constructor) { cout << test_filename << ".constructor: " << flush; const string jid = "jid"; const string jdir = "jdir"; const string bfn = "base filename"; const u_int16_t num_jfiles = 123; const bool ae = false; const u_int16_t ae_max_jfiles = 456; const u_int32_t jfsize_sblks = 789; jrnl_init_params jip(jid, jdir, bfn, num_jfiles, ae, ae_max_jfiles, jfsize_sblks); BOOST_CHECK_EQUAL(jip.jid(), jid); BOOST_CHECK_EQUAL(jip.jdir(), jdir); BOOST_CHECK_EQUAL(jip.base_filename(), bfn); BOOST_CHECK_EQUAL(jip.num_jfiles(), num_jfiles); BOOST_CHECK_EQUAL(jip.is_ae(), ae); BOOST_CHECK_EQUAL(jip.ae_max_jfiles(), ae_max_jfiles); BOOST_CHECK_EQUAL(jip.jfsize_sblks(), jfsize_sblks); cout << "ok" << endl; } QPID_AUTO_TEST_CASE(copy_constructor_1) { cout << test_filename << ".copy_constructor_1: " << flush; const string jid = "jid"; const string jdir = "jdir"; const string bfn = "base filename"; const u_int16_t num_jfiles = 123; const bool ae = false; const u_int16_t ae_max_jfiles = 456; const u_int32_t jfsize_sblks = 789; jrnl_init_params jip1(jid, jdir, bfn, num_jfiles, ae, ae_max_jfiles, jfsize_sblks); jrnl_init_params jip2(jip1); BOOST_CHECK_EQUAL(jip2.jid(), jid); BOOST_CHECK_EQUAL(jip2.jdir(), jdir); BOOST_CHECK_EQUAL(jip2.base_filename(), bfn); BOOST_CHECK_EQUAL(jip2.num_jfiles(), num_jfiles); BOOST_CHECK_EQUAL(jip2.is_ae(), ae); BOOST_CHECK_EQUAL(jip2.ae_max_jfiles(), ae_max_jfiles); BOOST_CHECK_EQUAL(jip2.jfsize_sblks(), jfsize_sblks); cout << "ok" << endl; } QPID_AUTO_TEST_CASE(copy_constructor_2) { cout << test_filename << ".copy_constructor_2: " << flush; const string jid = "jid"; const string jdir = "jdir"; const string bfn = "base filename"; const u_int16_t num_jfiles = 123; const bool ae = false; const u_int16_t ae_max_jfiles = 456; const u_int32_t jfsize_sblks = 789; jrnl_init_params::shared_ptr p(new jrnl_init_params(jid, jdir, bfn, num_jfiles, ae, ae_max_jfiles, jfsize_sblks)); jrnl_init_params jip2(p.get()); BOOST_CHECK_EQUAL(jip2.jid(), jid); BOOST_CHECK_EQUAL(jip2.jdir(), jdir); BOOST_CHECK_EQUAL(jip2.base_filename(), bfn); BOOST_CHECK_EQUAL(jip2.num_jfiles(), num_jfiles); BOOST_CHECK_EQUAL(jip2.is_ae(), ae); BOOST_CHECK_EQUAL(jip2.ae_max_jfiles(), ae_max_jfiles); BOOST_CHECK_EQUAL(jip2.jfsize_sblks(), jfsize_sblks); cout << "ok" << endl; } QPID_AUTO_TEST_SUITE_END() qpid-cpp-store-debian-0.16/tests/jrnl/jtt/_ut_test_case_result_agregation.cpp0000644000176300017630000001671111142067437026601 0ustar cajuscajus/* * Copyright (c) 2008 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #include "../../unit_test.h" #include #include #include "test_case_result_agregation.hpp" using namespace boost::unit_test; using namespace mrg::journal; using namespace mrg::jtt; using namespace std; QPID_AUTO_TEST_SUITE(jtt_test_case_result_agregation) const string test_filename("_ut_test_case_result_agregation"); // === Helper functions === void check_agregate(const test_case_result_agregation& tcra, const u_int32_t num_enq, const u_int32_t num_deq, const u_int32_t num_reads, const u_int32_t num_results, const u_int32_t num_exceptions, const std::time_t secs, const long nsec) { BOOST_CHECK_EQUAL(tcra.num_enq(), num_enq); BOOST_CHECK_EQUAL(tcra.num_deq(), num_deq); BOOST_CHECK_EQUAL(tcra.num_read(), num_reads); BOOST_CHECK_EQUAL(tcra.num_results(), num_results); BOOST_CHECK_EQUAL(tcra.exception_count(), num_exceptions); BOOST_CHECK_EQUAL(tcra.exception(), num_exceptions > 0); const time_ns& ts1 = tcra.test_time(); BOOST_CHECK_EQUAL(ts1.tv_sec, secs); BOOST_CHECK_EQUAL(ts1.tv_nsec, nsec); } test_case_result::shared_ptr make_result(const string& jid, const u_int32_t num_enq, const u_int32_t num_deq, const u_int32_t num_reads, const std::time_t secs, const long nsec) { test_case_result::shared_ptr tcrp(new test_case_result(jid)); for (unsigned i=0; iincr_num_enq(); for (unsigned i=0; iincr_num_deq(); for (unsigned i=0; iincr_num_read(); time_ns ts(secs, nsec); tcrp->set_test_time(ts); return tcrp; } // === Test suite === QPID_AUTO_TEST_CASE(constructor_1) { cout << test_filename << ".constructor_1: " << flush; test_case_result_agregation tcra; BOOST_CHECK_EQUAL(tcra.tc_average_mode(), true); BOOST_CHECK_EQUAL(tcra.jid(), "Average"); BOOST_CHECK_EQUAL(tcra.exception(), false); BOOST_CHECK_EQUAL(tcra.exception_count(), 0U); const time_ns& ts1 = tcra.start_time(); BOOST_CHECK(ts1.is_zero()); const time_ns& ts2 = tcra.stop_time(); BOOST_CHECK(ts2.is_zero()); const time_ns& ts3 = tcra.test_time(); BOOST_CHECK(ts3.is_zero()); cout << "ok" << endl; } QPID_AUTO_TEST_CASE(constructor_2) { cout << test_filename << ".constructor_2: " << flush; string jid("journal id"); test_case_result_agregation tcra(jid); BOOST_CHECK_EQUAL(tcra.tc_average_mode(), false); BOOST_CHECK_EQUAL(tcra.jid(), jid); BOOST_CHECK_EQUAL(tcra.exception(), false); BOOST_CHECK_EQUAL(tcra.exception_count(), 0U); const time_ns& ts1 = tcra.start_time(); BOOST_CHECK(ts1.is_zero()); const time_ns& ts2 = tcra.stop_time(); BOOST_CHECK(ts2.is_zero()); const time_ns& ts3 = tcra.test_time(); BOOST_CHECK(ts3.is_zero()); cout << "ok" << endl; } QPID_AUTO_TEST_CASE(add_test_case) { cout << test_filename << ".add_test_case: " << flush; string jid("jid1"); test_case_result::shared_ptr tcrp1 = make_result("jid1", 10, 10, 0, 1, 101010101L); test_case_result::shared_ptr tcrp2 = make_result("jid1", 25, 0, 35, 10, 20202020L); test_case_result::shared_ptr tcrp3 = make_result("jid1", 0, 15, 5, 2, 555555555L); test_case_result::shared_ptr tcrp4 = make_result("jid2", 100, 100, 100, 100, 323232324L); test_case_result::shared_ptr tcrp5 = make_result("jid1", 5, 0, 0, 0, 100L); tcrp5->add_exception(string("error 1"), false); test_case_result::shared_ptr tcrp6 = make_result("jid3", 0, 5, 0, 0, 100L); jexception e(0x123, "exception 2"); tcrp6->add_exception(e, false); test_case_result::shared_ptr tcrp7 = make_result("jid1", 0, 0, 0, 0, 0L); test_case_result::shared_ptr tcrp8 = make_result("jid1", 200, 100, 300, 12, 323232224L); test_case_result_agregation tcra(jid); check_agregate(tcra, 0, 0, 0, 0, 0, 0, 0L); tcra.add_test_result(tcrp1); check_agregate(tcra, 10, 10, 0, 1, 0, 1, 101010101L); tcra.add_test_result(tcrp2); check_agregate(tcra, 35, 10, 35, 2, 0, 11, 121212121L); tcra.add_test_result(tcrp3); check_agregate(tcra, 35, 25, 40, 3, 0, 13, 676767676L); tcra.add_test_result(tcrp4); check_agregate(tcra, 35, 25, 40, 3, 0, 13, 676767676L); tcra.add_test_result(tcrp5); check_agregate(tcra, 40, 25, 40, 4, 1, 13, 676767776L); tcra.add_test_result(tcrp6); check_agregate(tcra, 40, 25, 40, 4, 1, 13, 676767776L); tcra.add_test_result(tcrp7); check_agregate(tcra, 40, 25, 40, 5, 1, 13, 676767776L); tcra.add_test_result(tcrp8); check_agregate(tcra, 240, 125, 340, 6, 1, 26, 0L); cout << "ok" << endl; } QPID_AUTO_TEST_CASE(add_test_case_average) { cout << test_filename << ".add_test_case_average: " << flush; test_case_result::shared_ptr tcrp1 = make_result("jid1", 10, 10, 0, 1, 101010101L); test_case_result::shared_ptr tcrp2 = make_result("jid2", 25, 0, 35, 10, 20202020L); test_case_result::shared_ptr tcrp3 = make_result("jid3", 0, 15, 5, 2, 555555555L); test_case_result::shared_ptr tcrp4 = make_result("jid4", 100, 100, 100, 100, 323232324L); test_case_result::shared_ptr tcrp5 = make_result("jid5", 5, 0, 0, 0, 100L); tcrp5->add_exception(string("error 1"), false); test_case_result::shared_ptr tcrp6 = make_result("jid6", 0, 5, 0, 0, 100L); jexception e(0x123, "exception 2"); tcrp6->add_exception(e, false); test_case_result::shared_ptr tcrp7 = make_result("jid7", 0, 0, 0, 0, 0L); test_case_result::shared_ptr tcrp8 = make_result("jid8", 200, 100, 300, 12, 222222022L); test_case_result_agregation tcra; check_agregate(tcra, 0, 0, 0, 0, 0, 0, 0L); tcra.add_test_result(tcrp1); check_agregate(tcra, 10, 10, 0, 1, 0, 1, 101010101L); tcra.add_test_result(tcrp2); check_agregate(tcra, 35, 10, 35, 2, 0, 11, 121212121L); tcra.add_test_result(tcrp3); check_agregate(tcra, 35, 25, 40, 3, 0, 13, 676767676L); tcra.add_test_result(tcrp4); check_agregate(tcra, 135, 125, 140, 4, 0, 114, 0L); tcra.add_test_result(tcrp5); check_agregate(tcra, 140, 125, 140, 5, 1, 114, 100L); tcra.add_test_result(tcrp6); check_agregate(tcra, 140, 130, 140, 6, 2, 114, 200L); tcra.add_test_result(tcrp7); check_agregate(tcra, 140, 130, 140, 7, 2, 114, 200L); tcra.add_test_result(tcrp8); check_agregate(tcra, 340, 230, 440, 8, 2, 126, 222222222L); cout << "ok" << endl; } QPID_AUTO_TEST_SUITE_END() qpid-cpp-store-debian-0.16/tests/jrnl/jtt/_ut_test_case_result.cpp0000644000176300017630000001634611142067437024405 0ustar cajuscajus/* * Copyright (c) 2008 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #include "../../unit_test.h" #include #include "jrnl/jexception.hpp" #include "test_case_result.hpp" using namespace boost::unit_test; using namespace mrg::journal; using namespace mrg::jtt; using namespace std; QPID_AUTO_TEST_SUITE(jtt_test_case_result) const string test_filename("_ut_test_case_result"); QPID_AUTO_TEST_CASE(constructor) { cout << test_filename << ".constructor: " << flush; const string jid("journal id 1"); test_case_result tcr(jid); BOOST_CHECK_EQUAL(tcr.jid(), jid); BOOST_CHECK_EQUAL(tcr.exception(), false); BOOST_CHECK_EQUAL(tcr.exception_count(), 0U); const time_ns& ts1 = tcr.start_time(); BOOST_CHECK(ts1.is_zero()); const time_ns& ts2 = tcr.stop_time(); BOOST_CHECK(ts2.is_zero()); const time_ns& ts3 = tcr.test_time(); BOOST_CHECK(ts3.is_zero()); cout << "ok" << endl; } QPID_AUTO_TEST_CASE(start_stop) { cout << test_filename << ".start_stop: " << flush; const string jid("journal id 2"); test_case_result tcr(jid); BOOST_CHECK_EQUAL(tcr.exception(), false); BOOST_CHECK_EQUAL(tcr.exception_count(), 0U); const time_ns& ts1 = tcr.start_time(); BOOST_CHECK(ts1.is_zero()); const time_ns& ts2 = tcr.stop_time(); BOOST_CHECK(ts2.is_zero()); const time_ns& ts3 = tcr.test_time(); BOOST_CHECK(ts3.is_zero()); tcr.set_start_time(); BOOST_CHECK_EQUAL(tcr.exception(), false); BOOST_CHECK_EQUAL(tcr.exception_count(), 0U); const time_ns& ts4 = tcr.start_time(); BOOST_CHECK(!ts4.is_zero()); const time_ns& ts5 = tcr.stop_time(); BOOST_CHECK(ts5.is_zero()); const time_ns& ts6 = tcr.test_time(); BOOST_CHECK(ts6.is_zero()); ::usleep(1100000); // 1.1 sec in microseconds tcr.set_stop_time(); BOOST_CHECK_EQUAL(tcr.exception(), false); BOOST_CHECK_EQUAL(tcr.exception_count(), 0U); const time_ns& ts7 = tcr.stop_time(); BOOST_CHECK(!ts7.is_zero()); const time_ns& ts8 = tcr.test_time(); BOOST_CHECK(ts8.tv_sec == 1); BOOST_CHECK(ts8.tv_nsec > 100000000); // 0.1 sec in nanoseconds BOOST_CHECK(ts8.tv_nsec < 200000000); // 0.2 sec in nanoseconds cout << "ok" << endl; } QPID_AUTO_TEST_CASE(start_exception_stop_1) { cout << test_filename << ".start_exception_stop_1: " << flush; const string jid("journal id 3"); test_case_result tcr(jid); const u_int32_t err_code = 0x321; const string err_msg = "exception message"; const jexception e(err_code, err_msg); tcr.set_start_time(); ::usleep(1100000); // 1.1 sec in microseconds tcr.add_exception(e); BOOST_CHECK_EQUAL(tcr.exception(), true); BOOST_CHECK_EQUAL(tcr.exception_count(), 1U); BOOST_CHECK_EQUAL(tcr[0], e.what()); const time_ns& ts1 = tcr.stop_time(); BOOST_CHECK(!ts1.is_zero()); const time_ns& ts2 = tcr.test_time(); BOOST_CHECK(ts2.tv_sec == 1); BOOST_CHECK(ts2.tv_nsec > 100000000); // 0.1 sec in nanoseconds BOOST_CHECK(ts2.tv_nsec < 200000000); // 0.2 sec in nanoseconds cout << "ok" << endl; } QPID_AUTO_TEST_CASE(start_exception_stop_2) { cout << test_filename << ".start_exception_stop_2: " << flush; const string jid("journal id 4"); test_case_result tcr(jid); const string err_msg = "exception message"; tcr.set_start_time(); ::usleep(1100000); // 1.1 sec in microseconds tcr.add_exception(err_msg); BOOST_CHECK_EQUAL(tcr.exception(), true); BOOST_CHECK_EQUAL(tcr.exception_count(), 1U); BOOST_CHECK_EQUAL(tcr[0], err_msg); const time_ns& ts1 = tcr.stop_time(); BOOST_CHECK(!ts1.is_zero()); const time_ns& ts2 = tcr.test_time(); BOOST_CHECK(ts2.tv_sec == 1); BOOST_CHECK(ts2.tv_nsec > 100000000); // 0.1 sec in nanoseconds BOOST_CHECK(ts2.tv_nsec < 200000000); // 0.2 sec in nanoseconds cout << "ok" << endl; } QPID_AUTO_TEST_CASE(start_exception_stop_3) { cout << test_filename << ".start_exception_stop_3: " << flush; const string jid("journal id 5"); test_case_result tcr(jid); const char* err_msg = "exception message"; tcr.set_start_time(); ::usleep(1100000); // 1.1 sec in microseconds tcr.add_exception(err_msg); BOOST_CHECK_EQUAL(tcr.exception(), true); BOOST_CHECK_EQUAL(tcr.exception_count(), 1U); BOOST_CHECK_EQUAL(tcr[0], err_msg); const time_ns& ts1 = tcr.stop_time(); BOOST_CHECK(!ts1.is_zero()); const time_ns& ts2 = tcr.test_time(); BOOST_CHECK(ts2.tv_sec == 1); BOOST_CHECK(ts2.tv_nsec > 100000000); // 0.1 sec in nanoseconds BOOST_CHECK(ts2.tv_nsec < 200000000); // 0.2 sec in nanoseconds cout << "ok" << endl; } QPID_AUTO_TEST_CASE(start_exception) { cout << test_filename << ".start_exception: " << flush; const string jid("journal id 6"); test_case_result tcr(jid); u_int32_t err_code = 0x654; const string err_msg = "exception message"; const jexception e(err_code, err_msg); tcr.set_start_time(); ::usleep(1100000); // 1.1 sec in microseconds tcr.add_exception(e, false); BOOST_CHECK_EQUAL(tcr.exception(), true); BOOST_CHECK_EQUAL(tcr.exception_count(), 1U); BOOST_CHECK_EQUAL(tcr[0], e.what()); const time_ns& ts1 = tcr.stop_time(); BOOST_CHECK(ts1.is_zero()); const time_ns& ts2 = tcr.test_time(); BOOST_CHECK(ts2.is_zero()); cout << "ok" << endl; } QPID_AUTO_TEST_CASE(counters) { cout << test_filename << ".counters: " << flush; const u_int32_t num_enq = 125; const u_int32_t num_deq = 64; const u_int32_t num_read = 22; const string jid("journal id 7"); test_case_result tcr(jid); BOOST_CHECK_EQUAL(tcr.num_enq(), u_int32_t(0)); BOOST_CHECK_EQUAL(tcr.num_deq(), u_int32_t(0)); BOOST_CHECK_EQUAL(tcr.num_read(), u_int32_t(0)); for (unsigned i=0; i #include "read_arg.hpp" namespace mrg { namespace jtt { struct args { boost::program_options::options_description _options_descr; boost::program_options::variables_map _vmap; // Add args here std::string jfile_analyzer; std::string test_case_csv_file_name; std::string journal_dir; bool format_chk; bool keep_jrnls; unsigned lld_rd_num; unsigned lld_skip_num; unsigned num_jrnls; unsigned pause_secs; bool randomize; read_arg read_mode; unsigned read_prob; bool recover_mode; bool repeat_flag; bool reuse_instance; unsigned seed; args(std::string opt_title); bool parse(int argc, char** argv); // return true if error, false if ok bool usage() const; // return true void print_args() const; void print_flags() const; }; } // namespace jtt } // namespace mrg #endif // ifndef mrg_jtt_args_hpp qpid-cpp-store-debian-0.16/tests/jrnl/jtt/read_arg.hpp0000644000176300017630000000374611142067437021737 0ustar cajuscajus/* * Copyright (c) 2008 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #ifndef mrg_jtt_read_arg_hpp #define mrg_jtt_read_arg_hpp #include #include namespace mrg { namespace jtt { class read_arg { public: enum read_mode_t { NONE, ALL, RANDOM, LAZYLOAD}; private: static std::map _map; static std::string _description; static const bool init; static bool __init(); read_mode_t _rm; public: inline read_arg() : _rm(NONE) {} inline read_arg(read_mode_t rm) : _rm(rm) {} inline read_mode_t val() const { return _rm; } inline void set_val(const read_mode_t rm) { _rm = rm; } void parse(const std::string& str); inline const std::string& str() const { return str(_rm); } static const std::string& str(const read_mode_t rm); static const std::string& descr(); friend std::ostream& operator<<(std::ostream& os, const read_arg& ra); friend std::istream& operator>>(std::istream& is, read_arg& ra); }; } // namespace jtt } // namespace mrg #endif // ifndef mrg_jtt_read_arg_hpp qpid-cpp-store-debian-0.16/tests/jrnl/jtt/_ut_test_case_set.csv0000644000176300017630000001432111142067437023662 0ustar cajuscajus# Copyright (c) 2008 Red Hat Inc. # # This file is part of the Qpid async store library msgstore.so. # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 # USA # # The GNU Lesser General Public License is available in the file COPYING. ,,,,,,,"Msg size",,"Xid size",,,,,"enq-size",,"deq-size",,"txn-size",, "Col. 0","1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20" "Test #","tf","pf","amn","mn incr","#msgs","ms incr","Min","Max","Min","Max","auto-deq","transient","extern","bytes","dblks","bytes","dblks","bytes","dblks","comment" ,,,,,,,,,,,,,,,,,,,, "Initialize only",,,,,,,,,,,,,,,,,,,, 0,"L",0,0,0,0,0,0,0,0,0,FALSE,FALSE,FALSE,44,1,0,0,0,0,"No messages - journal creation/initialization only" ,,,,,,,,,,,,,,,,,,,, "Simple message combinations of persistent/deq transientueued/non-dequeued, transactional/non-transactional",,,,,,,,,,,,,,,,,,,, 1,"L",1,1,0,1,0,10,10,0,0,FALSE,FALSE,FALSE,54,1,0,0,0,0,"1 * 10-byte message" 2,"L",1,10,0,10,0,10,10,0,0,FALSE,FALSE,FALSE,54,1,0,0,0,0,"10 * 10-byte message" 3,"L",1,1,0,1,0,10,10,0,0,FALSE,TRUE,FALSE,54,1,0,0,0,0,"1 * 10-byte message [transient]" 4,"L",1,10,0,10,0,10,10,0,0,FALSE,TRUE,FALSE,54,1,0,0,0,0,"10 * 10-byte message [transient]" 5,"L",1,1,0,1,0,10,10,10,10,FALSE,FALSE,FALSE,64,1,0,0,0,0,"1 * 10-byte message [txn]" 6,"L",1,10,0,10,0,10,10,10,10,FALSE,FALSE,FALSE,64,1,0,0,0,0,"10 * 10-byte message [txn]" 7,"L",1,1,0,1,0,10,10,10,10,FALSE,TRUE,FALSE,64,1,0,0,0,0,"1 * 10-byte message [txn transient]" 8,"L",1,10,0,10,0,10,10,10,10,FALSE,TRUE,FALSE,64,1,0,0,0,0,"10 * 10-byte message [txn transient]" 9,"L",1,1,0,1,0,10,10,0,0,TRUE,FALSE,FALSE,54,1,32,1,0,0,"1 * 10-byte message [deq]" 10,"L",1,10,0,10,0,10,10,0,0,TRUE,FALSE,FALSE,54,1,32,1,0,0,"10 * 10-byte message [deq]" 11,"L",1,1,0,1,0,10,10,0,0,TRUE,TRUE,FALSE,54,1,32,1,0,0,"1 * 10-byte message [deq transient]" 12,"L",1,10,0,10,0,10,10,0,0,TRUE,TRUE,FALSE,54,1,32,1,0,0,"10 * 10-byte message [deq transient]" 13,"L",1,1,0,1,0,10,10,10,10,TRUE,FALSE,FALSE,64,1,54,1,46,1,"1 * 10-byte message [deq txn]" 14,"L",1,10,0,10,0,10,10,10,10,TRUE,FALSE,FALSE,64,1,54,1,46,1,"10 * 10-byte message [deq txn]" 15,"L",1,1,0,1,0,10,10,10,10,TRUE,TRUE,FALSE,64,1,54,1,46,1,"1 * 10-byte message [txn deq transient]" 16,"L",1,10,0,10,0,10,10,10,10,TRUE,TRUE,FALSE,64,1,54,1,46,1,"10 * 10-byte message [txn deq transient]" 17,"L",1,1,0,1,0,10,10,0,0,FALSE,FALSE,TRUE,54,1,0,0,0,0,"1 * 10-byte message [extern]" 18,"L",1,10,0,10,0,10,10,0,0,FALSE,FALSE,TRUE,54,1,0,0,0,0,"10 * 10-byte message [extern]" 19,"L",1,1,0,1,0,10,10,0,0,FALSE,TRUE,TRUE,54,1,0,0,0,0,"1 * 10-byte message [transient extern]" 20,"L",1,10,0,10,0,10,10,0,0,FALSE,TRUE,TRUE,54,1,0,0,0,0,"10 * 10-byte message [transient extern]" 21,"L",1,1,0,1,0,10,10,10,10,FALSE,FALSE,TRUE,64,1,0,0,0,0,"1 * 10-byte message [txn extern]" 22,"L",1,10,0,10,0,10,10,10,10,FALSE,FALSE,TRUE,64,1,0,0,0,0,"10 * 10-byte message [txn extern]" 23,"L",1,1,0,1,0,10,10,10,10,FALSE,TRUE,TRUE,64,1,0,0,0,0,"1 * 10-byte message [txn transient extern]" 24,"L",1,10,0,10,0,10,10,10,10,FALSE,TRUE,TRUE,64,1,0,0,0,0,"10 * 10-byte message [txn transient extern]" 25,"L",1,1,0,1,0,10,10,0,0,TRUE,FALSE,TRUE,54,1,32,1,0,0,"1 * 10-byte message [deq extern]" 26,"L",1,10,0,10,0,10,10,0,0,TRUE,FALSE,TRUE,54,1,32,1,0,0,"10 * 10-byte message [deq extern]" 27,"L",1,1,0,1,0,10,10,0,0,TRUE,TRUE,TRUE,54,1,32,1,0,0,"1 * 10-byte message [deq transient extern]" 28,"L",1,10,0,10,0,10,10,0,0,TRUE,TRUE,TRUE,54,1,32,1,0,0,"10 * 10-byte message [deq transient extern]" 29,"L",1,1,0,1,0,10,10,10,10,TRUE,FALSE,TRUE,64,1,54,1,46,1,"1 * 10-byte message [deq txn extern]" 30,"L",1,10,0,10,0,10,10,10,10,TRUE,FALSE,TRUE,64,1,54,1,46,1,"10 * 10-byte message [deq txn extern]" 31,"L",1,1,0,1,0,10,10,10,10,TRUE,TRUE,TRUE,64,1,54,1,46,1,"1 * 10-byte message [txn deq transient extern]" 32,"L",1,10,0,10,0,10,10,10,10,TRUE,TRUE,TRUE,64,1,54,1,46,1,"10 * 10-byte message [txn deq transient extern]" ,,,,,,,,,,,,,,,,,,,, "High volume tests of random message lengths - RHM_WRONLY req'd for auto-dequeue == FALSE",,,,,,,,,,,,,,,,,,,, 33,"M",1,5000000,0,5000000,0,0,100,1,100,FALSE,RANDOM,RANDOM,244,2,0,0,0,0,"100 bytes xid max + 100 bytes data max [txn]" 34,"M",3,3000000,0,3000000,0,0,300,1,300,FALSE,RANDOM,RANDOM,644,6,0,0,0,0,"300 bytes xid max + 300 bytes data max [txn]" 35,"M",10,1600000,0,1600000,0,0,1000,1,1000,FALSE,RANDOM,RANDOM,2044,16,0,0,0,0,"1000 bytes xid max + 1000 bytes data max [txn]" 36,"M",30,600000,0,600000,0,0,3000,1,3000,FALSE,RANDOM,RANDOM,6044,48,0,0,0,0,"3000 bytes xid max + 3000 bytes data max [txn]" 37,"M",100,200000,0,200000,0,0,10000,1,10000,FALSE,RANDOM,RANDOM,20044,157,0,0,0,0,"10000 bytes xid max + 10000 bytes data max [txn]" 38,"M",300,60000,0,60000,0,0,30000,1,30000,FALSE,RANDOM,RANDOM,60044,470,0,0,0,0,"30000 bytes xid max + 30000 bytes data max [txn]" 39,"M",1000,20000,0,20000,0,0,100000,1,100000,FALSE,RANDOM,RANDOM,200044,1563,0,0,0,0,"100000 bytes xid max + 100000 bytes data max [txn]" ,,,,,,,,,,,,,,,,,,,, "STANDARD PERFORMANCE BENCHMARK: 10,000,000 writes, data=212b (2 dblks)",,,,,,,,,,,,,,,,,,,, 40,"M",1,10000000,0,10000000,0,212,212,0,0,FALSE,FALSE,FALSE,256,2,0,0,0,0,"212 bytes data (2 dblks enq)" 41,"M",1,10000000,0,10000000,0,148,148,64,64,FALSE,FALSE,FALSE,256,2,0,0,0,0,"148 bytes data + 64 bytes xid (2 dblks enq)" 42,"M",1,10000000,0,10000000,0,212,212,0,0,TRUE,FALSE,FALSE,256,2,32,1,0,0,"212 bytes data (2 dblks enq + 1 dblk deq)" 43,"M",1,10000000,0,10000000,0,148,148,64,64,TRUE,FALSE,FALSE,256,2,108,1,100,1,"148 bytes data + 64 bytes xid (2 dblks enq + 1 dblks deq + 1 dblks txn)" qpid-cpp-store-debian-0.16/tests/jrnl/jtt/data_src.cpp0000644000176300017630000000445111372046135021735 0ustar cajuscajus/* * Copyright (c) 2008 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #include "data_src.hpp" #include #include #include namespace mrg { namespace jtt { char data_src::_data_src[data_src::max_dsize]; char data_src::_xid_src[data_src::max_xsize]; bool data_src::_initialized = data_src::__init(); u_int64_t data_src::_xid_cnt = 0ULL; mrg::journal::smutex data_src::_sm; data_src::data_src() {} bool data_src::__init() { for (unsigned i=0; i= max_dsize) return 0; return _data_src + offs; } std::string data_src::get_xid(const std::size_t xid_size) { if (xid_size == 0) return ""; std::ostringstream oss; oss << std::setfill('0'); if (xid_size < 9) oss << std::setw(xid_size) << get_xid_cnt(); else if (xid_size < 13) oss << "xid:" << std::setw(xid_size - 4) << get_xid_cnt(); else { oss << "xid:" << std::setw(8) << get_xid_cnt() << ":"; oss.write(get_xid_content(13), xid_size - 13); } return oss.str(); } const char* data_src::get_xid_content(const std::size_t offs) { if (offs >= max_xsize) return 0; return _xid_src + offs; } } // namespace jtt } // namespace mrg qpid-cpp-store-debian-0.16/tests/jrnl/jtt/_ut_read_arg.cpp0000644000176300017630000001135711142067437022576 0ustar cajuscajus/* * Copyright (c) 2008 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #include "../../unit_test.h" #include #include "read_arg.hpp" #include #include namespace po = boost::program_options; using namespace mrg::jtt; using namespace boost::unit_test; using namespace std; QPID_AUTO_TEST_SUITE(jtt_read_arg) const string test_filename("_ut_read_arg"); QPID_AUTO_TEST_CASE(constructor) { cout << test_filename << ".constructor: " << flush; read_arg ra1; BOOST_CHECK_EQUAL(ra1.val(), read_arg::NONE); BOOST_CHECK_EQUAL(ra1.str(), "NONE"); read_arg ra2(read_arg::NONE); BOOST_CHECK_EQUAL(ra2.val(), read_arg::NONE); BOOST_CHECK_EQUAL(ra2.str(), "NONE"); read_arg ra3(read_arg::ALL); BOOST_CHECK_EQUAL(ra3.val(), read_arg::ALL); BOOST_CHECK_EQUAL(ra3.str(), "ALL"); read_arg ra4(read_arg::RANDOM); BOOST_CHECK_EQUAL(ra4.val(), read_arg::RANDOM); BOOST_CHECK_EQUAL(ra4.str(), "RANDOM"); read_arg ra5(read_arg::LAZYLOAD); BOOST_CHECK_EQUAL(ra5.val(), read_arg::LAZYLOAD); BOOST_CHECK_EQUAL(ra5.str(), "LAZYLOAD"); cout << "ok" << endl; } QPID_AUTO_TEST_CASE(set_val) { cout << test_filename << ".set_val: " << flush; read_arg ra; BOOST_CHECK_EQUAL(ra.val(), read_arg::NONE); BOOST_CHECK_EQUAL(ra.str(), "NONE"); ra.set_val(read_arg::ALL); BOOST_CHECK_EQUAL(ra.val(), read_arg::ALL); BOOST_CHECK_EQUAL(ra.str(), "ALL"); ra.set_val(read_arg::RANDOM); BOOST_CHECK_EQUAL(ra.val(), read_arg::RANDOM); BOOST_CHECK_EQUAL(ra.str(), "RANDOM"); ra.set_val(read_arg::LAZYLOAD); BOOST_CHECK_EQUAL(ra.val(), read_arg::LAZYLOAD); BOOST_CHECK_EQUAL(ra.str(), "LAZYLOAD"); cout << "ok" << endl; } QPID_AUTO_TEST_CASE(parse) { cout << test_filename << ".parse: " << flush; read_arg ra; ra.parse("LAZYLOAD"); BOOST_CHECK_EQUAL(ra.val(), read_arg::LAZYLOAD); BOOST_CHECK_EQUAL(ra.str(), "LAZYLOAD"); ra.parse("ALL"); BOOST_CHECK_EQUAL(ra.val(), read_arg::ALL); BOOST_CHECK_EQUAL(ra.str(), "ALL"); BOOST_CHECK_THROW(ra.parse(""), po::invalid_option_value) BOOST_CHECK_EQUAL(ra.val(), read_arg::ALL); BOOST_CHECK_EQUAL(ra.str(), "ALL"); BOOST_CHECK_THROW(ra.parse("abc123"), po::invalid_option_value) BOOST_CHECK_EQUAL(ra.val(), read_arg::ALL); BOOST_CHECK_EQUAL(ra.str(), "ALL"); ra.parse("NONE"); BOOST_CHECK_EQUAL(ra.val(), read_arg::NONE); BOOST_CHECK_EQUAL(ra.str(), "NONE"); ra.parse("RANDOM"); BOOST_CHECK_EQUAL(ra.val(), read_arg::RANDOM); BOOST_CHECK_EQUAL(ra.str(), "RANDOM"); cout << "ok" << endl; } QPID_AUTO_TEST_CASE(istream_) { cout << test_filename << ".istream_: " << flush; read_arg ra; istringstream ss1("LAZYLOAD", ios::in); ss1 >> ra; BOOST_CHECK_EQUAL(ra.val(), read_arg::LAZYLOAD); BOOST_CHECK_EQUAL(ra.str(), "LAZYLOAD"); istringstream ss2("ALL", ios::in); ss2 >> ra; BOOST_CHECK_EQUAL(ra.val(), read_arg::ALL); BOOST_CHECK_EQUAL(ra.str(), "ALL"); istringstream ss3("NONE", ios::in); ss3 >> ra; BOOST_CHECK_EQUAL(ra.val(), read_arg::NONE); BOOST_CHECK_EQUAL(ra.str(), "NONE"); istringstream ss4("RANDOM", ios::in); ss4 >> ra; BOOST_CHECK_EQUAL(ra.val(), read_arg::RANDOM); BOOST_CHECK_EQUAL(ra.str(), "RANDOM"); cout << "ok" << endl; } QPID_AUTO_TEST_CASE(ostream_) { cout << test_filename << ".ostream_: " << flush; ostringstream s1; read_arg ra(read_arg::LAZYLOAD); s1 << ra; BOOST_CHECK_EQUAL(s1.str(), "LAZYLOAD"); ra.set_val(read_arg::ALL); ostringstream s2; s2 << ra; BOOST_CHECK_EQUAL(s2.str(), "ALL"); ra.set_val(read_arg::NONE); ostringstream s3; s3 << ra; BOOST_CHECK_EQUAL(s3.str(), "NONE"); ra.set_val(read_arg::RANDOM); ostringstream s4; s4 << ra; BOOST_CHECK_EQUAL(s4.str(), "RANDOM"); cout << "ok" << endl; } QPID_AUTO_TEST_SUITE_END() qpid-cpp-store-debian-0.16/tests/jrnl/jtt/test_case_result_agregation.hpp0000644000176300017630000000573611301004173025725 0ustar cajuscajus/* * Copyright (c) 2008 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #ifndef mrg_jtt_test_case_result_agregation_hpp #define mrg_jtt_test_case_result_agregation_hpp #include "test_case_result.hpp" #include #include namespace mrg { namespace jtt { class test_case_result_agregation : public test_case_result { public: typedef boost::shared_ptr shared_ptr; typedef std::vector tcrp_list; typedef tcrp_list::const_iterator tcrp_list_citr; private: bool _tc_average; bool _fmt_chk_done; bool _fmt_chk_err; tcrp_list _res_list; public: test_case_result_agregation(); // used for average across jrnl instances test_case_result_agregation(const std::string& jid); virtual ~test_case_result_agregation(); void add_test_result(const test_case_result::shared_ptr& tcrp); inline bool tc_average_mode() const { return _tc_average; } inline bool fmt_chk_done() const { return _fmt_chk_done; } inline bool fmt_chk_res() const { return _fmt_chk_err; } inline void set_fmt_chk_res(const bool err) { _fmt_chk_done = true; _fmt_chk_err |= err; if (err) add_exception("Journal format error"); } inline u_int32_t num_results() const { return _res_list.size(); } inline tcrp_list_citr rlist_begin() const { return _res_list.begin(); } inline tcrp_list_citr rlist_end() const { return _res_list.end(); } inline const test_case_result::shared_ptr& operator[](unsigned i) const { return _res_list[i]; } bool exception() const; unsigned exception_count() const; void clear(); const std::string str(const bool last_only, const bool summary) const; private: const std::string str_full(const bool last_only) const; const std::string str_summary(const bool last_only) const; const journal::time_ns& add_test_time(const journal::time_ns& t); }; } // namespace jtt } // namespace mrg #endif // ifndef mrg_jtt_test_case_result_agregation_hpp qpid-cpp-store-debian-0.16/tests/jrnl/jtt/read_arg.cpp0000644000176300017630000000456111142067437021726 0ustar cajuscajus/* * Copyright (c) 2008 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #include "read_arg.hpp" #include #include namespace po = boost::program_options; namespace mrg { namespace jtt { std::map read_arg::_map; std::string read_arg::_description; const bool read_arg::init = __init(); // static init fn bool read_arg::__init() { // Set string versions of each enum option here _map["NONE"] = NONE; _map["ALL"] = ALL; _map["RANDOM"] = RANDOM; _map["LAZYLOAD"] = LAZYLOAD; _description = "Determines if and when messages will be read prior to dequeueing. " "Values: (NONE | ALL | RANDOM | LAZYLOAD)"; return true; } void read_arg::parse(const std::string& str) { std::map::const_iterator i = _map.find(str); if (i == _map.end()) throw po::invalid_option_value(str); _rm = i->second; } // static fn const std::string& read_arg::str(const read_mode_t rm) { std::map::const_iterator i = _map.begin(); while (i->second != rm && i != _map.end()) i++; assert(i != _map.end()); return i->first; } // static fn const std::string& read_arg::descr() { return _description; } std::ostream& operator<<(std::ostream& os, const read_arg& ra) { os << ra.str(); return os; } std::istream& operator>>(std::istream& is, read_arg& ra) { std::string s; is >> s; ra.parse(s); return is; } } // namespace jtt } // namespace mrg qpid-cpp-store-debian-0.16/tests/jrnl/jtt/test_case_result.cpp0000644000176300017630000001272611142067437023534 0ustar cajuscajus/* * Copyright (c) 2008 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #include "test_case_result.hpp" #include #include namespace mrg { namespace jtt { test_case_result::test_case_result(const std::string& jid): _jid(jid), _num_enq(0), _num_deq(0), _num_read(0), _num_rproc(0), _start_time(), _stop_time(), _stopped(false), _test_time(), _exception_list() {} test_case_result::~test_case_result() {} const std::string test_case_result::test_time_str() const { return _test_time.str(9); } void test_case_result::add_exception(const journal::jexception& e, const bool set_stop_time_flag) { if (!_stopped && set_stop_time_flag) { set_stop_time(); _stopped = true; } _exception_list.push_back(e.what()); } void test_case_result::add_exception(const std::string& err_str, const bool set_stop_time_flag) { if (!_stopped && set_stop_time_flag) { set_stop_time(); _stopped = true; } _exception_list.push_back(err_str); } void test_case_result::add_exception(const char* err_str, const bool set_stop_time_flag) { if (!_stopped && set_stop_time_flag) { set_stop_time(); _stopped = true; } _exception_list.push_back(err_str); } void test_case_result::clear() { _num_enq = 0; _num_deq = 0; _num_read = 0; _start_time.set_zero(); _stop_time.set_zero(); _test_time.set_zero(); _exception_list.clear(); } const std::string test_case_result::str(const bool summary) const { std::ostringstream oss; if (summary) { oss << _jid << ":"; oss << str_summary(); if (_exception_list.size()) oss << "; fail: " << _exception_list[0] << std::endl; else oss << "; ok" << std::endl; } else { oss << "--- Journal instance: jid=\"" << _jid << "\" ---" << std::endl; oss << str_full(); if (_exception_list.size()) oss << " exception/error:" << _exception_list[0] << std::endl; } return oss.str(); } const std::string test_case_result::str_full() const { const double t = _test_time.tv_sec + (_test_time.tv_nsec/1e9); const bool no_exception = _exception_list.empty(); std::ostringstream oss; oss.setf(std::ios::fixed, std::ios::floatfield); oss.precision(2); if (no_exception) { oss.precision(6); oss << " total test time: " << t << "s" << std::endl; } oss.precision(3); oss << " total number enqueues: " << _num_enq; if (no_exception) oss << " (" << (_num_enq / t) << " enq/sec)"; oss << std::endl; oss << " total number dequeues: " << _num_deq; if (no_exception) oss << " (" << (_num_deq / t) << " deq/sec)"; oss << std::endl; oss << "total write operations: " << (_num_enq + _num_deq); if (no_exception) oss << " (" << ((_num_enq + _num_deq) / t) << " wrops/sec)"; oss << std::endl; oss << " total number reads: " << _num_read; if (no_exception) oss << " (" << (_num_read / t) << " rd/sec)"; oss << std::endl; oss << " total operations: " << (_num_enq + _num_deq + _num_read); if (no_exception) oss << " (" << ((_num_enq + _num_deq + _num_read) / t) << " ops/sec)"; oss << std::endl; oss << " overall result: " << (no_exception ? "PASS" : "*** FAIL ***") << std::endl; return oss.str(); } const std::string test_case_result::str_summary() const { const double t = _test_time.tv_sec + (_test_time.tv_nsec/1e9); const bool no_exception = _exception_list.empty(); std::ostringstream oss; oss.setf(std::ios::fixed, std::ios::floatfield); if (no_exception) { oss.precision(6); oss << " t=" << t << "s;"; } else oss << " exception"; oss.precision(3); oss << " enq=" << _num_enq; if (no_exception) oss << " (" << (_num_enq / t) << ")"; oss << "; deq=" << _num_deq; if (no_exception) oss << " (" << (_num_deq / t) << ")"; oss << "; wr=" << (_num_enq + _num_deq); if (no_exception) oss << " (" << ((_num_enq + _num_deq) / t) << ")"; oss << "; rd=" << _num_read; if (no_exception) oss << " (" << (_num_read / t) << ")"; oss << "; tot=" << (_num_enq + _num_deq + _num_read); if (no_exception) oss << " (" << ((_num_enq + _num_deq + _num_read) / t) << ")"; return oss.str(); } void test_case_result::calc_test_time() { if (!_start_time.is_zero() && _stop_time >= _start_time) _test_time = _stop_time - _start_time; } } // namespace jtt } // namespace mrg qpid-cpp-store-debian-0.16/tests/jrnl/jtt/jrnl_init_params.cpp0000644000176300017630000000551111142067437023511 0ustar cajuscajus/* * Copyright (c) 2008 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #include "jrnl_init_params.hpp" namespace mrg { namespace jtt { jrnl_init_params::jrnl_init_params(const std::string& jid, const std::string& jdir, const std::string& base_filename, const u_int16_t num_jfiles, const bool ae, const u_int16_t ae_max_jfiles, const u_int32_t jfsize_sblks, const u_int16_t wcache_num_pages, const u_int32_t wcache_pgsize_sblks): _jid(jid), _jdir(jdir), _base_filename(base_filename), _num_jfiles(num_jfiles), _ae(ae), _ae_max_jfiles(ae_max_jfiles), _jfsize_sblks(jfsize_sblks), _wcache_num_pages(wcache_num_pages), _wcache_pgsize_sblks(wcache_pgsize_sblks) {} jrnl_init_params::jrnl_init_params(const jrnl_init_params& jp): _jid(jp._jid), _jdir(jp._jdir), _base_filename(jp._base_filename), _num_jfiles(jp._num_jfiles), _ae(jp._ae), _ae_max_jfiles(jp._ae_max_jfiles), _jfsize_sblks(jp._jfsize_sblks), _wcache_num_pages(jp._wcache_num_pages), _wcache_pgsize_sblks(jp._wcache_pgsize_sblks) {} jrnl_init_params::jrnl_init_params(const jrnl_init_params* const jp_ptr): _jid(jp_ptr->_jid), _jdir(jp_ptr->_jdir), _base_filename(jp_ptr->_base_filename), _num_jfiles(jp_ptr->_num_jfiles), _ae(jp_ptr->_ae), _ae_max_jfiles(jp_ptr->_ae_max_jfiles), _jfsize_sblks(jp_ptr->_jfsize_sblks), _wcache_num_pages(jp_ptr->_wcache_num_pages), _wcache_pgsize_sblks(jp_ptr->_wcache_pgsize_sblks) {} // static initializers const u_int16_t jrnl_init_params::def_num_jfiles = 8; const bool jrnl_init_params::def_ae = false; const u_int16_t jrnl_init_params::def_ae_max_jfiles = 0; const u_int32_t jrnl_init_params::def_jfsize_sblks = 0xc00; const u_int16_t jrnl_init_params::def_wcache_num_pages = 32; const u_int32_t jrnl_init_params::def_wcache_pgsize_sblks = 64; } // namespace jtt } // namespace mrg qpid-cpp-store-debian-0.16/tests/jrnl/jtt/main.cpp0000644000176300017630000000335011142067437021101 0ustar cajuscajus/* * Copyright (c) 2008 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #include "test_mgr.hpp" #include "args.hpp" #include #include #define PACKAGE_NAME "Journal Test Tool" #define VERSION "0.1" namespace po = boost::program_options; int main(int argc, char** argv) { std::signal(SIGINT, mrg::jtt::test_mgr::signal_handler); std::signal(SIGTERM, mrg::jtt::test_mgr::signal_handler); std::cout << PACKAGE_NAME << " v." << VERSION << std::endl; std::ostringstream oss; oss << PACKAGE_NAME << " options"; mrg::jtt::args args(oss.str()); if (args.parse(argc, argv)) return 1; try { mrg::jtt::test_mgr tm(args); tm.run(); if (tm.error()) return 2; // One or more tests threw exceptions } catch (const std::exception& e) { std::cerr << e.what() << std::endl; return 3; } return 0; } qpid-cpp-store-debian-0.16/tests/jrnl/jtt/test_case.cpp0000644000176300017630000001272511142067437022135 0ustar cajuscajus/* * Copyright (c) 2008 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #include "test_case.hpp" #include #include #include namespace mrg { namespace jtt { test_case::test_case(const unsigned test_case_num, const u_int32_t num_msgs, const std::size_t min_data_size, const std::size_t max_data_size, const bool auto_deq, const std::size_t min_xid_size, const std::size_t max_xid_size, const transient_t transient, const external_t external, const std::string& comment): _test_case_num(test_case_num), _num_msgs(num_msgs), _min_data_size(min_data_size), _max_data_size(max_data_size), _auto_dequeue(auto_deq), _min_xid_size(min_xid_size), _max_xid_size(max_xid_size), _transient(transient), _external(external), _comment(comment), _result_average(), _result_jmap() {} test_case::~test_case() {} std::size_t test_case::this_data_size() const { if (_min_data_size == _max_data_size) return _max_data_size; std::size_t size_diff = _max_data_size - _min_data_size; return _min_data_size + std::size_t(1.0 * std::rand() * size_diff/(RAND_MAX + 1.0)); } std::size_t test_case::this_xid_size() const { // TODO: rework when probabilities are introduced. Assume 50% if _min_xid_size = 0 if (_max_xid_size == 0) return std::size_t(0); if (_min_xid_size == 0) { if (1.0 * std::rand() / RAND_MAX < 0.5) return std::size_t(0); } std::size_t size_diff = _max_xid_size - _min_xid_size; return _min_xid_size + std::size_t(1.0 * std::rand() * size_diff/(RAND_MAX + 1.0)); } bool test_case::this_transience() const { // TODO: rework when probabilities are introduced. Assume 50% if JTT_RANDOM if (_transient == JTT_TRANSIENT) return false; if (_transient == JTT_PERSISTNET) return true; return 1.0 * std::rand() / RAND_MAX < 0.5; } bool test_case::this_external() const { // TODO: rework when probabilities are introduced. Assume 50% if JDL_RANDOM if (_external == JDL_INTERNAL) return false; if (_external == JDL_EXTERNAL) return true; return 1.0 * std::rand() / RAND_MAX < 0.5; } void test_case::add_result(test_case_result::shared_ptr& tcrp) { _result_average.add_test_result(tcrp); res_map_citr ari = _result_jmap.find(tcrp->jid()); if (ari == _result_jmap.end()) { test_case_result_agregation::shared_ptr p(new test_case_result_agregation(tcrp->jid())); p->add_test_result(tcrp); _result_jmap.insert(res_map_pair(tcrp->jid(), p)); } else ari->second->add_test_result(tcrp); } void test_case::set_fmt_chk_res(const bool res, const std::string& jid) { _result_average.set_fmt_chk_res(res); res_map_citr ari = _result_jmap.find(jid); if (ari != _result_jmap.end()) ari->second->set_fmt_chk_res(res); } const test_case_result::shared_ptr test_case::jmap_last(std::string& jid) const { res_map_citr i = _result_jmap.find(jid); if (i == _result_jmap.end()) return test_case_result::shared_ptr(); u_int32_t num_res = (*i).second->num_results(); if (num_res) return (*(*i).second)[num_res - 1]; return test_case_result::shared_ptr(); } void test_case::clear() { _result_average.clear(); _result_jmap.clear(); } const std::string test_case::str() const { std::ostringstream oss; oss << "Test Parameters: Test case no. " << _test_case_num << ":" << std::endl; oss << " Comment: " << _comment << std::endl; oss << " Number of messages: " << _num_msgs << std::endl; oss << " Data size: " << _min_data_size; if (_min_data_size == _max_data_size) oss << " bytes (fixed)" << std::endl; else oss << " - " << _max_data_size << " bytes" << std::endl; oss << " XID size: " << _min_xid_size; if (_min_xid_size == _max_xid_size) oss << " bytes (fixed)" << std::endl; else oss << " - " << _max_xid_size << " bytes" << std::endl; oss << " Auto-dequeue: " << (_auto_dequeue ? "true" : "false") << std::endl; oss << " Persistence: "; switch (_transient) { case JTT_TRANSIENT: oss << "TRANSIENT" << std::endl; break; case JTT_PERSISTNET: oss << "PERSISTNET" << std::endl; break; case JTT_RANDOM: oss << "RANDOM" << std::endl; break; } oss << " Message Data: "; switch (_external) { case JDL_INTERNAL: oss << "INTERNAL"; break; case JDL_EXTERNAL: oss << "EXTERNAL"; break; case JDL_RANDOM: oss << "RANDOM"; break; } return oss.str(); } } // namespace jtt } // namespace mrg qpid-cpp-store-debian-0.16/tests/jrnl/jtt/jfile_chk.py0000755000176300017630000007632111234120567021750 0ustar cajuscajus#!/usr/bin/env python # Copyright (c) 2007, 2008 Red Hat, Inc. # # This file is part of the Qpid async store library msgstore.so. # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 # USA # # The GNU Lesser General Public License is available in the file COPYING. import sys import getopt import string import xml.parsers.expat from struct import unpack, calcsize from time import gmtime, strftime dblk_size = 128 sblk_size = 4 * dblk_size jfsize = None hdr_ver = 1 TEST_NUM_COL = 0 NUM_MSGS_COL = 5 MIN_MSG_SIZE_COL = 7 MAX_MSG_SIZE_COL = 8 MIN_XID_SIZE_COL = 9 MAX_XID_SIZE_COL = 10 AUTO_DEQ_COL = 11 TRANSIENT_COL = 12 EXTERN_COL = 13 COMMENT_COL = 20 owi_mask = 0x01 transient_mask = 0x10 extern_mask = 0x20 printchars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~ ' #== global functions =========================================================== def load(f, klass): args = load_args(f, klass) subclass = klass.discriminate(args) result = subclass(*args) if subclass != klass: result.init(f, *load_args(f, subclass)) result.skip(f) return result; def load_args(f, klass): size = calcsize(klass.format) foffs = f.tell(), bin = f.read(size) if len(bin) != size: raise Exception("end of file") return foffs + unpack(klass.format, bin) def size_blks(size, blk_size): return (size + blk_size - 1)/blk_size def rem_in_blk(f, blk_size): foffs = f.tell() return (size_blks(f.tell(), blk_size) * blk_size) - foffs; def file_full(f): return f.tell() >= jfsize def isprintable(s): return s.strip(printchars) == '' def print_xid(xidsize, xid): if xid == None: if xidsize > 0: raise Exception('Inconsistent XID size: xidsize=%d, xid=None' % xidsize) return '' if isprintable(xid): xidstr = split_str(xid) else: xidstr = hex_split_str(xid) if xidsize != len(xid): raise Exception('Inconsistent XID size: xidsize=%d, xid(%d)=\"%s\"' % (xidsize, len(xid), xidstr)) return 'xid(%d)=\"%s\" ' % (xidsize, xidstr) def print_data(dsize, data): if data == None: return '' if isprintable(data): datastr = split_str(data) else: datastr = hex_split_str(data) if dsize != len(data): raise Exception('Inconsistent data size: dsize=%d, data(%d)=\"%s\"' % (dsize, len(data), datastr)) return 'data(%d)=\"%s\" ' % (dsize, datastr) def hex_split_str(s, split_size = 50): if len(s) <= split_size: return hex_str(s, 0, len(s)) if len(s) > split_size + 25: return hex_str(s, 0, 10) + ' ... ' + hex_str(s, 55, 65) + ' ... ' + hex_str(s, len(s)-10, len(s)) return hex_str(s, 0, 10) + ' ... ' + hex_str(s, len(s)-10, len(s)) def hex_str(s, b, e): o = '' for i in range(b, e): if isprintable(s[i]): o += s[i] else: o += '\\%02x' % ord(s[i]) return o def split_str(s, split_size = 50): if len(s) < split_size: return s return s[:25] + ' ... ' + s[-25:] def inv_str(s): si = '' for i in range(0,len(s)): si += chr(~ord(s[i]) & 0xff) return si def load_file_data(f, size, data): if size == 0: return (data, True) if data == None: loaded = 0 else: loaded = len(data) foverflow = f.tell() + size - loaded > jfsize if foverflow: rsize = jfsize - f.tell() else: rsize = size - loaded bin = f.read(rsize) if data == None: data = unpack('%ds' % (rsize), bin)[0] else: data = data + unpack('%ds' % (rsize), bin)[0] return (data, not foverflow) def exit(code, qflag): if code != 0 or not qflag: print out.getvalue() out.close() sys.exit(code) #== class Sizeable ============================================================= class Sizeable: def size(self): classes = [self.__class__] size = 0 while classes: cls = classes.pop() if hasattr(cls, "format"): size += calcsize(cls.format) classes.extend(cls.__bases__) return size #== class Hdr ================================================================== class Hdr(Sizeable): format = '=4sBBHQ' def discriminate(args): return CLASSES.get(args[1][-1], Hdr) discriminate = staticmethod(discriminate) def __init__(self, foffs, magic, ver, end, flags, rid): self.foffs = foffs self.magic = magic self.ver = ver self.end = end self.flags = flags self.rid = rid if self.magic[-1] not in ['0x00', 'a', 'c', 'd', 'e', 'f', 'x']: error = 3 def __str__(self): if self.empty(): return '0x%08x: ' % (self.foffs) if self.magic[-1] == 'x': return '0x%08x: [\"%s\"]' % (self.foffs, self.magic) if self.magic[-1] in ['a', 'c', 'd', 'e', 'f', 'x']: return '0x%08x: [\"%s\" v=%d e=%d f=0x%04x rid=0x%x]' % (self.foffs, self.magic, self.ver, self.end, self.flags, self.rid) return '0x%08x: ' % (self.foffs, self.magic) def empty(self): return self.magic == '\x00'*4 def owi(self): return self.flags & owi_mask != 0 def skip(self, f): f.read(rem_in_blk(f, dblk_size)) def check(self): if self.empty() or self.magic[:3] != 'RHM' or self.magic[3] not in ['a', 'c', 'd', 'e', 'f', 'x']: return True if self.ver != hdr_ver and self.magic[-1] != 'x': raise Exception('%s: Invalid header version: found %d, expected %d.' % (self, self.ver, hdr_ver)) return False #== class FileHdr ============================================================== class FileHdr(Hdr): format = '=2H4x3Q' def init(self, f, foffs, fid, lid, fro, time_sec, time_ns): self.fid = fid self.lid = lid self.fro = fro self.time_sec = time_sec self.time_ns = time_ns def __str__(self): return '%s fid=%d lid=%d fro=0x%08x t=%s' % (Hdr.__str__(self), self.fid, self.lid, self.fro, self.timestamp_str()) def skip(self, f): f.read(rem_in_blk(f, sblk_size)) def timestamp(self): return (self.time_sec, self.time_ns) def timestamp_str(self): ts = gmtime(self.time_sec) fstr = '%%a %%b %%d %%H:%%M:%%S.%09d %%Y' % (self.time_ns) return strftime(fstr, ts) #== class DeqHdr =============================================================== class DeqHdr(Hdr): format = '=QQ' def init(self, f, foffs, deq_rid, xidsize): self.deq_rid = deq_rid self.xidsize = xidsize self.xid = None self.deq_tail = None self.xid_complete = False self.tail_complete = False self.tail_bin = None self.tail_offs = 0 self.load(f) def load(self, f): if self.xidsize == 0: self.xid_complete = True self.tail_complete = True else: if not self.xid_complete: ret = load_file_data(f, self.xidsize, self.xid) self.xid = ret[0] self.xid_complete = ret[1] if self.xid_complete and not self.tail_complete: ret = load_file_data(f, calcsize(RecTail.format), self.tail_bin) self.tail_bin = ret[0] if ret[1]: self.enq_tail = RecTail(self.tail_offs, *unpack(RecTail.format, self.tail_bin)) if self.enq_tail.magic_inv != inv_str(self.magic) or self.enq_tail.rid != self.rid: print " > %s" % self raise Exception('Invalid dequeue record tail (magic=%s; rid=%d) at 0x%08x' % (self.enq_tail, self.enq_tail.rid, self.enq_tail.foffs)) self.enq_tail.skip(f) self.tail_complete = ret[1] return self.complete() def complete(self): return self.xid_complete and self.tail_complete def __str__(self): return '%s %sdrid=0x%x' % (Hdr.__str__(self), print_xid(self.xidsize, self.xid), self.deq_rid) #== class TxnHdr =============================================================== class TxnHdr(Hdr): format = '=Q' def init(self, f, foffs, xidsize): self.xidsize = xidsize self.xid = None self.tx_tail = None self.xid_complete = False self.tail_complete = False self.tail_bin = None self.tail_offs = 0 self.load(f) def load(self, f): if not self.xid_complete: ret = load_file_data(f, self.xidsize, self.xid) self.xid = ret[0] self.xid_complete = ret[1] if self.xid_complete and not self.tail_complete: ret = load_file_data(f, calcsize(RecTail.format), self.tail_bin) self.tail_bin = ret[0] if ret[1]: self.enq_tail = RecTail(self.tail_offs, *unpack(RecTail.format, self.tail_bin)) if self.enq_tail.magic_inv != inv_str(self.magic) or self.enq_tail.rid != self.rid: print " > %s" % self raise Exception('Invalid transaction record tail (magic=%s; rid=%d) at 0x%08x' % (self.enq_tail, self.enq_tail.rid, self.enq_tail.foffs)) self.enq_tail.skip(f) self.tail_complete = ret[1] return self.complete() def complete(self): return self.xid_complete and self.tail_complete def __str__(self): return '%s %s' % (Hdr.__str__(self), print_xid(self.xidsize, self.xid)) #== class RecTail ============================================================== class RecTail(Sizeable): format = '=4sQ' def __init__(self, foffs, magic_inv, rid): self.foffs = foffs self.magic_inv = magic_inv self.rid = rid def __str__(self): magic = inv_str(self.magic_inv) return '[\"%s\" rid=0x%x]' % (magic, self.rid) def skip(self, f): f.read(rem_in_blk(f, dblk_size)) #== class EnqRec =============================================================== class EnqRec(Hdr): format = '=QQ' def init(self, f, foffs, xidsize, dsize): self.xidsize = xidsize self.dsize = dsize self.transient = self.flags & transient_mask > 0 self.extern = self.flags & extern_mask > 0 self.xid = None self.data = None self.enq_tail = None self.xid_complete = False self.data_complete = False self.tail_complete = False self.tail_bin = None self.tail_offs = 0 self.load(f) def load(self, f): if not self.xid_complete: ret = load_file_data(f, self.xidsize, self.xid) self.xid = ret[0] self.xid_complete = ret[1] if self.xid_complete and not self.data_complete: if self.extern: self.data_complete = True else: ret = load_file_data(f, self.dsize, self.data) self.data = ret[0] self.data_complete = ret[1] if self.data_complete and not self.tail_complete: ret = load_file_data(f, calcsize(RecTail.format), self.tail_bin) self.tail_bin = ret[0] if ret[1]: self.enq_tail = RecTail(self.tail_offs, *unpack(RecTail.format, self.tail_bin)) if self.enq_tail.magic_inv != inv_str(self.magic) or self.enq_tail.rid != self.rid: print " > %s" % self raise Exception('Invalid enqueue record tail (magic=%s; rid=%d) at 0x%08x' % (self.enq_tail, self.enq_tail.rid, self.enq_tail.foffs)) self.enq_tail.skip(f) self.tail_complete = ret[1] return self.complete() def complete(self): return self.xid_complete and self.data_complete and self.tail_complete def print_flags(self): s = '' if self.transient: s = '*TRANSIENT' if self.extern: if len(s) > 0: s += ',EXTERNAL' else: s = '*EXTERNAL' if len(s) > 0: s += '*' return s def __str__(self): return '%s %s%s %s %s' % (Hdr.__str__(self), print_xid(self.xidsize, self.xid), print_data(self.dsize, self.data), self.enq_tail, self.print_flags()) #== class Main ================================================================= class Main: def __init__(self, argv): self.bfn = None self.csvfn = None self.jdir = None self.aflag = False self.hflag = False self.qflag = False self.tnum = None self.num_jfiles = None self.num_msgs = None self.msg_len = None self.auto_deq = None self.xid_len = None self.transient = None self.extern = None self.file_start = 0 self.file_num = 0 self.fro = 0x200 self.emap = {} self.tmap = {} self.rec_cnt = 0 self.msg_cnt = 0 self.txn_msg_cnt = 0 self.fhdr = None self.f = None self.first_rec = False self.last_file = False self.last_rid = -1 self.fhdr_owi_at_msg_start = None self.proc_args(argv) self.proc_csv() self.read_jinf() def run(self): try: start_info = self.analyze_files() stop = self.advance_file(*start_info) except Exception: print 'WARNING: All journal files are empty.' if self.num_msgs > 0: raise Exception('All journal files are empty, but %d msgs expectd.' % self.num_msgs) else: stop = True while not stop: warn = '' if file_full(self.f): stop = self.advance_file() if stop: break hdr = load(self.f, Hdr) if hdr.empty(): stop = True; break if hdr.check(): stop = True; else: self.rec_cnt += 1 self.fhdr_owi_at_msg_start = self.fhdr.owi() if self.first_rec: if self.fhdr.fro != hdr.foffs: raise Exception('File header first record offset mismatch: fro=0x%08x; rec_offs=0x%08x' % (self.fhdr.fro, hdr.foffs)) else: if not self.qflag: print ' * fro ok: 0x%08x' % self.fhdr.fro self.first_rec = False if isinstance(hdr, EnqRec) and not stop: while not hdr.complete(): stop = self.advance_file() if stop: break hdr.load(self.f) if self.extern != None: if hdr.extern: if hdr.data != None: raise Exception('Message data found on external record') else: if self.msg_len > 0 and len(hdr.data) != self.msg_len: raise Exception('Message length (%d) incorrect; expected %d' % (len(hdr.data), self.msg_len)) else: if self.msg_len > 0 and len(hdr.data) != self.msg_len: raise Exception('Message length (%d) incorrect; expected %d' % (len(hdr.data), self.msg_len)) if self.xid_len > 0 and len(hdr.xid) != self.xid_len: print ' ERROR: XID length (%d) incorrect; expected %d' % (len(hdr.xid), self.xid_len) sys.exit(1) #raise Exception('XID length (%d) incorrect; expected %d' % (len(hdr.xid), self.xid_len)) if self.transient != None: if self.transient: if not hdr.transient: raise Exception('Expected transient record, found persistent') else: if hdr.transient: raise Exception('Expected persistent record, found transient') stop = not self.check_owi(hdr) if stop: warn = ' (WARNING: OWI mismatch - could be overwrite boundary.)' else: self.msg_cnt += 1 if self.aflag or self.auto_deq: if hdr.xid == None: self.emap[hdr.rid] = (self.fhdr.fid, hdr, False) else: self.txn_msg_cnt += 1 if hdr.xid in self.tmap: self.tmap[hdr.xid].append((self.fhdr.fid, hdr)) #Append tuple to existing list else: self.tmap[hdr.xid] = [(self.fhdr.fid, hdr)] # Create new list elif isinstance(hdr, DeqHdr) and not stop: while not hdr.complete(): stop = self.advance_file() if stop: break hdr.load(self.f) stop = not self.check_owi(hdr) if stop: warn = ' (WARNING: OWI mismatch - could be overwrite boundary.)' else: if self.auto_deq != None: if not self.auto_deq: warn = ' WARNING: Dequeue record rid=%d found in non-dequeue test - ignoring.' % hdr.rid if self.aflag or self.auto_deq: if hdr.xid == None: if hdr.deq_rid in self.emap: if self.emap[hdr.deq_rid][2]: warn = ' (WARNING: dequeue rid 0x%x dequeues locked enqueue record 0x%x)' % (hdr.rid, hdr.deq_rid) del self.emap[hdr.deq_rid] else: warn = ' (WARNING: rid being dequeued 0x%x not found in enqueued records)' % hdr.deq_rid else: if hdr.deq_rid in self.emap: t = self.emap[hdr.deq_rid] self.emap[hdr.deq_rid] = (t[0], t[1], True) # Lock enq record if hdr.xid in self.tmap: self.tmap[hdr.xid].append((self.fhdr.fid, hdr)) #Append to existing list else: self.tmap[hdr.xid] = [(self.fhdr.fid, hdr)] # Create new list elif isinstance(hdr, TxnHdr) and not stop: while not hdr.complete(): stop = self.advance_file() if stop: break hdr.load(self.f) stop = not self.check_owi(hdr) if stop: warn = ' (WARNING: OWI mismatch - could be overwrite boundary.)' else: if hdr.xid in self.tmap: mismatched_rids = [] if hdr.magic[-1] == 'c': # commit for rec in self.tmap[hdr.xid]: if isinstance(rec[1], EnqRec): self.emap[rec[1].rid] = (rec[0], rec[1], False) # Transfer enq to emap elif isinstance(rec[1], DeqHdr): if rec[1].deq_rid in self.emap: del self.emap[rec[1].deq_rid] # Delete from emap else: mismatched_rids.append('0x%x' % rec[1].deq_rid) else: raise Exception('Unknown header found in txn map: %s' % rec[1]) elif hdr.magic[-1] == 'a': # abort for rec in self.tmap[hdr.xid]: if isinstance(rec[1], DeqHdr): if rec[1].deq_rid in self.emap: t = self.emap[rec[1].deq_rid] self.emap[rec[1].deq_rid] = (t[0], t[1], False) # Unlock enq record del self.tmap[hdr.xid] if len(mismatched_rids) > 0: warn = ' (WARNING: transactional dequeues not found in enqueue map; rids=%s)' % mismatched_rids else: warn = ' (WARNING: %s not found in transaction map)' % print_xid(len(hdr.xid), hdr.xid) if not self.qflag: print ' > %s%s' % (hdr, warn) if not stop: stop = (self.last_file and hdr.check()) or hdr.empty() or self.fhdr.empty() def analyze_files(self): fname = '' fnum = -1 rid = -1 fro = -1 tss = '' if not self.qflag: print 'Analyzing journal files:' owi_found = False for i in range(0, self.num_jfiles): jfn = self.jdir + '/' + self.bfn + '.%04d.jdat' % i f = open(jfn) fhdr = load(f, Hdr) if fhdr.empty(): if not self.qflag: print ' %s: file empty' % jfn break if i == 0: init_owi = fhdr.owi() fname = jfn fnum = i rid = fhdr.rid fro = fhdr.fro tss = fhdr.timestamp_str() elif fhdr.owi() != init_owi and not owi_found: fname = jfn fnum = i rid = fhdr.rid fro = fhdr.fro tss = fhdr.timestamp_str() owi_found = True if not self.qflag: print ' %s: owi=%s rid=0x%x, fro=0x%08x ts=%s' % (jfn, fhdr.owi(), fhdr.rid, fhdr.fro, fhdr.timestamp_str()) if fnum < 0 or rid < 0 or fro < 0: raise Exception('All journal files empty') if not self.qflag: print ' Oldest complete file: %s: rid=%d, fro=0x%08x ts=%s' % (fname, rid, fro, tss) return (fnum, rid, fro) def advance_file(self, *start_info): seek_flag = False if len(start_info) == 3: self.file_start = self.file_num = start_info[0] self.fro = start_info[2] seek_flag = True if self.f != None and file_full(self.f): self.file_num = self.incr_fnum() if self.file_num == self.file_start: return True if self.file_start == 0: self.last_file = self.file_num == self.num_jfiles - 1 else: self.last_file = self.file_num == self.file_start - 1 if self.file_num < 0 or self.file_num >= self.num_jfiles: raise Exception('Bad file number %d' % self.file_num) jfn = self.jdir + '/' + self.bfn + '.%04d.jdat' % self.file_num self.f = open(jfn) self.fhdr = load(self.f, Hdr) if seek_flag and self.f.tell() != self.fro: self.f.seek(self.fro) self.first_rec = True if not self.qflag: print jfn, ": ", self.fhdr return False def incr_fnum(self): self.file_num += 1 if self.file_num >= self.num_jfiles: self.file_num = 0; return self.file_num def check_owi(self, hdr): return self.fhdr_owi_at_msg_start == hdr.owi() def check_rid(self, hdr): if self.last_rid != -1 and hdr.rid <= self.last_rid: return False self.last_rid = hdr.rid return True def read_jinf(self): filename = self.jdir + '/' + self.bfn + '.jinf' try: f = open(filename, 'r') except IOError: print 'ERROR: Unable to open jinf file %s' % filename sys.exit(1) p = xml.parsers.expat.ParserCreate() p.StartElementHandler = self.handleStartElement p.CharacterDataHandler = self.handleCharData p.EndElementHandler = self.handleEndElement p.ParseFile(f) if self.num_jfiles == None: print 'ERROR: number_jrnl_files not found in jinf file "%s"!' % filename if jfsize == None: print 'ERROR: jrnl_file_size_sblks not found in jinf file "%s"!' % filename if self.num_jfiles == None or jfsize == None: sys.exit(1) def handleStartElement(self, name, attrs): global jfsize if name == 'number_jrnl_files': self.num_jfiles = int(attrs['value']) if name == 'jrnl_file_size_sblks': jfsize = (int(attrs['value']) + 1) * sblk_size def handleCharData(self, data): pass def handleEndElement(self, name): pass def proc_csv(self): if self.csvfn != None and self.tnum != None: tparams = self.get_test(self.csvfn, self.tnum) if tparams == None: print 'ERROR: Test %d not found in CSV file "%s"' % (self.tnum, self.csvfn) sys.exit(1) self.num_msgs = tparams['num_msgs'] if tparams['min_size'] == tparams['max_size']: self.msg_len = tparams['max_size'] else: self.msg_len = 0 self.auto_deq = tparams['auto_deq'] if tparams['xid_min_size'] == tparams['xid_max_size']: self.xid_len = tparams['xid_max_size'] else: self.xid_len = 0 self.transient = tparams['transient'] self.extern = tparams['extern'] def get_test(self, filename, tnum): try: f=open(filename, 'r') except IOError: print 'ERROR: Unable to open CSV file "%s"' % filename sys.exit(1) for l in f: sl = l.strip().split(',') if len(sl[0]) > 0 and sl[0][0] != '"': try: if (int(sl[TEST_NUM_COL]) == tnum): return { 'num_msgs':int(sl[NUM_MSGS_COL]), 'min_size':int(sl[MIN_MSG_SIZE_COL]), 'max_size':int(sl[MAX_MSG_SIZE_COL]), 'auto_deq':not (sl[AUTO_DEQ_COL] == 'FALSE' or sl[AUTO_DEQ_COL] == '0'), 'xid_min_size':int(sl[MIN_XID_SIZE_COL]), 'xid_max_size':int(sl[MAX_XID_SIZE_COL]), 'transient':not (sl[TRANSIENT_COL] == 'FALSE' or sl[TRANSIENT_COL] == '0'), 'extern':not (sl[EXTERN_COL] == 'FALSE' or sl[EXTERN_COL] == '0'), 'comment':sl[COMMENT_COL] } except Exception: pass return None def proc_args(self, argv): try: opts, args = getopt.getopt(sys.argv[1:], "ab:c:d:hqt:", ["analyse", "base-filename=", "csv-filename=", "dir=", "help", "quiet", "test-num="]) except getopt.GetoptError: self.usage() sys.exit(2) for o, a in opts: if o in ("-h", "--help"): self.usage() sys.exit() if o in ("-a", "--analyze"): self.aflag = True if o in ("-b", "--base-filename"): self.bfn = a if o in ("-c", "--csv-filename"): self.csvfn = a if o in ("-d", "--dir"): self.jdir = a if o in ("-q", "--quiet"): self.qflag = True if o in ("-t", "--test-num"): if not a.isdigit(): print 'ERROR: Illegal test-num argument. Must be a non-negative number' sys.exit(2) self.tnum = int(a) if self.bfn == None or self.jdir == None: print 'ERROR: Missing requred args.' self.usage() sys.exit(2) if self.tnum != None and self.csvfn == None: print 'ERROR: Test number specified, but not CSV file' self.usage() sys.exit(2) def usage(self): print 'Usage: %s opts' % sys.argv[0] print ' where opts are in either short or long format (*=req\'d):' print ' -a --analyze Analyze enqueue/dequeue records' print ' -b --base-filename [string] * Base filename for journal files' print ' -c --csv-filename [string] CSV filename containing test parameters' print ' -d --dir [string] * Journal directory containing journal files' print ' -h --help Print help' print ' -q --quiet Quiet (reduced output)' print ' -t --test-num [int] Test number from CSV file - only valid if CSV file named' def report(self): if not self.qflag: print print ' === REPORT ====' if self.num_msgs > 0 and self.msg_cnt != self.num_msgs: print 'WARNING: Found %d messages; %d expected.' % (self.msg_cnt, self.num_msgs) if len(self.emap) > 0: print print 'Remaining enqueued records (sorted by rid): ' keys = sorted(self.emap.keys()) for k in keys: if self.emap[k][2] == True: # locked locked = ' (locked)' else: locked = '' print " fid=%d %s%s" % (self.emap[k][0], self.emap[k][1], locked) print 'WARNING: Enqueue-Dequeue mismatch, %d enqueued records remain.' % len(self.emap) if len(self.tmap) > 0: txn_rec_cnt = 0 print print 'Remaining transactions: ' for t in self.tmap: print_xid(len(t), t) for r in self.tmap[t]: print " fid=%d %s" % (r[0], r[1]) print " Total: %d records for xid %s" % (len(self.tmap[t]), t) txn_rec_cnt += len(self.tmap[t]) print 'WARNING: Incomplete transactions, %d xids remain containing %d records.' % (len(self.tmap), txn_rec_cnt) print '%d enqueues, %d journal records processed.' % (self.msg_cnt, self.rec_cnt) #=============================================================================== CLASSES = { "a": TxnHdr, "c": TxnHdr, "d": DeqHdr, "e": EnqRec, "f": FileHdr } m = Main(sys.argv) m.run() m.report() sys.exit(None) qpid-cpp-store-debian-0.16/tests/jrnl/jtt/test_case_result_agregation.cpp0000644000176300017630000001251211301004173025706 0ustar cajuscajus/* * Copyright (c) 2008 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #include "test_case_result_agregation.hpp" #include #include namespace mrg { namespace jtt { test_case_result_agregation::test_case_result_agregation(): test_case_result("Average"), _tc_average(true), _fmt_chk_done(false), _fmt_chk_err(false), _res_list() { } test_case_result_agregation::test_case_result_agregation(const std::string& jid): test_case_result(jid), _tc_average(false), _fmt_chk_done(false), _fmt_chk_err(false), _res_list() {} test_case_result_agregation::~test_case_result_agregation() {} void test_case_result_agregation::add_test_result(const test_case_result::shared_ptr& tcrp) { if (_tc_average || _jid.compare(tcrp->jid()) == 0) { _num_enq += tcrp->num_enq(); _num_deq += tcrp->num_deq(); _num_read += tcrp->num_read(); add_test_time(tcrp->test_time()); _exception_list.insert(_exception_list.end(), tcrp->begin(), tcrp->end()); _res_list.push_back(tcrp); } } bool test_case_result_agregation::exception() const { for (tcrp_list_citr i = _res_list.begin(); i < _res_list.end(); i++) if ((*i)->exception()) return true; return false; } unsigned test_case_result_agregation::exception_count() const { unsigned cnt = 0; for (tcrp_list_citr i = _res_list.begin(); i < _res_list.end(); i++) cnt += (*i)->exception_count(); return cnt; } void test_case_result_agregation::clear() { test_case_result::clear(); _res_list.clear(); } const std::string test_case_result_agregation::str(const bool last_only, const bool summary) const { std::ostringstream oss; if (last_only) oss << " " << _res_list.at(_res_list.size()-1)->str(summary); else { for (tcrp_list_citr i=_res_list.begin(); i!=_res_list.end(); i++) oss << " " << (*i)->str(summary); } if (_res_list.size() > 1) oss << " " << (summary ? str_summary(last_only) : str_full(last_only)); return oss.str(); } const std::string test_case_result_agregation::str_full(const bool /*last_only*/) const { std::ostringstream oss; oss.precision(2); if (_tc_average) oss << "Average across all journal instances:" << std::endl; else oss << "Average for jid=\"" << _jid << "\":" << std::endl; oss << " total number results: " << _res_list.size() << std::endl; oss << " number exceptions: " << _exception_list.size() << " (" << (100.0 * _res_list.size() / _exception_list.size()) << "%)" << std::endl; oss << test_case_result::str_full(); if (_exception_list.size()) { unsigned n = 0; oss << "List of exceptions/errors:" << std::endl; for (elist_citr i = _exception_list.begin(); i != _exception_list.end(); i++, n++) oss << " " << n << ". " << (*i) << std::endl; } if (!_tc_average && _res_list.size() > 1) { oss << "Individual results:" << std::endl; for (tcrp_list_citr i=_res_list.begin(); i!=_res_list.end(); i++) oss << " " << (*i)->str(false) << std::endl; oss << std::endl; } return oss.str(); } const std::string test_case_result_agregation::str_summary(const bool /*last_only*/) const { std::ostringstream oss; if (_tc_average) oss << "overall average [" << _res_list.size() << "]:"; else oss << "average (" << _res_list.size() << "):"; oss << test_case_result::str_summary(); if (_fmt_chk_done) oss << " fmt-chk=" << (_fmt_chk_err ? "fail" : "ok"); if (_exception_list.size()) { if (_tc_average) oss << " fail: " << _exception_list.size() << " exception" << (_exception_list.size()>1?"s":"") << std::endl; else { if (_exception_list.size() == 1) oss << " fail: " << *_exception_list.begin() << std::endl; else { oss << std::endl; unsigned n = 0; for (elist_citr i = _exception_list.begin(); i != _exception_list.end(); i++, n++) oss << " " << n << ". " << (*i) << std::endl; } } } else oss << " ok" << std::endl; return oss.str(); } const journal::time_ns& test_case_result_agregation::add_test_time(const journal::time_ns& t) { _test_time += t; return _test_time; } } // namespace jtt } // namespace mrg qpid-cpp-store-debian-0.16/tests/jrnl/jtt/test_case_result.hpp0000644000176300017630000000765711142067437023550 0ustar cajuscajus/* * Copyright (c) 2008 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #ifndef mrg_jtt_test_case_result_hpp #define mrg_jtt_test_case_result_hpp #include #include #include "jrnl/jexception.hpp" #include "jrnl/time_ns.hpp" #include namespace mrg { namespace jtt { class test_case_result { public: typedef boost::shared_ptr shared_ptr; typedef std::deque elist; typedef elist::const_iterator elist_citr; protected: std::string _jid; u_int32_t _num_enq; u_int32_t _num_deq; u_int32_t _num_read; // Messages actually read u_int32_t _num_rproc; // Messages handled by read thread (not all are read) journal::time_ns _start_time; journal::time_ns _stop_time; bool _stopped; journal::time_ns _test_time; elist _exception_list; public: test_case_result(const std::string& jid); virtual ~test_case_result(); inline const std::string& jid() const { return _jid; } inline u_int32_t num_enq() const { return _num_enq; } inline u_int32_t incr_num_enq() { return ++_num_enq; } inline u_int32_t num_deq() const { return _num_deq; } inline u_int32_t incr_num_deq() { return ++_num_deq; } inline u_int32_t num_read() const { return _num_read; } inline u_int32_t incr_num_read() { return ++_num_read; } inline u_int32_t num_rproc() const { return _num_rproc; } inline u_int32_t incr_num_rproc() { return ++_num_rproc; } inline const journal::time_ns& start_time() const { return _start_time; } inline void set_start_time() { ::clock_gettime(CLOCK_REALTIME, &_start_time); } inline const journal::time_ns& stop_time() const { return _stop_time; } inline void set_stop_time() { ::clock_gettime(CLOCK_REALTIME, &_stop_time); calc_test_time(); } inline void set_test_time(const journal::time_ns& ts) { _test_time = ts; } inline const journal::time_ns& test_time() const { return _test_time; } const std::string test_time_str() const; void add_exception(const journal::jexception& e, const bool set_stop_time_flag = true); void add_exception(const std::string& err_str, const bool set_stop_time_flag = true); void add_exception(const char* err_str, const bool set_stop_time_flag = true); inline bool exception() const { return _exception_list.size() > 0; } inline unsigned exception_count() const { return _exception_list.size(); } inline elist_citr begin() { return _exception_list.begin(); } inline elist_citr end() { return _exception_list.end(); } inline const std::string& operator[](unsigned i) { return _exception_list[i]; } void clear(); const std::string str(const bool summary) const; protected: const std::string str_full() const; const std::string str_summary() const; void calc_test_time(); }; } // namespace jtt } // namespace mrg #endif // ifndef mrg_jtt_test_case_result_hpp qpid-cpp-store-debian-0.16/tests/jrnl/jtt/_ut_test_case.cpp0000644000176300017630000001025311142067437022776 0ustar cajuscajus/* * Copyright (c) 2008 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #include "../../unit_test.h" #include #include #include #include "test_case.hpp" #include "test_case_result.hpp" using namespace boost::unit_test; using namespace mrg::jtt; using namespace std; QPID_AUTO_TEST_SUITE(jtt_test_case) const string test_filename("_ut_test_case"); QPID_AUTO_TEST_CASE(constructor) { cout << test_filename << ".constructor: " << flush; const unsigned test_case_num = 0x12345; const u_int32_t num_msgs = 0x100; const std::size_t min_data_size = 0x1000; const std::size_t max_data_size = 0; const bool auto_deq = true; const std::size_t min_xid_size = 0x200; const std::size_t max_xid_size = 0x200; using mrg::jtt::test_case; const test_case::transient_t transient = test_case::JTT_PERSISTNET; const test_case::external_t external = test_case::JDL_INTERNAL; const string comment = "This is a test"; test_case tc(test_case_num, num_msgs, min_data_size, max_data_size, auto_deq, min_xid_size, max_xid_size, transient, external, comment); BOOST_CHECK_EQUAL(tc.test_case_num(), test_case_num); BOOST_CHECK_EQUAL(tc.num_msgs(), num_msgs); BOOST_CHECK_EQUAL(tc.min_data_size(), min_data_size); BOOST_CHECK_EQUAL(tc.max_data_size(), max_data_size); BOOST_CHECK_EQUAL(tc.auto_deq(), auto_deq); BOOST_CHECK_EQUAL(tc.min_xid_size(), min_xid_size); BOOST_CHECK_EQUAL(tc.max_xid_size(), max_xid_size); BOOST_CHECK_EQUAL(tc.transient(), transient); BOOST_CHECK_EQUAL(tc.external(), external); BOOST_CHECK_EQUAL(tc.comment(), comment); cout << "ok" << endl; } QPID_AUTO_TEST_CASE(results) { cout << test_filename << ".results: " << flush; const unsigned test_case_num = 0x12345; const u_int32_t num_msgs = 0x100; const std::size_t min_data_size = 0x1000; const std::size_t max_data_size = 0; const bool auto_deq = true; const std::size_t min_xid_size = 0x200; const std::size_t max_xid_size = 0x200; using mrg::jtt::test_case; const test_case::transient_t transient = test_case::JTT_PERSISTNET; const test_case::external_t external = test_case::JDL_INTERNAL; const string comment = "This is a test"; const unsigned num_results = 20; test_case tc(test_case_num, num_msgs, min_data_size, max_data_size, auto_deq, min_xid_size, max_xid_size, transient, external, comment); for (unsigned i=0; ijid(), oss.str()); } for (unsigned i=0; ijid(), oss.str()); } tc.clear(); BOOST_CHECK_EQUAL(tc.num_results(), unsigned(0)); cout << "ok" << endl; } QPID_AUTO_TEST_SUITE_END() qpid-cpp-store-debian-0.16/tests/jrnl/jtt/jrnl_init_params.hpp0000644000176300017630000000610611142067437023517 0ustar cajuscajus/* * Copyright (c) 2008 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #ifndef mrg_jtt_jrnl_init_params_hpp #define mrg_jtt_jrnl_init_params_hpp #include #include #include namespace mrg { namespace jtt { class jrnl_init_params { public: static const u_int16_t def_num_jfiles; static const bool def_ae; static const u_int16_t def_ae_max_jfiles; static const u_int32_t def_jfsize_sblks; static const u_int16_t def_wcache_num_pages; static const u_int32_t def_wcache_pgsize_sblks; typedef boost::shared_ptr shared_ptr; private: std::string _jid; std::string _jdir; std::string _base_filename; u_int16_t _num_jfiles; bool _ae; u_int16_t _ae_max_jfiles; u_int32_t _jfsize_sblks; u_int16_t _wcache_num_pages; u_int32_t _wcache_pgsize_sblks; public: jrnl_init_params(const std::string& jid, const std::string& jdir, const std::string& base_filename, const u_int16_t num_jfiles = def_num_jfiles, const bool ae = def_ae, const u_int16_t ae_max_jfiles = def_ae_max_jfiles, const u_int32_t jfsize_sblks = def_jfsize_sblks, const u_int16_t wcache_num_pages = def_wcache_num_pages, const u_int32_t wcache_pgsize_sblks = def_wcache_pgsize_sblks); jrnl_init_params(const jrnl_init_params& jp); jrnl_init_params(const jrnl_init_params* const jp_ptr); inline const std::string& jid() const { return _jid; } inline const std::string& jdir() const { return _jdir; } inline const std::string& base_filename() const { return _base_filename; } inline u_int16_t num_jfiles() const { return _num_jfiles; } inline bool is_ae() const { return _ae; } inline u_int16_t ae_max_jfiles() const { return _ae_max_jfiles; } inline u_int32_t jfsize_sblks() const { return _jfsize_sblks; } inline u_int16_t wcache_num_pages() const { return _wcache_num_pages; } inline u_int32_t wcache_pgsize_sblks() const { return _wcache_pgsize_sblks; } }; } // namespace jtt } // namespace mrg #endif // ifndef mrg_jtt_jrnl_init_params_hpp qpid-cpp-store-debian-0.16/tests/jrnl/jtt/test_case_set.cpp0000644000176300017630000001445311142067437023010 0ustar cajuscajus/* * Copyright (c) 2008 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #include "test_case_set.hpp" #include #include #include namespace mrg { namespace jtt { test_case_set::test_case_set(): _tc_list(), _csv_ignored(0) {} test_case_set::test_case_set(const std::string& csv_filename, const bool recover_mode, const csv_map& cols): _tc_list(), _csv_ignored(0) { append_from_csv(csv_filename, recover_mode, cols); } test_case_set::~test_case_set() {} void test_case_set::append(const unsigned test_case_num, const u_int32_t num_msgs, const std::size_t min_data_size, const std::size_t max_data_size, const bool auto_deq, const std::size_t min_xid_size, const std::size_t max_xid_size, const test_case::transient_t transient, const test_case::external_t external, const std::string& comment) { test_case::shared_ptr tcp(new test_case(test_case_num, num_msgs, min_data_size, max_data_size, auto_deq, min_xid_size, max_xid_size, transient, external, comment)); append(tcp); } #define CSV_BUFF_SIZE 2048 void test_case_set::append_from_csv(const std::string& csv_filename, const bool recover_mode, const csv_map& cols) { char buff[CSV_BUFF_SIZE]; std::ifstream ifs(csv_filename.c_str()); while (ifs.good()) { ifs.getline(buff, (std::streamsize)CSV_BUFF_SIZE); if (ifs.gcount()) { test_case::shared_ptr tcp = get_tc_from_csv(buff, cols); if (tcp.get()) { if (!recover_mode || tcp->auto_deq()) append(tcp); else _csv_ignored++; } } } } test_case::shared_ptr test_case_set::get_tc_from_csv(const std::string& csv_line, const csv_map& cols) { unsigned test_case_num = 0; u_int32_t num_msgs = 0; std::size_t min_data_size = 0; std::size_t max_data_size = 0; bool auto_deq = false; std::size_t min_xid_size = 0; std::size_t max_xid_size = 0; test_case::transient_t transient = test_case::JTT_TRANSIENT; test_case::external_t external = test_case::JDL_INTERNAL; std::string comment; csv_tok t(csv_line); unsigned col_num = 0; for (csv_tok_citr t_itr = t.begin(); t_itr != t.end(); ++t_itr, ++col_num) { const std::string& tok = *t_itr; csv_map_citr m_citr = cols.find(col_num); if (m_citr != cols.end()) { switch (m_citr->second) { case CSV_TC_NUM: if (!tok.size() || tok[0] < '0' || tok[0] > '9') return test_case::shared_ptr(); test_case_num = unsigned(std::atol(tok.c_str())); break; case CSV_TC_NUM_MSGS: num_msgs = u_int32_t(std::atol(tok.c_str())); break; case CSV_TC_MIN_DATA_SIZE: min_data_size = std::size_t(std::atol(tok.c_str())); break; case CSV_TC_MAX_DATA_SIZE: max_data_size = std::size_t(std::atol(tok.c_str())); break; case CSV_TC_AUTO_DEQ: if (tok == "TRUE" || tok == "1") auto_deq = true; break; case CSV_TC_MIN_XID_SIZE: min_xid_size = std::size_t(std::atol(tok.c_str())); break; case CSV_TC_MAX_XID_SIZE: max_xid_size = std::size_t(std::atol(tok.c_str())); break; case CSV_TC_TRANSIENT: if (tok == "TRUE" || tok == "1") transient = test_case::JTT_PERSISTNET; else if (tok == "RANDOM" || tok == "-1") transient = test_case::JTT_RANDOM; break; case CSV_TC_EXTERNAL: if (tok == "TRUE" || tok == "1") external = test_case::JDL_EXTERNAL; else if (tok == "RANDOM" || tok == "-1") external = test_case::JDL_RANDOM; break; case CSV_TC_COMMENT: comment = *t_itr; break; } } } if (col_num) return test_case::shared_ptr(new test_case(test_case_num, num_msgs, min_data_size, max_data_size, auto_deq, min_xid_size, max_xid_size, transient, external, comment)); else return test_case::shared_ptr(); } // Static member initializations // This csv_map is for use on the standard spreadsheet-derived test case csv files. test_case_set::csv_map test_case_set::std_csv_map; const bool test_case_set::_map_init = __init(); bool test_case_set::__init() { std_csv_map.insert(test_case_set::csv_pair(0, test_case_set::CSV_TC_NUM)); std_csv_map.insert(test_case_set::csv_pair(5, test_case_set::CSV_TC_NUM_MSGS)); std_csv_map.insert(test_case_set::csv_pair(7, test_case_set::CSV_TC_MIN_DATA_SIZE)); std_csv_map.insert(test_case_set::csv_pair(8, test_case_set::CSV_TC_MAX_DATA_SIZE)); std_csv_map.insert(test_case_set::csv_pair(11, test_case_set::CSV_TC_AUTO_DEQ)); std_csv_map.insert(test_case_set::csv_pair(9, test_case_set::CSV_TC_MIN_XID_SIZE)); std_csv_map.insert(test_case_set::csv_pair(10, test_case_set::CSV_TC_MAX_XID_SIZE)); std_csv_map.insert(test_case_set::csv_pair(12, test_case_set::CSV_TC_TRANSIENT)); std_csv_map.insert(test_case_set::csv_pair(13, test_case_set::CSV_TC_EXTERNAL)); std_csv_map.insert(test_case_set::csv_pair(20, test_case_set::CSV_TC_COMMENT)); return true; } } // namespace jtt } // namespace mrg qpid-cpp-store-debian-0.16/tests/jrnl/jtt/test_case.hpp0000644000176300017630000001067311142067437022142 0ustar cajuscajus/* * Copyright (c) 2008 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #ifndef mrg_jtt_test_case_hpp #define mrg_jtt_test_case_hpp #include #include #include #include "test_case_result.hpp" #include "test_case_result_agregation.hpp" #include namespace mrg { namespace jtt { class test_case { public: enum transient_type { JTT_TRANSIENT = 0, JTT_PERSISTNET, JTT_RANDOM }; typedef transient_type transient_t; enum data_location { JDL_INTERNAL = 0, JDL_EXTERNAL, JDL_RANDOM }; typedef data_location external_t; typedef boost::shared_ptr shared_ptr; typedef std::map res_map; typedef std::pair res_map_pair; typedef res_map::const_iterator res_map_citr; private: unsigned _test_case_num; u_int32_t _num_msgs; std::size_t _min_data_size; std::size_t _max_data_size; bool _auto_dequeue; // TODO: add probability of transaction to these params std::size_t _min_xid_size; std::size_t _max_xid_size; // TODO: change these enums (transient_t & external_t) to probabilities transient_t _transient; external_t _external; std::string _comment; test_case_result_agregation _result_average; // overall average result res_map _result_jmap; // map of per-journal averages public: test_case(const unsigned test_case_num, const u_int32_t num_msgs, const std::size_t min_data_size, const std::size_t max_data_size, const bool auto_deq, const std::size_t min_xid_size, const std::size_t max_xid_size, const transient_t transient, const external_t external, const std::string& comment); virtual ~test_case(); inline unsigned test_case_num() const { return _test_case_num; } inline u_int32_t num_msgs() const { return _num_msgs; } inline std::size_t min_data_size() const { return _min_data_size; } inline std::size_t max_data_size() const { return _max_data_size; } std::size_t this_data_size() const; inline bool auto_deq() const { return _auto_dequeue; } inline std::size_t min_xid_size() const { return _min_xid_size; } inline std::size_t max_xid_size() const { return _max_xid_size; } std::size_t this_xid_size() const; inline transient_t transient() const { return _transient; } bool this_transience() const; inline external_t external() const { return _external; } bool this_external() const; inline const std::string& comment() const { return _comment; } void add_result(test_case_result::shared_ptr& p); void set_fmt_chk_res(const bool res, const std::string& jid); inline const test_case_result_agregation& average() const { return _result_average; } inline u_int32_t num_results() const { return _result_average.num_results(); } inline unsigned num_jrnls() const { return _result_jmap.size(); } inline res_map_citr jrnl_average(std::string& jid) const { return _result_jmap.find(jid); } inline res_map_citr jmap_begin() const { return _result_jmap.begin(); } inline res_map_citr jmap_end() const { return _result_jmap.end(); } const test_case_result::shared_ptr jmap_last(std::string& jid) const; void clear(); const std::string str() const; }; } // namespace jtt } // namespace mrg #endif // ifndef mrg_jtt_test_case_hpp qpid-cpp-store-debian-0.16/tests/jrnl/jtt/Makefile.am0000644000176300017630000001136311453147040021502 0ustar cajuscajus# Copyright (c) 2008 Red Hat, Inc. # # This file is part of the Qpid async store library msgstore.so. # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 # USA # # The GNU Lesser General Public License is available in the file COPYING. abs_builddir=@abs_builddir@ abs_srcdir=@abs_srcdir@ AM_CXXFLAGS = $(WARNING_CFLAGS) -I$(top_srcdir)/lib -pthread -DBOOST_TEST_DYN_LINK TMP_DATA_DIR=$(abs_srcdir)/../../tmp_data_dir LINK_BDB = $(top_builddir)/lib/msgstore.la TESTS_ENVIRONMENT = \ VALGRIND=$(VALGRIND) \ abs_srcdir=$(abs_srcdir) \ TMP_DATA_DIR=$(TMP_DATA_DIR) \ $(srcdir)/../../run_test all-local: .valgrindrc .valgrind.supp .valgrindrc: $(top_srcdir)/tests/.valgrindrc cp $^ . .valgrind.supp: $(top_srcdir)/tests/.valgrind.supp cp $^ . TESTS = LONG_TESTS = \ _ut_data_src \ _ut_long_data_src \ _ut_jrnl_init_params \ _ut_read_arg \ _ut_test_case \ _ut_test_case_result \ _ut_test_case_result_agregation \ _ut_test_case_set \ _ut_jrnl_instance check_PROGRAMS = jtt \ _ut_data_src \ _ut_long_data_src \ _ut_jrnl_init_params \ _ut_read_arg \ _ut_test_case \ _ut_test_case_result \ _ut_test_case_result_agregation \ _ut_test_case_set \ _ut_jrnl_instance jtt_SOURCES = \ args.cpp \ data_src.cpp \ jrnl_init_params.cpp \ jrnl_instance.cpp \ main.cpp \ read_arg.cpp \ test_case.cpp \ test_case_result.cpp \ test_case_result_agregation.cpp \ test_case_set.cpp \ test_mgr.cpp \ args.hpp \ data_src.hpp \ jrnl_init_params.hpp \ jrnl_instance.hpp \ read_arg.hpp \ test_case.hpp \ test_case_result.hpp \ test_case_result_agregation.hpp \ test_case_set.hpp \ test_mgr.hpp jtt_LDADD = -laio -lrt -lboost_program_options $(LINK_BDB) _ut_data_src_SOURCES = \ _ut_data_src.cpp \ data_src.cpp \ ../../unit_test.cpp _ut_data_src_LDADD = -lboost_unit_test_framework -lrt $(LINK_BDB) _ut_long_data_src_SOURCES = \ _ut_data_src.cpp \ data_src.cpp \ ../../unit_test.cpp _ut_long_data_src_CXXFLAGS = $(AM_CXXFLAGS) -DLONG_TEST _ut_long_data_src_LDADD = -lboost_unit_test_framework -lrt $(LINK_BDB) _ut_jrnl_init_params_SOURCES = \ _ut_jrnl_init_params.cpp \ jrnl_init_params.cpp \ ../../unit_test.cpp _ut_jrnl_init_params_LDADD = -lboost_unit_test_framework -lrt $(LINK_BDB) _ut_read_arg_SOURCES = \ _ut_read_arg.cpp \ args.cpp \ data_src.cpp \ jrnl_init_params.cpp \ jrnl_instance.cpp \ read_arg.cpp \ test_case.cpp \ test_case_result.cpp \ test_case_result_agregation.cpp \ ../../unit_test.cpp _ut_read_arg_LDADD = -lboost_unit_test_framework -lrt -lboost_program_options $(LINK_BDB) _ut_jrnl_instance_SOURCES = \ _ut_jrnl_instance.cpp \ args.cpp \ data_src.cpp \ jrnl_init_params.cpp \ jrnl_instance.cpp \ read_arg.cpp \ test_case.cpp \ test_case_result.cpp \ test_case_result_agregation.cpp \ ../../unit_test.cpp _ut_jrnl_instance_LDADD = -lboost_unit_test_framework -laio -lrt -lboost_program_options $(LINK_BDB) _ut_test_case_SOURCES = \ _ut_test_case.cpp \ test_case.cpp \ test_case_result.cpp \ test_case_result_agregation.cpp \ ../../unit_test.cpp _ut_test_case_LDADD = -lboost_unit_test_framework -lrt $(LINK_BDB) _ut_test_case_result_SOURCES = \ _ut_test_case_result.cpp \ test_case_result.cpp \ ../../unit_test.cpp _ut_test_case_result_LDADD = -lboost_unit_test_framework -lrt $(LINK_BDB) _ut_test_case_result_agregation_SOURCES = \ _ut_test_case_result_agregation.cpp \ test_case_result.cpp \ test_case_result_agregation.cpp \ ../../unit_test.cpp _ut_test_case_result_agregation_LDADD = -lboost_unit_test_framework -lrt $(LINK_BDB) _ut_test_case_set_SOURCES = \ _ut_test_case_set.cpp \ test_case.cpp \ test_case_set.cpp \ test_case_result.cpp \ test_case_result_agregation.cpp \ ../../unit_test.cpp _ut_test_case_set_LDADD = -lboost_unit_test_framework -lrt $(LINK_BDB) EXTRA_DIST = \ jfile_chk.py \ jtt.csv \ _ut_test_case_set.csv check-long: $(MAKE) check TESTS="$(LONG_TESTS)" SUBDIRS=. qpid-cpp-store-debian-0.16/tests/jrnl/jtt/jrnl_instance.cpp0000644000176300017630000003656611423363204023016 0ustar cajuscajus/* * Copyright (c) 2008 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #include "jrnl_instance.hpp" #include #include "data_src.hpp" #include "jrnl/data_tok.hpp" #include "jrnl/jerrno.hpp" #include "test_case_result.hpp" #define MAX_WR_WAIT 10 // in ms #define MAX_RD_WAIT 100 // in ms #define MAX_ENQCAPTHRESH_CNT 1000 // 10s if MAX_WR_WAIT is 10 ms namespace mrg { namespace jtt { jrnl_instance::jrnl_instance(const std::string& jid, const std::string& jdir, const std::string& base_filename, const u_int16_t num_jfiles, const bool ae, const u_int16_t ae_max_jfiles, const u_int32_t jfsize_sblks, const u_int16_t wcache_num_pages, const u_int32_t wcache_pgsize_sblks): mrg::journal::jcntl(jid, jdir, base_filename), _jpp(new jrnl_init_params(jid, jdir, base_filename, num_jfiles, ae, ae_max_jfiles, jfsize_sblks, wcache_num_pages, wcache_pgsize_sblks)), _args_ptr(0), _dtok_master_enq_list(), _dtok_master_txn_list(), _dtok_rd_list(), _dtok_deq_list(), _rd_aio_cv(_rd_aio_mutex), _wr_full_cv(_wr_full_mutex), _rd_list_cv(_rd_list_mutex), _deq_list_cv(_deq_list_mutex), _tcp(), _tcrp() {} jrnl_instance::jrnl_instance(const jrnl_init_params::shared_ptr& p): mrg::journal::jcntl(p->jid(), p->jdir(), p->base_filename()), _jpp(p), _args_ptr(0), _dtok_master_enq_list(), _dtok_master_txn_list(), _dtok_rd_list(), _dtok_deq_list(), _rd_aio_cv(_rd_aio_mutex), _wr_full_cv(_wr_full_mutex), _rd_list_cv(_rd_list_mutex), _deq_list_cv(_deq_list_mutex), _tcp(), _tcrp() {} jrnl_instance::~jrnl_instance() {} void jrnl_instance::init_tc(test_case::shared_ptr& tcp, const args* const args_ptr) throw () { test_case_result::shared_ptr p(new test_case_result(_jpp->jid())); _tcrp = p; _args_ptr = args_ptr; try { _tcp = tcp; _dtok_master_enq_list.clear(); _dtok_master_txn_list.clear(); _dtok_rd_list.clear(); _dtok_deq_list.clear(); if (_args_ptr->recover_mode) { try { u_int64_t highest_rid; recover(_jpp->num_jfiles(), _jpp->is_ae(), _jpp->ae_max_jfiles(), _jpp->jfsize_sblks(), _jpp->wcache_num_pages(), _jpp->wcache_pgsize_sblks(), this, 0, highest_rid); recover_complete(); } catch (const mrg::journal::jexception& e) { if (e.err_code() == mrg::journal::jerrno::JERR_JDIR_STAT) initialize(_jpp->num_jfiles(), _jpp->is_ae(), _jpp->ae_max_jfiles(), _jpp->jfsize_sblks(), _jpp->wcache_num_pages(), _jpp->wcache_pgsize_sblks(), this); else throw; } } else initialize(_jpp->num_jfiles(), _jpp->is_ae(), _jpp->ae_max_jfiles(), _jpp->jfsize_sblks(), _jpp->wcache_num_pages(), _jpp->wcache_pgsize_sblks(), this); } catch (const mrg::journal::jexception& e) { _tcrp->add_exception(e); } catch (const std::exception& e) { _tcrp->add_exception(e.what()); } catch (...) { _tcrp->add_exception("Unknown exception"); } } void jrnl_instance::run_tc() throw () { _tcrp->set_start_time(); ::pthread_create(&_enq_thread, 0, run_enq, this); ::pthread_create(&_read_thread, 0, run_read, this); ::pthread_create(&_deq_thread, 0, run_deq, this); } void jrnl_instance::tc_wait_compl() throw () { try { ::pthread_join(_deq_thread, 0); ::pthread_join(_read_thread, 0); ::pthread_join(_enq_thread, 0); stop(true); } catch (const mrg::journal::jexception& e) { _tcrp->add_exception(e); panic(); } catch (const std::exception& e) { _tcrp->add_exception(e.what()); panic(); } catch (...) { _tcrp->add_exception("Unknown exception"); panic(); } _lpmgr.finalize(); _tcrp->set_stop_time(); _tcp->add_result(_tcrp); } void jrnl_instance::run_enq() throw () { try { unsigned sleep_cnt = 0U; while(_tcrp->num_enq() < _tcp->num_msgs() && !_tcrp->exception()) { dtok_ptr p(new mrg::journal::data_tok); _dtok_master_enq_list.push_back(p); const char* msgp = data_src::get_data(_tcrp->num_enq() % 10); const std::size_t msg_size = _tcp->this_data_size(); const std::size_t xid_size = _tcp->this_xid_size(); const std::string xid(data_src::get_xid(xid_size)); const bool external = _tcp->this_external(); const bool transient = _tcp->this_transience(); mrg::journal::iores res; if (xid_size) { if (external) res = enqueue_extern_txn_data_record(msg_size, p.get(), xid, transient); else res = enqueue_txn_data_record(msgp, msg_size, msg_size, p.get(), xid, transient); } else { if (external) res = enqueue_extern_data_record(msg_size, p.get(), transient); else res = enqueue_data_record(msgp, msg_size, msg_size, p.get(), transient); } switch (res) { case mrg::journal::RHM_IORES_SUCCESS: sleep_cnt = 0U; _tcrp->incr_num_enq(); if (p->has_xid() && !_tcp->auto_deq()) commit(p.get()); break; case mrg::journal::RHM_IORES_ENQCAPTHRESH: if (++sleep_cnt > MAX_ENQCAPTHRESH_CNT) { _tcrp->add_exception("Timeout waiting for RHM_IORES_ENQCAPTHRESH to clear."); panic(); } else if (get_wr_events(0) == 0) // *** GEV2 { mrg::journal::slock sl(_wr_full_mutex); _wr_full_cv.waitintvl(MAX_WR_WAIT * 1000000); // MAX_WR_WAIT in ms } break; default: std::ostringstream oss; oss << "ERROR: enqueue operation in journal \"" << _jid << "\" returned "; oss << mrg::journal::iores_str(res) << "."; _tcrp->add_exception(oss.str()); } } flush(true); } catch (const mrg::journal::jexception& e) { _tcrp->add_exception(e); panic(); } catch (const std::exception& e) { _tcrp->add_exception(e.what()); panic(); } catch (...) { _tcrp->add_exception("Unknown exception"); panic(); } } void jrnl_instance::run_read() throw () { try { read_arg::read_mode_t rd_mode = _args_ptr->read_mode.val(); if (rd_mode != read_arg::NONE) { while (_tcrp->num_rproc() < _tcp->num_msgs() && !_tcrp->exception()) { journal::data_tok* dtokp = 0; { mrg::journal::slock sl(_rd_list_mutex); if (_dtok_rd_list.empty()) _rd_list_cv.wait(); if (!_dtok_rd_list.empty()) { dtokp = _dtok_rd_list.front(); _dtok_rd_list.pop_front(); } } if (dtokp) { _tcrp->incr_num_rproc(); bool do_read = true; if (rd_mode == read_arg::RANDOM) do_read = 1.0 * std::rand() / RAND_MAX < _args_ptr->read_prob / 100.0; else if (rd_mode == read_arg::LAZYLOAD) do_read = _tcrp->num_rproc() >= _args_ptr->lld_skip_num && _tcrp->num_read() < _args_ptr->lld_rd_num; bool read_compl = false; while (do_read && !read_compl && !_tcrp->exception()) { void* dptr = 0; std::size_t dsize = 0; void* xptr = 0; std::size_t xsize = 0; bool tr = false; bool ext = false; mrg::journal::iores res = read_data_record(&dptr, dsize, &xptr, xsize, tr, ext, dtokp); switch (res) { case mrg::journal::RHM_IORES_SUCCESS: { mrg::journal::slock sl(_deq_list_mutex); _dtok_deq_list.push_back(dtokp); _deq_list_cv.broadcast(); } read_compl = true; _tcrp->incr_num_read(); // clean up if (xsize) std::free(xptr); else if (dsize) std::free(dptr); dptr = 0; xptr = 0; break; case mrg::journal::RHM_IORES_PAGE_AIOWAIT: if (get_rd_events(0) == 0) { mrg::journal::slock sl(_rd_aio_mutex); _rd_aio_cv.waitintvl(MAX_RD_WAIT * 1000000); // MAX_RD_WAIT in ms } break; default: std::ostringstream oss; oss << "ERROR: read operation in journal \"" << _jid; oss << "\" returned " << mrg::journal::iores_str(res) << "."; _tcrp->add_exception(oss.str()); { mrg::journal::slock sl(_deq_list_mutex); _deq_list_cv.broadcast(); // wake up deq thread } } } } } } } catch (const mrg::journal::jexception& e) { _tcrp->add_exception(e); panic(); } catch (const std::exception& e) { _tcrp->add_exception(e.what()); panic(); } catch (...) { _tcrp->add_exception("Unknown exception"); panic(); } } void jrnl_instance::run_deq() throw () { try { if (_tcp->auto_deq()) { while(_tcrp->num_deq() < _tcp->num_msgs() && !_tcrp->exception()) { journal::data_tok* dtokp = 0; { mrg::journal::slock sl(_deq_list_mutex); if (_dtok_deq_list.empty()) _deq_list_cv.wait(); if (!_dtok_deq_list.empty()) { dtokp = _dtok_deq_list.front(); _dtok_deq_list.pop_front(); } } if (dtokp) { mrg::journal::iores res; if (dtokp->has_xid()) res = dequeue_txn_data_record(dtokp, dtokp->xid()); else res = dequeue_data_record(dtokp); if (res == mrg::journal::RHM_IORES_SUCCESS) { _tcrp->incr_num_deq(); commit(dtokp); } else { std::ostringstream oss; oss << "ERROR: dequeue operation in journal \"" << _jid; oss << "\" returned " << mrg::journal::iores_str(res) << "."; _tcrp->add_exception(oss.str()); } } } flush(true); } } catch (const mrg::journal::jexception& e) { _tcrp->add_exception(e); panic(); } catch (const std::exception& e) { _tcrp->add_exception(e.what()); panic(); } catch (...) { _tcrp->add_exception("Unknown exception"); panic(); } } void jrnl_instance::abort(const mrg::journal::data_tok* dtokp) { txn(dtokp, false); } void jrnl_instance::commit(const mrg::journal::data_tok* dtokp) { txn(dtokp, true); } void jrnl_instance::txn(const mrg::journal::data_tok* dtokp, const bool commit) { if (dtokp->has_xid()) { mrg::journal::data_tok* p = prep_txn_dtok(dtokp); mrg::journal::iores res = commit ? txn_commit(p, p->xid()) : txn_abort(p, p->xid()); if (res != mrg::journal::RHM_IORES_SUCCESS) { std::ostringstream oss; oss << "ERROR: " << (commit ? "commit" : "abort") << " operation in journal \""; oss << _jid << "\" returned " << mrg::journal::iores_str(res) << "."; _tcrp->add_exception(oss.str()); } } } mrg::journal::data_tok* jrnl_instance::prep_txn_dtok(const mrg::journal::data_tok* dtokp) { dtok_ptr p(new mrg::journal::data_tok); _dtok_master_txn_list.push_back(p); p->set_xid(dtokp->xid()); return p.get(); } void jrnl_instance::panic() { // In the event of a panic or exception condition, release all waiting CVs _rd_aio_cv.broadcast(); _wr_full_cv.broadcast(); _rd_list_cv.broadcast(); _deq_list_cv.broadcast(); } // AIO callbacks void jrnl_instance::wr_aio_cb(std::vector& dtokl) { for (std::vector::const_iterator i=dtokl.begin(); i!=dtokl.end(); i++) { if ((*i)->wstate() == journal::data_tok::ENQ || (*i)->wstate() == journal::data_tok::DEQ) { journal::data_tok* dtokp = *i; if (dtokp->wstate() == journal::data_tok::ENQ) { if (_args_ptr->read_mode.val() == read_arg::NONE) { mrg::journal::slock sl(_deq_list_mutex); _dtok_deq_list.push_back(dtokp); _deq_list_cv.broadcast(); } else { mrg::journal::slock sl(_rd_list_mutex); _dtok_rd_list.push_back(dtokp); _rd_list_cv.broadcast(); } } else // DEQ { mrg::journal::slock sl(_wr_full_mutex); _wr_full_cv.broadcast(); } } } } void jrnl_instance::rd_aio_cb(std::vector& /*pil*/) { mrg::journal::slock sl(_rd_aio_mutex); _rd_aio_cv.broadcast(); } } // namespace jtt } // namespace mrg qpid-cpp-store-debian-0.16/tests/jrnl/jtt/test_mgr.cpp0000644000176300017630000001722411303313552021775 0ustar cajuscajus/* * Copyright (c) 2008 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #include "test_mgr.hpp" #include #include #include #include "test_case_set.hpp" namespace mrg { namespace jtt { test_mgr::test_mgr(args& args): _ji_list(), _args(args), _err_flag(false), _random_fn_ptr(random_fn) { if (_args.seed) std::srand(_args.seed); } test_mgr::~test_mgr() {} void test_mgr::run() { // TODO: complete tidy-up of non-summary (verbose) results, then pull through // a command-line summary to control this. // Idea: --summary: prints short results afterwards // --verbose: prints long version as test progresses // defualt: none of these, similar to current summary = true version. const bool summary = true; std::cout << "CSV file: \"" << _args.test_case_csv_file_name << "\""; test_case_set tcs(_args.test_case_csv_file_name, _args.recover_mode); if (tcs.size()) { std::cout << " (found " << tcs.size() << " test case" << (tcs.size() != 1 ? "s" : "") << ")" << std::endl; if (tcs.ignored()) std::cout << "WARNING: " << tcs.ignored() << " test cases were ignored. (All test " "cases without auto-dequeue are ignored when recover-mode is selected.)" << std::endl; _args.print_args(); } else if(tcs.ignored()) { std::cout << " WARNING: All " << tcs.ignored() << " test case(s) were ignored. (All test " "cases without auto-dequeue are ignored when recover-mode is selected.)" << std::endl; } else std::cout << " (WARNING: This CSV file is empty or does not exist.)" << std::endl; do { unsigned u = 0; if (_args.randomize) random_shuffle(tcs.begin(), tcs.end(), _random_fn_ptr); for (test_case_set::tcl_itr tci = tcs.begin(); tci != tcs.end(); tci++, u++) { if (summary) std::cout << "Test case " << (*tci)->test_case_num() << ": \"" << (*tci)->comment() << "\"" << std::endl; else std::cout << (*tci)->str() << std::endl; if (!_args.reuse_instance || _ji_list.empty()) initialize_jrnls(); for (ji_list_citr jii=_ji_list.begin(); jii!=_ji_list.end(); jii++) (*jii)->init_tc(*tci, &_args); for (ji_list_citr jii=_ji_list.begin(); jii!=_ji_list.end(); jii++) (*jii)->run_tc(); for (ji_list_citr jii=_ji_list.begin(); jii!=_ji_list.end(); jii++) (*jii)->tc_wait_compl(); if (_args.format_chk) { for (ji_list_citr jii=_ji_list.begin(); jii!=_ji_list.end(); jii++) { jrnl_init_params::shared_ptr jpp = (*jii)->params(); std::string ja = _args.jfile_analyzer; if (ja.empty()) ja = "./jfile_chk.py"; if (!exists(ja)) { std::ostringstream oss; oss << "ERROR: Validation program \"" << ja << "\" does not exist" << std::endl; throw std::runtime_error(oss.str()); } std::ostringstream oss; oss << ja << " -b " << jpp->base_filename(); // TODO: When jfile_check.py can handle previously recovered journals for // specific tests, then remove this exclusion. if (!_args.recover_mode) { oss << " -c " << _args.test_case_csv_file_name; oss << " -t " << (*tci)->test_case_num(); } oss << " -q " << jpp->jdir(); bool res = system(oss.str().c_str()) != 0; (*tci)->set_fmt_chk_res(res, jpp->jid()); if (res) _err_flag = true; } } if (!_args.recover_mode && !_args.keep_jrnls) for (ji_list_citr jii=_ji_list.begin(); jii!=_ji_list.end(); jii++) try { mrg::journal::jdir::delete_dir((*jii)->jrnl_dir()); } catch (...) {} // TODO - work out exception strategy for failure here... print_results(*tci, summary); if ((*tci)->average().exception()) _err_flag = true; if (_abort || (!_args.repeat_flag && _signal)) break; if (_args.pause_secs && tci != tcs.end()) ::usleep(_args.pause_secs * 1000000); } } while (_args.repeat_flag && !_signal); } // static fn: void test_mgr::signal_handler(int sig) { if (_signal) _abort = true; _signal = sig; std::cout << std::endl; std::cout << "********************************" << std::endl; std::cout << "Caught signal " << sig << std::endl; if (_abort) std::cout << "Aborting..." << std::endl; else std::cout << "Completing current test cycle..." << std::endl; std::cout << "********************************" << std::endl << std::endl; } bool test_mgr::exists(std::string fname) { struct stat s; if (::stat(fname.c_str(), &s)) { if (errno == ENOENT) // No such dir or file return false; // Throw for any other condition std::ostringstream oss; oss << "ERROR: test_mgr::exists(): file=\"" << fname << "\": " << FORMAT_SYSERR(errno); throw std::runtime_error(oss.str()); } return true; } void test_mgr::initialize_jrnls() { _ji_list.clear(); for (unsigned i=0; i<_args.num_jrnls; i++) { std::ostringstream jid; jid << std::hex << std::setfill('0'); jid << "test_" << std::setw(4) << std::hex << i; std::ostringstream jdir; jdir << _args.journal_dir << "/" << jid.str(); jrnl_init_params::shared_ptr jpp(new jrnl_init_params(jid.str(), jdir.str(), jid.str())); jrnl_instance::shared_ptr jip(new jrnl_instance(jpp)); _ji_list.push_back(jip); } } void test_mgr::print_results(test_case::shared_ptr tcp, const bool summary) { if (!summary) std::cout << " === Results ===" << std::endl; // TODO - the reporting is broken when --repeat is used. The following commented-out // section was an attempt to fix it, but there are too many side-effects. // for (test_case::res_map_citr i=tcp->jmap_begin(); i!=tcp->jmap_end(); i++) // std::cout << (*i).second->str(summary, summary); // if (tcp->num_jrnls() > 1) std::cout << tcp->average().str(false, summary); if (!summary) std::cout << std::endl; } // static instances volatile sig_atomic_t test_mgr::_signal = 0; volatile bool test_mgr::_abort = false; } // namespace jtt } // namespace mrg qpid-cpp-store-debian-0.16/tests/jrnl/jtt/jrnl_instance.hpp0000644000176300017630000001154311372046135023013 0ustar cajuscajus/* * Copyright (c) 2008 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #ifndef mrg_jtt_jrnl_instance_hpp #define mrg_jtt_jrnl_instance_hpp #include "args.hpp" #include "jrnl_init_params.hpp" #include "test_case.hpp" #include #include "jrnl/cvar.hpp" #include "jrnl/data_tok.hpp" #include "jrnl/jcntl.hpp" #include "jrnl/slock.hpp" #include "jrnl/smutex.hpp" #include #include namespace mrg { namespace jtt { class jrnl_instance : public mrg::journal::jcntl, public virtual mrg::journal::aio_callback { public: typedef boost::shared_ptr shared_ptr; typedef boost::shared_ptr dtok_ptr; private: jrnl_init_params::shared_ptr _jpp; const args* _args_ptr; std::vector _dtok_master_enq_list; std::vector _dtok_master_txn_list; std::list _dtok_rd_list; std::list _dtok_deq_list; mrg::journal::smutex _rd_aio_mutex; ///< Mutex for read aio wait conditions mrg::journal::cvar _rd_aio_cv; ///< Condition var for read aio wait conditions mrg::journal::smutex _wr_full_mutex; ///< Mutex for write full conditions mrg::journal::cvar _wr_full_cv; ///< Condition var for write full conditions mrg::journal::smutex _rd_list_mutex; ///< Mutex for _dtok_rd_list mrg::journal::cvar _rd_list_cv; ///< Condition var for _dtok_rd_list mrg::journal::smutex _deq_list_mutex; ///< Mutex for _dtok_deq_list mrg::journal::cvar _deq_list_cv; ///< Condition var for _dtok_deq_list pthread_t _enq_thread; pthread_t _deq_thread; pthread_t _read_thread; test_case::shared_ptr _tcp; test_case_result::shared_ptr _tcrp; public: jrnl_instance(const std::string& jid, const std::string& jdir, const std::string& base_filename, const u_int16_t num_jfiles = jrnl_init_params::def_num_jfiles, const bool ae = jrnl_init_params::def_ae, const u_int16_t ae_max_jfiles = jrnl_init_params::def_ae_max_jfiles, const u_int32_t jfsize_sblks = jrnl_init_params::def_jfsize_sblks, const u_int16_t wcache_num_pages = jrnl_init_params::def_wcache_num_pages, const u_int32_t wcache_pgsize_sblks = jrnl_init_params::def_wcache_pgsize_sblks); jrnl_instance(const jrnl_init_params::shared_ptr& params); virtual ~jrnl_instance(); inline const jrnl_init_params::shared_ptr& params() const { return _jpp; } inline const std::string& jid() const { return _jpp->jid(); } void init_tc(test_case::shared_ptr& tcp, const args* const args_ptr) throw (); void run_tc() throw (); void tc_wait_compl() throw (); // AIO callbacks virtual void wr_aio_cb(std::vector& dtokl); virtual void rd_aio_cb(std::vector& pil); private: void run_enq() throw (); inline static void* run_enq(void* p) { static_cast(p)->run_enq(); return 0; } void run_read() throw (); inline static void* run_read(void* p) { static_cast(p)->run_read(); return 0; } void run_deq() throw (); inline static void* run_deq(void* p) { static_cast(p)->run_deq(); return 0; } void abort(const mrg::journal::data_tok* dtokp); void commit(const mrg::journal::data_tok* dtokp); void txn(const mrg::journal::data_tok* dtokp, const bool commit); mrg::journal::data_tok* prep_txn_dtok(const mrg::journal::data_tok* dtokp); void panic(); // // static callbacks // static void aio_rd_callback(jcntl* journal, std::vector& pil); // static void aio_wr_callback(jcntl* journal, std::vector& dtokl); }; } // namespace jtt } // namespace mrg #endif // ifndef mrg_jtt_jrnl_instance_hpp qpid-cpp-store-debian-0.16/tests/jrnl/jtt/_ut_jrnl_instance.cpp0000644000176300017630000001373511222162407023655 0ustar cajuscajus/* * Copyright (c) 2008 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #include "../../unit_test.h" #include #include "jrnl_init_params.hpp" #include "jrnl_instance.hpp" #include "jrnl/jdir.hpp" #include "jrnl/jerrno.hpp" using namespace boost::unit_test; using namespace mrg::journal; using namespace mrg::jtt; using namespace std; QPID_AUTO_TEST_SUITE(jtt_jrnl_instance) const string test_filename("_ut_jrnl_instance"); const char* tdp = getenv("TMP_DATA_DIR"); const string test_dir(tdp && strlen(tdp) > 0 ? tdp : "/tmp/JttTest"); QPID_AUTO_TEST_CASE(constructor_1) { cout << test_filename << ".constructor_1: " << flush; const string jid = "jid1"; const string jdir = test_dir + "/test1"; const string bfn = "test"; const u_int16_t num_jfiles = 20; const bool ae = false; const u_int16_t ae_max_jfiles = 45; const u_int32_t jfsize_sblks = 128; args a("a1"); using mrg::jtt::test_case; test_case::shared_ptr p(new test_case(1, 0, 0, 0, false, 0, 0, test_case::JTT_PERSISTNET, test_case::JDL_INTERNAL, "t1")); jrnl_instance ji(jid, jdir, bfn, num_jfiles, ae, ae_max_jfiles, jfsize_sblks); ji.init_tc(p, &a); ji.run_tc(); ji.tc_wait_compl(); try { jdir::verify_dir(jdir, bfn); } catch (const jexception& e) { BOOST_ERROR(e.what()); } jdir::delete_dir(jdir); cout << "ok" << endl; } QPID_AUTO_TEST_CASE(constructor_2) { cout << test_filename << ".constructor_2: " << flush; const string jid = "jid2"; const string jdir = test_dir + "/test2"; const string bfn = "test"; const u_int16_t num_jfiles = 20; const bool ae = false; const u_int16_t ae_max_jfiles = 45; const u_int32_t jfsize_sblks = 128; args a("a2"); using mrg::jtt::test_case; test_case::shared_ptr p(new test_case(2, 0, 0, 0, false, 0, 0, test_case::JTT_PERSISTNET, test_case::JDL_INTERNAL, "t2")); jrnl_init_params::shared_ptr jpp(new jrnl_init_params(jid, jdir, bfn, num_jfiles, ae, ae_max_jfiles, jfsize_sblks)); jrnl_instance ji(jpp); ji.init_tc(p, &a); ji.run_tc(); ji.tc_wait_compl(); try { jdir::verify_dir(jdir, bfn); } catch (const jexception& e) { BOOST_ERROR(e.what()); } jdir::delete_dir(jdir); cout << "ok" << endl; } QPID_AUTO_TEST_CASE(constructor_3) { cout << test_filename << ".constructor_3: " << flush; const string jid = "jid3"; const string jdir = test_dir + "/test3"; const string bfn = "test"; const u_int16_t num_jfiles = 20; const bool ae = false; const u_int16_t ae_max_jfiles = 45; const u_int32_t jfsize_sblks = 128; args a("a3"); using mrg::jtt::test_case; test_case::shared_ptr p(new test_case(3, 0, 0, 0, false, 0, 0, test_case::JTT_PERSISTNET, test_case::JDL_INTERNAL, "t3")); jrnl_init_params::shared_ptr jpp(new jrnl_init_params(jid, jdir, bfn, num_jfiles, ae, ae_max_jfiles, jfsize_sblks)); jrnl_instance ji(jpp); ji.init_tc(p, &a); ji.run_tc(); ji.tc_wait_compl(); try { jdir::verify_dir(jdir, bfn); } catch (const jexception& e) { BOOST_ERROR(e.what()); } jdir::delete_dir(jdir); cout << "ok" << endl; } QPID_AUTO_TEST_CASE(recover) { cout << test_filename << ".recover: " << flush; const string jid = "jid5"; const string jdir = test_dir + "/test5"; const string bfn = "test"; const u_int16_t num_jfiles = 20; const bool ae = false; const u_int16_t ae_max_jfiles = 0; const u_int32_t jfsize_sblks = 128; args a("a4"); using mrg::jtt::test_case; test_case::shared_ptr p(new test_case(5, 0, 0, 0, false, 0, 0, test_case::JTT_PERSISTNET, test_case::JDL_INTERNAL, "t5")); jrnl_init_params::shared_ptr jpp(new jrnl_init_params(jid, jdir, bfn, num_jfiles, ae, ae_max_jfiles, jfsize_sblks)); jrnl_instance ji(jpp); ji.init_tc(p, &a); ji.run_tc(); ji.tc_wait_compl(); try { jdir::verify_dir(jdir, bfn); } catch (const jexception& e) { BOOST_ERROR(e.what()); } a.recover_mode = true; ji.init_tc(p, &a); ji.run_tc(); ji.tc_wait_compl(); try { jdir::verify_dir(jdir, bfn); } catch (const jexception& e) { BOOST_ERROR(e.what()); } jdir::delete_dir(jdir); cout << "ok" << endl; } QPID_AUTO_TEST_CASE(recover_no_files) { cout << test_filename << ".recover_no_files: " << flush; const string jid = "jid6"; const string jdir = test_dir + "/test6"; const string bfn = "test"; const u_int16_t num_jfiles = 20; const bool ae = false; const u_int16_t ae_max_jfiles = 0; const u_int32_t jfsize_sblks = 128; args a("a5"); a.recover_mode = true; using mrg::jtt::test_case; test_case::shared_ptr p(new test_case(6, 0, 0, 0, false, 0, 0, test_case::JTT_PERSISTNET, test_case::JDL_INTERNAL, "t6")); jrnl_init_params::shared_ptr jpp(new jrnl_init_params(jid, jdir, bfn, num_jfiles, ae, ae_max_jfiles, jfsize_sblks)); jrnl_instance ji(jpp); ji.init_tc(p, &a); ji.run_tc(); ji.tc_wait_compl(); try { jdir::verify_dir(jdir, bfn); } catch (const jexception& e) { BOOST_ERROR(e.what()); } jdir::delete_dir(jdir); cout << "ok" << endl; } QPID_AUTO_TEST_SUITE_END() qpid-cpp-store-debian-0.16/tests/python_tests/0000755000176300017630000000000011761422620020441 5ustar cajuscajusqpid-cpp-store-debian-0.16/tests/python_tests/flow_to_disk.py0000644000176300017630000015216111530501747023506 0ustar cajuscajus""" Copyright (c) 2008 Red Hat, Inc. This file is part of the Qpid async store library msgstore.so. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA The GNU Lesser General Public License is available in the file COPYING. """ import qpid from brokertest import EXPECT_EXIT_OK, EXPECT_UNKNOWN from qpid.datatypes import uuid4 from store_test import StoreTest, store_args from qpid.messaging import Message, TargetCapacityExceeded, ServerError #SessionError, SendError class FlowToDisk(StoreTest): """Tests for async store flow-to-disk""" def _broker_args(self): """ Disable flow control so we can easily overflow a queue! """ fc_off = ["--default-flow-stop-threshold", "0", "--default-flow-resume-threshold", "0"] return store_args() + fc_off; @staticmethod def _broker_name(queue_name, txn_produce, txn_consume): """Create a broker name based on the queue name and the transaction parameters""" name = queue_name if txn_produce: name += "_TxP" if txn_consume: name += "_TxC" return name def _tx_simple_limit(self, queue_name, kwargs): """ Test a simple case of message limits which will force flow-to-disk. * queue_args sets a limit - either max_count and/or max_size * messages are added. Some will flow to disk. * Consume all messages sent. * Check the broker has no messages left. """ # Unpack args txn_produce = kwargs.get("txn_produce", False) txn_consume = kwargs.get("txn_consume", False) recover = kwargs.get("recover", False) max_count = kwargs.get("max_count") max_size = kwargs.get("max_size") policy = kwargs.get("policy", "flow_to_disk") num_msgs = kwargs.get("num_msgs", 15) msg_size = kwargs.get("msg_size", 10) msg_durable = kwargs.get("msg_durable", False) sync = kwargs.get("sync", False) browse = kwargs.get("browse", False) bname = self._broker_name(queue_name, txn_produce, txn_consume) if recover: expect = EXPECT_UNKNOWN else: expect = EXPECT_EXIT_OK broker = self.broker(self._broker_args(), name=bname, expect=expect, log_level="debug+") prod_session = broker.connect().session(transactional=txn_produce) sender = prod_session.sender(self.snd_addr(queue_name, auto_create=True, durable=True, ftd_count=max_count, ftd_size=max_size, policy=policy)) # Send messages msgs = [] pre_recover_ftd_msgs = [] # msgs released before a recover post_recover_ftd_msgs = [] # msgs released after a recover cum_msg_size = 0 for index in range(0, num_msgs): msg = Message(self.make_message(index, msg_size), durable=msg_durable, id=uuid4(), correlation_id="msg-%04d"%index) #print "Sending msg %s" % msg.id msgs.append(msg) cum_msg_size += msg_size if (max_count != None and index >= max_count) or (max_size != None and cum_msg_size > max_size): pre_recover_ftd_msgs.append(msg) sender.send(msg, sync=sync) if not sync: sender.sync() # Close transaction (if needed) if txn_produce: prod_session.commit() # Browse messages if browse: self.check_messages(broker, queue_name, msgs, browse=True) if recover: broker.terminate() if msg_durable: post_recover_ftd_msgs = pre_recover_ftd_msgs else: del msgs[:] # Transient messages will be discarded on recover old_broker = broker # keep for log analysis broker = self.broker(self._broker_args(), name=bname, expect=EXPECT_EXIT_OK, log_level="debug+") # Browse messages after recover if browse: self.check_messages(broker, queue_name, msgs, browse=True) # Consume messages self.check_messages(broker, queue_name, msgs, transactional=txn_consume, empty=True) broker.terminate() # Check broker logs for released messages if recover: if txn_produce: self.check_msg_release_on_commit(old_broker, pre_recover_ftd_msgs) else: self.check_msg_release(old_broker, pre_recover_ftd_msgs) self.check_msg_release_on_recover(broker, post_recover_ftd_msgs) else: if txn_produce: self.check_msg_release_on_commit(broker, pre_recover_ftd_msgs) else: self.check_msg_release(broker, pre_recover_ftd_msgs) def simple_limit(self, queue_name, **kwargs): """Adapter for adding transactions to test""" # Cycle through the produce/consume block transaction combinations for index in range(0, 4): kwargs["txn_produce"] = index & 1 != 0 # Transactional produce kwargs["txn_consume"] = index & 2 != 0 # Transactional consume self._tx_simple_limit(queue_name, kwargs) class SimpleMaxCountTest(FlowToDisk): """Flow-to-disk tests based on setting max_count""" def test_base(self): """Base test""" self.simple_limit("SimpleMaxCount", max_count=10) def test_recover(self): """Recover test""" self.simple_limit("SimpleMaxCountRecover", max_count=10, recover=True) def test_durable(self): """Durable message test""" self.simple_limit("SimpleMaxCountDurable", max_count=10, msg_durable=True) def test_durable_recover(self): """Durable message recover test""" self.simple_limit("SimpleMaxCountDurableRecover", max_count=10, msg_durable=True, recover=True) def test_browse(self): """Browse test""" self.simple_limit("SimpleMaxCountBrowse", max_count=10, browse=True) def test_browse_recover(self): """Browse before and after recover test""" self.simple_limit("SimpleMaxCountBrowseRecover", max_count=10, browse=True, recover=True) def test_durable_browse(self): """Browse durable message test""" self.simple_limit("SimpleMaxCountDurableBrowse", max_count=10, msg_durable=True, browse=True) def test_durable_browse_recover(self): """Browse durable messages before and after recover""" self.simple_limit("SimpleMaxCountDurableBrowseRecover", max_count=10, msg_durable=True, browse=True, recover=True) def test_large_msg(self): """Large message test""" self.simple_limit("SimpleMaxCountLargeMsg", max_count=10, max_size=10000000, num_msgs=100, msg_size=10000) def test_large_msg_recover(self): """Large message test""" self.simple_limit("SimpleMaxCountLargeMsgRecover", max_count=10, max_size=10000000, num_msgs=100, msg_size=10000, recover=True) def test_large_msg_durable(self): """Large durable message test""" self.simple_limit("SimpleMaxCountLargeMsgDurable", max_count=10, max_size=10000000, msg_durable=True, num_msgs=100, msg_size=10000) def test_large_msg_durable_recover(self): """Large durable message test""" self.simple_limit("SimpleMaxCountLargeMsgDurableRecover", max_count=10, max_size=10000000, msg_durable=True, num_msgs=100, msg_size=10000, recover=True) def test_large_msg_browse(self): """Large message browse test""" self.simple_limit("SimpleMaxCountLargeMsgBrowse", max_count=10, max_size=10000000, browse=True, num_msgs=100, msg_size=10000) def test_large_msg_browse_recover(self): """Large message browse test""" self.simple_limit("SimpleMaxCountLargeMsgBrowseRecover", max_count=10, max_size=10000000, browse=True, num_msgs=100, msg_size=10000, recover=True) def test_large_msg_durable_browse(self): """Large durable message browse test""" self.simple_limit("SimpleMaxCountLargeMsgDurableBrowse", max_count=10, max_size=10000000, msg_durable=True, browse=True, num_msgs=100, msg_size=10000) def test_large_msg_durable_browse_recover(self): """Large durable message browse test""" self.simple_limit("SimpleMaxCountLargeMsgDurableBrowseRecover", max_count=10, max_size=10000000, msg_durable=True, browse=True, num_msgs=100, msg_size=10000, recover=True) class SimpleMaxSizeTest(FlowToDisk): """Flow-to-disk tests based on setting max_size""" def test_base(self): """Base test""" self.simple_limit("SimpleMaxSize", max_size=100) def test_recover(self): """Recover test""" self.simple_limit("SimpleMaxSizeRecover", max_size=100, recover=True) def test_durable(self): """Durable message test""" self.simple_limit("SimpleMaxSizeDurable", max_size=100, msg_durable=True) def test_durable_recover(self): """Durable message recover test""" self.simple_limit("SimpleMaxSizeDurable", max_size=100, msg_durable=True, recover=True) def test_browse(self): """Browse test""" self.simple_limit("SimpleMaxSizeBrowse", max_size=100, browse=True) def test_browse_recover(self): """Browse before and after recover test""" self.simple_limit("SimpleMaxSizeBrowseRecover", max_size=100, browse=True, recover=True) def test_durable_browse(self): """Browse durable message test""" self.simple_limit("SimpleMaxSizeDurableBrowse", max_size=100, msg_durable=True, browse=True) def test_durable_browse_recover(self): """Browse durable messages before and after recover""" self.simple_limit("SimpleMaxSizeDurableBrowseRecover", max_size=100, msg_durable=True, browse=True, recover=True) def test_large_msg(self): """Large message test""" self.simple_limit("SimpleMaxSizeLargeMsg", max_size=100000, num_msgs=100, msg_size=10000) def test_large_msg_recover(self): """Large message test""" self.simple_limit("SimpleMaxSizeLargeMsgRecover", max_size=100000, num_msgs=100, msg_size=10000, recover=True) def test_large_msg_durable(self): """Large durable message test""" self.simple_limit("SimpleMaxSizeLargeMsgDurable", max_size=100000, msg_durable=True, num_msgs=100, msg_size=10000) def test_large_msg_durable_recover(self): """Large durable message test""" self.simple_limit("SimpleMaxSizeLargeMsgDurableRecover", max_size=100000, msg_durable=True, num_msgs=100, msg_size=10000, recover=True) def test_large_msg_browse(self): """Large message browse test""" self.simple_limit("SimpleMaxSizeLargeMsgBrowse", max_size=100, browse=True, num_msgs=100, msg_size=10000) def test_large_msg_browse_recover(self): """Large message browse test""" self.simple_limit("SimpleMaxSizeLargeMsgBrowseRecover", max_size=100, browse=True, num_msgs=100, msg_size=10000, recover=True) def test_large_msg_durable_browse(self): """Large durable message browse test""" self.simple_limit("SimpleMaxSizeLargeMsgDurableBrowse", max_size=100, msg_durable=True, browse=True, num_msgs=100, msg_size=10000) def test_large_msg_durable_browse_recover(self): """Large durable message browse test""" self.simple_limit("SimpleMaxSizeLargeMsgDurableBrowseRecover", max_size=100, msg_durable=True, browse=True, num_msgs=100, msg_size=10000, recover=True) class SimpleMaxSizeCountTest(FlowToDisk): """Flow-to-disk tests based on setting both max_count and max_size at the same time""" def test_base(self): """Base test""" self.simple_limit("MaxSizeMaxCount", max_count=10, max_size=1000) def test_recover(self): """Recover test""" self.simple_limit("MaxSizeMaxCountRecover", max_count=10, max_size=1000, recover=True) def test_durable(self): """Durable message test""" self.simple_limit("MaxSizeMaxCountDurable", max_count=10, max_size=1000, msg_size=250, msg_durable=True) def test_durable_recover(self): """Durable message recover test""" self.simple_limit("MaxSizeMaxCountDurableRecover", max_count=10, max_size=1000, msg_size=250, msg_durable=True, recover=True) def test_browse(self): """Browse test""" self.simple_limit("MaxSizeMaxCountBrowse", max_count=10, max_size=1000, browse=True) def test_browse_recover(self): """Browse before and after recover test""" self.simple_limit("MaxSizeMaxCountBrowseRecover", max_count=10, max_size=1000, browse=True, recover=True) def test_durable_browse(self): """Browse durable message test""" self.simple_limit("MaxSizeMaxCountDurableBrowse", max_count=10, max_size=1000, msg_size=250, msg_durable=True, browse=True) def test_durable_browse_recover(self): """Browse durable messages before and after recover""" self.simple_limit("MaxSizeMaxCountDurableBrowseRecover", max_count=10, max_size=1000, msg_size=250, msg_durable=True, browse=True, recover=True) # ====================================================================================================================== class MultiQueueFlowToDisk(FlowToDisk): """Tests for async store flow-to-disk involving multiple queues""" def _multi_queue_setup(self, queue_map, broker, exchange_name, txn_produce, txn_consume, policy, exclusive = False): """Create the send session and receive sessions for multi-queue scenarios""" connection = broker.connect() snd_session = connection.session(transactional=txn_produce) addr = self.snd_addr(exchange_name, topic_flag=True, exchage_type="fanout") #print "snd_addr=\"%s\"" % addr sndr = snd_session.sender(addr) for queue_name, queue_properties in queue_map.iteritems(): if "durable" in queue_properties.keys(): durable = queue_properties["durable"] else: durable = False max_count = None if "max_count" in queue_properties.keys(): max_count = queue_properties["max_count"] max_size = None if "max_size" in queue_properties.keys(): max_size = queue_properties["max_size"] rcv_session = connection.session(transactional=txn_consume) addr = self.rcv_addr(exchange_name, auto_create=False, link_name=queue_name, durable=durable, exclusive=exclusive, ftd_count=max_count, ftd_size=max_size, policy=policy) #print "rcv_addr=\"%s\"" % addr rcv_session.receiver(addr) return snd_session, sndr @staticmethod def _make_policy_dict(src, marker, delim=";"): """Create a dictionary of key/value strings from a formatted string src of the form '... marker key1=val1, key2=val2, ..., keyN=valN delimiter ...' where the portion of interest starts at marker m until the following delimiter d (default: ';').""" pos = src.find(marker) + len(marker) res = [] for index in src[pos:src.find(delim, pos)].split(): if "=" in index: res.append(index.strip(",").split("=")) if len(res) > 0: return dict(res) @staticmethod def _make_policy_val(src, marker, delim=";"): """Return a string value from a formatted string of the form '... marker val delimiter ...' where the value lies between marker and delimiter d (default: ';')""" pos = src.find(marker) + len(marker) return src[pos:src.find(delim, pos)].strip() @staticmethod def _check_error(error_str, fail_list=None): """Check a policy exception string to ensure the failure occurred on the expected queue and at the expected count.""" if error_str.startswith("resource-limit-exceeded"): fail_policy = MultiQueueFlowToDisk._make_policy_val(error_str, "type=", delim="(") fail_queue_name = MultiQueueFlowToDisk._make_policy_val(error_str, "Policy exceeded on ", delim=",") fail_count_dict = MultiQueueFlowToDisk._make_policy_dict(error_str, "count: ") fail_size_dict = MultiQueueFlowToDisk._make_policy_dict(error_str, "size: ") if fail_list == None: return False # Not expected - no failure should have occurred for fail in fail_list: if fail_queue_name == fail["queue"]: if fail_policy != fail["type"]: return False if (fail_count_dict != None and "count" in fail and \ int(fail_count_dict["current"]) != fail["count"]) \ or \ (fail_size_dict != None and "size" in fail and int(fail_size_dict["current"]) != fail["size"]): return False return True return False @staticmethod def _check_target_capacity_exceeded_error(err, fail_list=None): """Check that an error is a TargetCapacityExceeded.""" if not isinstance(err, TargetCapacityExceeded): return False return MultiQueueFlowToDisk._check_error(str(err), fail_list) @staticmethod def _check_server_error(err, txn=False): """Check that an error is a ServerError.""" if not isinstance(err, ServerError): return False if txn and str(err).startswith("internal-error: Commit failed"): return True return False @staticmethod def _is_queue_durable(queue_map, index): """Return true if the indexed queue is durable (indexed by queue_map.keys() or queue_map.values())""" return "durable" in queue_map.values()[index] and queue_map.values()[index]["durable"] @staticmethod def _expected_msg_loss(fail_list): """Examine the fail_list for expected failures and return a tuple containing the expected failure conditions""" count_exp_loss = None count_exp_loss_queues = None size_exp_loss = None size_exp_loss_queues = None if fail_list != None: for fail in fail_list: if "count" in fail: this_count = fail["count"] if count_exp_loss == None: count_exp_loss = this_count count_exp_loss_queues = [fail["queue"]] elif this_count < count_exp_loss: count_exp_loss = this_count count_exp_loss_queues = [fail["queue"]] elif this_count == count_exp_loss: count_exp_loss_queues.append(fail["queue"]) if "size" in fail: this_size = fail["size"] if size_exp_loss == None: size_exp_loss = this_size size_exp_loss_queues = [fail["queue"]] elif this_size < size_exp_loss: size_exp_loss = this_size size_exp_loss_queues = [fail["queue"]] elif this_size == size_exp_loss: size_exp_loss_queues.append(fail["queue"]) return (count_exp_loss, count_exp_loss_queues, size_exp_loss, size_exp_loss_queues) @staticmethod def _expected_msg_ftd(queue_map): max_count = None max_size = None for queue_props in queue_map.itervalues(): if "durable" in queue_props and queue_props["durable"]: if "max_count" in queue_props and queue_props["max_count"] != None and \ (max_count == None or queue_props["max_count"] < max_count): max_count = queue_props["max_count"] if "max_size" in queue_props and queue_props["max_size"] != None and \ (max_size == None or queue_props["max_size"] < max_size): max_size = queue_props["max_size"] return (max_count, max_size) def tx_multi_queue_limit(self, broker_base_name, queue_map, exchange_name, **kwargs): """ Test a multi-queue case queue_map = queue map where map is queue name (key) against queue args (value) """ # Unpack args msg_durable = kwargs.get("msg_durable", False) num_msgs = kwargs.get("num_msgs", 15) msg_size = kwargs.get("msg_size", 10) txn_produce = kwargs.get("txn_produce", False) txn_consume = kwargs.get("txn_consume", False) browse = kwargs.get("browse", False) policy = kwargs.get("policy", "flow_to_disk") recover = kwargs.get("recover", False) sync = kwargs.get("sync", False) fail_list = kwargs.get("fail_list") bname = self._broker_name(broker_base_name, txn_produce, txn_consume) broker = self.broker(self._broker_args(), name=bname, expect=EXPECT_EXIT_OK, log_level="debug+") snd_session, sndr = self._multi_queue_setup(queue_map, broker, exchange_name, txn_produce, txn_consume, policy) # Find expected limits count_exp_loss, count_exp_loss_queues, size_exp_loss, size_exp_loss_queues = self._expected_msg_loss(fail_list) max_count, max_size = self._expected_msg_ftd(queue_map) # Send messages try: msgs = [] pre_recover_ftd_msgs = [] # msgs released before a recover post_recover_ftd_msgs = [] # msgs released after a recover cum_msg_size = 0 target_queues = [] for index in range(0, num_msgs): msg = Message(self.make_message(index, msg_size), durable=msg_durable, id=uuid4(), correlation_id="msg-%04d"%index) #print "Sending msg %s" % msg.id sndr.send(msg, sync=sync) if msg_size != None: cum_msg_size += msg_size if count_exp_loss != None and index >= count_exp_loss: target_queues.extend(count_exp_loss_queues) if size_exp_loss != None and cum_msg_size > size_exp_loss: target_queues.extend(size_exp_loss_queues) if (count_exp_loss == None or index < count_exp_loss) and \ (size_exp_loss == None or cum_msg_size <= size_exp_loss): msgs.append(msg) if (max_count != None and index >= max_count) or (max_size != None and cum_msg_size > max_size): pre_recover_ftd_msgs.append(msg) if not sync: sndr.sync() if txn_produce: snd_session.commit() except TargetCapacityExceeded, err: if not self._check_target_capacity_exceeded_error(err, fail_list): raise except ServerError, err: msgs[:] = [] # Transaction failed, all messages lost if not self._check_server_error(err, txn_produce): raise # Browse messages if browse: for index in range(0, len(queue_map)): self.check_messages(broker, queue_map.keys()[index], msgs, browse=True) if recover: broker.terminate() if msg_durable: post_recover_ftd_msgs = pre_recover_ftd_msgs else: del msgs[:] # Transient messages will be discarded on recover old_broker = broker # keep for log analysis broker = self.broker(self._broker_args(), name=bname, expect=EXPECT_EXIT_OK, log_level="debug+") # Browse messages if browse: for index in range(0, len(queue_map)): empty = not self._is_queue_durable(queue_map, index) self.check_messages(broker, queue_map.keys()[index], msgs, browse=True, emtpy_flag=empty) # Consume messages for index in range(0, len(queue_map)): empty_chk = txn_produce or queue_map.keys()[index] in target_queues empty = recover and not self._is_queue_durable(queue_map, index) self.check_messages(broker, queue_map.keys()[index], msgs, transactional=txn_consume, empty=empty_chk, emtpy_flag=empty) broker.terminate() # Check broker logs for released messages if recover: if txn_produce: if msg_durable: self.check_msg_release_on_commit(old_broker, pre_recover_ftd_msgs) else: self.check_msg_block_on_commit(old_broker, pre_recover_ftd_msgs) else: if msg_durable: self.check_msg_release(old_broker, pre_recover_ftd_msgs) else: self.check_msg_block(old_broker, pre_recover_ftd_msgs) self.check_msg_release_on_recover(broker, post_recover_ftd_msgs) else: if txn_produce: if msg_durable: self.check_msg_release_on_commit(broker, pre_recover_ftd_msgs) else: self.check_msg_block_on_commit(broker, pre_recover_ftd_msgs) else: if msg_durable: self.check_msg_release(broker, pre_recover_ftd_msgs) else: self.check_msg_block(broker, pre_recover_ftd_msgs) # --- Parameterized test methods --- def no_limit(self, num, queue_durable=False, msg_durable=False, browse=False, recover=False, txn_produce=False, txn_consume=False): """No policy test""" queue_map_1 = {"a%02d" % num : {"durable":queue_durable, "max_count":None, "max_size": None}, "b%02d" % num : {"durable":queue_durable, "max_count":None, "max_size": None} } self.tx_multi_queue_limit("MultiQueue_NoLimit", queue_map_1, exchange_name="Fanout_a%02d" % num, msg_durable=msg_durable, browse=browse, recover=recover, txn_produce=txn_produce, txn_consume=txn_consume) def max_count(self, num, queue_durable=False, msg_durable=False, browse=False, recover=False, txn_produce=False, txn_consume=False): """Count policy test""" queue_map_2 = {"c%02d" % num : {"durable":queue_durable, "max_count":None, "max_size": None}, "d%02d" % num : {"durable":queue_durable, "max_count":10, "max_size": None} } fail_list = None if not queue_durable: fail_list = [{"queue":"d%02d" % num, "type":"reject", "count":10}] self.tx_multi_queue_limit("MultiQueue_MaxCount", queue_map_2, exchange_name="Fanout_b%02d" % num, msg_durable=msg_durable, browse=browse, recover=recover, fail_list=fail_list, txn_produce=txn_produce, txn_consume=txn_consume) def max_size(self, num, queue_durable=False, msg_durable=False, browse=False, recover=False, txn_produce=False, txn_consume=False): """Size policy test""" queue_map_3 = {"e%02d" % num : {"durable":queue_durable, "max_count":None, "max_size": None}, "f%02d" % num : {"durable":queue_durable, "max_count":None, "max_size": 1000} } fail_list = None if not queue_durable: fail_list = [{"queue":"f%02d" % num, "type":"reject", "size":1000}] self.tx_multi_queue_limit("MultiQueue_MaxSize", queue_map_3, exchange_name="Fanout_c%02d" % num, msg_size=100, msg_durable=msg_durable, browse=browse, recover=recover, fail_list=fail_list, txn_produce=txn_produce, txn_consume=txn_consume) def dual_max_count(self, num, queue_durable=False, msg_durable=False, browse=False, recover=False, txn_produce=False, txn_consume=False): """Multiple count policy test""" queue_map_4 = {"g%02d" % num : {"durable":queue_durable, "max_count":10, "max_size": None}, "h%02d" % num : {"durable":queue_durable, "max_count":8, "max_size": None} } fail_list = None if not queue_durable: fail_list = [{"queue":"h%02d" % num, "type":"reject", "count":8}] self.tx_multi_queue_limit("MultiQueue_DualMaxCount", queue_map_4, exchange_name="Fanout_d%02d" % num, msg_durable=msg_durable, browse=browse, recover=recover, fail_list=fail_list, txn_produce=txn_produce, txn_consume=txn_consume) def dual_max_size(self, num, queue_durable=False, msg_durable=False, browse=False, recover=False, txn_produce=False, txn_consume=False): """Multiple size policy test""" queue_map_5 = {"i%02d" % num : {"durable":queue_durable, "max_count":None, "max_size": 1000}, "j%02d" % num : {"durable":queue_durable, "max_count":None, "max_size": 800} } fail_list = None if not queue_durable: fail_list = [{"queue":"j%02d" % num, "type":"reject", "size":800}] self.tx_multi_queue_limit("MultiQueue_DualMaxSize", queue_map_5, exchange_name="Fanout_e%02d" % num, msg_size=100, msg_durable=msg_durable, browse=browse, recover=recover, fail_list=fail_list, txn_produce=txn_produce, txn_consume=txn_consume) def mixed_limit_1(self, num, queue_durable=False, msg_durable=False, browse=False, recover=False, txn_produce=False, txn_consume=False): """Both count and size polices active with the same queue having equal probabilities of triggering the policy""" queue_map_6 = {"k%02d" % num : {"durable":queue_durable, "max_count":None, "max_size": None}, "l%02d" % num : {"durable":queue_durable, "max_count":10, "max_size": None}, "m%02d" % num : {"durable":queue_durable, "max_count":None, "max_size": 1000}, "n%02d" % num : {"durable":queue_durable, "max_count":8, "max_size": 800} } fail_list = None if not queue_durable: fail_list = [{"queue":"n%02d" % num, "type":"reject", "count":8, "size":800}] self.tx_multi_queue_limit("MultiQueue_MixedLimit", queue_map_6, exchange_name="Fanout_f%02d" % num, msg_size=100, msg_durable=msg_durable, browse=browse, recover=recover, fail_list=fail_list, txn_produce=txn_produce, txn_consume=txn_consume) def mixed_limit_2(self, num, queue_durable=False, msg_durable=False, browse=False, recover=False, txn_produce=False, txn_consume=False): """Both count and size polices active with different queues having equal probabilities of triggering the policy""" queue_map_7 = {"o%02d" % num : {"durable":queue_durable, "max_count":None, "max_size": None}, "p%02d" % num : {"durable":queue_durable, "max_count":10, "max_size": None}, "q%02d" % num : {"durable":queue_durable, "max_count":None, "max_size": 800}, "r%02d" % num : {"durable":queue_durable, "max_count":8, "max_size": 1000} } fail_list = None if not queue_durable: fail_list = [{"queue":"q%02d" % num, "type":"reject", "size":800}, {"queue":"r%02d" % num, "type":"reject", "count":8,}] self.tx_multi_queue_limit("MultiQueue_MixedLimit", queue_map_7, exchange_name="Fanout_g%02d" % num, msg_size=100, msg_durable=msg_durable, browse=browse, recover=recover, fail_list=fail_list, txn_produce=txn_produce, txn_consume=txn_consume) # --- Non-parameterized test methods - these will be run by Python test framework --- _num = None _queue_durable = False _msg_durable = False _browse = False _recover = False _txn_produce = False _txn_consume = False def test_no_limit(self): """No policy test (non-parameterized)""" self.no_limit(self._num, queue_durable=self._queue_durable, msg_durable=self._msg_durable, browse=self._browse, recover=self._recover, txn_produce=self._txn_produce, txn_consume=self._txn_consume) def test_max_count(self): """Count policy test (non-parameterized)""" self.max_count(self._num, queue_durable=self._queue_durable, msg_durable=self._msg_durable, browse=self._browse, recover=self._recover, txn_produce=self._txn_produce, txn_consume=self._txn_consume) def test_max_size(self): """Size policy test (non-parameterized)""" self.max_size(self._num, queue_durable=self._queue_durable, msg_durable=self._msg_durable, browse=self._browse, recover=self._recover, txn_produce=self._txn_produce, txn_consume=self._txn_consume) def test_dual_max_count(self): """Multiple count policy test (non-parameterized)""" self.dual_max_count(self._num, queue_durable=self._queue_durable, msg_durable=self._msg_durable, browse=self._browse, recover=self._recover, txn_produce=self._txn_produce, txn_consume=self._txn_consume) def test_dual_max_size(self): """Multiple size policy test (non-parameterized)""" self.dual_max_size(self._num, queue_durable=self._queue_durable, msg_durable=self._msg_durable, browse=self._browse, recover=self._recover, txn_produce=self._txn_produce, txn_consume=self._txn_consume) def test_mixed_limit_1(self): """Both count and size polices active with the same queue having equal probabilities of triggering the policy (non-parameterized)""" self.mixed_limit_1(self._num, queue_durable=self._queue_durable, msg_durable=self._msg_durable, browse=self._browse, recover=self._recover, txn_produce=self._txn_produce, txn_consume=self._txn_consume) def test_mixed_limit_2(self): """Both count and size polices active with different queues having equal probabilities of triggering the policy (non-parameterized)""" self.mixed_limit_2(self._num, queue_durable=self._queue_durable, msg_durable=self._msg_durable, browse=self._browse, recover=self._recover, txn_produce=self._txn_produce, txn_consume=self._txn_consume) # --- Tests --- class MultiQueueTest(MultiQueueFlowToDisk): """Transient messages sent to multiple transient queues""" _num = 1 class MultiDurableQueueTest(MultiQueueFlowToDisk): """Transient messages sent to multiple durable queues""" _num = 2 _queue_durable = True class MultiQueueDurableMsgTest(MultiQueueFlowToDisk): """Durable messages sent to multiple transient queues""" _num = 3 _msg_durable = True class MultiDurableQueueDurableMsgTest(MultiQueueFlowToDisk): """Durable messages sent to multiple durable queues""" _num = 4 _queue_durable = True _msg_durable = True class MultiQueueBrowseTest(MultiQueueFlowToDisk): """Transient messages sent to multiple transient queues with messages browsed before being consumed""" _num = 5 _browse = True class MultiDurableQueueBrowseTest(MultiQueueFlowToDisk): """Transient messages sent to multiple durable queues with messages browsed before being consumed""" _num = 6 _queue_durable = True _browse = True class MultiQueueDurableMsgBrowseTest(MultiQueueFlowToDisk): """Durable messages sent to multiple transient queues with messages browsed before being consumed""" _num = 7 _msg_durable = True _browse = True class MultiDurableQueueDurableMsgBrowseTest(MultiQueueFlowToDisk): """Durable messages sent to multiple durable queues with messages browsed before being consumed""" _num = 8 _queue_durable = True _msg_durable = True _browse = True class MultiQueueRecoverTest(MultiQueueFlowToDisk): """Transient messages sent to multiple transient queues and broker terminated/recovered""" _num = 9 _recover = True class MultiDurableQueueRecoverTest(MultiQueueFlowToDisk): """Transient messages sent to multiple durable queues and broker terminated/recovered""" _num = 10 _queue_durable = True _recover = True class MultiQueueDurableMsgRecoverTest(MultiQueueFlowToDisk): """Durable messages sent to multiple transient queues and broker terminated/recovered""" _num = 11 _msg_durable = True _recover = True class MultiDurableQueueDurableMsgRecoverTest(MultiQueueFlowToDisk): """Durable messages sent to multiple durable queues and broker terminated/recovered""" _num = 12 _queue_durable = True _msg_durable = True _recover = True class MultiQueueBrowseRecoverTest(MultiQueueFlowToDisk): """Transient messages sent to multiple transient queues with messages browsed before broker terminated/recovered and are consumed""" _num = 13 _browse = True _recover = True class MultiDurableQueueBrowseRecoverTest(MultiQueueFlowToDisk): """Transient messages sent to multiple durable queues with messages browsed before broker terminated/recovered and are consumed""" _num = 14 _queue_durable = True _browse = True _recover = True class MultiQueueDurableMsgBrowseRecoverTest(MultiQueueFlowToDisk): """Durable messages sent to multiple transient queues with messages browsed before broker terminated/recovered and are consumed""" _num = 15 _msg_durable = True _browse = True _recover = True class MultiDurableQueueDurableMsgBrowseRecoverTest(MultiQueueFlowToDisk): """Durable messages sent to multiple durable queues with messages browsed before broker terminated/recovered and are consumed""" _num = 16 _queue_durable = True _msg_durable = True _browse = True _recover = True class MultiQueueTxPTest(MultiQueueFlowToDisk): """Transient messages sent to multiple transient queues under transactional produce""" _num = 17 _txn_produce = True class MultiDurableQueueTxPTest(MultiQueueFlowToDisk): """Transient messages sent to multiple durable queues under transactional produce""" _num = 18 _queue_durable = True _txn_produce = True class MultiQueueDurableMsgTxPTest(MultiQueueFlowToDisk): """Durable messages sent to multiple transient queues under transactional produce""" _num = 19 _msg_durable = True _txn_produce = True class MultiDurableQueueDurableMsgTxPTest(MultiQueueFlowToDisk): """Durable messages sent to multiple durable queues under transactional produce""" _num = 20 _queue_durable = True _msg_durable = True _txn_produce = True class MultiQueueBrowseTxPTest(MultiQueueFlowToDisk): """Transient messages sent to multiple transient queues under transactional produce with messages browsed before being consumed""" _num = 21 _browse = True _txn_produce = True class MultiDurableQueueBrowseTxPTest(MultiQueueFlowToDisk): """Transient messages sent to multiple durable queues under transactional produce with messages browsed before being consumed""" _num = 22 _queue_durable = True _browse = True _txn_produce = True class MultiQueueDurableMsgBrowseTxPTest(MultiQueueFlowToDisk): """Durable messages sent to multiple transient queues under transactional produce with messages browsed before being consumed""" _num = 23 _msg_durable = True _browse = True _txn_produce = True class MultiDurableQueueDurableMsgBrowseTxPTest(MultiQueueFlowToDisk): """Durable messages sent to multiple durable queues under transactional produce with messages browsed before being consumed""" _num = 24 _queue_durable = True _msg_durable = True _browse = True _txn_produce = True class MultiQueueRecoverTxPTest(MultiQueueFlowToDisk): """Transient messages sent to multiple transient queues under transactional produce and broker terminated/recovered""" _num = 25 _recover = True _txn_produce = True class MultiDurableQueueRecoverTxPTest(MultiQueueFlowToDisk): """Transient messages sent to multiple durable queues under transactional produce and broker terminated/recovered""" _num = 26 _queue_durable = True _recover = True _txn_produce = True class MultiQueueDurableMsgRecoverTxPTest(MultiQueueFlowToDisk): """Durable messages sent to multiple transient queues under transactional produce and broker terminated/recovered""" _num = 27 _msg_durable = True _recover = True _txn_produce = True class MultiDurableQueueDurableMsgRecoverTxPTest(MultiQueueFlowToDisk): """Durable messages sent to multiple durable queues under transactional produce and broker terminated/recovered""" _num = 28 _queue_durable = True _msg_durable = True _recover = True _txn_produce = True class MultiQueueBrowseRecoverTxPTest(MultiQueueFlowToDisk): """Transient messages sent to multiple transient queues under transactional produce with messages browsed before broker terminated/recovered and are consumed""" _num = 29 _browse = True _recover = True _txn_produce = True class MultiDurableQueueBrowseRecoverTxPTest(MultiQueueFlowToDisk): """Transient messages sent to multiple durable queues under transactional produce with messages browsed before broker terminated/recovered and are consumed""" _num = 30 _queue_durable = True _browse = True _recover = True _txn_produce = True class MultiQueueDurableMsgBrowseRecoverTxPTest(MultiQueueFlowToDisk): """Durable messages sent to multiple transient queues under transactional produce with messages browsed before broker terminated/recovered and are consumed""" _num = 31 _msg_durable = True _browse = True _recover = True _txn_produce = True class MultiDurableQueueDurableMsgBrowseRecoverTxPTest(MultiQueueFlowToDisk): """Durable messages sent to multiple durable queues under transactional produce with messages browsed before broker terminated/recovered and are consumed""" _num = 32 _queue_durable = True _msg_durable = True _browse = True _recover = True _txn_produce = True class MultiQueueTxCTest(MultiQueueFlowToDisk): """Transient messages sent to multiple transient queues and consumed transactionally""" _num = 33 _txn_consume = True class MultiDurableQueueTxCTest(MultiQueueFlowToDisk): """Transient messages sent to multiple durable queues and consumed transactionally""" _num = 34 _queue_durable = True _txn_consume = True class MultiQueueDurableMsgTxCTest(MultiQueueFlowToDisk): """Durable messages sent to multiple transient queues and consumed transactionally""" _num = 35 _msg_durable = True _txn_consume = True class MultiDurableQueueDurableMsgTxCTest(MultiQueueFlowToDisk): """Durable messages sent to multiple durable queues and consumed transactionally""" _num = 36 _queue_durable = True _msg_durable = True _txn_consume = True class MultiQueueBrowseTxCTest(MultiQueueFlowToDisk): """Transient messages sent to multiple transient queues with messages browsed before being consumed transactionally""" _num = 37 _browse = True _txn_consume = True class MultiDurableQueueBrowseTxCTest(MultiQueueFlowToDisk): """Transient messages sent to multiple durable queues with messages browsed before being consumed transactionally""" _num = 38 _queue_durable = True _browse = True _txn_consume = True class MultiQueueDurableMsgBrowseTxCTest(MultiQueueFlowToDisk): """Durable messages sent to multiple transient queues with messages browsed before being consumed transactionally""" _num = 39 _msg_durable = True _browse = True _txn_consume = True class MultiDurableQueueDurableMsgBrowseTxCTest(MultiQueueFlowToDisk): """Durable messages sent to multiple durable queues with messages browsed before being consumed transactionally""" _num = 40 _queue_durable = True _msg_durable = True _browse = True _txn_consume = True class MultiQueueRecoverTxCTest(MultiQueueFlowToDisk): """Transient messages sent to multiple transient queues and broker terminated/recovered before being consumed transactionally""" _num = 41 _recover = True _txn_consume = True class MultiDurableQueueRecoverTxCTest(MultiQueueFlowToDisk): """Transient messages sent to multiple durable queues and broker terminated/recovered before being consumed transactionally""" _num = 42 _queue_durable = True _recover = True _txn_consume = True class MultiQueueDurableMsgRecoverTxCTest(MultiQueueFlowToDisk): """Durable messages sent to multiple transient queues and broker terminated/recovered before being consumed transactionally""" _num = 43 _msg_durable = True _recover = True _txn_consume = True class MultiDurableQueueDurableMsgRecoverTxCTest(MultiQueueFlowToDisk): """Durable messages sent to multiple durable queues and broker terminated/recovered before being consumed transactionally""" _num = 44 _queue_durable = True _msg_durable = True _recover = True _txn_consume = True class MultiQueueBrowseRecoverTxCTest(MultiQueueFlowToDisk): """Transient messages sent to multiple transient queues with messages browsed before broker terminated/recovered and are consumed transactionally""" _num = 45 _browse = True _recover = True _txn_consume = True class MultiDurableQueueBrowseRecoverTxCTest(MultiQueueFlowToDisk): """Transient messages sent to multiple durable queues with messages browsed before broker terminated/recovered and are consumed transactionally""" _num = 46 _queue_durable = True _browse = True _recover = True _txn_consume = True class MultiQueueDurableMsgBrowseRecoverTxCTest(MultiQueueFlowToDisk): """Durable messages sent to multiple transient queues with messages browsed before broker terminated/recovered and are consumed transactionally""" _num = 47 _msg_durable = True _browse = True _recover = True _txn_consume = True class MultiDurableQueueDurableMsgBrowseRecoverTxCTest(MultiQueueFlowToDisk): """Durable messages sent to multiple durable queues with messages browsed before broker terminated/recovered and are consumed transactionally""" _num = 48 _queue_durable = True _msg_durable = True _browse = True _recover = True _txn_consume = True class MultiQueueTxPTxCTest(MultiQueueFlowToDisk): """Transient messages sent to multiple transient queues under transactional produce and are consumed transactionally""" _num = 49 _txn_produce = True _txn_consume = True class MultiDurableQueueTxPTxCTest(MultiQueueFlowToDisk): """Transient messages sent to multiple durable queues under transactional produce and are consumed transactionally""" _num = 50 _queue_durable = True _txn_produce = True _txn_consume = True class MultiQueueDurableMsgTxPTxCTest(MultiQueueFlowToDisk): """Durable messages sent to multiple transient queues under transactional produce and are consumed transactionally""" _num = 51 _msg_durable = True _txn_produce = True _txn_consume = True class MultiDurableQueueDurableMsgTxPTxCTest(MultiQueueFlowToDisk): """Durable messages sent to multiple durable queues under transactional produce and are consumed transactionally""" _num = 52 _queue_durable = True _msg_durable = True _txn_produce = True _txn_consume = True class MultiQueueBrowseTxPTxCTest(MultiQueueFlowToDisk): """Transient messages sent to multiple transient queues under transactional produce with messages browsed before being consumed transactionally""" _num = 53 _browse = True _txn_produce = True _txn_consume = True class MultiDurableQueueBrowseTxPTxCTest(MultiQueueFlowToDisk): """Transient messages sent to multiple durable queues under transactional produce with messages browsed before being consumed transactionally""" _num = 54 _queue_durable = True _browse = True _txn_produce = True _txn_consume = True class MultiQueueDurableMsgBrowseTxPTxCTest(MultiQueueFlowToDisk): """Durable messages sent to multiple transient queues under transactional produce with messages browsed before being consumed transactionally""" _num = 55 _msg_durable = True _browse = True _txn_produce = True _txn_consume = True class MultiDurableQueueDurableMsgBrowseTxPTxCTest(MultiQueueFlowToDisk): """Durable messages sent to multiple durable queues under transactional produce with messages browsed before being consumed transactionally""" _num = 56 _queue_durable = True _msg_durable = True _browse = True _txn_produce = True _txn_consume = True class MultiQueueRecoverTxPTxCTest(MultiQueueFlowToDisk): """Transient messages sent to multiple transient queues under transactional produce and broker terminated/recovered before they are consumed transactionally""" _num = 57 _recover = True _txn_produce = True _txn_consume = True class MultiDurableQueueRecoverTxPTxCTest(MultiQueueFlowToDisk): """Transient messages sent to multiple durable queues under transactional produce and broker terminated/recovered before they are consumed transactionally""" _num = 58 _queue_durable = True _recover = True _txn_produce = True _txn_consume = True class MultiQueueDurableMsgRecoverTxPTxCTest(MultiQueueFlowToDisk): """Durable messages sent to multiple transient queues under transactional produce and broker terminated/recovered before they are consumed transactionally""" _num = 59 _msg_durable = True _recover = True _txn_produce = True _txn_consume = True class MultiDurableQueueDurableMsgRecoverTxPTxCTest(MultiQueueFlowToDisk): """Durable messages sent to multiple durable queues under transactional produce and broker terminated/recovered before they are consumed transactionally""" _num = 60 _queue_durable = True _msg_durable = True _recover = True _txn_produce = True _txn_consume = True class MultiQueueBrowseRecoverTxPTxCTest(MultiQueueFlowToDisk): """Transient messages sent to multiple transient queues under transactional produce with messages browsed before broker terminated/recovered and are consumed transactionally""" _num = 61 _browse = True _recover = True _txn_produce = True class MultiDurableQueueBrowseRecoverTxPTxCTest(MultiQueueFlowToDisk): """Transient messages sent to multiple durable queues under transactional produce with messages browsed before broker terminated/recovered and are consumed transactionally""" _num = 62 _queue_durable = True _browse = True _recover = True _txn_produce = True _txn_consume = True class MultiQueueDurableMsgBrowseRecoverTxPTxCTest(MultiQueueFlowToDisk): """Durable messages sent to multiple transient queues under transactional produce with messages browsed before broker terminated/recovered and are consumed transactionally""" _num = 63 _msg_durable = True _browse = True _recover = True _txn_produce = True _txn_consume = True class MultiDurableQueueDurableMsgBrowseRecoverTxPTxCTest(MultiQueueFlowToDisk): """Durable messages sent to multiple durable queues under transactional produce with messages browsed before broker terminated/recovered and are consumed transactionally""" _num = 64 _queue_durable = True _msg_durable = True _browse = True _recover = True _txn_produce = True _txn_consume = True # --- Long and randomized tests --- # def test_12_Randomized(self): # """Randomized flow-to-disk tests""" # seed = long(1000.0 * time.time()) # print "seed=0x%x" % seed # random.seed(seed) # for index in range(0, 10): # self.randomLimit(index) qpid-cpp-store-debian-0.16/tests/python_tests/store_test.py0000644000176300017630000004466111752237112023221 0ustar cajuscajus""" Copyright (c) 2008 Red Hat, Inc. This file is part of the Qpid async store library msgstore.so. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA The GNU Lesser General Public License is available in the file COPYING. """ import re from brokertest import BrokerTest from qpid.messaging import Empty from qmf.console import Session def store_args(store_dir = None): """Return the broker args necessary to load the async store""" assert BrokerTest.store_lib if store_dir == None: return [] return ["--store-dir", store_dir] class Qmf: """ QMF functions not yet available in the new QMF API. Remove this and replace with new API when it becomes available. """ def __init__(self, broker): self.__session = Session() self.__broker = self.__session.addBroker("amqp://localhost:%d"%broker.port()) def add_exchange(self, exchange_name, exchange_type, alt_exchange_name=None, passive=False, durable=False, arguments = None): """Add a new exchange""" amqp_session = self.__broker.getAmqpSession() if arguments == None: arguments = {} if alt_exchange_name: amqp_session.exchange_declare(exchange=exchange_name, type=exchange_type, alternate_exchange=alt_exchange_name, passive=passive, durable=durable, arguments=arguments) else: amqp_session.exchange_declare(exchange=exchange_name, type=exchange_type, passive=passive, durable=durable, arguments=arguments) def add_queue(self, queue_name, alt_exchange_name=None, passive=False, durable=False, arguments = None): """Add a new queue""" amqp_session = self.__broker.getAmqpSession() if arguments == None: arguments = {} if alt_exchange_name: amqp_session.queue_declare(queue_name, alternate_exchange=alt_exchange_name, passive=passive, durable=durable, arguments=arguments) else: amqp_session.queue_declare(queue_name, passive=passive, durable=durable, arguments=arguments) def delete_queue(self, queue_name): """Delete an existing queue""" amqp_session = self.__broker.getAmqpSession() amqp_session.queue_delete(queue_name) def _query(self, name, _class, package, alt_exchange_name=None): """Qmf query function which can optionally look for the presence of an alternate exchange name""" try: obj_list = self.__session.getObjects(_class=_class, _package=package) found = False for obj in obj_list: if obj.name == name: found = True if alt_exchange_name != None: alt_exch_list = self.__session.getObjects(_objectId=obj.altExchange) if len(alt_exch_list) == 0 or alt_exch_list[0].name != alt_exchange_name: return False break return found except Exception: return False def query_exchange(self, exchange_name, alt_exchange_name=None): """Test for the presence of an exchange, and optionally whether it has an alternate exchange set to a known value.""" return self._query(exchange_name, "exchange", "org.apache.qpid.broker", alt_exchange_name) def query_queue(self, queue_name, alt_exchange_name=None): """Test for the presence of an exchange, and optionally whether it has an alternate exchange set to a known value.""" return self._query(queue_name, "queue", "org.apache.qpid.broker", alt_exchange_name) def queue_message_count(self, queue_name): """Query the number of messages on a queue""" queue_list = self.__session.getObjects(_class="queue", _name=queue_name) if len(queue_list): return queue_list[0].msgDepth def queue_empty(self, queue_name): """Check if a queue is empty (has no messages waiting)""" return self.queue_message_count(queue_name) == 0 def close(self): self.__session.delBroker(self.__broker) self.__session = None class StoreTest(BrokerTest): """ This subclass of BrokerTest adds some convenience test/check functions """ def _chk_empty(self, queue, receiver): """Check if a queue is empty (has no more messages)""" try: msg = receiver.fetch(timeout=0) self.assert_(False, "Queue \"%s\" not empty: found message: %s" % (queue, msg)) except Empty: pass @staticmethod def make_message(msg_count, msg_size): """Make message content. Format: 'abcdef....' followed by 'msg-NNNN', where NNNN is the message count""" msg = "msg-%04d" % msg_count msg_len = len(msg) buff = "" if msg_size != None and msg_size > msg_len: for index in range(0, msg_size - msg_len): if index == msg_size - msg_len - 1: buff += "-" else: buff += chr(ord('a') + (index % 26)) return buff + msg # Functions for formatting address strings @staticmethod def _fmt_csv(string_list, list_braces = None): """Format a list using comma-separation. Braces are optionally added.""" if len(string_list) == 0: return "" first = True str_ = "" if list_braces != None: str_ += list_braces[0] for string in string_list: if string != None: if first: first = False else: str_ += ", " str_ += string if list_braces != None: str_ += list_braces[1] return str_ def _fmt_map(self, string_list): """Format a map {l1, l2, l3, ...} from a string list. Each item in the list must be a formatted map element('key:val').""" return self._fmt_csv(string_list, list_braces="{}") def _fmt_list(self, string_list): """Format a list [l1, l2, l3, ...] from a string list.""" return self._fmt_csv(string_list, list_braces="[]") def addr_fmt(self, node_name, **kwargs): """Generic AMQP to new address formatter. Takes common (but not all) AMQP options and formats an address string.""" # Get keyword args node_subject = kwargs.get("node_subject") create_policy = kwargs.get("create_policy") delete_policy = kwargs.get("delete_policy") assert_policy = kwargs.get("assert_policy") mode = kwargs.get("mode") link = kwargs.get("link", False) link_name = kwargs.get("link_name") node_type = kwargs.get("node_type") durable = kwargs.get("durable", False) link_reliability = kwargs.get("link_reliability") x_declare_list = kwargs.get("x_declare_list", []) x_bindings_list = kwargs.get("x_bindings_list", []) x_subscribe_list = kwargs.get("x_subscribe_list", []) node_flag = not link and (node_type != None or durable or len(x_declare_list) > 0 or len(x_bindings_list) > 0) link_flag = link and (link_name != None or durable or link_reliability != None or len(x_declare_list) > 0 or len(x_bindings_list) > 0 or len(x_subscribe_list) > 0) assert not (node_flag and link_flag) opt_str_list = [] if create_policy != None: opt_str_list.append("create: %s" % create_policy) if delete_policy != None: opt_str_list.append("delete: %s" % delete_policy) if assert_policy != None: opt_str_list.append("assert: %s" % assert_policy) if mode != None: opt_str_list.append("mode: %s" % mode) if node_flag or link_flag: node_str_list = [] if link_name != None: node_str_list.append("name: \"%s\"" % link_name) if node_type != None: node_str_list.append("type: %s" % node_type) if durable: node_str_list.append("durable: True") if link_reliability != None: node_str_list.append("reliability: %s" % link_reliability) if len(x_declare_list) > 0: node_str_list.append("x-declare: %s" % self._fmt_map(x_declare_list)) if len(x_bindings_list) > 0: node_str_list.append("x-bindings: %s" % self._fmt_list(x_bindings_list)) if len(x_subscribe_list) > 0: node_str_list.append("x-subscribe: %s" % self._fmt_map(x_subscribe_list)) if node_flag: opt_str_list.append("node: %s" % self._fmt_map(node_str_list)) else: opt_str_list.append("link: %s" % self._fmt_map(node_str_list)) addr_str = node_name if node_subject != None: addr_str += "/%s" % node_subject if len(opt_str_list) > 0: addr_str += "; %s" % self._fmt_map(opt_str_list) return addr_str def snd_addr(self, node_name, **kwargs): """ Create a send (node) address""" # Get keyword args topic = kwargs.get("topic") topic_flag = kwargs.get("topic_flag", False) auto_create = kwargs.get("auto_create", True) auto_delete = kwargs.get("auto_delete", False) durable = kwargs.get("durable", False) exclusive = kwargs.get("exclusive", False) ftd_count = kwargs.get("ftd_count") ftd_size = kwargs.get("ftd_size") policy = kwargs.get("policy", "flow-to-disk") exchage_type = kwargs.get("exchage_type") create_policy = None if auto_create: create_policy = "always" delete_policy = None if auto_delete: delete_policy = "always" node_type = None if topic != None or topic_flag: node_type = "topic" x_declare_list = ["\"exclusive\": %s" % exclusive] if ftd_count != None or ftd_size != None: queue_policy = ["\'qpid.policy_type\': %s" % policy] if ftd_count: queue_policy.append("\'qpid.max_count\': %d" % ftd_count) if ftd_size: queue_policy.append("\'qpid.max_size\': %d" % ftd_size) x_declare_list.append("arguments: %s" % self._fmt_map(queue_policy)) if exchage_type != None: x_declare_list.append("type: %s" % exchage_type) return self.addr_fmt(node_name, topic=topic, create_policy=create_policy, delete_policy=delete_policy, node_type=node_type, durable=durable, x_declare_list=x_declare_list) def rcv_addr(self, node_name, **kwargs): """ Create a receive (link) address""" # Get keyword args auto_create = kwargs.get("auto_create", True) auto_delete = kwargs.get("auto_delete", False) link_name = kwargs.get("link_name") durable = kwargs.get("durable", False) browse = kwargs.get("browse", False) exclusive = kwargs.get("exclusive", False) binding_list = kwargs.get("binding_list", []) ftd_count = kwargs.get("ftd_count") ftd_size = kwargs.get("ftd_size") policy = kwargs.get("policy", "flow-to-disk") create_policy = None if auto_create: create_policy = "always" delete_policy = None if auto_delete: delete_policy = "always" mode = None if browse: mode = "browse" x_declare_list = ["\"exclusive\": %s" % exclusive] if ftd_count != None or ftd_size != None: queue_policy = ["\'qpid.policy_type\': %s" % policy] if ftd_count: queue_policy.append("\'qpid.max_count\': %d" % ftd_count) if ftd_size: queue_policy.append("\'qpid.max_size\': %d" % ftd_size) x_declare_list.append("arguments: %s" % self._fmt_map(queue_policy)) x_bindings_list = [] for binding in binding_list: x_bindings_list.append("{exchange: %s, key: %s}" % binding) if durable: reliability = 'at-least-once' else: reliability = None return self.addr_fmt(node_name, create_policy=create_policy, delete_policy=delete_policy, mode=mode, link=True, link_name=link_name, durable=durable, x_declare_list=x_declare_list, x_bindings_list=x_bindings_list, link_reliability=reliability) def check_message(self, broker, queue, exp_msg, transactional=False, empty=False, ack=True, browse=False): """Check that a message is on a queue by dequeuing it and comparing it to the expected message""" return self.check_messages(broker, queue, [exp_msg], transactional, empty, ack, browse) def check_messages(self, broker, queue, exp_msg_list, transactional=False, empty=False, ack=True, browse=False, emtpy_flag=False): """Check that messages is on a queue by dequeuing them and comparing them to the expected messages""" if emtpy_flag: num_msgs = 0 else: num_msgs = len(exp_msg_list) ssn = broker.connect().session(transactional=transactional) rcvr = ssn.receiver(self.rcv_addr(queue, browse=browse), capacity=num_msgs) if num_msgs > 0: try: recieved_msg_list = [rcvr.fetch(timeout=0) for i in range(num_msgs)] except Empty: self.assert_(False, "Queue \"%s\" is empty, unable to retrieve expected message %d." % (queue, i)) for i in range(0, len(recieved_msg_list)): self.assertEqual(recieved_msg_list[i].content, exp_msg_list[i].content) self.assertEqual(recieved_msg_list[i].correlation_id, exp_msg_list[i].correlation_id) if empty: self._chk_empty(queue, rcvr) if ack: ssn.acknowledge() if transactional: ssn.commit() ssn.connection.close() else: if transactional: ssn.commit() return ssn # Functions for finding strings in the broker log file (or other files) @staticmethod def _read_file(file_name): """Returns the content of file named file_name as a string""" file_handle = file(file_name) try: return file_handle.read() finally: file_handle.close() def _get_hits(self, broker, search): """Find all occurrences of the search in the broker log (eliminating possible duplicates from msgs on multiple queues)""" # TODO: Use sets when RHEL-4 is no longer supported hits = [] for hit in search.findall(self._read_file(broker.log)): if hit not in hits: hits.append(hit) return hits def _reconsile_hits(self, broker, ftd_msgs, release_hits): """Remove entries from list release_hits if they match the message id in ftd_msgs. Check for remaining release_hits.""" for msg in ftd_msgs: found = False for hit in release_hits: if str(msg.id) in hit: release_hits.remove(hit) #print "Found %s in %s" % (msg.id, broker.log) found = True break if not found: self.assert_(False, "Unable to locate released message %s in log %s" % (msg.id, broker.log)) if len(release_hits) > 0: err = "Messages were unexpectedly released in log %s:\n" % broker.log for hit in release_hits: err += " %s\n" % hit self.assert_(False, err) def check_msg_release(self, broker, ftd_msgs): """ Check for 'Content released' messages in broker log for messages in ftd_msgs""" hits = self._get_hits(broker, re.compile("debug Message id=\"[0-9a-f-]{36}\"; pid=0x[0-9a-f]+: " "Content released$", re.MULTILINE)) self._reconsile_hits(broker, ftd_msgs, hits) def check_msg_release_on_commit(self, broker, ftd_msgs): """ Check for 'Content released on commit' messages in broker log for messages in ftd_msgs""" hits = self._get_hits(broker, re.compile("debug Message id=\"[0-9a-f-]{36}\"; pid=0x[0-9a-f]+: " "Content released on commit$", re.MULTILINE)) self._reconsile_hits(broker, ftd_msgs, hits) def check_msg_release_on_recover(self, broker, ftd_msgs): """ Check for 'Content released after recovery' messages in broker log for messages in ftd_msgs""" hits = self._get_hits(broker, re.compile("debug Message id=\"[0-9a-f-]{36}\"; pid=0x[0-9a-f]+: " "Content released after recovery$", re.MULTILINE)) self._reconsile_hits(broker, ftd_msgs, hits) def check_msg_block(self, broker, ftd_msgs): """Check for 'Content release blocked' messages in broker log for messages in ftd_msgs""" hits = self._get_hits(broker, re.compile("debug Message id=\"[0-9a-f-]{36}\"; pid=0x[0-9a-f]+: " "Content release blocked$", re.MULTILINE)) self._reconsile_hits(broker, ftd_msgs, hits) def check_msg_block_on_commit(self, broker, ftd_msgs): """Check for 'Content release blocked' messages in broker log for messages in ftd_msgs""" hits = self._get_hits(broker, re.compile("debug Message id=\"[0-9a-f-]{36}\"; pid=0x[0-9a-f]+: " "Content release blocked on commit$", re.MULTILINE)) self._reconsile_hits(broker, ftd_msgs, hits) qpid-cpp-store-debian-0.16/tests/python_tests/__init__.py0000644000176300017630000000204211430306520022541 0ustar cajuscajus# Do not delete - marks this directory as a python package. # Copyright (c) 2008, 2009 Red Hat, Inc. # # This file is part of the Qpid async store library msgstore.so. # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 # USA # # The GNU Lesser General Public License is available in the file COPYING. from client_persistence import * from flow_to_disk import * from resize import * qpid-cpp-store-debian-0.16/tests/python_tests/client_persistence.py0000644000176300017630000002413711752237112024704 0ustar cajuscajus""" Copyright (c) 2008 Red Hat, Inc. This file is part of the Qpid async store library msgstore.so. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA The GNU Lesser General Public License is available in the file COPYING. """ from brokertest import EXPECT_EXIT_OK from store_test import StoreTest, Qmf, store_args from qpid.messaging import * class ExchangeQueueTests(StoreTest): """ Simple tests of the broker exchange and queue types """ def test_direct_exchange(self): """Test Direct exchange.""" broker = self.broker(store_args(), name="test_direct_exchange", expect=EXPECT_EXIT_OK) msg1 = Message("A_Message1", durable=True, correlation_id="Msg0001") msg2 = Message("B_Message1", durable=True, correlation_id="Msg0002") broker.send_message("a", msg1) broker.send_message("b", msg2) broker.terminate() broker = self.broker(store_args(), name="test_direct_exchange") self.check_message(broker, "a", msg1, True) self.check_message(broker, "b", msg2, True) def test_topic_exchange(self): """Test Topic exchange.""" broker = self.broker(store_args(), name="test_topic_exchange", expect=EXPECT_EXIT_OK) ssn = broker.connect().session() snd1 = ssn.sender("abc/key1; {create:always, node:{type:topic, durable:True}}") snd2 = ssn.sender("abc/key2; {create:always, node:{type:topic, durable:True}}") ssn.receiver("a; {create:always, link:{x-bindings:[{exchange:abc, key:key1}]}, node:{durable:True}}") ssn.receiver("b; {create:always, link:{x-bindings:[{exchange:abc, key:key1}]}, node:{durable:True}}") ssn.receiver("c; {create:always, link:{x-bindings:[{exchange:abc, key:key1}, " "{exchange:abc, key: key2}]}, node:{durable:True}}") ssn.receiver("d; {create:always, link:{x-bindings:[{exchange:abc, key:key2}]}, node:{durable:True}}") ssn.receiver("e; {create:always, link:{x-bindings:[{exchange:abc, key:key2}]}, node:{durable:True}}") msg1 = Message("Message1", durable=True, correlation_id="Msg0003") snd1.send(msg1) msg2 = Message("Message2", durable=True, correlation_id="Msg0004") snd2.send(msg2) broker.terminate() broker = self.broker(store_args(), name="test_topic_exchange") self.check_message(broker, "a", msg1, True) self.check_message(broker, "b", msg1, True) self.check_messages(broker, "c", [msg1, msg2], True) self.check_message(broker, "d", msg2, True) self.check_message(broker, "e", msg2, True) def test_legacy_lvq(self): """Test legacy LVQ.""" broker = self.broker(store_args(), name="test_lvq", expect=EXPECT_EXIT_OK) ma1 = Message("A1", durable=True, correlation_id="Msg0005", properties={"qpid.LVQ_key":"A"}) ma2 = Message("A2", durable=True, correlation_id="Msg0006", properties={"qpid.LVQ_key":"A"}) mb1 = Message("B1", durable=True, correlation_id="Msg0007", properties={"qpid.LVQ_key":"B"}) mb2 = Message("B2", durable=True, correlation_id="Msg0008", properties={"qpid.LVQ_key":"B"}) mb3 = Message("B3", durable=True, correlation_id="Msg0009", properties={"qpid.LVQ_key":"B"}) mc1 = Message("C1", durable=True, correlation_id="Msg0010", properties={"qpid.LVQ_key":"C"}) broker.send_messages("lvq-test", [mb1, ma1, ma2, mb2, mb3, mc1], xprops="arguments:{\"qpid.last_value_queue\":True}") broker.terminate() broker = self.broker(store_args(), name="test_lvq", expect=EXPECT_EXIT_OK) ssn = self.check_messages(broker, "lvq-test", [ma2, mb3, mc1], empty=True, ack=False) # Add more messages while subscriber is active (no replacement): ma3 = Message("A3", durable=True, correlation_id="Msg0011", properties={"qpid.LVQ_key":"A"}) ma4 = Message("A4", durable=True, correlation_id="Msg0012", properties={"qpid.LVQ_key":"A"}) mc2 = Message("C2", durable=True, correlation_id="Msg0013", properties={"qpid.LVQ_key":"C"}) mc3 = Message("C3", durable=True, correlation_id="Msg0014", properties={"qpid.LVQ_key":"C"}) mc4 = Message("C4", durable=True, correlation_id="Msg0015", properties={"qpid.LVQ_key":"C"}) broker.send_messages("lvq-test", [mc2, mc3, ma3, ma4, mc4], session=ssn) ssn.acknowledge() broker.terminate() broker = self.broker(store_args(), name="test_lvq") self.check_messages(broker, "lvq-test", [ma4, mc4], True) def test_fanout_exchange(self): """Test Fanout Exchange""" broker = self.broker(store_args(), name="test_fanout_exchange", expect=EXPECT_EXIT_OK) ssn = broker.connect().session() snd = ssn.sender("TestFanoutExchange; {create: always, node: {type: topic, x-declare: {type: fanout}}}") ssn.receiver("TestFanoutExchange; {link: {name: \"q1\", durable: True, reliability:at-least-once}}") ssn.receiver("TestFanoutExchange; {link: {name: \"q2\", durable: True, reliability:at-least-once}}") ssn.receiver("TestFanoutExchange; {link: {name: \"q3\", durable: True, reliability:at-least-once}}") msg1 = Message("Msg1", durable=True, correlation_id="Msg0001") snd.send(msg1) msg2 = Message("Msg2", durable=True, correlation_id="Msg0002") snd.send(msg2) broker.terminate() broker = self.broker(store_args(), name="test_fanout_exchange") self.check_messages(broker, "q1", [msg1, msg2], True) self.check_messages(broker, "q2", [msg1, msg2], True) self.check_messages(broker, "q3", [msg1, msg2], True) def test_message_reject(self): broker = self.broker(store_args(), name="test_message_reject", expect=EXPECT_EXIT_OK) ssn = broker.connect().session() snd = ssn.sender("tmr; {create:always, node:{type:queue, durable:True}}") rcv = ssn.receiver("tmr; {create:always, node:{type:queue, durable:True}}") m1 = Message("test_message_reject", durable=True, correlation_id="Msg0001") snd.send(m1) m2 = rcv.fetch() ssn.acknowledge(message=m2, disposition=Disposition(REJECTED)) broker.terminate() broker = self.broker(store_args(), name="test_message_reject") qmf = Qmf(broker) assert qmf.queue_message_count("tmr") == 0 class AlternateExchangePropertyTests(StoreTest): """ Test the persistence of the Alternate Exchange property for exchanges and queues. """ def test_exchange(self): """Exchange alternate exchange property persistence test""" broker = self.broker(store_args(), name="test_exchange", expect=EXPECT_EXIT_OK) qmf = Qmf(broker) qmf.add_exchange("altExch", "direct", durable=True) # Serves as alternate exchange instance qmf.add_exchange("testExch", "direct", durable=True, alt_exchange_name="altExch") qmf.close() broker.terminate() broker = self.broker(store_args(), name="test_exchange") qmf = Qmf(broker) try: qmf.add_exchange("altExch", "direct", passive=True) except Exception, error: self.fail("Alternate exchange (\"altExch\") instance not recovered: %s" % error) try: qmf.add_exchange("testExch", "direct", passive=True) except Exception, error: self.fail("Test exchange (\"testExch\") instance not recovered: %s" % error) self.assertTrue(qmf.query_exchange("testExch", alt_exchange_name = "altExch"), "Alternate exchange property not found or is incorrect on exchange \"testExch\".") qmf.close() def test_queue(self): """Queue alternate exchange property persistexchangeNamece test""" broker = self.broker(store_args(), name="test_queue", expect=EXPECT_EXIT_OK) qmf = Qmf(broker) qmf.add_exchange("altExch", "direct", durable=True) # Serves as alternate exchange instance qmf.add_queue("testQueue", durable=True, alt_exchange_name="altExch") qmf.close() broker.terminate() broker = self.broker(store_args(), name="test_queue") qmf = Qmf(broker) try: qmf.add_exchange("altExch", "direct", passive=True) except Exception, error: self.fail("Alternate exchange (\"altExch\") instance not recovered: %s" % error) try: qmf.add_queue("testQueue", passive=True) except Exception, error: self.fail("Test queue (\"testQueue\") instance not recovered: %s" % error) self.assertTrue(qmf.query_queue("testQueue", alt_exchange_name = "altExch"), "Alternate exchange property not found or is incorrect on queue \"testQueue\".") qmf.close() class RedeliveredTests(StoreTest): """ Test the behavior of the redelivered flag in the context of persistence """ def test_broker_recovery(self): """Test that the redelivered flag is set on messages after recovery of broker""" broker = self.broker(store_args(), name="test_broker_recovery", expect=EXPECT_EXIT_OK) msg_content = "xyz"*100 msg = Message(msg_content, durable=True) broker.send_message("testQueue", msg) broker.terminate() broker = self.broker(store_args(), name="test_broker_recovery") rcv_msg = broker.get_message("testQueue") self.assertEqual(msg_content, rcv_msg.content) self.assertTrue(rcv_msg.redelivered) qpid-cpp-store-debian-0.16/tests/python_tests/resize.py0000644000176300017630000001714611751743435022336 0ustar cajuscajus""" Copyright (c) 2008 Red Hat, Inc. This file is part of the Qpid async store library msgstore.so. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA The GNU Lesser General Public License is available in the file COPYING. """ import glob import os import subprocess from brokertest import EXPECT_EXIT_OK from qpid.datatypes import uuid4 from store_test import StoreTest, store_args from qpid.messaging import Message class ResizeTest(StoreTest): src_dir = os.getenv("abs_srcdir") resize_tool = os.getenv("RESIZE_TOOL", src_dir + "/../tools/resize") def _resize_store(self, store_dir, queue_name, resize_num_files, resize_file_size, exp_fail): for f in glob.glob(os.path.join(store_dir, "*")): final_store_dir = os.path.join(f, queue_name) p = subprocess.Popen([self.resize_tool, final_store_dir, "--num-jfiles", str(resize_num_files), "--jfile-size-pgs", str(resize_file_size), "--quiet"], stdout = subprocess.PIPE, stderr = subprocess.STDOUT) res = p.wait() err_found = False try: for l in p.stdout: if exp_fail: err_found = True print "[Expected error]:", print l, finally: p.stdout.close() return res def _resize_test(self, queue_name, num_msgs, msg_size, resize_num_files, resize_file_size, init_num_files = 8, init_file_size = 24, exp_fail = False, wait_time = None): # Using a sender will force the creation of an empty persistent queue which is needed for some tests broker = self.broker(store_args(), name="broker", expect=EXPECT_EXIT_OK, wait=wait_time) ssn = broker.connect().session() snd = ssn.sender("%s; {create:always, node:{durable:True}}" % queue_name) msgs = [] for index in range(0, num_msgs): msg = Message(self.make_message(index, msg_size), durable=True, id=uuid4(), correlation_id="msg-%04d"%index) msgs.append(msg) snd.send(msg) broker.terminate() res = self._resize_store(os.path.join(self.dir, "broker", "rhm", "jrnl"), queue_name, resize_num_files, resize_file_size, exp_fail) if res != 0: if exp_fail: return self.fail("ERROR: Resize operation failed with return code %d" % res) elif exp_fail: self.fail("ERROR: Resize operation succeeded, but a failure was expected") broker = self.broker(store_args(), name="broker") self.check_messages(broker, queue_name, msgs, True) # TODO: Check the physical files to check number and size are as expected. class SimpleTest(ResizeTest): """ Simple tests of the resize utility for resizing a journal to larger and smaller sizes. """ def test_empty_store_same(self): self._resize_test(queue_name = "empty_store_same", num_msgs = 0, msg_size = 0, init_num_files = 8, init_file_size = 24, resize_num_files = 8, resize_file_size = 24) def test_empty_store_up(self): self._resize_test(queue_name = "empty_store_up", num_msgs = 0, msg_size = 0, init_num_files = 8, init_file_size = 24, resize_num_files = 16, resize_file_size = 48) def test_empty_store_down(self): self._resize_test(queue_name = "empty_store_down", num_msgs = 0, msg_size = 0, init_num_files = 8, init_file_size = 24, resize_num_files = 6, resize_file_size = 12) # TODO: Put into long tests, make sure there is > 128GB free disk space # def test_empty_store_max(self): # self._resize_test(queue_name = "empty_store_max", # num_msgs = 0, msg_size = 0, # init_num_files = 8, init_file_size = 24, # resize_num_files = 64, resize_file_size = 32768, # wait_time = 120) def test_empty_store_min(self): self._resize_test(queue_name = "empty_store_min", num_msgs = 0, msg_size = 0, init_num_files = 8, init_file_size = 24, resize_num_files = 4, resize_file_size = 1) def test_basic_up(self): self._resize_test(queue_name = "basic_up", num_msgs = 100, msg_size = 10000, init_num_files = 8, init_file_size = 24, resize_num_files = 16, resize_file_size = 48) def test_basic_down(self): self._resize_test(queue_name = "basic_down", num_msgs = 100, msg_size = 10000, init_num_files = 8, init_file_size = 24, resize_num_files = 4, resize_file_size = 15) def test_basic_low(self): self._resize_test(queue_name = "basic_low", num_msgs = 100, msg_size = 10000, init_num_files = 8, init_file_size = 24, resize_num_files = 4, resize_file_size = 4, exp_fail = True) def test_basic_under(self): self._resize_test(queue_name = "basic_under", num_msgs = 100, msg_size = 10000, init_num_files = 8, init_file_size = 24, resize_num_files = 4, resize_file_size = 3, exp_fail = True) def test_very_large_msg_up(self): self._resize_test(queue_name = "very_large_msg_up", num_msgs = 4, msg_size = 2000000, init_num_files = 8, init_file_size = 24, resize_num_files = 16, resize_file_size = 48) def test_very_large_msg_down(self): self._resize_test(queue_name = "very_large_msg_down", num_msgs = 4, msg_size = 2000000, init_num_files = 16, init_file_size = 64, resize_num_files = 16, resize_file_size = 48) def test_very_large_msg_low(self): self._resize_test(queue_name = "very_large_msg_low", num_msgs = 4, msg_size = 2000000, init_num_files = 8, init_file_size = 24, resize_num_files = 7, resize_file_size = 20, exp_fail = True) def test_very_large_msg_under(self): self._resize_test(queue_name = "very_large_msg_under", num_msgs = 4, msg_size = 2000000, init_num_files = 8, init_file_size = 24, resize_num_files = 6, resize_file_size = 8, exp_fail = True) qpid-cpp-store-debian-0.16/tests/run_test0000755000176300017630000000446411142067437017503 0ustar cajuscajus#!/bin/bash # Copyright (c) 2008 Red Hat, Inc. # # This file is part of the Qpid async store library msgstore.so. # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 # USA # # The GNU Lesser General Public License is available in the file COPYING. # Set up environment and run a test executable or script. # # Output nothing if test passes, show the output if it fails and # leave output in .log for examination. # # If qpidd.port exists run test with QPID_PORT=`cat qpidd.port` # # If $VALGRIND if is set run under valgrind. If there are valgrind # erros show valgrind output, also leave it in .valgrind for # examination. # source `dirname $0`/vg_check # Export variables from makefile. export VALGRIND srcdir # Export QPID_PORT if qpidd.port exists. test -f qpidd.port && export QPID_PORT=`cat qpidd.port` # Avoid silly libtool error messages if these are not defined test -z "$LC_ALL" && export LC_ALL= test -z "$LC_CTYPE" && export LC_CTYPE= test -z "$LC_COLLATE" && export LC_COLLATE= test -z "$LC_MESSAGES" && export LC_MESSAGES= VG_LOG="$1.vglog" rm -f $VG_LOG* if grep -l "^# Generated by .*libtool" "$1" >/dev/null 2>&1; then # This is a libtool "executable". Valgrind it if VALGRIND specified. test -n "$VALGRIND" && VALGRIND="$VALGRIND --log-file=$VG_LOG --" # Hide output unless there's an error. libtool --mode=execute $VALGRIND "$@" 2>&1 || ERROR=$? test -n "$VALGRIND" && vg_check $VG_LOG* else # This is a non-libtool shell script, just execute it. export VALGRIND srcdir exec "$@" fi if test -z "$ERROR"; then # Clean up logs if there was no error. rm -f $VG_LOG* exit 0 else exit $ERROR fi qpid-cpp-store-debian-0.16/tests/TwoPhaseCommitTest.cpp0000644000176300017630000004723011611361170022147 0ustar cajuscajus/* Copyright (c) 2007, 2008, 2009 Red Hat, Inc. This file is part of the Qpid async store library msgstore.so. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA The GNU Lesser General Public License is available in the file COPYING. */ #include "unit_test.h" #include "MessageStoreImpl.h" #include #include "MessageUtils.h" #include "qpid/broker/Queue.h" #include "qpid/broker/RecoveryManagerImpl.h" #include "qpid/framing/AMQHeaderBody.h" #include "qpid/log/Statement.h" #include "TxnCtxt.h" #include "qpid/log/Logger.h" #include "qpid/sys/Timer.h" qpid::sys::Timer timer; #define SET_LOG_LEVEL(level) \ qpid::log::Options opts(""); \ opts.selectors.clear(); \ opts.selectors.push_back(level); \ qpid::log::Logger::instance().configure(opts); using namespace mrg::msgstore; using namespace qpid; using namespace qpid::broker; using namespace qpid::framing; using namespace std; QPID_AUTO_TEST_SUITE(TwoPhaseCommitTest) const string test_filename("TwoPhaseCommitTest"); const char* tdp = getenv("TMP_DATA_DIR"); string test_dir(tdp && strlen(tdp) > 0 ? tdp : "/tmp/TwoPhaseCommitTest"); // === Helper fns === class TwoPhaseCommitTest { class Strategy { public: virtual void init() = 0; virtual void run(TPCTransactionContext* txn) = 0; virtual void check(bool committed) = 0; virtual ~Strategy(){} }; class Swap : public Strategy { TwoPhaseCommitTest* const test; const string messageId; boost::intrusive_ptr msg; public: Swap(TwoPhaseCommitTest* const test_, const string& messageId_): test(test_), messageId(messageId_) {} void init(){ msg = test->deliver(messageId, test->queueA); } void run(TPCTransactionContext* txn) { test->swap(txn, test->queueA, test->queueB); } void check(bool committed) { test->swapCheck(committed, messageId, test->queueA, test->queueB); } }; class Enqueue : public Strategy { TwoPhaseCommitTest* const test; boost::intrusive_ptr msg1; boost::intrusive_ptr msg2; boost::intrusive_ptr msg3; public: Enqueue(TwoPhaseCommitTest* const test_): test(test_) {} void init() {} void run(TPCTransactionContext* txn) { msg1 = test->enqueue(txn, "Enqueue1", test->queueA); msg2 = test->enqueue(txn, "Enqueue2", test->queueA); msg3 = test->enqueue(txn, "Enqueue3", test->queueA); } void check(bool committed) { if (committed) { test->checkMsg(test->queueA, 3, "Enqueue1"); test->checkMsg(test->queueA, 2, "Enqueue2"); test->checkMsg(test->queueA, 1, "Enqueue3"); } test->checkMsg(test->queueA, 0); } }; class Dequeue : public Strategy { TwoPhaseCommitTest* const test; boost::intrusive_ptr msg1; boost::intrusive_ptr msg2; boost::intrusive_ptr msg3; public: Dequeue(TwoPhaseCommitTest* const test_): test(test_) {} void init() { msg1 = test->deliver("Dequeue1", test->queueA); msg2 = test->deliver("Dequeue2", test->queueA); msg3 = test->deliver("Dequeue3", test->queueA); } void run(TPCTransactionContext* txn) { test->dequeue(txn, test->queueA); test->dequeue(txn, test->queueA); test->dequeue(txn, test->queueA); } void check(bool committed) { if (!committed) { test->checkMsg(test->queueA, 3, "Dequeue1"); test->checkMsg(test->queueA, 2, "Dequeue2"); test->checkMsg(test->queueA, 1, "Dequeue3"); } test->checkMsg(test->queueA, 0); } }; class MultiQueueTxn : public Strategy { TwoPhaseCommitTest* const test; boost::intrusive_ptr msg1; boost::intrusive_ptr msg2; std::set queueset; public: MultiQueueTxn(TwoPhaseCommitTest* const test_): test(test_) {} virtual void init() {} virtual void run(TPCTransactionContext* txn) { queueset.insert(test->queueA); queueset.insert(test->queueB); msg1 = test->enqueue(txn, "Message1", queueset); msg2 = test->enqueue(txn, "Message2", queueset); queueset.clear(); } virtual void check(bool committed) { TestMessageStore* sptr = static_cast(test->store.get()); if (committed) { test->checkMsg(test->queueA, 2, "Message1"); test->checkMsg(test->queueB, 2, "Message1"); test->checkMsg(test->queueA, 1, "Message2"); test->checkMsg(test->queueB, 1, "Message2"); } test->checkMsg(test->queueA, 0); test->checkMsg(test->queueB, 0); // Check there are no remaining open txns in store BOOST_CHECK_EQUAL(u_int32_t(0), sptr->getRemainingTxns(*(test->queueA))); BOOST_CHECK_EQUAL(u_int32_t(0), sptr->getRemainingTxns(*(test->queueB))); BOOST_CHECK_EQUAL(u_int32_t(0), sptr->getRemainingPreparedListTxns()); } }; // Test txn context which has special setCompleteFailure() method which prevents entire "txn complete" process from hapenning class TestTPCTxnCtxt : public TPCTxnCtxt { public: TestTPCTxnCtxt(const std::string& _xid, IdSequence* _loggedtx) : TPCTxnCtxt(_xid, _loggedtx) {} void setCompleteFailure(const unsigned num_queues_rem, const bool complete_prepared_list) { // Remove queue members from back of impactedQueues until queues_rem reamin. // to end to simulate multi-queue txn complete failure. while (impactedQueues.size() > num_queues_rem) impactedQueues.erase(impactedQueues.begin()); // If prepared list is not to be committed, set pointer to 0 if (!complete_prepared_list) preparedXidStorePtr = 0; } }; // Test store which has sepcial begin() which returns a TestTPCTxnCtxt, and a method to check for // reamining open transactions class TestMessageStore: public MessageStoreImpl { public: TestMessageStore(qpid::sys::Timer& timer, const char* envpath = 0) : MessageStoreImpl(timer, envpath) {} std::auto_ptr begin(const std::string& xid) { checkInit(); IdSequence* jtx = &messageIdSequence; // pass sequence number for c/a return auto_ptr(new TestTPCTxnCtxt(xid, jtx)); } u_int32_t getRemainingTxns(const PersistableQueue& queue) { return static_cast(queue.getExternalQueueStore())->get_open_txn_cnt(); } u_int32_t getRemainingPreparedListTxns() { return tplStorePtr->get_open_txn_cnt(); } }; const string nameA; const string nameB; std::auto_ptr store; std::auto_ptr dtxmgr; std::auto_ptr queues; std::auto_ptr links; Queue::shared_ptr queueA; Queue::shared_ptr queueB; boost::intrusive_ptr msg1; boost::intrusive_ptr msg2; boost::intrusive_ptr msg4; void recoverPrepared(bool commit) { setup(); Swap swap(this, "RecoverPrepared"); swap.init(); std::auto_ptr txn(store->begin("my-xid")); swap.run(txn.get()); store->prepare(*txn); restart(); //check that the message is not available from either queue BOOST_CHECK_EQUAL((u_int32_t) 0, queueA->getMessageCount()); BOOST_CHECK_EQUAL((u_int32_t) 0, queueB->getMessageCount()); //commit/abort the txn - through the dtx manager, not directly on the store if (commit) { dtxmgr->commit("my-xid", false); } else { dtxmgr->rollback("my-xid"); } swap.check(commit); restart(); swap.check(commit); } void testMultiQueueTxn(const unsigned num_queues_rem, const bool complete_prepared_list, const bool commit) { setup(); MultiQueueTxn mqtTest(this); mqtTest.init(); std::auto_ptr txn(static_cast(store.get())->begin("my-xid")); mqtTest.run(txn.get()); store->prepare(*txn); // As the commits and aborts should happen through DtxManager, and it is too complex to // pass all these test params through, we bypass DtxManager and use the store directly. // This will prevent the queues from seeing committed txns, however. To test the success // or failure of static_cast(txn.get())->setCompleteFailure(num_queues_rem, complete_prepared_list); if (commit) store->commit(*txn); else store->abort(*txn); restart(); mqtTest.check(commit); } void commit(Strategy& strategy) { setup(); strategy.init(); std::auto_ptr txn(store->begin("my-xid")); strategy.run(txn.get()); store->prepare(*txn); store->commit(*txn); restart(); strategy.check(true); } void abort(Strategy& strategy, bool prepare) { setup(); strategy.init(); std::auto_ptr txn(store->begin("my-xid")); strategy.run(txn.get()); if (prepare) store->prepare(*txn); store->abort(*txn); restart(); strategy.check(false); } void swap(TPCTransactionContext* txn, Queue::shared_ptr& from, Queue::shared_ptr& to) { QueuedMessage msg1 = from->get();//just dequeues in memory //move the message from one queue to the other as part of a //distributed transaction to->enqueue(txn, msg1.payload);//note: need to enqueue it first to avoid message being deleted from->dequeue(txn, msg1); } void dequeue(TPCTransactionContext* txn, Queue::shared_ptr& queue) { QueuedMessage msg2 = queue->get();//just dequeues in memory queue->dequeue(txn, msg2); } boost::intrusive_ptr enqueue(TPCTransactionContext* txn, const string& msgid, Queue::shared_ptr& queue) { boost::intrusive_ptr msg = createMessage(msgid); queue->enqueue(txn, msg); return msg; } boost::intrusive_ptr enqueue(TPCTransactionContext* txn, const string& msgid, std::set& queueset) { boost::intrusive_ptr msg = createMessage(msgid); for (std::set::iterator i = queueset.begin(); i != queueset.end(); i++) { (*i)->enqueue(txn, msg); } return msg; } boost::intrusive_ptr deliver(const string& msgid, Queue::shared_ptr& queue) { msg4 = createMessage(msgid); queue->deliver(msg4); return msg4; } template void setup() { store = std::auto_ptr(new T(timer)); store->init(test_dir, 4, 1, true); // truncate store //create two queues: FieldTable settings; queueA = Queue::shared_ptr(new Queue(nameA, 0, store.get(), 0)); queueA->create(settings); queueB = Queue::shared_ptr(new Queue(nameB, 0, store.get(), 0)); queueB->create(settings); } boost::intrusive_ptr createMessage(const string& id, const string& exchange="exchange", const string& key="routing_key") { boost::intrusive_ptr msg = MessageUtils::createMessage(exchange, key, Uuid(), true, 0, id); return msg; } template void restart() { queueA.reset(); queueB.reset(); store.reset(); queues.reset(); links.reset(); store = std::auto_ptr(new T(timer)); store->init(test_dir, 4, 1); sys::Timer t; ExchangeRegistry exchanges; queues = std::auto_ptr(new QueueRegistry); links = std::auto_ptr(new LinkRegistry); dtxmgr = std::auto_ptr(new DtxManager(t)); dtxmgr->setStore (store.get()); RecoveryManagerImpl recovery(*queues, exchanges, *links, *dtxmgr); store->recover(recovery); queueA = queues->find(nameA); queueB = queues->find(nameB); } void checkMsg(Queue::shared_ptr& queue, u_int32_t size, const string& msgid = "") { BOOST_REQUIRE(queue); BOOST_CHECK_EQUAL(size, queue->getMessageCount()); if (size > 0) { boost::intrusive_ptr msg = queue->get().payload; BOOST_REQUIRE(msg); BOOST_CHECK_EQUAL(msgid, msg->getProperties()->getCorrelationId()); } } void swapCheck(bool swapped, const string& msgid, Queue::shared_ptr& from, Queue::shared_ptr& to) { BOOST_REQUIRE(from); BOOST_REQUIRE(to); Queue::shared_ptr x; //the queue from which the message was swapped Queue::shared_ptr y; //the queue on which the message is expected to be if (swapped) { x = from; y = to; } else { x = to; y = from; } checkMsg(x, 0); checkMsg(y, 1, msgid); checkMsg(y, 0); } public: TwoPhaseCommitTest() : nameA("queueA"), nameB("queueB") {} void testCommitEnqueue() { Enqueue enqueue(this); commit(enqueue); } void testCommitDequeue() { Dequeue dequeue(this); commit(dequeue); } void testCommitSwap() { Swap swap(this, "SwapMessageId"); commit(swap); } void testPrepareAndAbortEnqueue() { Enqueue enqueue(this); abort(enqueue, true); } void testPrepareAndAbortDequeue() { Dequeue dequeue(this); abort(dequeue, true); } void testPrepareAndAbortSwap() { Swap swap(this, "SwapMessageId"); abort(swap, true); } void testAbortNoPrepareEnqueue() { Enqueue enqueue(this); abort(enqueue, false); } void testAbortNoPrepareDequeue() { Dequeue dequeue(this); abort(dequeue, false); } void testAbortNoPrepareSwap() { Swap swap(this, "SwapMessageId"); abort(swap, false); } void testRecoverPreparedThenCommitted() { recoverPrepared(true); } void testRecoverPreparedThenAborted() { recoverPrepared(false); } void testMultiQueueCommit() { testMultiQueueTxn(2, true, true); } void testMultiQueueAbort() { testMultiQueueTxn(2, true, false); } void testMultiQueueNoQueueCommitRecover() { testMultiQueueTxn(0, false, true); } void testMultiQueueNoQueueAbortRecover() { testMultiQueueTxn(0, false, false); } void testMultiQueueSomeQueueCommitRecover() { testMultiQueueTxn(1, false, true); } void testMultiQueueSomeQueueAbortRecover() { testMultiQueueTxn(1, false, false); } void testMultiQueueAllQueueCommitRecover() { testMultiQueueTxn(2, false, true); } void testMultiQueueAllQueueAbortRecover() { testMultiQueueTxn(2, false, false); } }; TwoPhaseCommitTest tpct; // === Test suite === QPID_AUTO_TEST_CASE(CommitEnqueue) { SET_LOG_LEVEL("error+"); // This only needs to be set once. cout << test_filename << ".CommitEnqueue: " << flush; tpct.testCommitEnqueue(); cout << "ok" << endl; } QPID_AUTO_TEST_CASE(CommitDequeue) { cout << test_filename << ".CommitDequeue: " << flush; tpct.testCommitDequeue(); cout << "ok" << endl; } QPID_AUTO_TEST_CASE(CommitSwap) { cout << test_filename << ".CommitSwap: " << flush; tpct.testCommitSwap(); cout << "ok" << endl; } QPID_AUTO_TEST_CASE(PrepareAndAbortEnqueue) { cout << test_filename << ".PrepareAndAbortEnqueue: " << flush; tpct.testPrepareAndAbortEnqueue(); cout << "ok" << endl; } QPID_AUTO_TEST_CASE(PrepareAndAbortDequeue) { cout << test_filename << ".PrepareAndAbortDequeue: " << flush; tpct.testPrepareAndAbortDequeue(); cout << "ok" << endl; } QPID_AUTO_TEST_CASE(PrepareAndAbortSwap) { cout << test_filename << ".PrepareAndAbortSwap: " << flush; tpct.testPrepareAndAbortSwap(); cout << "ok" << endl; } QPID_AUTO_TEST_CASE(AbortNoPrepareEnqueue) { cout << test_filename << ".AbortNoPrepareEnqueue: " << flush; tpct.testAbortNoPrepareEnqueue(); cout << "ok" << endl; } QPID_AUTO_TEST_CASE(AbortNoPrepareDequeue) { cout << test_filename << ".AbortNoPrepareDequeue: " << flush; tpct.testAbortNoPrepareDequeue(); cout << "ok" << endl; } QPID_AUTO_TEST_CASE(AbortNoPrepareSwap) { cout << test_filename << ".AbortNoPrepareSwap: " << flush; tpct.testAbortNoPrepareSwap(); cout << "ok" << endl; } QPID_AUTO_TEST_CASE(RecoverPreparedThenCommitted) { cout << test_filename << ".RecoverPreparedThenCommitted: " << flush; tpct.testRecoverPreparedThenCommitted(); cout << "ok" << endl; } QPID_AUTO_TEST_CASE(RecoverPreparedThenAborted) { cout << test_filename << ".RecoverPreparedThenAborted: " << flush; tpct.testRecoverPreparedThenAborted(); cout << "ok" << endl; } QPID_AUTO_TEST_CASE(MultiQueueCommit) { cout << test_filename << ".MultiQueueCommit: " << flush; tpct.testMultiQueueCommit(); cout << "ok" << endl; } QPID_AUTO_TEST_CASE(MultiQueueAbort) { cout << test_filename << ".MultiQueueAbort: " << flush; tpct.testMultiQueueAbort(); cout << "ok" << endl; } QPID_AUTO_TEST_CASE(MultiQueueNoQueueCommitRecover) { cout << test_filename << ".MultiQueueNoQueueCommitRecover: " << flush; tpct.testMultiQueueNoQueueCommitRecover(); cout << "ok" << endl; } QPID_AUTO_TEST_CASE(MultiQueueNoQueueAbortRecover) { cout << test_filename << ".MultiQueueNoQueueAbortRecover: " << flush; tpct.testMultiQueueNoQueueAbortRecover(); cout << "ok" << endl; } QPID_AUTO_TEST_CASE(MultiQueueSomeQueueCommitRecover) { cout << test_filename << ".MultiQueueSomeQueueCommitRecover: " << flush; tpct.testMultiQueueSomeQueueCommitRecover(); cout << "ok" << endl; } QPID_AUTO_TEST_CASE(MultiQueueSomeQueueAbortRecover) { cout << test_filename << ".MultiQueueSomeQueueAbortRecover: " << flush; tpct.testMultiQueueSomeQueueAbortRecover(); cout << "ok" << endl; } QPID_AUTO_TEST_CASE(MultiQueueAllQueueCommitRecover) { cout << test_filename << ".MultiQueueAllQueueCommitRecover: " << flush; tpct.testMultiQueueAllQueueCommitRecover(); cout << "ok" << endl; } QPID_AUTO_TEST_CASE(MultiQueueAllQueueAbortRecover) { cout << test_filename << ".MultiQueueAllQueueAbortRecover: " << flush; tpct.testMultiQueueAllQueueAbortRecover(); cout << "ok" << endl; } QPID_AUTO_TEST_SUITE_END() qpid-cpp-store-debian-0.16/tests/run_short_python_tests0000755000176300017630000000170011361352000022456 0ustar cajuscajus#!/bin/bash # # Copyright (c) 2008, 2009 Red Hat, Inc. # # This file is part of the Qpid async store library msgstore.so. # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 # USA # # The GNU Lesser General Public License is available in the file COPYING. ./run_python_tests SHORT_TEST qpid-cpp-store-debian-0.16/tests/Makefile.am0000644000176300017630000000676611611361170017745 0ustar cajuscajus# Copyright (c) 2007, 2008 Red Hat, Inc. # # This file is part of the Qpid async store library msgstore.so. # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 # USA # # The GNU Lesser General Public License is available in the file COPYING. abs_builddir=@abs_builddir@ abs_srcdir=@abs_srcdir@ AM_CXXFLAGS = $(WARNING_CFLAGS) $(APR_CXXFLAGS) $(QPID_CXXFLAGS) -DBOOST_TEST_DYN_LINK INCLUDES=-I$(top_srcdir)/lib -I$(abs_builddir)/../lib -I$(top_srcdir)/lib/gen TMP_DATA_DIR=$(abs_srcdir)/tmp_data_dir TMP_PYTHON_TEST_DIR=$(abs_srcdir)/python_tests.tmp SUBDIRS = jrnl . federation if DO_CLUSTER_TESTS SUBDIRS += cluster endif TESTS = \ SimpleTest \ OrderingTest \ TransactionalTest \ TwoPhaseCommitTest \ run_python_tests \ system_test.sh \ clean.sh LONG_TESTS = \ SimpleTest \ OrderingTest \ TransactionalTest \ TwoPhaseCommitTest \ run_long_python_tests \ system_test.sh \ clean.sh SHORT_TESTS = \ SimpleTest \ TransactionalTest \ run_short_python_tests \ clean.sh check_PROGRAMS = \ SimpleTest \ OrderingTest \ TransactionalTest \ TwoPhaseCommitTest UNIT_TEST_SRCS = unit_test.cpp unit_test.h UNIT_TEST_LDADD = -lboost_unit_test_framework $(top_builddir)/lib/msgstore.la SimpleTest_SOURCES = SimpleTest.cpp $(UNIT_TEST_SRCS) SimpleTest_LDADD = $(UNIT_TEST_LDADD) -lrt OrderingTest_SOURCES = OrderingTest.cpp $(UNIT_TEST_SRCS) OrderingTest_LDADD = $(UNIT_TEST_LDADD) -lrt TransactionalTest_SOURCES = TransactionalTest.cpp $(UNIT_TEST_SRCS) TransactionalTest_LDADD = $(UNIT_TEST_LDADD) -lrt TwoPhaseCommitTest_SOURCES = TwoPhaseCommitTest.cpp $(UNIT_TEST_SRCS) TwoPhaseCommitTest_LDADD = $(UNIT_TEST_LDADD) -lrt EXTRA_DIST = \ clean.sh \ failing_python_tests.txt \ python_tests \ persistence.py \ run_long_python_tests \ run_python_tests \ run_short_python_tests \ run_test \ start_broker \ stop_broker \ system_test.sh \ tests_env.sh \ MessageUtils.h \ vg_check \ .valgrindrc \ .valgrind.supp TESTS_ENVIRONMENT = \ QPID_DIR=$(QPID_DIR) \ QPID_BLD=$(QPID_BLD) \ VALGRIND=$(VALGRIND) \ abs_srcdir=$(abs_srcdir) \ TMP_DATA_DIR=$(TMP_DATA_DIR) \ TMP_PYTHON_TEST_DIR=$(TMP_PYTHON_TEST_DIR) \ $(srcdir)/run_test # Note: Auto-recursion is not supported for custom targets, so add a ${MAKE} -C for each dir in the SUBDIRS list above. check-long: all $(MAKE) -C jrnl check-long $(MAKE) check TESTS="$(LONG_TESTS)" SUBDIRS=. if DO_CLUSTER_TESTS $(MAKE) -C cluster check-long endif $(MAKE) -C federation check-long check-short: all $(MAKE) check TESTS="$(SHORT_TESTS)" SUBDIRS=. qpid-cpp-store-debian-0.16/tests/MessageUtils.h0000644000176300017630000000522411761163730020463 0ustar cajuscajus/* Copyright (c) 2007, 2008, 2009 Red Hat, Inc. This file is part of the Qpid async store library msgstore.so. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA The GNU Lesser General Public License is available in the file COPYING. */ #include #include #include #include using namespace qpid::broker; using namespace qpid::framing; struct MessageUtils { static boost::intrusive_ptr createMessage(const std::string& exchange, const std::string& routingKey, const Uuid& messageId=Uuid(), const bool persistent = false, const uint64_t contentSize = 0, const std::string& correlationId = std::string()) { boost::intrusive_ptr msg(new Message()); AMQFrame method((MessageTransferBody(ProtocolVersion(), exchange, 0, 0))); AMQFrame header((AMQHeaderBody())); header.setLastSegment(contentSize == 0); msg->getFrames().append(method); msg->getFrames().append(header); MessageProperties* props = msg->getFrames().getHeaders()->get(true); props->setContentLength(contentSize); props->setMessageId(messageId); props->setCorrelationId(correlationId); msg->getFrames().getHeaders()->get(true)->setRoutingKey(routingKey); if (persistent) msg->getFrames().getHeaders()->get(true)->setDeliveryMode(PERSISTENT); return msg; } static void addContent(boost::intrusive_ptr msg, const std::string& data) { AMQFrame content((AMQContentBody(data))); msg->getFrames().append(content); } static void deliver(QueuedMessage& msg, FrameHandler& h, uint16_t framesize) { msg.payload->sendHeader(h, framesize); msg.payload->sendContent(*(msg.queue), h, framesize); } }; qpid-cpp-store-debian-0.16/tests/failing_python_tests.txt0000644000176300017630000000000011022061612022647 0ustar cajuscajusqpid-cpp-store-debian-0.16/tests/run_python_tests0000755000176300017630000000437611611361170021261 0ustar cajuscajus#!/bin/bash # # Copyright (c) 2008, 2009 Red Hat, Inc. # # This file is part of the Qpid async store library msgstore.so. # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 # USA # # The GNU Lesser General Public License is available in the file COPYING. if test -z ${QPID_DIR} ; then cat <&2 echo $* 1>&2 exit 1 } vg_check() { test -z "$1" || VG_LOG=$1 test -f $VG_LOG || vg_failed Valgrind log file $VG_LOG missing. # Ensure there is an ERROR SUMMARY line. grep -E '^==[0-9]+== ERROR SUMMARY:' $VG_LOG > /dev/null || \ vg_failed "No valgrind ERROR SUMMARY line in $$vg_failed." # Ensure that the number of errors is 0. grep -E '^==[0-9]+== ERROR SUMMARY: [^0]' $VG_LOG > /dev/null && \ vg_failed "Valgrind reported errors in $vg_out; see above." # Check for leaks. grep -E '^==[0-9]+== +.* lost: [^0]' $VG_LOG && \ vg_failed "Found memory leaks (see log file, $VG_LOG); see above." true } qpid-cpp-store-debian-0.16/perf/0000755000176300017630000000000011761423652015476 5ustar cajuscajusqpid-cpp-store-debian-0.16/perf/JournalPerformanceTest.cpp0000644000176300017630000003021711500511440022621 0ustar cajuscajus/** * \file JournalPerformanceTest.cpp * * Qpid asynchronous store plugin library * * This file contains performance test code for the journal. * * \author Kim van der Riet * * Copyright (c) 2010 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #include "JournalPerformanceTest.hpp" #include // uint16_t, uint32_t #include // atof, atoi, atol #include #include // getopt_long(), required_argument, no_argument #include #include #include #include #include "ScopedTimer.hpp" #ifdef JOURNAL2 #include "jrnl2/JournalDirectory.hpp" #else #include "jrnl/jdir.hpp" #endif namespace mrg { namespace jtest { #ifdef JOURNAL2 JournalPerformanceTest::JournalPerformanceTest(const TestParameters& tp, const mrg::journal2::JournalParameters& jp) : #else JournalPerformanceTest::JournalPerformanceTest(const TestParameters& tp, const JournalParameters& jp) : #endif Streamable(), _testParams(tp), _jrnlParams(jp), _jrnlPerf(tp), msgData(new char[tp._msgSize]) {} JournalPerformanceTest::~JournalPerformanceTest() { while (_jrnlList.size()) { delete _jrnlList.back(); _jrnlList.pop_back(); } delete[] msgData; } void JournalPerformanceTest::_prepareJournals() { #ifdef JOURNAL2 if (mrg::journal2::JournalDirectory::s_exists(_jrnlParams._jrnlDir)) { mrg::journal2::JournalDirectory::s_delete(_jrnlParams._jrnlDir); } mrg::journal2::JournalDirectory::s_create(_jrnlParams._jrnlDir); mrg::journal2::Journal* jp; #else if (mrg::journal::jdir::exists(_jrnlParams._jrnlDir)) { mrg::journal::jdir::delete_dir(_jrnlParams._jrnlDir); } mrg::journal::jdir::create_dir(_jrnlParams._jrnlDir); mrg::journal::jcntl* jp; #endif JournalInstance* ptp; for (uint16_t j = 0; j < _testParams._numQueues; j++) { std::ostringstream jname; jname << "jrnl_" << std::setw(4) << std::setfill('0') << j; std::ostringstream jdir; jdir << _jrnlParams._jrnlDir << "/" << jname.str(); #ifdef JOURNAL2 jp = new mrg::journal2::Journal(jname.str(), jdir.str(), _jrnlParams._jrnlBaseFileName); #else jp = new mrg::journal::jcntl(jname.str(), jdir.str(), _jrnlParams._jrnlBaseFileName); #endif ptp = new JournalInstance(_testParams._numMsgs, _testParams._msgSize, msgData, jp); #ifdef JOURNAL2 jp->initialize(&_jrnlParams, ptp); #else jp->initialize(_jrnlParams._numJrnlFiles, _jrnlParams._autoExpand, _jrnlParams._autoExpandMaxJrnlFiles, _jrnlParams._jrnlFileSize_sblks, _jrnlParams._writeBuffNumPgs, _jrnlParams._writeBuffPgSize_sblks, ptp); #endif _jrnlList.push_back(ptp); } } void JournalPerformanceTest::run() { std::deque threads; std::thread* tp; _prepareJournals(); { // --- Start of timed section --- ScopedTimer st(_jrnlPerf); for (uint16_t q = 0; q < _testParams._numQueues; q++) { // Launch threads in pairs uint16_t numThreads = _testParams._numThreadPairsPerQueue * 2; for (uint16_t t = 0; t < numThreads; t++) { tp = new std::thread(std::ref(*_jrnlList[q])); threads.push_back(tp); } } while (threads.size()) { threads.front()->join(); delete threads.front(); threads.pop_front(); } } // --- End of timed section --- } void JournalPerformanceTest::toStream(std::ostream& os) const { os << _testParams << std::endl; os << _jrnlParams << std::endl; os << _jrnlPerf << std::endl; } void printArgs(std::ostream& os) { os << " -h --help: This help message" << std::endl; os << std::endl; os << "Test params:" << std::endl; os << " -M --num_msgs: Number of messages to send [" << TestParameters::_s_defaultNumMsgs << "]" << std::endl; os << " -S --msg_size: Size of each message to be sent [" << TestParameters::_s_defaultMsgSize << "]" << std::endl; os << " -Q --num_queues: Number of simultaneous queues [" << TestParameters::_s_defaultNumQueues << "]" << std::endl; os << " -T --num_thread_pairs_per_queue: Number of thread pairs per queue [" << TestParameters::_s_defaultNumThreadPairsPerQueue << "]" << std::endl; os << " -E --enq_txn_blk_size: Enqueue transaction block size (0=non-txn) [" << TestParameters::_s_defaultEnqTxnBlkSize << "]" << std::endl; os << " -D --deq_txn_blk_size: Dequeue transaction block size (0=non-txn) [" << TestParameters::_s_defaultDeqTxnBlkSize << "]" << std::endl; os << std::endl; os << "Store params:" << std::endl; #ifdef JOURNAL2 os << " -d --jrnl_dir: Store directory [\"" << mrg::journal2::JournalParameters::_s_defaultJrnlDir << "\"]" << std::endl; os << " -b --jrnl_base_filename: Base name for journal files [\"" << mrg::journal2::JournalParameters::_s_defaultJrnlBaseFileName << "\"]" << std::endl; os << " -f --num_jfiles: Number of journal files [" << mrg::journal2::JournalParameters::_s_defaultNumJrnlFiles << "]" << std::endl; os << " -s --jfsize_sblks: Size of each journal file in sblks (512 byte blocks) [" << mrg::journal2::JournalParameters::_s_defaultJrnlFileSize_sblks << "]" << std::endl; os << " -a --auto_expand: Auto-expand the journal [" << (mrg::journal2::JournalParameters::_s_defaultAutoExpand?"T":"F") << "]" << std::endl; os << " -e --ae_max_jfiles: Upper limit on number of auto-expanded journal files [" << mrg::journal2::JournalParameters::_s_defaultAutoExpandMaxJrnlFiles << "]" << std::endl; os << " -p --wcache_num_pages: Number of write buffer pages [" << mrg::journal2::JournalParameters::_s_defaultWriteBuffNumPgs << "]" << std::endl; os << " -c --wcache_pgsize_sblks: Size of each write buffer page in sblks (512 byte blocks) [" << mrg::journal2::JournalParameters::_s_defaultWriteBuffPgSize_sblks << "]" << std::endl; #else os << " -d --jrnl_dir: Store directory [\"" << JournalParameters::_s_defaultJrnlDir << "\"]" << std::endl; os << " -b --jrnl_base_filename: Base name for journal files [\"" << JournalParameters::_s_defaultJrnlBaseFileName << "\"]" << std::endl; os << " -f --num_jfiles: Number of journal files [" << JournalParameters::_s_defaultNumJrnlFiles << "]" << std::endl; os << " -s --jfsize_sblks: Size of each journal file in sblks (512 byte blocks) [" << JournalParameters::_s_defaultJrnlFileSize_sblks << "]" << std::endl; os << " -a --auto_expand: Auto-expand the journal [" << (JournalParameters::_s_defaultAutoExpand?"T":"F") << "]" << std::endl; os << " -e --ae_max_jfiles: Upper limit on number of auto-expanded journal files [" << JournalParameters::_s_defaultAutoExpandMaxJrnlFiles << "]" << std::endl; os << " -p --wcache_num_pages: Number of write buffer pages [" << JournalParameters::_s_defaultWriteBuffNumPgs << "]" << std::endl; os << " -c --wcache_pgsize_sblks: Size of each write buffer page in sblks (512 byte blocks) [" << JournalParameters::_s_defaultWriteBuffPgSize_sblks << "]" << std::endl; #endif } bool #ifdef JOURNAL2 readArgs(int argc, char** argv, TestParameters& tp, mrg::journal2::JournalParameters& sp) #else readArgs(int argc, char** argv, TestParameters& tp, JournalParameters& sp) #endif { static struct option long_options[] = { {"help", no_argument, 0, 'h'}, // Test params {"num_msgs", required_argument, 0, 'm'}, {"msg_size", required_argument, 0, 'S'}, {"num_queues", required_argument, 0, 'q'}, {"num_threads_per_queue", required_argument, 0, 't'}, // Journal params {"jrnl_dir", required_argument, 0, 'd'}, {"jrnl_base_filename", required_argument, 0, 'b'}, {"num_jfiles", required_argument, 0, 'f'}, {"jfsize_sblks", required_argument, 0, 's'}, {"auto_expand", no_argument, 0, 'a'}, {"ae_max_jfiles", required_argument, 0, 'e'}, {"wcache_num_pages", required_argument, 0, 'p'}, {"wcache_pgsize_sblks", required_argument, 0, 'c'}, {0, 0, 0, 0} }; bool err = false; int c = 0; while (true) { int option_index = 0; c = getopt_long(argc, argv, "ab:c:d:e:f:hm:p:q:s:S:t:", long_options, &option_index); if (c == -1) break; switch (c) { // Test params case 'm': tp._numMsgs = uint32_t(std::atol(optarg)); break; case 'S': tp._msgSize = uint32_t(std::atol(optarg)); break; case 'q': tp._numQueues = uint16_t(std::atoi(optarg)); break; case 't': tp._numThreadPairsPerQueue = uint16_t(std::atoi(optarg)); break; // Store params case 'd': sp._jrnlDir.assign(optarg); break; case 'b': sp._jrnlBaseFileName.assign(optarg); break; case 'f': sp._numJrnlFiles = uint16_t(std::atoi(optarg)); break; case 's': sp._jrnlFileSize_sblks = uint32_t(std::atol(optarg)); break; case 'a': sp._autoExpand = true; break; case 'e': sp._autoExpandMaxJrnlFiles = uint16_t(std::atoi(optarg)); break; case 'p': sp._writeBuffNumPgs = uint16_t(std::atoi(optarg)); break; case 'c': sp._writeBuffPgSize_sblks = uint32_t(std::atol(optarg)); break; // Other case 'h': default: err = true; printArgs(); } } return err; } } // namespace jtest } // namespace mrg // ----------------------------------------------------------------- int main(int argc, char** argv) { mrg::jtest::TestParameters tp; #ifdef JOURNAL2 mrg::journal2::JournalParameters jp; #else mrg::jtest::JournalParameters jp; #endif if (mrg::jtest::readArgs(argc, argv, tp, jp)) return 1; mrg::jtest::JournalPerformanceTest jpt(tp, jp); jpt.run(); std::cout << jpt << std::endl; return 0; } qpid-cpp-store-debian-0.16/perf/ScopedTimable.hpp0000644000176300017630000000475211477733350020735 0ustar cajuscajus/** * \file ScopedTimable.hpp * * Qpid asynchronous store plugin library * * This file contains performance test code for the journal. * * \author Kim van der Riet * * Copyright (c) 2010 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #ifndef mrg_jtest_ScopedTimable_hpp #define mrg_jtest_ScopedTimable_hpp namespace mrg { namespace jtest { class ScopedTimable; } // namespace jtest } // namespace mrg #include "ScopedTimer.hpp" // circular include namespace mrg { namespace jtest { /** * \brief Scoped timer class that starts timing on construction and finishes on destruction. * * This class is designed to be the parent class for a performance result class which depends on the elapsed * time of some process or event. By passing this (or its subclasses) to ScopedTimer (which only exists within * the scope of the event), the _elapsed member of this class will be written with the elapsed time when the * ScopedTimer object goes out of scope or is destroyed. * * Subclasses may be aware of the parameters being timed, and may thus print and/or display performance and/or * rate information for these parameters. */ class ScopedTimable { protected: double _elapsed; ///< Elapsed time, will be written on destruction of ScopedTimer instances public: /** * \brief Constructor */ inline ScopedTimable() : _elapsed(0.0) {} /** * \brief Destructor */ virtual inline ~ScopedTimable() {} friend class ScopedTimer; }; } // namespace jtest } // namespace mrg #endif // mrg_jtest_ScopedTimer_hpp qpid-cpp-store-debian-0.16/perf/JournalParameters.cpp0000644000176300017630000001054711514124615021640 0ustar cajuscajus/** * \file perf/JournalParameters.cpp * * Qpid asynchronous store plugin library * * This file contains performance test code for the journal. * * \author Kim van der Riet * * Copyright (c) 2010 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #include "JournalParameters.hpp" namespace mrg { namespace jtest { // static declarations std::string JournalParameters::_s_defaultJrnlDir = "/tmp/store"; std::string JournalParameters::_s_defaultJrnlBaseFileName = "JournalData"; uint16_t JournalParameters::_s_defaultNumJrnlFiles = 8; uint32_t JournalParameters::_s_defaultJrnlFileSize_sblks = 3072; bool JournalParameters::_s_defaultAutoExpand = false; uint16_t JournalParameters::_s_defaultAutoExpandMaxJrnlFiles = 0; uint16_t JournalParameters::_s_defaultWriteBuffNumPgs = 32; uint32_t JournalParameters::_s_defaultWriteBuffPgSize_sblks = 128; JournalParameters::JournalParameters() : Streamable(), _jrnlDir(_s_defaultJrnlDir), _jrnlBaseFileName(_s_defaultJrnlBaseFileName), _numJrnlFiles(_s_defaultNumJrnlFiles), _jrnlFileSize_sblks(_s_defaultJrnlFileSize_sblks), _autoExpand(_s_defaultAutoExpand), _autoExpandMaxJrnlFiles(_s_defaultAutoExpandMaxJrnlFiles), _writeBuffNumPgs(_s_defaultWriteBuffNumPgs), _writeBuffPgSize_sblks(_s_defaultWriteBuffPgSize_sblks) {} JournalParameters::JournalParameters(const std::string& jrnlDir, const std::string& jrnlBaseFileName, const uint16_t numJrnlFiles, const uint32_t jrnlFileSize_sblks, const bool autoExpand, const uint16_t autoExpandMaxJrnlFiles, const uint16_t writeBuffNumPgs, const uint32_t writeBuffPgSize_sblks) : Streamable(), _jrnlDir(jrnlDir), _jrnlBaseFileName(jrnlBaseFileName), _numJrnlFiles(numJrnlFiles), _jrnlFileSize_sblks(jrnlFileSize_sblks), _autoExpand(autoExpand), _autoExpandMaxJrnlFiles(autoExpandMaxJrnlFiles), _writeBuffNumPgs(writeBuffNumPgs), _writeBuffPgSize_sblks(writeBuffPgSize_sblks) {} JournalParameters::JournalParameters(const JournalParameters& jp) : Streamable(), _jrnlDir(jp._jrnlDir), _jrnlBaseFileName(jp._jrnlBaseFileName), _numJrnlFiles(jp._numJrnlFiles), _jrnlFileSize_sblks(jp._jrnlFileSize_sblks), _autoExpand(jp._autoExpand), _autoExpandMaxJrnlFiles(jp._autoExpandMaxJrnlFiles), _writeBuffNumPgs(jp._writeBuffNumPgs), _writeBuffPgSize_sblks(jp._writeBuffPgSize_sblks) {} void JournalParameters::toStream(std::ostream& os) const { os << "Store Parameters:" << std::endl; os << " jrnlDir = \"" << _jrnlDir << "\"" << std::endl; os << " jrnlBaseFileName = \"" << _jrnlBaseFileName << "\"" << std::endl; os << " numJrnlFiles = " << _numJrnlFiles << std::endl; os << " jrnlFileSize_sblks = " << _jrnlFileSize_sblks << std::endl; os << " autoExpand = " << _autoExpand << std::endl; os << " autoExpandMaxJrnlFiles = " << _autoExpandMaxJrnlFiles << std::endl; os << " writeBuffNumPgs = " << _writeBuffNumPgs << std::endl; os << " writeBuffPgSize_sblks = " << _writeBuffPgSize_sblks << std::endl; } } // namespace jtest } // namespace mrg qpid-cpp-store-debian-0.16/perf/PerformanceResult.cpp0000644000176300017630000000445511500164314021636 0ustar cajuscajus/** * \file PerformanceResult.cpp * * Qpid asynchronous store plugin library * * This file contains performance test code for the journal. * * \author Kim van der Riet * * Copyright (c) 2010 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #include "PerformanceResult.hpp" #include // uint32_t namespace mrg { namespace jtest { PerformanceResult::PerformanceResult(const TestParameters& tp) : ScopedTimable(), Streamable(), _testParams(tp) {} void PerformanceResult::toStream(std::ostream& os) const { os << "TEST RESULTS:" << std::endl; os << " Msgs per thread: " << _testParams._numMsgs << std::endl; os << " Msg size: " << _testParams._msgSize << std::endl; os << " No. queues: " << _testParams._numQueues << std::endl; os << " No. threads/queue: " << _testParams._numThreadPairsPerQueue << std::endl; os << " Time taken: " << _elapsed << " sec" << std::endl; uint32_t totalMsgs = _testParams._numMsgs * _testParams._numQueues * _testParams._numThreadPairsPerQueue; os << " Total no. msgs: " << totalMsgs << std::endl; double msgsRate = double(totalMsgs) / _elapsed; os << " Msg throughput: " << (msgsRate / 1e3) << " kMsgs/sec" << std::endl; os << " " << (msgsRate * _testParams._msgSize / 1e6) << " MB/sec" << std::endl; } } // namespace jtest } // namespace mrg qpid-cpp-store-debian-0.16/perf/TestParameters.hpp0000644000176300017630000001103111514124615021137 0ustar cajuscajus/** * \file TestParameters.hpp * * Qpid asynchronous store plugin library * * This file contains performance test code for the journal. * * \author Kim van der Riet * * Copyright (c) 2010 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #ifndef mrg_jtest_TestParameters_hpp #define mrg_jtest_TestParameters_hpp #include // uint16_t, uint32_t #include "Streamable.hpp" namespace mrg { namespace jtest { /** * \brief Struct for aggregating the test parameters * * This struct is used to aggregate and keep together all the test parameters. These affect the test itself, the * journal geometry is aggregated in class JournalParameters. */ struct TestParameters : public Streamable { static uint32_t _s_defaultNumMsgs; ///< Default number of messages to be sent static uint32_t _s_defaultMsgSize; ///< Default message size in bytes static uint16_t _s_defaultNumQueues; ///< Default number of queues to test simultaneously static uint16_t _s_defaultNumThreadPairsPerQueue; ///< Default number of thread pairs (enq and deq) per queue static uint16_t _s_defaultEnqTxnBlkSize; ///< Default transaction block size for enqueues static uint16_t _s_defaultDeqTxnBlkSize; ///< Default transaction block size for dequeues uint32_t _numMsgs; ///< Number of messages to be sent uint32_t _msgSize; ///< Message size in bytes uint16_t _numQueues; ///< Number of queues to test simultaneously uint16_t _numThreadPairsPerQueue; ///< Number of thread pairs (enq and deq) per queue uint16_t _enqTxnBlockSize; ///< Transaction block size for enqueues uint16_t _deqTxnBlockSize; ///< Transaction block size for dequeues /** * \brief Defaault constructor * * Default constructor. Uses the default values for all parameters. */ TestParameters(); /** * \brief Constructor * * Convenience constructor. * * \param numMsgs Number of messages to be sent * \param msgSize Message size in bytes * \param numQueues Number of queues to test simultaneously * \param numThreadPairsPerQueue Number of thread pairs (enq and deq) per queue * \param enqTxnBlockSize Transaction block size for enqueues * \param deqTxnBlockSize Transaction block size for dequeues */ TestParameters(const uint32_t numMsgs, const uint32_t msgSize, const uint16_t numQueues, const uint16_t numThreadPairsPerQueue, const uint16_t enqTxnBlockSize, const uint16_t deqTxnBlockSize); /** * \brief Copy constructor * * \param tp Reference to TestParameters instance to be copied */ TestParameters(const TestParameters& tp); /** * \brief Virtual destructor */ virtual ~TestParameters() {} /*** * \brief Stream the test parameters to an output stream * * Convenience feature which streams a multi-line representation of all the test parameters, one per line to an * output stream. * * \param os Output stream to which the class data is to be streamed */ void toStream(std::ostream& os = std::cout) const; }; } // namespace jtest } // namespace mrg #endif // mrg_jtest_TestParameters_hpp qpid-cpp-store-debian-0.16/perf/Streamable.cpp0000644000176300017630000000314111477730641020263 0ustar cajuscajus/** * \file Streamable.cpp * * Qpid asynchronous store plugin library * * This file contains performance test code for the journal. * * \author Kim van der Riet * * Copyright (c) 2010 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #include "Streamable.hpp" #include namespace mrg { namespace jtest { std::string Streamable::toString() const { std::ostringstream oss; toStream(oss); return oss.str(); } std::ostream& operator<<(std::ostream& os, const Streamable& s) { s.toStream(os); return os; } std::ostream& operator<<(std::ostream& os, const Streamable* sPtr) { sPtr->toStream(os); return os; } } // namespace jtest } // namespace mrg qpid-cpp-store-debian-0.16/perf/JournalParameters.hpp0000644000176300017630000001254711514124615021647 0ustar cajuscajus/** * \file perf/JournalParameters.hpp * * Qpid asynchronous store plugin library * * This file contains performance test code for the journal. * * \author Kim van der Riet * * Copyright (c) 2010 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #ifndef mrg_jtest_JournalParameters_hpp #define mrg_jtest_JournalParameters_hpp #include #include "Streamable.hpp" namespace mrg { namespace jtest { /** * \brief Stuct for aggregating the common journal parameters * * This struct is used to aggregate and keep together all the common journal parameters. These affect the journal * geometry and buffers. The test parameters are aggregated in class TestParameters. */ struct JournalParameters : public Streamable { // static default store params static std::string _s_defaultJrnlDir; ///< Default journal directory static std::string _s_defaultJrnlBaseFileName; ///< Default journal base file name static uint16_t _s_defaultNumJrnlFiles; ///< Default number of journal data files static uint32_t _s_defaultJrnlFileSize_sblks; ///< Default journal data file size in softblocks static bool _s_defaultAutoExpand; ///< Default auto-expand flag (allows auto-expansion of journal data files) static uint16_t _s_defaultAutoExpandMaxJrnlFiles; ///< Default auto-expand file number limit (0 = no limit) static uint16_t _s_defaultWriteBuffNumPgs; ///< Default number of write buffer pages static uint32_t _s_defaultWriteBuffPgSize_sblks; ///< Default size of each write buffer page in softblocks std::string _jrnlDir; ///< Journal directory std::string _jrnlBaseFileName; ///< Journal base file name uint16_t _numJrnlFiles; ///< Number of journal data files uint32_t _jrnlFileSize_sblks; ///< Journal data file size in softblocks bool _autoExpand; ///< Auto-expand flag (allows auto-expansion of journal data files) uint16_t _autoExpandMaxJrnlFiles; ///< Auto-expand file number limit (0 = no limit) uint16_t _writeBuffNumPgs; ///< Number of write buffer pages uint32_t _writeBuffPgSize_sblks; ///< Size of each write buffer page in softblocks /** * \brief Default constructor * * Default constructor. Uses the default values for all parameters. */ JournalParameters(); /** * \brief Constructor * * Convenience constructor. * * \param jrnlDir Journal directory * \param jrnlBaseFileName Journal base file name * \param numJrnlFiles Number of journal data files * \param jrnlFileSize_sblks Journal data file size in softblocks * \param autoExpand Auto-expand flag (allows auto-expansion of journal data files) * \param autoExpandMaxJrnlFiles Default auto-expand file number limit (0 = no limit) * \param writeBuffNumPgs Number of write buffer pages * \param writeBuffPgSize_sblks Size of each write buffer page in softblocks */ JournalParameters(const std::string& jrnlDir, const std::string& jrnlBaseFileName, const uint16_t numJrnlFiles, const uint32_t jrnlFileSize_sblks, const bool autoExpand, const uint16_t autoExpandMaxJrnlFiles, const uint16_t writeBuffNumPgs, const uint32_t writeBuffPgSize_sblks); /** * \brief Copy constructor * * \param jp Reference to JournalParameters instance to be copied */ JournalParameters(const JournalParameters& jp); /** * \brief Virtual destructor */ virtual ~JournalParameters() {} /*** * \brief Stream the journal parameters to an output stream * * Convenience feature which streams a multi-line representation of all the journal parameters, one per line to * an output stream. * * \param os Output stream to which the class data is to be streamed */ void toStream(std::ostream& os = std::cout) const; }; } // namespace jtest } // namespace mrg #endif // mrg_jtest_JournalParameters_hpp qpid-cpp-store-debian-0.16/perf/m0000644000176300017630000000515711514124615015656 0ustar cajuscajus#!/bin/bash # This script builds an executable 'perf' directly from the source files. This is suitable for testing # using valgrind and similar tools which don't play well with libtool. # The variable JOURNAL2, if defined, will link with the new journal2 namespace journal. Otherwise the old journal # namespace will be used. JOURNAL2=1 # Optimization options #OPT="-O0 -ggdb" OPT="-O3 -g0 -DNDEBUG" WARN_COMMON="-Wall -Werror -Wextra -pedantic-errors" WARN_OTHER="-Wundef -Wpointer-arith -Wcast-qual -Wcast-align -Wwrite-strings -Wsign-compare \ -Wmissing-field-initializers -Wpacked -Wredundant-decls -Wunreachable-code -Wno-invalid-offsetof \ -Winvalid-pch -Wvolatile-register-var" PERF_FILES="JournalInstance.cpp \ JournalParameters.cpp \ JournalPerformanceTest.cpp \ PerformanceResult.cpp \ ScopedTimer.cpp \ Streamable.cpp \ TestParameters.cpp" if [[ ${JOURNAL2}x == x ]] ; then JRNL_FILES="../lib/jrnl/aio.cpp \ ../lib/jrnl/enq_map.cpp \ ../lib/jrnl/jdir.cpp \ ../lib/jrnl/jrec.cpp \ ../lib/jrnl/rfc.cpp \ ../lib/jrnl/smutex.cpp \ ../lib/jrnl/wmgr.cpp \ ../lib/jrnl/cvar.cpp \ ../lib/jrnl/enq_rec.cpp \ ../lib/jrnl/jerrno.cpp \ ../lib/jrnl/lp_map.cpp \ ../lib/jrnl/rmgr.cpp \ ../lib/jrnl/time_ns.cpp \ ../lib/jrnl/wrfc.cpp \ ../lib/jrnl/data_tok.cpp \ ../lib/jrnl/fcntl.cpp \ ../lib/jrnl/jexception.cpp \ ../lib/jrnl/lpmgr.cpp \ ../lib/jrnl/rrfc.cpp \ ../lib/jrnl/txn_map.cpp \ ../lib/jrnl/deq_rec.cpp \ ../lib/jrnl/jcntl.cpp \ ../lib/jrnl/jinf.cpp \ ../lib/jrnl/pmgr.cpp \ ../lib/jrnl/slock.cpp \ ../lib/jrnl/txn_rec.cpp" WARN="${WARN_COMMON} ${WARN_OTHER}" else DEFINES=-DJOURNAL2 JRNL_FILES="../lib/jrnl2/DataTokenState.cpp \ ../lib/jrnl2/DataToken.cpp \ ../lib/jrnl2/Journal.cpp \ ../lib/jrnl2/JournalDirectory.cpp \ ../lib/jrnl2/JournalErrors.cpp \ ../lib/jrnl2/JournalException.cpp \ ../lib/jrnl2/JournalParameters.cpp \ ../lib/jrnl2/JournalState.cpp" WARN="${WARN_COMMON} ${WARN_OTHER} -Wshadow -Wunsafe-loop-optimizations" fi rm -f *.o perf echo g++ -o perf -I ../lib -std=c++0x ${OPT} ${WARN} -lrt -laio -lpthread ${DEFINES} ${PERF_FILES} ${JRNL_FILES} g++ -o perf -I ../lib -std=c++0x ${OPT} ${WARN} -lrt -laio -lpthread ${DEFINES} ${PERF_FILES} ${JRNL_FILES} qpid-cpp-store-debian-0.16/perf/ScopedTimer.cpp0000644000176300017630000000345211627445272020427 0ustar cajuscajus/** * \file ScopedTimer.cpp * * Qpid asynchronous store plugin library * * This file contains performance test code for the journal. * * \author Kim van der Riet * * Copyright (c) 2010 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #include "ScopedTimer.hpp" namespace mrg { namespace jtest { ScopedTimer::ScopedTimer(double& elapsed) : _elapsed(elapsed) { ::clock_gettime(CLOCK_REALTIME, &_startTime); } ScopedTimer::ScopedTimer(ScopedTimable& st) : _elapsed(st._elapsed) { ::clock_gettime(CLOCK_REALTIME, &_startTime); } ScopedTimer::~ScopedTimer() { ::timespec stopTime; ::clock_gettime(CLOCK_REALTIME, &stopTime); _elapsed = _s_getDoubleTime(stopTime) - _s_getDoubleTime(_startTime); } // static double ScopedTimer::_s_getDoubleTime(const std::timespec& ts) { return ts.tv_sec + (double(ts.tv_nsec) / 1e9); } } // namespace jtest } // namespace mrg qpid-cpp-store-debian-0.16/perf/JournalPerformanceTest.hpp0000644000176300017630000001244111477730641022650 0ustar cajuscajus/** * \file JournalPerformanceTest.hpp * * Qpid asynchronous store plugin library * * This file contains performance test code for the journal. * * \author Kim van der Riet * * Copyright (c) 2010 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #ifndef mrg_jtest_JournalPerformanceTest_hpp #define mrg_jtest_JournalPerformanceTest_hpp #include #include "JournalInstance.hpp" #include "PerformanceResult.hpp" #include "Streamable.hpp" #include "TestParameters.hpp" #ifdef JOURNAL2 #include "jrnl2/JournalParameters.hpp" #else #include "JournalParameters.hpp" #endif namespace mrg { namespace jtest { /** * \brief Main test class; Create an instance and execute run() * * Main test class which aggregates the components of a test. */ class JournalPerformanceTest : public Streamable { const TestParameters& _testParams; ///< Ref to a struct containing test params #ifdef JOURNAL2 const mrg::journal2::JournalParameters& _jrnlParams; ///< Ref to a struct containing the journal parameters #else const JournalParameters& _jrnlParams; ///< Ref to a struct containing the journal parameters #endif PerformanceResult _jrnlPerf; ///< Journal performance object const char* msgData; ///< Pointer to msg data, which is the same for all messages std::vector _jrnlList; ///< List of journals (JournalInstance instances) being tested /** * \brief Creates journals and JournalInstance classes for all journals (queues) to be tested * * Creates a new journal instance and JournalInstance instance for each queue. The journals are initialized * which creates a new set of journal files on the local storage media (which is determined by path in * JournalParameters._jrnlDir). This activity is not timed, and is not a part of the performance test per se. */ void _prepareJournals(); public: /** * \brief Constructor * * \param tp Test parameters for the test * \param jp Journal parameters for all queues (journals) in the test */ #ifdef JOURNAL2 JournalPerformanceTest(const TestParameters& tp, const mrg::journal2::JournalParameters& jp); #else JournalPerformanceTest(const TestParameters& tp, const JournalParameters& jp); #endif /** * \brief Virtual destructor */ virtual ~JournalPerformanceTest(); /** * \brief Runs the test and prints out the results. * * Runs the test as set by the test parameters and journal parameters. */ void run(); /** * \brief Stream the test setup and results to an output stream * * Convenience feature which streams the test setup and results to an output stream. * * \param os Output stream to which the test setup and results are to be streamed. */ void toStream(std::ostream& os = std::cout) const; }; /** * \brief Print out the program arguments * * Print out the arguments to the performance program if requested by help or a parameter error. * * \param os Stream to which the arguments should be streamed. */ void printArgs(std::ostream& os = std::cout); /** * \brief Process the command-line arguments * * Process the command-line arguments and populate the TestParameters and JournalParameters structs. Only the * arguments supplied are on the command-line are changed in these structs, the others remain unchanged. It is * important therefore to make sure that defaults are pre-loaded (the default behavior of the default constructors * for these structs). * * \param argc Number of command-line arguments. Process directly from main(). * \param argv Pointer to array of command-line argument pointers. Process directly from main(). * \param tp Reference to test parameter object. Only params on the command-line are changed. * \param jp Reference to journal parameter object. Only params on the command-line are changed. */ #ifdef JOURNAL2 bool readArgs(int argc, char** argv, TestParameters& tp, mrg::journal2::JournalParameters& jp); #else bool readArgs(int argc, char** argv, TestParameters& tp, JournalParameters& jp); #endif } // namespace jtest } // namespace mrg #endif // mrg_jtest_JournalPerformanceTest_hpp qpid-cpp-store-debian-0.16/perf/JournalInstance.cpp0000644000176300017630000002100311500511440021255 0ustar cajuscajus/** * \file JournalInstance.cpp * * Qpid asynchronous store plugin library * * This file contains performance test code for the journal. * * \author Kim van der Riet * * Copyright (c) 2010 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #include "JournalInstance.hpp" #include namespace mrg { namespace jtest { JournalInstance::JournalInstance(const uint32_t numMsgs, const uint32_t msgSize, const char* msgData, #ifdef JOURNAL2 mrg::journal2::Journal* const jrnlPtr) : #else mrg::journal::jcntl* const jrnlPtr) : #endif _numMsgs(numMsgs), _msgSize(msgSize), _msgData(msgData), _jrnlPtr(jrnlPtr), _threadSwitch(false) {} JournalInstance::~JournalInstance() { delete _jrnlPtr; } // *** MUST BE THREAD-SAFE **** // This method will be called by multiple threads simultaneously // Enqueue thread entry point void JournalInstance::_doEnqueues() { bool misfireFlag = false; uint32_t i = 0; while (i < _numMsgs) { #ifdef JOURNAL2 mrg::journal2::DataToken* dtokPtr = new mrg::journal2::DataToken(); mrg::journal2::ioRes jrnlIoRes = _jrnlPtr->enqueue(_msgData, _msgSize, dtokPtr); #else mrg::journal::data_tok* dtokPtr = new mrg::journal::data_tok(); mrg::journal::iores jrnlIoRes = _jrnlPtr->enqueue_data_record(_msgData, _msgSize, _msgSize, dtokPtr); #endif switch (jrnlIoRes) { #ifdef JOURNAL2 case 0: #else case mrg::journal::RHM_IORES_SUCCESS: #endif i++; misfireFlag = false; break; #ifdef JOURNAL2 case mrg::journal2::RHM_IORES_BUSY: #else case mrg::journal::RHM_IORES_BUSY: #endif if (!misfireFlag) std::cout << "-" << std::flush; delete dtokPtr; misfireFlag = true; break; #ifdef JOURNAL2 case mrg::journal2::RHM_IORES_ENQCAPTHRESH: #else case mrg::journal::RHM_IORES_ENQCAPTHRESH: #endif //std::cout << "_doEnqueues() RHM_IORES_ENQCAPTHRESH: " << dtokPtr->status_str() << std::endl; if (!misfireFlag) std::cout << "*" << std::flush; //std::cout << "."; delete dtokPtr; misfireFlag = true; ::usleep(10); break; default: delete dtokPtr; #ifdef JOURNAL2 std::cerr << "enqueue_data_record FAILED with " << mrg::journal2::g_ioResAsString(jrnlIoRes) << std::endl; #else std::cerr << "enqueue_data_record FAILED with " << mrg::journal::iores_str(jrnlIoRes) << std::endl; #endif } } _jrnlPtr->flush(false); } // *** MUST BE THREAD-SAFE **** // This method will be called by multiple threads simultaneously // Dequeue thread entry point void JournalInstance::_doDequeues() { uint32_t i = 0; while (i < _numMsgs) { #ifdef JOURNAL2 mrg::journal2::DataToken* dtokPtr = 0; #else mrg::journal::data_tok* dtokPtr = 0; #endif while (!dtokPtr) { bool aioEventsFlag; // thread local { // --- START OF CRITICAL SECTION --- std::lock_guard l(_unprocCallbackListMutex); aioEventsFlag = _unprocCallbackList.size() == 0; if (!aioEventsFlag) { dtokPtr = _unprocCallbackList.front(); _unprocCallbackList.pop(); } } // --- END OF CRITICAL SECTION --- if (aioEventsFlag) { #ifdef JOURNAL2 _jrnlPtr->processCompletedAioWriteEvents(0); #else _jrnlPtr->get_wr_events(0); #endif ::usleep(1); } } bool done = false; while (!done) { #ifdef JOURNAL2 mrg::journal2::ioRes jrnlIoRes = _jrnlPtr->dequeue(dtokPtr); #else mrg::journal::iores jrnlIoRes = _jrnlPtr->dequeue_data_record(dtokPtr); #endif switch (jrnlIoRes) { #ifdef JOURNAL2 case 0: #else case mrg::journal::RHM_IORES_SUCCESS: #endif i ++; done = true; break; #ifdef JOURNAL2 case mrg::journal2::RHM_IORES_BUSY: #else case mrg::journal::RHM_IORES_BUSY: #endif //::usleep(10); break; default: #ifdef JOURNAL2 std::cerr << "dequeue_data_record FAILED with " << mrg::journal2::g_ioResAsString(jrnlIoRes) << ": " << dtokPtr->statusStr() << std::endl; #else std::cerr << "dequeue_data_record FAILED with " << mrg::journal::iores_str(jrnlIoRes) << ": " << dtokPtr->status_str() << std::endl; #endif delete dtokPtr; done = true; } } #ifdef JOURNAL2 _jrnlPtr->processCompletedAioWriteEvents(0); #else _jrnlPtr->get_wr_events(0); #endif } _jrnlPtr->flush(true); } // *** MUST BE THREAD-SAFE **** // This method will be called by multiple threads simultaneously // Main thread entry point. // _threadSwitch flips on each call; this makes alternate threads run _doEnqueues() and _doDequeues() void JournalInstance::operator()() { bool localThreadSwitch; // thread local { // --- START OF CRITICAL SECTION --- std::lock_guard l(_threadSwitchLock); localThreadSwitch = _threadSwitch; _threadSwitch = !_threadSwitch; } // --- END OF CRITICAL SECTION --- if (localThreadSwitch) { _doDequeues(); } else { _doEnqueues(); } } // *** MUST BE THREAD-SAFE **** // This method will be called by multiple threads simultaneously void #ifdef JOURNAL2 JournalInstance::writeAioCompleteCallback(std::vector& dataTokenList) { mrg::journal2::DataToken* dtokPtr; #else JournalInstance::wr_aio_cb(std::vector& dataTokenList) { mrg::journal::data_tok* dtokPtr; #endif while (dataTokenList.size()) { dtokPtr = dataTokenList.back(); dataTokenList.pop_back(); #ifdef JOURNAL2 switch (dtokPtr->getDataTokenState().getOpState()) { case mrg::journal2::OP_ENQUEUE: #else switch (dtokPtr->wstate()) { case mrg::journal::data_tok::ENQ: #endif { // --- START OF CRITICAL SECTION --- std::lock_guard l(_unprocCallbackListMutex); _unprocCallbackList.push(dtokPtr); } // --- END OF CRITICAL SECTION --- break; default: delete dtokPtr; } } } // *** MUST BE THREAD-SAFE **** // This method will be called by multiple threads simultaneously void #ifdef JOURNAL2 JournalInstance::readAioCompleteCallback(std::vector& /*buffPageCtrlBlkIndexList*/) #else JournalInstance::rd_aio_cb(std::vector& /*buffPageCtrlBlkIndexList*/) #endif {} } // namespace jtest } // namespace mrg qpid-cpp-store-debian-0.16/perf/Streamable.hpp0000644000176300017630000000521111477730641020270 0ustar cajuscajus/** * \file Streamable.hpp * * Qpid asynchronous store plugin library * * This file contains performance test code for the journal. * * \author Kim van der Riet * * Copyright (c) 2010 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #ifndef mrg_jtest_Streamable_hpp #define mrg_jtest_Streamable_hpp #include #include namespace mrg { namespace jtest { /** * \brief Abstract class which provides the mechanisms to stream * * An abstract class which provides stream functions. The toStream() function must be implemented by subclasses, * and is used by the remaining functions. For convenience, toString() returns a std::string object. */ struct Streamable { /** * \brief Virtual destructor */ virtual ~Streamable() {} /*** * \brief Stream some representation of the object to an output stream * * \param os Output stream to which the class data is to be streamed */ virtual void toStream(std::ostream& os = std::cout) const = 0; /** * \brief Creates a string representation of the test parameters * * Convenience feature which creates and returns a std::string object containing the content of toStream(). * * \return Content of toStream() */ std::string toString() const; /** * \brief Stream the object to an output stream */ friend std::ostream& operator<<(std::ostream& os, const Streamable& s); /** * \brief Stream the object to an output stream through an object pointer */ friend std::ostream& operator<<(std::ostream& os, const Streamable* sPtr); }; } // namespace jtest } // namespace mrg #endif // mrg_jtest_Streamable_hpp qpid-cpp-store-debian-0.16/perf/ScopedTimer.hpp0000644000176300017630000000677711670231232020434 0ustar cajuscajus/** * \file ScopedTimer.hpp * * Qpid asynchronous store plugin library * * This file contains performance test code for the journal. * * \author Kim van der Riet * * Copyright (c) 2010 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #ifndef mrg_jtest_ScopedTimer_hpp #define mrg_jtest_ScopedTimer_hpp #include namespace mrg { namespace jtest { class ScopedTimer; } // namespace jtest } // namespace mrg #include "ScopedTimable.hpp" // circular include namespace mrg { namespace jtest { /** * \brief Scoped timer class that starts timing on construction and finishes on destruction. * * The scoped timer will take the current time on construction and again on destruction. The destructor * will calculate the elapsed time from the difference between these two times and write the result * as a double to the double ref supplied to the constructor. A second constructor will accept a class (or * subclass) of ScopedTimable, which contains a double to which the result may be written and accessed at a * later time. */ class ScopedTimer { double& _elapsed; ///< Ref to elapsed time, will be written on destruction of ScopedTimer instances ::timespec _startTime; ///< Start time, set on construction /** * \brief Convert ::timespec to seconds * * Static function to convert a ::timespec struct into a double representation in seconds. * * \param ts std::timespec struct containing the time to be converted. * \return A double which represents the time in parameter ts in seconds. */ static double _s_getDoubleTime(const ::timespec& ts); public: /** * \brief Constructor * * Constructor which accepts a ref to a double. Will start the time interval measurement. * * \param elapsed A ref to a double which will contain the elapsed time in seconds after this class instance * is destroyed. */ ScopedTimer(double& elapsed); /** * \brief Constructor * * Constructor which accepts a ref to a ScopedTimable. Will start the time interval measurement. * * \param st A ref to a ScopedTimable into which the result of the ScopedTimer can be written. */ ScopedTimer(ScopedTimable& st); /** * \brief Destructor * * Destructor. Will stop the time interval measurement and write the calculated elapsed time into _elapsed. */ ~ScopedTimer(); }; } // namespace jtest } // namespace mrg #endif // mrg_jtest_ScopedTimer_hpp qpid-cpp-store-debian-0.16/perf/TestParameters.cpp0000644000176300017630000000657611500164314021147 0ustar cajuscajus/** * \file TestParameters.cpp * * Qpid asynchronous store plugin library * * This file contains performance test code for the journal. * * \author Kim van der Riet * * Copyright (c) 2010 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #include "TestParameters.hpp" namespace mrg { namespace jtest { // static declarations uint32_t TestParameters::_s_defaultNumMsgs = 100; uint32_t TestParameters::_s_defaultMsgSize = 10; uint16_t TestParameters::_s_defaultNumQueues = 1; uint16_t TestParameters::_s_defaultNumThreadPairsPerQueue = 1; uint16_t TestParameters::_s_defaultEnqTxnBlkSize = 0; uint16_t TestParameters::_s_defaultDeqTxnBlkSize = 0; TestParameters::TestParameters(): Streamable(), _numMsgs(_s_defaultNumMsgs), _msgSize(_s_defaultMsgSize), _numQueues(_s_defaultNumQueues), _numThreadPairsPerQueue(_s_defaultNumThreadPairsPerQueue), _enqTxnBlockSize(_s_defaultEnqTxnBlkSize), _deqTxnBlockSize(_s_defaultDeqTxnBlkSize) {} TestParameters::TestParameters(const uint32_t numMsgs, const uint32_t msgSize, const uint16_t numQueues, const uint16_t numThreadPairsPerQueue, const uint16_t enqTxnBlockSize, const uint16_t deqTxnBlockSize) : Streamable(), _numMsgs(numMsgs), _msgSize(msgSize), _numQueues(numQueues), _numThreadPairsPerQueue(numThreadPairsPerQueue), _enqTxnBlockSize(enqTxnBlockSize), _deqTxnBlockSize(deqTxnBlockSize) {} TestParameters::TestParameters(const TestParameters& tp): Streamable(), _numMsgs(tp._numMsgs), _msgSize(tp._msgSize), _numQueues(tp._numQueues), _numThreadPairsPerQueue(tp._numThreadPairsPerQueue), _enqTxnBlockSize(tp._enqTxnBlockSize), _deqTxnBlockSize(tp._deqTxnBlockSize) {} void TestParameters::toStream(std::ostream& os) const { os << "Test Parameters:" << std::endl; os << " num_msgs = " << _numMsgs << std::endl; os << " msg_size = " << _msgSize << std::endl; os << " num_queues = " << _numQueues << std::endl; os << " num_thread_pairs_per_queue = " << _numThreadPairsPerQueue << std::endl; os << " enq_txn_blk_size = " << _enqTxnBlockSize << std::endl; os << " deq_txn_blk_size = " << _deqTxnBlockSize << std::endl; } } // namespace jtest } // namespace mrg qpid-cpp-store-debian-0.16/perf/Makefile.am0000644000176300017630000000311511477730641017535 0ustar cajuscajus# Copyright (c) 2010 Red Hat, Inc. # # This file is part of the Qpid async store library msgstore.so. # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 # USA # # The GNU Lesser General Public License is available in the file COPYING. abs_builddir=@abs_builddir@ abs_srcdir=@abs_srcdir@ AM_CXXFLAGS = $(WARNING_CFLAGS) $(APR_CXXFLAGS) $(QPID_CXXFLAGS) -DBOOST_TEST_DYN_LINK INCLUDES=-I$(top_srcdir)/lib -I$(abs_builddir)/../lib -I$(top_srcdir)/lib/gen bin_PROGRAMS = perf perf_SOURCES = \ JournalInstance.cpp \ JournalParameters.cpp \ JournalPerformanceTest.cpp \ PerformanceResult.cpp \ ScopedTimer.cpp \ Streamable.cpp \ TestParameters.cpp \ JournalInstance.hpp \ JournalParameters.hpp \ JournalPerformanceTest.hpp \ PerformanceResult.hpp \ ScopedTimable.hpp \ ScopedTimer.hpp \ Streamable.hpp \ TestParameters.hpp perf_CXXFLAGS = -std=c++0x -lpthread perf_LDADD = -lrt $(top_builddir)/lib/msgstore.la qpid-cpp-store-debian-0.16/perf/JournalInstance.hpp0000644000176300017630000001621111500511440021267 0ustar cajuscajus/** * \file JournalInstance.hpp * * Qpid asynchronous store plugin library * * This file contains performance test code for the journal. * * \author Kim van der Riet * * Copyright (c) 2010 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #ifndef mrg_jtest_JournalInstance_hpp #define mrg_jtest_JournalInstance_hpp #include #include #include #ifdef JOURNAL2 #include "jrnl2/AioCallback.hpp" #include "jrnl2/DataToken.hpp" #include "jrnl2/Journal.hpp" #else #include "jrnl/aio_callback.hpp" #include "jrnl/data_tok.hpp" #include "jrnl/jcntl.hpp" #endif namespace mrg { namespace jtest { /** * \brief Test journal instance. Each queue to be tested results in one instance of this class. * * Journal test harness which contains the journal to be tested. Each queue to be tested in the test parameters * results in one instance of this class being instantiated, and consequently one set of journals on disk. The * journal instance is provided as a pointer to the constructor. */ #ifdef JOURNAL2 class JournalInstance: public mrg::journal2::AioCallback #else class JournalInstance: public mrg::journal::aio_callback #endif { const uint32_t _numMsgs; ///< Number of messages to be processed by this journal instance const uint32_t _msgSize; ///< Size of each message (in bytes) const char* _msgData; ///< Pointer to message content to be used for each message. #ifdef JOURNAL2 mrg::journal2::Journal* const _jrnlPtr; ///< Journal instance pointer std::queue _unprocCallbackList; ///< Queue of unprocessed callbacks to be dequeued #else mrg::journal::jcntl* const _jrnlPtr; ///< Journal instance pointer std::queue _unprocCallbackList; ///< Queue of unprocessed callbacks to be dequeued #endif std::mutex _unprocCallbackListMutex; ///< Mutex which protects the unprocessed callback queue bool _threadSwitch; ///< A switch which alternates worker threads between enq and deq std::mutex _threadSwitchLock; ///< Mutex which protects the thread switch /** * \brief Worker thread enqueue task * * This function is the worker thread enqueue task entry point. It enqueues _numMsgs onto the journal instance. * A data tokens is created for each record, this is the start of the data token life cycle. All possible * returns from the journal are handled appropriately. Since the enqueue threads also perform * callbacks on completed AIO operations, the data tokens from completed enqueues are placed onto the * unprocessed callback list (_unprocCallbackList) for dequeueing by the dequeue worker thread(s). * * This function must be thread safe. */ void _doEnqueues(); /** * \brief Worker thread dequeue task * * This function is the worker thread dequeue task entry point. It dequeues messages which are on the * unprocessed callback list (_unprocCallbackList). * * This function must be thread safe. */ void _doDequeues(); public: /** * \brief Constructor * * \param numMsgs Number of messages per thread to be enqueued then dequeued (ie both ways through broker) * \param msgSize Size of each message being enqueued * \param msgData Pointer to message content (all messages have identical content) * \param jrnlPtr Pinter to journal instance which is to be tested */ #ifdef JOURNAL2 JournalInstance(const uint32_t numMsgs, const uint32_t msgSize, const char* msgData, mrg::journal2::Journal* const jrnlPtr); #else JournalInstance(const uint32_t numMsgs, const uint32_t msgSize, const char* msgData, mrg::journal::jcntl* const jrnlPtr); #endif /** * \brief virtual destructor */ virtual ~JournalInstance(); /** * \brief Worker thread entry point used by std::thread for classes. Threads must be provided in pairs. * * This is the entry point for worker threads. To successfully use this class, threads must be provided in * pairs, with alternate threads being used for enqueueing and deqeueing tasks (_doEnqueues and _doDequeues * respectively). See these functions for more detail on these tasks. The bool member _threadSwitch is * responsible for alternating threads calling this function between these two tasks. */ void operator()(); /** * \brief Write callback function. When AIO operations return, this function is called. * * When AIO operations return, this function will sort the enqueue ops from the rest and place the data tokens * of these records onto the unprocessed callback list (_unprocCallbackList) for dequeueing by another thread. * * Returning dequeue ops have their data tokens destroyed, as this is the end of the life cycle of the data * tokens. * * Required by all subclasses of mrg::journal::aio_callback. * * \param dataTokenList A vector of data tokens for those messages which have completed their AIO write * operations */ #ifdef JOURNAL2 void writeAioCompleteCallback(std::vector& dataTokenList); #else void wr_aio_cb(std::vector& dataTokenList); #endif /** * \brief Read callback function. When read AIO operations return, this function is called. * * Not used in this test, but required by all subclasses of mrg::journal::aio_callback. * * \param buffPageCtrlBlkIndexList A vector of indices to the buffer page control blocks for completed reads */ #ifdef JOURNAL2 void readAioCompleteCallback(std::vector& buffPageCtrlBlkIndexList); #else void rd_aio_cb(std::vector& buffPageCtrlBlkIndexList); #endif }; } // namespace jtest } // namespace mrg #endif // mrg_jtest_JournalInstance_hpp qpid-cpp-store-debian-0.16/perf/PerformanceResult.hpp0000644000176300017630000000654211477730641021661 0ustar cajuscajus/** * \file PerformanceResult.hpp * * Qpid asynchronous store plugin library * * This file contains performance test code for the journal. * * \author Kim van der Riet * * Copyright (c) 2010 Red Hat, Inc. * * This file is part of the Qpid async store library msgstore.so. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * * The GNU Lesser General Public License is available in the file COPYING. */ #ifndef mrg_jtest_PerformanceResult_hpp #define mrg_jtest_PerformanceResult_hpp #include #include "TestParameters.hpp" #include "ScopedTimable.hpp" #include "Streamable.hpp" namespace mrg { namespace jtest { /** * \brief Results class that accepts an elapsed time to calculate the rate of message throughput in the journal. * * This class (being subclassed from ScopedTimable) is passed to a ScopedTimer object on construction, and the * inherited _elapsed member will be written with the calculated elapsed time (in seconds) on destruction of the * ScopedTimer object. This time (initially set to 0.0) is used to calculate message and message byte throughput. * The message number and size information comes from the TestParameters object passed to the constructor. * * Results are available through the use of toStream(), toString() or the << operators. * * Output is in the following format: *
     * TEST RESULTS:
     *     Msgs per thread: 10000
     *            Msg size: 2048
     *          No. queues: 2
     *   No. threads/queue: 2
     *          Time taken: 1.6626 sec
     *      Total no. msgs: 40000
     *      Msg throughput: 24.0587 kMsgs/sec
     *                      49.2723 MB/sec
     * 
*/ class PerformanceResult : public ScopedTimable, public Streamable { TestParameters _testParams; ///< Test parameters used for performance calculations public: /** * \brief Constructor * * Constructor. Will start the time interval measurement. * * \param tp Test parameter details used to calculate the performance results. */ PerformanceResult(const TestParameters& tp); /** * \brief Virtual destructor */ virtual ~PerformanceResult() {} /** * \brief Stream the performance test results to an output stream * * Convenience feature which streams a multi-line performance result an output stream. * * \param os Output stream to which the results are to be streamed */ void toStream(std::ostream& os = std::cout) const; }; } // namespace jtest } // namespace mrg #endif // mrg_jtest_PerformanceResult_hpp qpid-cpp-store-debian-0.16/perf/README0000644000176300017630000000276211463611711016357 0ustar cajuscajusTHIS IS EXPERIMENTAL CODE ------------------------- This folder contains a general async store test and performance measurement tool. It uses the jcntl interface to enqueue and dequeue messages as fast as possible from a number of persistent store instances. The parameters fall into two groups: 1. Test parameters: -m --num_msgs: Number of messages to send -S --msg_size: Size of each message to be sent -q --num_queues: Number of simultaneous queues -t --num_threads_per_queue: Number of threads per queue 2. Store parameters, which control the attributes of the store itself: -d --jrnl_dir: Store directory -b --jrnl_base_filename: Base name for journal files -f --num_jfiles: Number of journal files -s --jfsize_sblks: Size of each journal file in sblks (512 byte blocks) -a --auto_expand: Auto-expand the journal -e --ae_max_jfiles: Upper limit on number of auto-expanded journal files -p --wcache_num_pages: Number of write buffer pages -c --wcache_pgsize_sblks: Size of each write buffer page in sblks (512 byte blocks) For each test: a. The correct number of store instances are created with their formatted store files; b. The timer begins; c. The specified number of threads are created and do the work of enqueueing and dequeueing persistent messages on the specified number of store instances; d. When all threads have finished working, the timer is stopped; e. The results of the test are printed. qpid-cpp-store-debian-0.16/COPYING0000644000176300017630000005747510610442115015602 0ustar cajuscajus GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS qpid-cpp-store-debian-0.16/configure.ac0000644000176300017630000001771611752237112017036 0ustar cajuscajusdnl Copyright (c) 2007, 2008 Red Hat, Inc. dnl dnl This file is part of the Qpid async store library msgstore.so. dnl dnl This library is free software; you can redistribute it and/or dnl modify it under the terms of the GNU Lesser General Public dnl License as published by the Free Software Foundation; either dnl version 2.1 of the License, or (at your option) any later version. dnl dnl This library is distributed in the hope that it will be useful, dnl but WITHOUT ANY WARRANTY; without even the implied warranty of dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU dnl Lesser General Public License for more details. dnl dnl You should have received a copy of the GNU Lesser General Public dnl License along with this library; if not, write to the Free Software dnl Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 dnl USA dnl dnl The GNU Lesser General Public License is available in the file COPYING. dnl dnl Process this file with autoconf to produce a configure script. AC_INIT([msg-store], [0.7], [rhemrg-users-list@redhat.com]) AC_CONFIG_AUX_DIR([build-aux]) AM_INIT_AUTOMAKE([dist-bzip2]) # Minimum Autoconf version required. AC_PREREQ(2.59) AC_CONFIG_HEADERS([config.h:config.in]) AC_CONFIG_SRCDIR([lib/MessageStoreImpl.cpp]) AC_PROG_CC_STDC AM_PROG_CC_C_O AC_PROG_CXX AC_USE_SYSTEM_EXTENSIONS AC_LANG([C++]) AC_ARG_ENABLE(warnings, [ --enable-warnings turn on lots of compiler warnings (recommended)], [case "${enableval}" in yes|no) enable_WARNINGS=${enableval};; *) AC_MSG_ERROR([bad value ${enableval} for warnings option]) ;; esac], [enable_WARNINGS=yes]) AC_ARG_ENABLE(cluster-tests, [ --enable-cluster-tests enable cluster tests with persistence], [case "${enableval}" in yes|no) enable_CLUSTER_TESTS=${enableval} ;; *) AC_MSG_ERROR([bad value ${enableval} for cluster test option]) ;; esac], [enable_CLUSTER_TESTS=none]) # Warnings: Enable as many as possible, keep the code clean. Please # do not disable warnings or remove -Werror without discussing on # rhemrg-users-list list. # # The following warnings are deliberately omitted, they warn on valid code. # -Wunreachable-code -Wpadded -Winline # -Wshadow - warns about boost headers. if test "$enable_WARNINGS" = yes; then gl_COMPILER_FLAGS(-Werror) gl_COMPILER_FLAGS(-pedantic) gl_COMPILER_FLAGS(-Wall) gl_COMPILER_FLAGS(-Wextra) gl_COMPILER_FLAGS(-Wno-shadow) gl_COMPILER_FLAGS(-Wpointer-arith) gl_COMPILER_FLAGS(-Wcast-qual) gl_COMPILER_FLAGS(-Wcast-align) gl_COMPILER_FLAGS(-Wno-long-long) gl_COMPILER_FLAGS(-Wvolatile-register-var) gl_COMPILER_FLAGS(-Winvalid-pch) gl_COMPILER_FLAGS(-Wno-system-headers) AC_SUBST([WARNING_CFLAGS], [$COMPILER_FLAGS]) AC_DEFINE([lint], 1, [Define to 1 if the compiler is checking for lint.]) COMPILER_FLAGS= fi AC_DISABLE_STATIC AC_PROG_LIBTOOL AC_SUBST([LIBTOOL_DEPS]) # Select building against qpid checkout or install. AC_ARG_WITH([qpid-checkout], [AS_HELP_STRING([--with-qpid-checkout], [Location of qpid checkout to build against (by default use installed qpid)])]) AC_ARG_WITH([qpid-build], [AS_HELP_STRING([--with-qpid-build], [Qpid build directory if different from --with-qpid-checkout])]) AC_ARG_WITH([qpid-prefix], [AS_HELP_STRING([--with-qpid-prefix], [Location of installed qpid prefix to build against (by default "/")])], [], [with_qpid_prefix="/"]) if test x$with_qpid_checkout != x; then QPID_DIR=$with_qpid_checkout QPID_BLD=$QPID_DIR/cpp QPID_SRC=$QPID_DIR/cpp/src test -f $QPID_SRC/qpid/broker/MessageStore.h || \ AC_MSG_ERROR([$QPID_DIR does not appear to be a valid qpid checkout.]) test -d $QPID_DIR/python -a -d $QPID_DIR/specs || \ AC_MSG_ERROR([$QPID_DIR does not have python and specs directories.]) QPID_LIBS="$QPID_SRC/libqpidbroker.la $QPID_SRC/libqpidcommon.la" QPID_CXXFLAGS="-I$QPID_DIR/cpp/include -I$QPID_SRC" else QPID_PREFIX=$with_qpid_prefix QPID_INCLUDE=$QPID_PREFIX/include QPID_LIB=$QPID_PREFIX/lib fail=0 test -f $QPID_INCLUDE/qpid/broker/MessageStore.h || \ fail=1 # Give any/all diagnostics before failing. test $fail = 1 && AC_MSG_ERROR([Missing required qpid libraries/headers. Install source rpms or use one of --with-qpid-checkout or --with-qpid-prefix]) QPID_CXXFLAGS="-I$QPID_INCLUDE" QPID_LIBS="-L$QPID_LIB -lqpidbroker -lqpidcommon" fi if test x$with_qpid_build != x; then test x$with_qpid_checkout != x || AC_MSG_ERROR([--with-qpid-build requires --with-qpid-checkout]) QPID_BLD=$with_qpid_build QPID_LIBS="$QPID_BLD/src/libqpidbroker.la $QPID_BLD/src/libqpidcommon.la" QPID_CXXFLAGS="-I$QPID_DIR/cpp/include -I$QPID_SRC -I$QPID_BLD/include -I$QPID_BLD/src" fi AC_SUBST([QPID_PREFIX]) AC_SUBST([QPID_DIR]) AC_SUBST([QPID_BLD]) AC_SUBST([QPID_LIBS]) AC_SUBST([QPID_CXXFLAGS]) # Check for cluster module to see whether to enable cluster tests AC_MSG_CHECKING([presence of qpid cluster module]) found_cluster_so=no if test x$with_qpid_build != x -a -f $QPID_BLD/src/.libs/cluster.so ; then found_cluster_so=yes elif test x$with_qpid_checkout != x -a -f $QPID_SRC/.libs/cluster.so ; then found_cluster_so=yes elif test -f $QPID_LIB/qpid/daemon/cluster.so ; then found_cluster_so=yes fi case "$enable_CLUSTER_TESTS" in yes) if test $found_cluster_so = no; then AC_MSG_ERROR([didn't find cluster module: can't enable cluster tests]) fi ;; no) ;; none) if test $found_cluster_so = yes; then enable_CLUSTER_TESTS=yes else enable_CLUSTER_TESTS=no fi ;; *) AC_MSG_ERROR([bad value $enable_CLUSTER_TESTS for cluster tests]) ;; esac AC_MSG_RESULT([$enable_CLUSTER_TESTS]) # Check for libaio and libaio-devel libaio_fail=0 AC_CHECK_LIB([aio], [io_setup], ,[libaio_fail=1]) AC_CHECK_HEADER([libaio.h], ,[libaio_fail=1]) test $libaio_fail == 1 && \ AC_MSG_ERROR([libaio-devel package missing. Please ensure both libaio and libaio-devel are installed. (hint: "yum install libaio-devel" should do it...)]) # For libraries (libcommon) that use dlopen, dlerror, etc., # test whether we need to link with -ldl. gl_saved_libs=$LIBS AC_SEARCH_LIBS(dlopen, [dl], [test "$ac_cv_search_dlopen" = "none required" || LIB_DLOPEN=$ac_cv_search_dlopen]) AC_SUBST([LIB_DLOPEN]) LIBS=$gl_saved_libs # Require libdb_cxx (any version between 4.2 and 4.8), for the library, and for db_cxx.h. db4_devel_fail=0 AC_CHECK_HEADER([db_cxx.h], ,[db4_devel_fail=1]) test $db4_devel_fail == 1 && \ AC_MSG_ERROR([db4-devel package missing. Please ensure both db4 and db4-devel are installed. (hint: "yum install db4-devel" should do it...)]) gl_saved_libs=$LIBS AC_SEARCH_LIBS([__db_open], [db_cxx-4.8 db_cxx-4.7 db_cxx-4.6 db_cxx-4.5 db_cxx-4.4 db_cxx-4.3 db_cxx-4.2], [test "$ac_cv_search___db_open" = "none required" || LIB_BERKELEY_DB=$ac_cv_search___db_open], AC_MSG_ERROR([Couldn't find required library in range db_cxx-4.2 through db_cxx-4.8])) AC_SUBST([LIB_BERKELEY_DB]) LIBS=$gl_saved_libs # Determine how to include db_cxx.h: # Red Hat needs , Debian needs . AC_CHECK_HEADER([db4/db_cxx.h], [DB_CXX_HEADER_PREFIX=db4/]) if test x$DB_CXX_HEADER_PREFIX = x; then AC_CHECK_HEADER([db_cxx.h], [DB_CXX_HEADER_PREFIX=]) fi AC_SUBST(DB_CXX_HEADER_PREFIX) gl_CLOCK_TIME # We use valgrind for the tests. See if it's available. AC_CHECK_PROG([VALGRIND], [valgrind], [valgrind]) # If rpmlint is available we'll run it when building RPMs. AC_CHECK_PROG([RPMLINT], [rpmlint], [rpmlint]) AM_CONDITIONAL([HAS_RPMLINT], [test -n "$RPMLINT"]) # Also doxygen for documentation... AC_CHECK_PROG([do_doxygen], [doxygen], [yes]) AM_CONDITIONAL([DOXYGEN], [test x$do_doxygen = xyes]) AM_CONDITIONAL([DO_CLUSTER_TESTS], [test $enable_CLUSTER_TESTS = yes]) # Check for Python libraries for the Journal Tools AM_PATH_PYTHON() AC_CONFIG_FILES([ Makefile docs/Makefile lib/Makefile lib/jrnl2/Makefile perf/Makefile tests/Makefile tests/cluster/Makefile tests/federation/Makefile tests/jrnl/Makefile tests/jrnl/jtt/Makefile tools/Makefile ]) AC_OUTPUT qpid-cpp-store-debian-0.16/README0000644000176300017630000000372511142327241015417 0ustar cajuscajus== Asynchronous Message Store Plugin for Qpid c++ Broker == -- PREREQUISITES -- - libaio-devel libs - qpid broker headers and libraries (see below) - all prerequisites for qpid (see qpid's cpp/INSTALL file) - berkeley db4 (tested with version 4.3.29) - python (to run system tests) For the qpid requirements you can either install the qpidd-devel package or check-out the qpid source from: https://svn.apache.org/repos/asf/qpid/trunk/qpid -- BUILDING -- The following steps should build the package (all from the cpp directory): 0. If you are using an svn checkout of qpidd, then build this package following the instructions in the cpp/INSTALL file from that project. The store will not link without the libqpidcommon.so library from that project. If you installed the qpidd package, then this library is installed. 1. './bootstrap' (This initializes the autotools) 2. If the qpidd package is installed, then: './configure' Otherwise, if using an svn checkout of qpidd (as opposed to installing the qpidd package), then: './configure --with-qpid-checkout=' (Point to the top-level qpid dir in which the cpp and python dirs exist) 3. 'make' or 'make all' will build the module 'make check' will run unit and system tests. Note: Some system tests require qpid's python test framework, and therefore will not run unless python is installed and qpidd is installed from subversion and the --with-qpid-checkout option is used in configure. -- DISTRIBUTION-SPECIFIC NOTES --- On a RHEL4, some files must be patched to accomodate an early version of boost and gcc. See cpp/rhel4-support/README. Note that there are some warnings seen on RHEL4 builds and tests that are not present on other platforms. On a Fedora system install prerequisites via: yum install libaio-devel db4-devel qpidd-devel On a Debian-based system, install prerequisites via: apt-get install libdb4.3++-dev On Debian, I need to get this file: /usr/include/db_cxx.h