IPP Software Navigation Tools IPP Links Communication Pan-STARRS Links

Changeset 26292


Ignore:
Timestamp:
Nov 30, 2009, 1:54:49 PM (16 years ago)
Author:
watersc1
Message:

Nebulous: Implemented a number of bug fixes and modifications to

ensure no loss of data.

  • Each volume can now be associated into a cabinet, defining their physical arrangement.
  • File creation: new files without a specified volume are created on a random volume from a subset of the N least full.
  • Replication: file replication uses the same volume selection method, with the constraints that a copy not be automatically placed on the same volume as another, and that the first copy not be housed within the same volume.
  • Cull: instead of removing the first available instance, first attempt to remove an instance on the same volume as another, then attempt to remove an instance in the same cabinet as another, and then remove the first only if those fail.
  • Corrected bug that prevented the deletion of an object if it had more than one unavailable instance.
  • Fixed nebdiskd to synchronize between volume->mountedvol tables and to properly remove volumes that were no longer accessible.
Location:
trunk
Files:
1 added
7 edited

Legend:

Unmodified
Added
Removed
  • trunk/Nebulous-Server/Build.PL

    r24488 r26292  
    4646        bin/neb-fsck
    4747        bin/neb-initdb
     48        bin/neb-cabadd
    4849        bin/neb-voladd
    4950        bin/neb-voladm
  • trunk/Nebulous-Server/MANIFEST

    r24915 r26292  
    88bin/neb-initdb
    99bin/neb-voladd
     10bin/neb-cabadd
     11bin/neb-voladm
    1012bin/nebdiskd
    1113docs/database_setup.txt
  • trunk/Nebulous-Server/bin/neb-voladm

    r24842 r26292  
    2323    $allocate,
    2424    $available,
     25    $mounted,
    2526    $db,
    2627    $dbhost,
     
    3132    $vname,
    3233    $xattr,
     34    $cab_id,
    3335);
    3436
     
    4143    'allocate=i'        => \$allocate,
    4244    'available=i'       => \$available,
     45    'mounted'           => \$mounted,
    4346    'db|d=s'            => \$db,
    4447    'debug'             => \$debug,
     
    4851    'vhost=s'           => \$vhost,
    4952    'vname|n=s'         => \$vname,
     53    'cab_id|c=i'        => \$cab_id,
    5054    'xattr=i'           => \$xattr,
    5155) || pod2usage( 2 );
     
    5660
    5761# are we listing the volumes or updating?
    58 if (defined $allocate or defined $available or defined $xattr) {
    59     pod2usage( -msg => "Options: --allocate, --available, and --xattr Require options --vhost or --vname", -exitval => 2 )
     62if (defined $allocate or defined $available or defined $xattr or defined $cab_id) {
     63    pod2usage( -msg => "Options: --allocate, --available, --xattr, and --cab_id Require options --vhost or --vname", -exitval => 2 )
    6064        unless defined $vhost or defined $vname;
    6165    pod2usage( -msg => "Options: --allocate, --available, and --xattr Must be 0 or 1", -exitval => 2)
     
    8488$set{'v.available'} = $available if defined $available;
    8589$set{'v.xattr'} = $xattr if defined $xattr;
     90$set{'v.cab_id'} = $cab_id if defined $cab_id;
    8691
    8792if (%set) {
     
    97102        die $@;
    98103    }
     104    if ($mounted) {
     105        eval {
     106            my ($q, @bind) = sql_interp("UPDATE mountedvol AS v SET", \%set, "WHERE", \%constraint);
     107            warn "$q\n" if $debug;
     108            my $query = $dbh->prepare($q);
     109            $query->execute(@bind);
     110            $dbh->commit;
     111        };
     112        if ($@) {
     113            $dbh->rollback;
     114            die $@;
     115        }
     116    }
    99117}
    100118
     
    132150    neb-voladm [--vname <volume name>] [--vhost <volume host>]
    133151    [--allocate <0|1>] [--available <0|1>] [--xattr <0|1>]
     152    [--cab_id <cab_id>]
    134153    [--host <database host> ] [--db <database name>]
    135154    [--user <database username>] [--pass <database password>]
     
    164183
    165184Enables/Disables these flags on the storage volume.
     185
     186Requires either the C<--vname> or c<--vhost> options.
     187
     188=item * --cab_id <cab_id>
     189
     190Sets the cabinet id for this storage volume.
    166191
    167192Requires either the C<--vname> or c<--vhost> options.
  • trunk/Nebulous-Server/bin/nebdiskd

    r24617 r26292  
    177177
    178178    eval {
    179         my $r_query = $dbh->prepare_cached("REPLACE INTO mountedvol SELECT *, ?, ? FROM volume WHERE mountpoint = ?");
     179        my $r_query = $dbh->prepare_cached("REPLACE INTO mountedvol SELECT vol_id,name,host,path,allocate,available,xattr,mountpoint, ?, ? FROM volume WHERE mountpoint = ?");
     180#       my $d_query = $dbh->prepare_cached("UPDATE mountedvol SET allocate = ?, available = ? WHERE mountpoint = ?");
    180181        my $d_query = $dbh->prepare_cached("DELETE FROM mountedvol WHERE mountpoint = ?");
    181182
     
    185186            # there may be multiple vol_ids per mountpoint but we only need to
    186187            # check each mointpont once
    187             my $query = $dbh->prepare_cached("SELECT DISTINCT mountpoint FROM volume WHERE available = 1");
     188            my $query = $dbh->prepare_cached("SELECT DISTINCT mountpoint FROM volume");
    188189            $query->execute;
    189190            while (my $row = $query->fetchrow_hashref) {
     
    200201            # automounter and it fails to mount
    201202            my $tries = 0;
     203            my $valid_mountpoint = 1;
    202204            TEST: eval {
    203205                $tries++;
    204206                unless (is_mountpoint($mountpoint)) {
    205                     $log->warn("$mnt is not a valid mountpoint");
     207                    $log->warn("$mountpoint is not a valid mountpoint ($tries)");
     208                    $valid_mountpoint = 0;
    206209                }
    207210            };
    208             if ($@) {
     211            if (!$valid_mountpoint) {
    209212                # try is_mountpoint() again if $retry > 1
    210213                if ($tries < $retry) {
     
    212215                    goto TEST;
    213216                }
    214                 $log->warn($@);
     217                $log->warn("Removing $mountpoint from the mountedvol table ($tries > $retry)");
     218 #               $log->warn($@);
     219
    215220                $d_query->execute($mountpoint);
    216221                next;
  • trunk/Nebulous-Server/lib/Nebulous/Server.pm

    r24995 r26292  
    3535use constant NFS_RETRY_WAIT     => 1;
    3636use constant TRANS_RETRY_WAIT   => 1;
     37
     38# This is the umask hack.
     39umask(0002);
     40
     41# This determines how many entries from the list of volumes sorted by free space are randomized.
     42my $topfew_count = 3;
    3743
    3844# transaction restart/retry regex
     
    905911            }
    906912
    907             # In MySQL you can't select from a table your deleting rows from so
     913            # In MySQL you can't select from a table you're deleting rows from so
    908914            # we first have to get a list of instances to be removed, and then
    909             # removed them.
     915            # remove them.
    910916            my $rows_found;
    911917            {
    912918                my $query = $db->prepare_cached( $sql->find_dead_instances_by_so_id );
    913919                $rows_found = $query->execute( $so_id );
    914                 foreach my $row ($query->fetchrow_hashref) {
     920                my $i = 0;
     921                while (my $row = $query->fetchrow_hashref) {
    915922                    # remove dead instances
     923#                   $log->warn("copied: $rows_copied found: $rows_found removed: $rows_removed");
    916924                    my $query = $db->prepare_cached( $sql->delete_instance_by_ins_id);
    917925                    $rows_removed += $query->execute( $row->{ins_id} );
     
    919927                $query->finish;
    920928            }
    921 
     929#           $log->warn("copied: $rows_copied found: $rows_found removed: $rows_removed");
    922930            # sanity check
    923931            die("instances inaccessible ($rows_copied) != instances removed ($rows_removed)")
     
    16921700}
    16931701
     1702sub find_instances_for_cull
     1703{
     1704    my $self = shift;
     1705
     1706    my $log = $self->log;
     1707    $log->debug("entered - @_");
     1708
     1709    my ($key, $vol_name) = validate_pos(@_,
     1710        {
     1711            type        => SCALAR,
     1712            callbacks   => {
     1713                'is valid object key' => sub { $self->_is_valid_object_key($_[0]) },
     1714            },
     1715        },
     1716        {
     1717            type        => SCALAR|UNDEF,
     1718#            callbacks   => {
     1719#                # check that the volume name requested is valid
     1720#                'is valid volume name' => sub {
     1721#                    return 1 if not defined $_[0];
     1722#                    $self->_is_valid_volume_name($_[0])
     1723#                },
     1724#            },
     1725            optional    => 1,
     1726        },
     1727    );
     1728
     1729    my $sql = $self->sql;
     1730
     1731#    unless ($key) {
     1732#        $log->warn("key was undefined after validate_pos(), trying again...");
     1733#        return $self->find_instances(@_);
     1734#    }
     1735
     1736    # vol_name overrides the key implied volume
     1737    eval {
     1738        $key = parse_neb_key($key, $vol_name);
     1739    };
     1740    $log->logdie("$@") if $@;
     1741    $vol_name = $key->volume;
     1742
     1743    my $db  = $self->db($key);
     1744
     1745    # the key's volume can't be validiated on input for this method so we have
     1746    # to check it after parsing the key
     1747    if (defined $vol_name
     1748        and not $self->_is_valid_volume_name($key, $key->volume)) {
     1749        if ($key->hard_volume) {
     1750            $log->logdie("$vol_name is not a valid volume name");
     1751        } else {
     1752            $log->warn( "$vol_name is not a known volume name" );
     1753            $vol_name = undef;
     1754        }
     1755    }
     1756
     1757    my @locations;
     1758    eval {
     1759        my $query;
     1760        if ($vol_name) {
     1761            $query = $db->prepare_cached( $sql->get_object_instances_by_vol_name );
     1762            # ext_id, name, available
     1763            my $rows = $query->execute($key->path, $vol_name, 1);
     1764            unless ($rows > 0) {
     1765                $query->finish;
     1766                die("no instances on storage volume or volume is not avaiable for key: $key volume: $vol_name");
     1767            }
     1768        } else {
     1769            $query = $db->prepare_cached( $sql->get_object_instances );
     1770            # ext_id, available
     1771            my $rows = $query->execute($key->path, 1);
     1772            unless ($rows > 0) {
     1773                $query->finish;
     1774                die("no instances available for key: $key");
     1775            }
     1776        }
     1777       
     1778        while (my $row = $query->fetchrow_hashref) {
     1779#           my $instance_hash  = { uri => $row->{ 'uri' },
     1780#                                  vol_id => $row->{ 'vol_id' },
     1781#                                  cab_id => $row->{ 'cab_id'} };
     1782            my $instance = $row->{ 'uri' };
     1783            push @locations, $row if $instance;
     1784        }
     1785    };
     1786    if ($@) {
     1787        $db->rollback;
     1788        # handle soft volumes
     1789        if (defined $vol_name and not defined $key->hard_volume) {
     1790            $log->debug("retrying with 'any' volume");
     1791            return $self->find_instances($key->path, 'any');
     1792        }
     1793        $log->logdie("database error: $@");
     1794    }
     1795
     1796    # XXX remove this?
     1797    $log->logdie("no instances found") unless (scalar @locations);
     1798
     1799    $log->debug("found: @locations");
     1800
     1801    $log->debug("leaving");
     1802
     1803    return \@locations;
     1804}
     1805
    16941806
    16951807sub delete_instance
     
    19842096
    19852097    my ($key, $name, $hard_volume) = @_;
    1986 
     2098   
     2099#    $log->warn("_g_s_v: key:>$key< name:>$name< hard_vol:>$hard_volume<");
    19872100    my $sql = $self->sql;
    19882101    my $db  = $self->db($key);
     
    19962109            # %free, name, avaiable, allocate
    19972110            $rows = $query->execute(0.95, $name, 1, 1);
    1998             # XXX destinguish between non-existant and unaviable
     2111            # XXX destinguish between non-existant and unavailable
    19992112            unless ($rows > 0) {
    20002113                $query->finish;
     
    20162129            $query = $db->prepare_cached( $sql->get_storage_volume );
    20172130            # %free, avaiable, allocate
    2018             $rows = $query->execute(0.95, 1, 1);
     2131            $rows = $query->execute(0.95, 1, 1, $topfew_count);
    20192132            # there has to be atleast one storage volume
    20202133            unless ($rows > 0) {
     
    20622175    my $db  = $self->db($key);
    20632176
    2064     my ($vol_id, $vol_host, $vol_path, $xattr);
     2177    my ($vol_id, $vol_host, $vol_path, $xattr, $forbidden_cabinet);
    20652178    eval {
    20662179        my $rows;
    2067         my $query = $db->prepare_cached( $sql->get_replication_volume_for_ext_id );
     2180       
     2181        my $query = $db->prepare_cached( $sql->get_cabinets_for_ext_id );
     2182        $rows = $query->execute($key->path);
     2183        unless ($rows > 0) {
     2184            $query->finish;
     2185            die("Requested key $key does not exist");
     2186        }
     2187        if ($rows == 1) {
     2188            ($forbidden_cabinet) = $query->fetchrow_array;
     2189            unless (defined($forbidden_cabinet)) {
     2190                $forbidden_cabinet = 0;
     2191            }
     2192            $query->finish;
     2193        }
     2194        else {
     2195            $forbidden_cabinet = 0;
     2196            $query->finish;
     2197        }
     2198           
     2199
     2200        $query = $db->prepare_cached( $sql->get_replication_volume_for_ext_id );
    20682201        # ext_id, %free, avaiable, allocate
    2069         $rows = $query->execute($key->path, 0.95, 1, 1);
     2202        $rows = $query->execute($key->path, 0.95, 1, 1, $forbidden_cabinet, $topfew_count);
    20702203        # XXX destinguish between non-existant and unaviable
    20712204        unless ($rows > 0) {
     
    21752308
    21762309    # handle "any" volume
    2177     if ($vol_name eq 'any') {
     2310    if (($vol_name eq 'any')||($vol_name eq 'any.0')) {
    21782311        $log->debug( "found volume name $vol_name" );
    21792312        $log->debug( "leaving" );
     
    22002333    $log->debug( "leaving" );
    22012334
    2202     return;
     2335    return 0;
    22032336}
    22042337
  • trunk/Nebulous-Server/lib/Nebulous/Server/SQL.pm

    r24915 r26292  
    237237            storage_object.so_id,
    238238            uri,
    239             available
     239            mountedvol.available,
     240            vol_id,
     241            cab_id
    240242        FROM storage_object
    241243        JOIN instance
     
    243245        JOIN mountedvol
    244246            USING(vol_id)
    245         WHERE ext_id = ?
    246             AND available = ?
     247        JOIN volume
     248            USING(vol_id)
     249        WHERE ext_id = ?
     250            AND mountedvol.available = ?
    247251    },
    248252    get_object_instances_by_vol_name => qq{
    249253        SELECT
    250254            storage_object.so_id,
    251             uri
     255            uri,
     256            vol_id,
     257            cab_id
    252258        FROM storage_object
    253259        JOIN instance
     
    255261        JOIN mountedvol
    256262            USING(vol_id)
    257         WHERE ext_id = ?
    258             AND name = ?
    259             AND available = ?
    260     },
     263        JOIN volume
     264            USING(vol_id)
     265        WHERE ext_id = ?
     266            AND mountedvol.name = ?
     267            AND mountedvol.available = ?
     268    },
     269    # volume handler
    261270    get_storage_volume_by_name   => qq{
    262271        SELECT
     
    275284        LIMIT 1
    276285    },
    277     get_replication_volume_for_ext_id   => qq{
     286    # volume handler
     287    get_cabinets_for_ext_id            => qq{
     288        SELECT DISTINCT
     289            cab_id
     290        FROM instance
     291        JOIN volume ON instance.vol_id = volume.vol_id
     292        JOIN storage_object USING(so_id)
     293        WHERE ext_id = ?
     294    },
     295    get_replication_volume_for_ext_id    => qq{
     296        SELECT * FROM (
    278297        SELECT
    279298            m.vol_id,
    280             host,
    281             path,
    282             xattr,
     299            m.host,
     300            m.path,
     301            m.xattr,
    283302            total - used as free
    284303        FROM mountedvol AS m
    285         LEFT JOIN instance AS i
     304        JOIN volume AS v USING(vol_id)
     305        LEFT JOIN (
     306                   SELECT
     307                       instance.vol_id,
     308                       so_id
     309                   FROM instance
     310                   JOIN volume
     311                   ON instance.vol_id = volume.vol_id
     312                  ) AS i
    286313            ON m.vol_id = i.vol_id
    287314            AND i.so_id = (
     
    290317                WHERE ext_id = ?
    291318            )
    292         WHERE
    293             i.vol_id IS NULL
    294             AND used / total < ?
    295             AND available = ?
    296             AND allocate = ?
    297             ORDER BY RAND()
    298             LIMIT 1
    299     },
     319         WHERE
     320             i.vol_id IS NULL
     321             AND used / total < ?
     322             AND m.available = ?
     323             AND m.allocate = ?
     324             AND ( (v.cab_id IS NULL) ||
     325                   (v.cab_id != ?) )
     326         ORDER BY free DESC
     327         LIMIT ?) as topfew
     328         ORDER BY RAND()
     329         LIMIT 1
     330    },
     331    # volume handler
    300332    get_storage_volume          => qq{
     333        SELECT * from (
    301334        SELECT
    302335            vol_id,
     
    311344            AND allocate = ?
    312345        ORDER BY free DESC
     346        LIMIT ?) as topfew
     347        ORDER BY RAND()
    313348        LIMIT 1
    314349    },
     350    new_cabinet         => qq{
     351        INSERT INTO cabinet (name, location, cab_id)
     352        VALUES (?, ?, NULL)
     353    },
    315354    new_volume          => qq{
    316         INSERT INTO volume (name, host, path, allocate, available, xattr, mountpoint)
    317         VALUES (?, ?, ?, TRUE, TRUE, FALSE, ?)
     355        INSERT INTO volume (name, host, path, allocate, available, xattr, mountpoint, cab_id)
     356        VALUES (?, ?, ?, TRUE, TRUE, FALSE, ?, NULL)
    318357    },
    319358    get_volume_by_name => qq{
     
    331370            v.available,
    332371            v.xattr,
    333             mountedvol.vol_id IS NOT NULL as mounted
     372            mountedvol.vol_id IS NOT NULL as mounted,
     373            v.cab_id
    334374        FROM volume AS v
    335375        LEFT JOIN mountedvol
     
    614654###
    615655
     656CREATE TABLE cabinet (
     657    cab_id INT NOT NULL AUTO_INCREMENT,
     658    name VARCHAR(255) NOT NULL,
     659    location VARCHAR(255),
     660    PRIMARY KEY(cab_id),
     661    UNIQUE KEY(name),
     662    KEY (location)
     663) ENGINE=innodb DEFAULT CHARSET=latin1;
     664
     665###
     666
    616667CREATE TABLE volume (
    617668    vol_id INT NOT NULL AUTO_INCREMENT,
     
    623674    xattr BOOLEAN DEFAULT FALSE,
    624675    mountpoint VARCHAR(255) NOT NULL,
     676    cab_id INT,
    625677    PRIMARY KEY(vol_id),
    626678    UNIQUE KEY(name),
     
    629681    KEY(allocate),
    630682    KEY(available),
     683    FOREIGN KEY(cab_id) REFERENCES cabinet(cab_id),
    631684    KEY(mountpoint(255))
    632685) ENGINE=innodb DEFAULT CHARSET=latin1;
  • trunk/Nebulous/lib/Nebulous/Client.pm

    r25121 r26292  
    315315    my $locations;
    316316    if (defined $vol_name) {
    317         $locations = $self->find_instances($key, $vol_name);
     317        $locations = $self->find_instances_for_cull($key, $vol_name);
    318318    } else {
    319         $locations = $self->find_instances($key);
     319        $locations = $self->find_instances_for_cull($key);
    320320    }
    321321
     
    326326        return;
    327327    }
    328 
    329     my $uri = $self->delete_instance($key, @$locations[0]);
     328   
     329    # Calculate which is the best instance to remove. First, see if we have two copies
     330    # on one volume, if so, delete the first of those copies.
     331    my $delete_index = -1;
     332
     333    for (my $i = 0 ; $i <= $#{ $locations }; $i++) {
     334        for (my $j = $i + 1; $j <= $#{ $locations }; $j++) {
     335            if (@$locations[$i]->{vol_id} eq @$locations[$j]->{vol_id}) {
     336                $delete_index = $i;
     337            }
     338        }
     339    }
     340    # If we don't have two copies on one volume, see if we have two copies on a single
     341    # cabinet. If so, delete the first of those copies.
     342    if ($delete_index == -1) {
     343        for (my $i = 0 ; $i <= $#{ $locations }; $i++) {
     344            for (my $j = $i + 1; $j <= $#{ $locations }; $j++) {
     345                if (@$locations[$i]->{cab_id} eq @$locations[$j]->{cab_id}) {
     346                    $delete_index = $i;
     347                }
     348            }
     349        }
     350    }
     351    # Fail-safe. We didn't have any duplicates (the instances are "well-mixed"), so
     352    # delete the first.
     353    if ($delete_index == -1) {
     354        $delete_index = 0;
     355    }   
     356    my $uri = $self->delete_instance($key, @$locations[$delete_index]->{uri});
    330357
    331358    eval {
    332         _nuke_file(_get_file_path(@$locations[0]));
     359        _nuke_file(_get_file_path(@$locations[$delete_index]->{uri}));
    333360    };
    334361    if ($@) {
     
    758785}
    759786
     787sub find_instances_for_cull
     788{
     789    my $self = shift;
     790
     791    my ( $key, @params ) = validate_pos( @_,
     792        {
     793            type        => SCALAR,
     794        },
     795        {
     796            #volume
     797            type        => SCALAR|UNDEF,
     798            optional    => 1,
     799        },
     800    );
     801
     802    $log->debug( "entered - @_" );
     803
     804    my $response = $self->{ 'server' }->find_instances_for_cull( $key, @params );
     805    if ( $response->fault ) {
     806        $self->set_err($response->faultstring);
     807        # check to see if this failure is because $key doesn't exist
     808        if ($response->faultstring =~ /is valid object key/) {
     809            $log->debug( "leaving" );
     810            return;
     811        }
     812        # key is valid but no instances are on the specified volume
     813        if ($response->faultstring =~ /no instances on storage volume/) {
     814            $log->debug( "leaving" );
     815            return;
     816        }
     817
     818        $log->logdie("unhandled fault - ", $self->err);
     819    }
     820
     821    my $uris = $response->result;
     822
     823    $log->debug( "server found: @$uris" );
     824
     825    $log->debug( "leaving" );
     826
     827    return $uris;
     828}
     829
    760830
    761831sub find
Note: See TracChangeset for help on using the changeset viewer.