create_rec.cpp

This example demonstrates creating a file of records. It also demonstrates scanning the file of records, creating a device and volume, and use of the root index. It must also contain, of course, the creation of options, starting up and shutting down a storage manager.

00001 /*<std-header orig-src='shore'>
00002 
00003  $Id: create_rec.cpp,v 1.7 2010/09/21 14:26:28 nhall Exp $
00004 
00005 SHORE -- Scalable Heterogeneous Object REpository
00006 
00007 Copyright (c) 1994-99 Computer Sciences Department, University of
00008                       Wisconsin -- Madison
00009 All Rights Reserved.
00010 
00011 Permission to use, copy, modify and distribute this software and its
00012 documentation is hereby granted, provided that both the copyright
00013 notice and this permission notice appear in all copies of the
00014 software, derivative works or modified versions, and any portions
00015 thereof, and that both notices appear in supporting documentation.
00016 
00017 THE AUTHORS AND THE COMPUTER SCIENCES DEPARTMENT OF THE UNIVERSITY
00018 OF WISCONSIN - MADISON ALLOW FREE USE OF THIS SOFTWARE IN ITS
00019 "AS IS" CONDITION, AND THEY DISCLAIM ANY LIABILITY OF ANY KIND
00020 FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
00021 
00022 This software was developed with support by the Advanced Research
00023 Project Agency, ARPA order number 018 (formerly 8230), monitored by
00024 the U.S. Army Research Laboratory under contract DAAB07-91-C-Q518.
00025 Further funding for this work was provided by DARPA through
00026 Rome Research Laboratory Contract No. F30602-97-2-0247.
00027 
00028 */
00029 
00030 #include "w_defines.h"
00031 
00032 /*  -- do not edit anything above this line --   </std-header>*/
00033 
00034 /**\anchor create_rec_example */
00035 /*
00036  * This program is a simple test of creating records.
00037  * It illustrates:
00038  * - formatting and mounting devices and volumes.
00039  * - use of the volume's root index (create an entry, 
00040  *   find an entry with a given key)
00041  * - creating a file
00042  * - using a file created by an earlier run of this program.
00043  * - creating records in a file
00044  * - scanning a file of records
00045  * - startup and shutdown of the storage manager
00046  * - a single storage manager thread does the work.
00047  */
00048 
00049 #include "sm_vas.h"
00050 ss_m* ssm = 0;
00051 
00052 // shorten error-code type name
00053 typedef w_rc_t rc_t;
00054 
00055 // this is implemented in init_config_options.cpp
00056 w_rc_t init_config_options(option_group_t& options,
00057                         const char* prog_type,
00058                         int& argc, char** argv);
00059 
00060 
00061 /* This program stores information about a file in 
00062  * this structure, which it makes persistent by
00063  * storing it in the volumes root index.
00064  * When we run this program with the -i option (initialize),
00065  * we start over with a blank slate: reformat the device/volume and
00066  * create a new file. If -i is not given, we look for a file_info_t
00067  * in the root index under the key "SCANFILE", and use the file
00068  * whose identifier is stored therein. We also expect to find
00069  * the given number of records of the given size in the file.
00070  */
00071 struct file_info_t {
00072     static const char* key;
00073     stid_t             fid;
00074     rid_t              first_rid;
00075     int                num_rec;
00076     int                rec_size;
00077 };
00078 const char* file_info_t::key = "SCANFILE";
00079 
00080 ostream &
00081 operator << (ostream &o, const file_info_t &info)
00082 {
00083     o << "key " << info.key
00084     << " fid " << info.fid
00085     << " first_rid " << info.first_rid
00086     << " num_rec " << info.num_rec
00087     << " rec_size " << info.rec_size ;
00088     return o;
00089 }
00090 
00091 static char *argv0(NULL);
00092 void
00093 usage(option_group_t& options)
00094 {
00095     cerr << "Usage: " << argv0 << " [-h] [-i] [options]" << endl;
00096     cerr << "       -i initialize device/volume and create file of records" << endl;
00097     cerr << "Valid options are: " << endl;
00098     options.print_usage(true, cerr);
00099 }
00100 
00101 /* create an smthread based class for all sm-related work */
00102 class smthread_driver_t : public smthread_t {
00103         int         _argc;
00104         char**      _argv;
00105 
00106 protected:
00107         const char *_device_name;
00108         smsize_t    _quota; // for device and volume
00109         int         _num_rec; // number of records to put in file
00110         smsize_t    _rec_size; // record size
00111         lvid_t      _lvid;   // persistent volume id to give the volume
00112         vid_t       _vid;    // short (integer) volume id
00113         rid_t       _start_rid; // first record in the file
00114         stid_t      _fid;    // file id
00115         bool        _initialize_device; // shall we start from scratch?
00116         option_group_t* _options; // run-time options
00117         int         _retval; // return value from run()
00118 
00119         /* two helpers for run -- virtual only because other examples override them*/
00120         virtual w_rc_t do_work() ;
00121         virtual void   start_ssm() ;
00122 public:
00123 
00124         smthread_driver_t(int ac, char **av) 
00125                 : smthread_t(t_regular, "smthread_driver_t"),
00126                 _argc(ac), _argv(av), 
00127                 _device_name(NULL),
00128                 _quota(0),
00129                 _num_rec(0),
00130                 _rec_size(0),
00131                 _vid(1),
00132                 _initialize_device(false),
00133                 _options(NULL),
00134                 _retval(0) { }
00135 
00136         virtual ~smthread_driver_t()  { if(_options) delete _options; }
00137 
00138         virtual void run();
00139         void statistics() const;
00140         int  return_value() const { return _retval; }
00141 
00142         // helpers for run() -- this compartmentalizes the
00143         // sm functionality a bit.
00144         w_rc_t handle_options(); // run-time options
00145         w_rc_t find_file_info(); // look up file info in root index
00146         w_rc_t create_the_file();// create a file
00147         w_rc_t scan_the_file();  // scan existing file
00148         w_rc_t scan_the_root_index(); //scan root index
00149         w_rc_t do_init(); // called when -i command-line flag is given
00150         w_rc_t no_init(); // called with -i is NOT given
00151 
00152 };
00153 
00154 w_rc_t 
00155 smthread_driver_t::handle_options()
00156 {
00157     // Create an option group for my options.
00158     // I use a 3-level naming scheme:
00159     // executable-name.server.option-name
00160     // Thus, the file will contain lines like this:
00161     // create_rec.server.device_name : /tmp/example/device
00162     // or, with wildcard:
00163     // create_rec.*.device_name : /tmp/example/device
00164     //          *.server.device_name : /tmp/example/device
00165     //          *.*.device_name : /tmp/example/device
00166     //
00167     const int option_level_cnt = 3; 
00168 
00169     _options = new option_group_t (option_level_cnt);
00170     if(!_options) {
00171         cerr << "Out of memory: could not allocate from heap." <<
00172             endl;
00173         _retval = 1;
00174         return RC(fcINTERNAL);
00175     }
00176     option_group_t &options(*_options);
00177 
00178     /* Add my own option for a device's path name. It's required,
00179      * and has no default value. */
00180     option_t* opt_device_name = 0;
00181     W_DO(options.add_option(
00182              "device_name",  /* option name */
00183              "device/file name", /* syntax */
00184              NULL,  /* no default value */
00185              "device containg volume holding file to scan", /* description */
00186              true,  /* required */
00187              option_t::set_value_charstr,/* function to parse the value */
00188              opt_device_name /* handle for option created */
00189              ));
00190 
00191     /* Add my own option for a device's quota */
00192     option_t* opt_device_quota = 0;
00193     W_DO(options.add_option(
00194             "device_quota",  /* option name */
00195             "# > 1000", /* syntax */
00196              "2000",  /* default value */
00197              "quota for device", /* description */
00198              false,  /* not required */
00199              option_t::set_value_long, /* function to parse value */
00200              opt_device_quota /* handle */
00201              ));
00202 
00203     /* Add my own option for the number of records to create.
00204      * Default number of records is 1.
00205      */
00206     option_t* opt_num_rec = 0;
00207     W_DO(options.add_option(
00208             "num_rec",  /* option name */
00209             "# > 0", /* syntax */
00210              "100",  /* default value */
00211              "number of records in file", /* description */
00212              true,  /* required */
00213              option_t::set_value_long, /* func to parse value */
00214              opt_num_rec /* handle */
00215              ));
00216 
00217     /* 
00218      * Add the SSM options to my group.
00219      */
00220     W_DO(ss_m::setup_options(&options));
00221 
00222     /*
00223      * Call external function to read options' values from
00224      * a file and from the command line.
00225      */
00226     w_rc_t rc = init_config_options(options, "server", _argc, _argv);
00227     if (rc.is_error()) {
00228         usage(options);
00229         _retval = 1;
00230         return rc;
00231     }
00232 
00233     /*
00234      * Process what's left of the command line,
00235      * to look for the -i and/or -h flags.
00236      */
00237 
00238     int option;
00239     while ((option = getopt(_argc, _argv, "hi")) != -1) {
00240         switch (option) {
00241         case 'i' :
00242             _initialize_device = true;
00243             break;
00244 
00245         case 'h' :
00246             usage(options);
00247             break;
00248 
00249         default: // unrecognized flag or option
00250             usage(options);
00251             _retval = 1;
00252             return RC(fcNOTIMPLEMENTED);
00253             break;
00254         }
00255     }
00256 
00257     // Grab the options values for later use by run()
00258     _device_name = opt_device_name->value();
00259     _quota = strtol(opt_device_quota->value(), 0, 0);
00260     _num_rec = strtol(opt_num_rec->value(), 0, 0);
00261 
00262     return RCOK;
00263 }
00264 
00265 void
00266 smthread_driver_t::statistics() const 
00267 {
00268     sm_stats_info_t       stats;
00269     W_COERCE(ss_m::gather_stats(stats));
00270     cout << " SM Statistics : " << endl
00271          << stats  << endl;
00272 }
00273 /*
00274  * Look up file info in the root index.
00275  * Assumes we already know the volume id _vid.
00276 */
00277 w_rc_t
00278 smthread_driver_t::find_file_info()
00279 {
00280     file_info_t  info;
00281 
00282     /* start a transaction */
00283     W_DO(ssm->begin_xct());
00284 
00285     /* Get the identifier of the root index. 
00286      * Failure causes us to return from find_file_info().
00287      */
00288     stid_t      root_iid;
00289     W_DO(ss_m::vol_root_index(_vid, root_iid));
00290 
00291     /* Create a vector containing the key under which the
00292      * file info is stored in the root index
00293      */
00294     const vec_t key_vec_tmp(file_info_t::key, strlen(file_info_t::key));
00295 
00296     /* Get the size of the value associated with the above key */
00297     smsize_t    info_len = sizeof(info);
00298 
00299     /* See if there's an entry in the root index for the file info.
00300      * If it's not there, "found" will be set to false.
00301      * Failure causes us to return from find_file_info(), but
00302      * failure here does not mean it's not found; rather, it's
00303      * something like an illegal argument was passed in.
00304      */
00305     bool        found;
00306     W_DO(ss_m::find_assoc(root_iid,
00307                           key_vec_tmp,
00308                           &info, info_len, found));
00309     if (!found) {
00310         cerr << "No file information found" <<endl;
00311         return RC(fcASSERT);
00312     } else {
00313         /* found. extract the stuff we need and populate
00314          * this class's attributes
00315          */
00316        cout << "Found assoc "
00317             << file_info_t::key << " --> " << info << endl;
00318 
00319         _fid = info.fid;
00320         _start_rid = info.first_rid;
00321         _rec_size = info.rec_size;
00322         _num_rec = info.num_rec;
00323     }
00324 
00325     /* end of transaction */
00326     W_DO(ssm->commit_xct());
00327 
00328     return RCOK;
00329 }
00330 
00331 rc_t
00332 smthread_driver_t::create_the_file() 
00333 {
00334     file_info_t info;  
00335 
00336     /* start a transaction */
00337     W_DO(ssm->begin_xct());
00338 
00339     /* Create a file in the volume _vid. 
00340      * Let its logging type (store flags) be
00341      * regular, meaning it's logged.
00342      * Stuff its fid into info.fid.
00343      *
00344      * Failure will cause return from create_the_file().
00345      */
00346     W_DO(ssm->create_file(_vid, _fid, smlevel_3::t_regular));
00347     info.fid = _fid;
00348 
00349     /* end of transaction */
00350     W_DO(ssm->commit_xct());
00351 
00352     /* Now we'll create a bunch of records.
00353      * The record size was taken from the storage manager's
00354      * build-time configuration. 
00355      *
00356      * We'll put an int in the record's header; that will
00357      * be the ordinal number of the record, so we'll reduce
00358      * the record size to account for that.
00359      */
00360 
00361     /// each record will have its ordinal number in the header
00362     /// and zeros for data 
00363     _rec_size -= align(sizeof(int));
00364 
00365     char* dummy = new char[_rec_size];
00366     memset(dummy, '\0', _rec_size);
00367     /* Create a vector for the data. The same
00368      * vector will be used for every record created.
00369      */
00370     vec_t data(dummy, _rec_size);
00371 
00372     /* new transaction */
00373     W_DO(ssm->begin_xct());
00374 
00375     /* _num_rec was taken from run-time options */
00376     for(int j=0; j < _num_rec; j++)
00377     {
00378         /* Header contains record #.
00379          * Create a vector for the header.
00380          */
00381         const vec_t hdr(&j, sizeof(j));
00382         rid_t rid;
00383 
00384         W_DO(ssm->create_rec(
00385                 info.fid,  // file in which to put the record
00386                 hdr,       // header
00387                 _rec_size,  // size of data
00388                 data,      // data
00389                 rid        // resulting record id
00390                 ));
00391         if (j == 0) {
00392             info.first_rid = rid;
00393         }        
00394         cout << "Record number " << j  << " " << rid << endl;
00395 
00396     }
00397     delete [] dummy;
00398     cout << "Created all."
00399         << endl
00400         << " First rid " << info.first_rid << endl;
00401 
00402     /*
00403      * Store data about the file created. This info will
00404      * be made persistent.
00405      */
00406     info.num_rec = _num_rec;
00407     info.rec_size = _rec_size;
00408 
00409     /* get the volume's root index identifier */
00410     stid_t      _root_iid;
00411     W_DO(ss_m::vol_root_index(_vid, _root_iid));
00412 
00413     /* create a vector for the key with which to associate the
00414      * file info
00415      */
00416     const vec_t key_vec_tmp(file_info_t::key, strlen(file_info_t::key));
00417     /* create a vector for the datum to associate with the key */
00418     const vec_t info_vec_tmp(&info, sizeof(info));
00419     /* create the association in the root index. */
00420     W_DO(ss_m::create_assoc(_root_iid,
00421                             key_vec_tmp,
00422                             info_vec_tmp));
00423     cout << "Creating assoc "
00424             << file_info_t::key << " --> " << info << endl;
00425     /* done. end of transaction */
00426     W_DO(ssm->commit_xct());
00427     return RCOK;
00428 }
00429 
00430 rc_t
00431 smthread_driver_t::scan_the_root_index() 
00432 {
00433     W_DO(ssm->begin_xct());
00434     stid_t _root_iid;
00435     W_DO(ss_m::vol_root_index(_vid, _root_iid));
00436     cout << "Scanning index " << _root_iid << endl;
00437     scan_index_i scan(_root_iid, 
00438             scan_index_i::ge, vec_t::neg_inf,
00439             scan_index_i::le, vec_t::pos_inf, false,
00440             ss_m::t_cc_kvl);
00441     bool        eof(false);
00442     int         i(0);
00443     smsize_t    klen(0);
00444     smsize_t    elen(0);
00445 #define MAXKEYSIZE 100
00446     char        keybuf[MAXKEYSIZE];
00447     file_info_t info;
00448 
00449     do {
00450         w_rc_t rc = scan.next(eof);
00451         if(rc.is_error()) {
00452             cerr << "Error getting next: " << rc << endl;
00453             _retval = rc.err_num();
00454             return rc;
00455         }
00456         if(eof) break;
00457 
00458         // get the key len and element len
00459         W_DO(scan.curr(NULL, klen, NULL, elen));
00460         // Create vectors for the given lengths.
00461         vec_t key(&keybuf[0], klen);
00462         vec_t elem(&info, elen);
00463         // Get the key and element value
00464         W_DO(scan.curr(&key, klen, &elem, elen));
00465         keybuf[klen] = '\0';
00466         cout << "Key " << (const char *)keybuf << endl;
00467         cout << "Value " 
00468         << " { fid " << info.fid 
00469         << " first_rid " << info.first_rid
00470         << " #rec " << info.num_rec
00471         << " rec size " << info.rec_size << " }"
00472         << endl;
00473         i++;
00474     } while (!eof);
00475     W_DO(ssm->commit_xct());
00476     return RCOK;
00477 }
00478 
00479 rc_t
00480 smthread_driver_t::scan_the_file() 
00481 {
00482     cout << "Scanning file " << _fid << endl;
00483     W_DO(ssm->begin_xct());
00484 
00485     scan_file_i scan(_fid);
00486     pin_i*      cursor(NULL);
00487     bool        eof(false);
00488     int         i(0);
00489 
00490     do {
00491         w_rc_t rc = scan.next(cursor, 0, eof);
00492         if(rc.is_error()) {
00493             cerr << "Error getting next: " << rc << endl;
00494             _retval = rc.err_num();
00495             return rc;
00496         }
00497         if(eof) break;
00498 
00499         cout << "Record " << i 
00500             << " Rid "  << cursor->rid() ;
00501         vec_t       header (cursor->hdr(), cursor->hdr_size());
00502         int         hdrcontents;
00503         header.copy_to(&hdrcontents, sizeof(hdrcontents));
00504         cout << " Hdr {"  << hdrcontents << "}";
00505 
00506         const char *body = cursor->body();
00507         w_assert0(cursor->body_size() == _rec_size);
00508         cout << " Body {"  << body << "}";
00509         cout << endl;
00510         i++;
00511     } while (!eof);
00512     w_assert1(i == _num_rec);
00513 
00514     W_DO(ssm->commit_xct());
00515     return RCOK;
00516 }
00517 
00518 rc_t
00519 smthread_driver_t::do_init()
00520 {
00521     cout << "-i: Initialize " << endl;
00522 
00523     {
00524         devid_t        devid;
00525         cout << "Formatting device: " << _device_name 
00526              << " with a " << _quota << "KB quota ..." << endl;
00527         W_DO(ssm->format_dev(_device_name, _quota, true));
00528 
00529         cout << "Mounting device: " << _device_name  << endl;
00530         // mount the new device
00531         u_int        vol_cnt;
00532         W_DO(ssm->mount_dev(_device_name, vol_cnt, devid));
00533 
00534         cout << "Mounted device: " << _device_name  
00535              << " volume count " << vol_cnt
00536              << " device " << devid
00537              << endl;
00538 
00539         // generate a volume ID for the new volume we are about to
00540         // create on the device
00541         cout << "Generating new lvid: " << endl;
00542         W_DO(ssm->generate_new_lvid(_lvid));
00543         cout << "Generated lvid " << _lvid <<  endl;
00544 
00545         // create the new volume 
00546         cout << "Creating a new volume on the device" << endl;
00547         cout << "    with a " << _quota << "KB quota ..." << endl;
00548 
00549         W_DO(ssm->create_vol(_device_name, _lvid, _quota, false, _vid));
00550         cout << "    with local handle(phys volid) " << _vid << endl;
00551 
00552     } 
00553 
00554     W_DO(create_the_file());
00555     return RCOK;
00556 }
00557 
00558 rc_t
00559 smthread_driver_t::no_init()
00560 {
00561     cout << "Using already-existing device: " << _device_name << endl;
00562     // mount already existing device
00563     devid_t      devid;
00564     u_int        vol_cnt;
00565     w_rc_t rc = ssm->mount_dev(_device_name, vol_cnt, devid);
00566     if (rc.is_error()) {
00567         cerr << "Error: could not mount device: " 
00568             << _device_name << endl;
00569         cerr << "   Did you forget to run the server with -i?" 
00570             << endl;
00571         return rc;
00572     }
00573     
00574     // find ID of the volume on the device
00575     lvid_t* lvid_list;
00576     u_int   lvid_cnt;
00577     W_DO(ssm->list_volumes(_device_name, lvid_list, lvid_cnt));
00578     if (lvid_cnt == 0) {
00579         cerr << "Error, device has no volumes" << endl;
00580         exit(1);
00581     }
00582     _lvid = lvid_list[0];
00583     delete [] lvid_list;
00584 
00585     W_DO(find_file_info());
00586     W_DO(scan_the_root_index());
00587     W_DO(scan_the_file());
00588     return RCOK;
00589 }
00590 
00591 /* helper for run() */
00592 rc_t
00593 smthread_driver_t::do_work()
00594 {
00595     if (_initialize_device) W_DO(do_init());
00596     else  W_DO(no_init());
00597     statistics();
00598     return RCOK;
00599 }
00600 
00601 void 
00602 smthread_driver_t::start_ssm() 
00603 {
00604     // Now start a storage manager.
00605     cout << "Starting SSM and performing recovery ..." << endl;
00606     ssm = new ss_m();
00607 }
00608 
00609 /* Here's the driver */
00610 void smthread_driver_t::run()
00611 {
00612     /* deal with run-time options */
00613     w_rc_t rc = handle_options();
00614     if(rc.is_error()) {
00615         _retval = 1;
00616         return;
00617     }
00618 
00619     // Now start a storage manager.
00620     start_ssm();
00621     if (!ssm) {
00622         cerr << "Error: Out of memory for ss_m" << endl;
00623         _retval = 1;
00624         return;
00625     }
00626 
00627     /* get static (build-time) configuration info for the
00628      * storage manager. This is so that we can illustrate
00629      * use of the configuration info and also illustrate the
00630      * record ids. 
00631      * Record size is based on the
00632      * largest small record that will fit in a page.
00633      *
00634      * You can see that two will fit on a page
00635      * and if your number of records (run-time option) is
00636      * larger than two, the pages numbers will change as
00637      * we create more records.
00638      */
00639 
00640     sm_config_info_t config_info;
00641     rc = ss_m::config_info(config_info);
00642     if(rc.is_error()) {
00643         cerr << "Could not get storage manager configuration info: " << rc << endl; 
00644         _retval = 1;
00645         return;
00646     }
00647     _rec_size = config_info.max_small_rec; // largest record we can put on a page w/o
00648     // its becoming a large record.  Takes into account the record tag (internal header).
00649     _rec_size += config_info.small_rec_overhead; // record tag added back in.
00650     _rec_size /= 2; // number of records per page.
00651     _rec_size -= config_info.small_rec_overhead; // removed again.
00652 
00653     rc = do_work();
00654     if(rc.is_error()) {
00655         cerr << "Failure: " << rc << endl; 
00656         _retval = 1;
00657     }
00658 
00659     // Clean up and shut down
00660     cout << "\nShutting down SSM ..." << endl;
00661     delete ssm;
00662     cout << "Finished!" << endl;
00663 
00664     return;
00665 }
00666 
00667 // SUBSTITUTE_MAIN is defined when this file is #included in 
00668 // other example .cpp files.  For the create_rec example,
00669 // this is not defined, so the following  main() is in effect.
00670 #ifndef SUBSTITUTE_MAIN
00671 int
00672 main(int argc, char* argv[])
00673 {
00674     /* set argv0 for usage()  -- this is so that other examples
00675      * that use this code can function properly*/
00676     argv0 = argv[0];
00677 
00678     /* create a thread to do the work */
00679     smthread_driver_t *smtu = new smthread_driver_t(argc, argv);
00680     if (!smtu)
00681             W_FATAL(fcOUTOFMEMORY);
00682 
00683     /* cause the thread's run() method to start */
00684     w_rc_t e = smtu->fork();
00685     if(e.is_error()) {
00686         cerr << "Error forking thread: " << e <<endl;
00687         return 1;
00688     }
00689 
00690     /* wait for the thread's run() method to end */
00691     e = smtu->join();
00692     if(e.is_error()) {
00693         cerr << "Error joining thread: " << e <<endl;
00694         return 1;
00695     }
00696 
00697     /* get the return value */
00698     int        rv = smtu->return_value();
00699 
00700     /* clean up */
00701     delete smtu;
00702 
00703     return rv;
00704 }
00705 #endif
00706 

Generated on Thu Dec 9 08:42:26 2010 for Shore Storage Manager by  doxygen 1.4.7