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