1package MogileFS::FID; 2use strict; 3use warnings; 4use Carp qw(croak); 5use MogileFS::ReplicationRequest qw(rr_upgrade); 6use MogileFS::Server; 7use overload '""' => \&as_string; 8 9BEGIN { 10 my $testing = $ENV{TESTING} ? 1 : 0; 11 eval "sub TESTING () { $testing }"; 12} 13 14sub new { 15 my ($class, $fidid) = @_; 16 croak("Invalid fidid") unless $fidid; 17 return bless { 18 fidid => $fidid, 19 dmid => undef, 20 dkey => undef, 21 length => undef, 22 classid => undef, 23 devcount => undef, 24 _loaded => 0, 25 _devids => undef, # undef, or pre-loaded arrayref devid list 26 }, $class; 27} 28 29sub as_string { 30 my $self = shift; 31 "FID[f=$self->{fidid}]"; 32} 33 34# mutates/blesses given row. 35sub new_from_db_row { 36 my ($class, $row) = @_; 37 # TODO: sanity check provided row more? 38 $row->{fidid} = delete $row->{fid} or die "Missing 'fid' column"; 39 $row->{_loaded} = 1; 40 return bless $row, $class; 41} 42 43# quick port of old API. perhaps not ideal. 44sub new_from_dmid_and_key { 45 my ($class, $dmid, $key) = @_; 46 my $row = Mgd::get_store()->read_store->file_row_from_dmid_key($dmid, $key) 47 or return undef; 48 return $class->new_from_db_row($row); 49} 50 51# given a bunch of ::FID objects, populates their devids en-masse 52# (for the fsck worker, which doesn't want to do many database 53# round-trips) 54sub mass_load_devids { 55 my ($class, @fids) = @_; 56 my $sto = Mgd::get_store(); 57 my $locs = $sto->fid_devids_multiple(map { $_->id } @fids); 58 my @ret; 59 foreach my $fid (@fids) { 60 $fid->{_devids} = $locs->{$fid->id} || []; 61 } 62} 63# -------------------------------------------------------------------------- 64 65sub exists { 66 my $self = shift; 67 $self->_tryload; 68 return $self->{_loaded}; 69} 70 71sub classid { 72 my $self = shift; 73 $self->_load; 74 return $self->{classid}; 75} 76 77sub dmid { 78 my $self = shift; 79 $self->_load; 80 return $self->{dmid}; 81} 82 83sub length { 84 my $self = shift; 85 $self->_load; 86 return $self->{length}; 87} 88 89sub devcount { 90 my $self = shift; 91 $self->_load; 92 return $self->{devcount}; 93} 94 95sub id { $_[0]{fidid} } 96 97# force loading, or die. 98sub _load { 99 return 1 if $_[0]{_loaded}; 100 my $self = shift; 101 croak("FID\#$self->fidid} doesn't exist") unless $self->_tryload; 102} 103 104# return 1 if loaded, or 0 if not exist 105sub _tryload { 106 return 1 if $_[0]{_loaded}; 107 my $self = shift; 108 my $row = Mgd::get_store()->file_row_from_fidid($self->{fidid}) 109 or return 0; 110 $self->{$_} = $row->{$_} foreach qw(dmid dkey length classid devcount); 111 $self->{_loaded} = 1; 112 return 1; 113} 114 115sub update_devcount { 116 my ($self, %opts) = @_; 117 118 my $no_lock = delete $opts{no_lock}; 119 croak "Bogus options" if %opts; 120 121 return 1 if MogileFS::Config->server_setting_cached('skip_devcount'); 122 123 my $fidid = $self->{fidid}; 124 125 my $sto = Mgd::get_store(); 126 if ($no_lock) { 127 return $sto->update_devcount($fidid); 128 } else { 129 return $sto->update_devcount_atomic($fidid); 130 } 131} 132 133sub update_class { 134 my ($self, %opts) = @_; 135 136 my $classid = delete $opts{classid}; 137 croak "Bogus options" if %opts; 138 139 my $sto = Mgd::get_store(); 140 return $sto->update_classid($self->{fidid}, $classid); 141} 142 143sub enqueue_for_replication { 144 my ($self, %opts) = @_; 145 my $in = delete $opts{in}; 146 my $from_dev = delete $opts{from_device}; # devid or Device object 147 croak("Unknown options to enqueue_for_replication") if %opts; 148 my $from_devid = (ref $from_dev ? $from_dev->id : $from_dev) || undef; 149 # Still schedule for the future, but don't delay long 150 $in = 1 if (TESTING && $in); 151 Mgd::get_store()->enqueue_for_replication($self->id, $from_devid, $in); 152} 153 154sub delete { 155 my $fid = shift; 156 my $sto = Mgd::get_store(); 157 my $memc = MogileFS::Config->memcache_client; 158 if ($memc) { 159 $fid->_tryload; 160 } 161 $sto->delete_fidid($fid->id); 162 if ($memc && $fid->{_loaded}) { 163 $memc->delete("mogfid:$fid->{dmid}:$fid->{dkey}"); 164 } 165} 166 167# returns 1 on success, 0 on duplicate key error, dies on exception 168sub rename { 169 my ($fid, $to_key) = @_; 170 my $sto = Mgd::get_store(); 171 return $sto->rename_file($fid->id, $to_key); 172} 173 174# returns array of devids that this fid is on 175# NOTE: TODO: by default, this doesn't cache. callers might be surprised from 176# having an old version later on. before caching is added, auditing needs 177# to be done. 178sub devids { 179 my $self = shift; 180 181 # if it was mass-loaded and stored in _devids arrayref, use 182 # that instead of going to db... 183 return @{$self->{_devids}} if $self->{_devids}; 184 185 # else get it from the database 186 return Mgd::get_store()->read_store->fid_devids($self->id); 187} 188 189sub devs { 190 my $self = shift; 191 return map { Mgd::device_factory()->get_by_id($_) } $self->devids; 192} 193 194sub devfids { 195 my $self = shift; 196 return map { MogileFS::DevFID->new($_, $self) } $self->devids; 197} 198 199 200# return FID's class 201sub class { 202 my $self = shift; 203 return Mgd::class_factory()->get_by_id($self->dmid, $self->classid); 204} 205 206# Get reloaded the next time we're bothered. 207sub want_reload { 208 my $self = shift; 209 $self->{_loaded} = 0; 210} 211 212# returns bool: if fid's presumed-to-be-on devids meet the file class' 213# replication policy rules. dies on failure to load class, world 214# info, etc. 215sub devids_meet_policy { 216 my $self = shift; 217 my $cls = $self->class; 218 219 my $polobj = $cls->repl_policy_obj; 220 221 my $alldev = Mgd::device_factory()->map_by_id 222 or die "No global device map"; 223 224 my @devs = $self->devs; 225 226 my %rep_args = ( 227 fid => $self->id, 228 on_devs => [@devs], 229 all_devs => $alldev, 230 failed => {}, 231 min => $cls->mindevcount, 232 ); 233 my $rr = rr_upgrade($polobj->replicate_to(%rep_args)); 234 return $rr->is_happy && ! $rr->too_happy; 235} 236 237sub fsck_log { 238 my ($self, $code, $dev) = @_; 239 Mgd::get_store()->fsck_log( 240 code => $code, 241 fid => $self->id, 242 devid => ($dev ? $dev->id : undef), 243 ); 244 245} 246 247sub forget_cached_devids { 248 my $self = shift; 249 $self->{_devids} = undef; 250} 251 252# returns MogileFS::DevFID object, after noting in the db that this fid is on this DB. 253# it trusts you that it is, and that you've verified it. 254sub note_on_device { 255 my ($fid, $dev) = @_; 256 my $dfid = MogileFS::DevFID->new($dev, $fid); 257 $dfid->add_to_db; 258 $fid->forget_cached_devids; 259 return $dfid; 260} 261 262sub forget_about_device { 263 my ($fid, $dev) = @_; 264 $dev->forget_about($fid); 265 $fid->forget_cached_devids; 266 return 1; 267} 268 269# return an FID's checksum object, undef if it's missing 270sub checksum { 271 my $self = shift; 272 my $row = Mgd::get_store()->get_checksum($self->{fidid}) or return undef; 273 274 MogileFS::Checksum->new($row); 275} 276 2771; 278 279__END__ 280 281=head1 NAME 282 283MogileFS::FID - represents a unique, immutable version of a file 284 285=head1 ABOUT 286 287This class represents a "fid", or "file id", which is a unique 288revision of a file. If you upload a file with the same key 289("filename") a dozen times, each one has a unique "fid". Fids are 290immutable, and are what are replicated around the MogileFS farm. 291