add UploadCPAN back to .shipit
[MogileFS-Server.git] / lib / MogileFS / FID.pm
blobfb58b2c76e5b1d1f05918c559a6e5ca4fc62b7af
1 package MogileFS::FID;
2 use strict;
3 use warnings;
4 use Carp qw(croak);
5 use MogileFS::ReplicationRequest qw(rr_upgrade);
6 use overload '""' => \&as_string;
8 sub new {
9 my ($class, $fidid) = @_;
10 croak("Invalid fidid") unless $fidid;
11 return bless {
12 fidid => $fidid,
13 dmid => undef,
14 dkey => undef,
15 length => undef,
16 classid => undef,
17 devcount => undef,
18 _loaded => 0,
19 _devids => undef, # undef, or pre-loaded arrayref devid list
20 }, $class;
23 sub as_string {
24 my $self = shift;
25 "FID[f=$self->{fidid}]";
28 # mutates/blesses given row.
29 sub new_from_db_row {
30 my ($class, $row) = @_;
31 # TODO: sanity check provided row more?
32 $row->{fidid} = delete $row->{fid} or die "Missing 'fid' column";
33 $row->{_loaded} = 1;
34 return bless $row, $class;
37 # quick port of old API. perhaps not ideal.
38 sub new_from_dmid_and_key {
39 my ($class, $dmid, $key) = @_;
40 my $row = Mgd::get_store()->read_store->file_row_from_dmid_key($dmid, $key)
41 or return undef;
42 return $class->new_from_db_row($row);
45 # given a bunch of ::FID objects, populates their devids en-masse
46 # (for the fsck worker, which doesn't want to do many database
47 # round-trips)
48 sub mass_load_devids {
49 my ($class, @fids) = @_;
50 my $sto = Mgd::get_store();
51 my $locs = $sto->fid_devids_multiple(map { $_->id } @fids);
52 my @ret;
53 foreach my $fid (@fids) {
54 $fid->{_devids} = $locs->{$fid->id} || [];
57 # --------------------------------------------------------------------------
59 sub exists {
60 my $self = shift;
61 $self->_tryload;
62 return $self->{_loaded};
65 sub classid {
66 my $self = shift;
67 $self->_load;
68 return $self->{classid};
71 sub dmid {
72 my $self = shift;
73 $self->_load;
74 return $self->{dmid};
77 sub length {
78 my $self = shift;
79 $self->_load;
80 return $self->{length};
83 sub devcount {
84 my $self = shift;
85 $self->_load;
86 return $self->{devcount};
89 sub id { $_[0]{fidid} }
91 # force loading, or die.
92 sub _load {
93 return 1 if $_[0]{_loaded};
94 my $self = shift;
95 croak("FID\#$self->fidid} doesn't exist") unless $self->_tryload;
98 # return 1 if loaded, or 0 if not exist
99 sub _tryload {
100 return 1 if $_[0]{_loaded};
101 my $self = shift;
102 my $row = Mgd::get_store()->file_row_from_fidid($self->{fidid})
103 or return 0;
104 $self->{$_} = $row->{$_} foreach qw(dmid dkey length classid devcount);
105 $self->{_loaded} = 1;
106 return 1;
109 sub update_devcount {
110 my ($self, %opts) = @_;
112 my $no_lock = delete $opts{no_lock};
113 croak "Bogus options" if %opts;
115 my $fidid = $self->{fidid};
117 my $sto = Mgd::get_store();
118 if ($no_lock) {
119 return $sto->update_devcount($fidid);
120 } else {
121 return $sto->update_devcount_atomic($fidid);
125 sub update_class {
126 my ($self, %opts) = @_;
128 my $classid = delete $opts{classid};
129 croak "Bogus options" if %opts;
131 my $sto = Mgd::get_store();
132 return $sto->update_classid($self->{fidid}, $classid);
135 sub enqueue_for_replication {
136 my ($self, %opts) = @_;
137 my $in = delete $opts{in};
138 my $from_dev = delete $opts{from_device}; # devid or Device object
139 croak("Unknown options to enqueue_for_replication") if %opts;
140 my $from_devid = (ref $from_dev ? $from_dev->id : $from_dev) || undef;
141 Mgd::get_store()->enqueue_for_replication($self->id, $from_devid, $in);
144 sub mark_unreachable {
145 my $self = shift;
146 # update database table
147 Mgd::get_store()->mark_fidid_unreachable($self->id);
150 sub delete {
151 my $fid = shift;
152 my $sto = Mgd::get_store();
153 $sto->delete_fidid($fid->id);
156 # returns 1 on success, 0 on duplicate key error, dies on exception
157 sub rename {
158 my ($fid, $to_key) = @_;
159 my $sto = Mgd::get_store();
160 return $sto->rename_file($fid->id, $to_key);
163 # returns array of devids that this fid is on
164 # NOTE: TODO: by default, this doesn't cache. callers might be surprised from
165 # having an old version later on. before caching is added, auditing needs
166 # to be done.
167 sub devids {
168 my $self = shift;
170 # if it was mass-loaded and stored in _devids arrayref, use
171 # that instead of going to db...
172 return @{$self->{_devids}} if $self->{_devids};
174 # else get it from the database
175 return Mgd::get_store()->read_store->fid_devids($self->id);
178 sub devs {
179 my $self = shift;
180 return map { MogileFS::Device->of_devid($_) } $self->devids;
183 sub devfids {
184 my $self = shift;
185 return map { MogileFS::DevFID->new($_, $self) } $self->devids;
189 # return FID's class
190 sub class {
191 my $self = shift;
192 return MogileFS::Class->of_fid($self);
195 # Get reloaded the next time we're bothered.
196 sub want_reload {
197 my $self = shift;
198 $self->{_loaded} = 0;
201 # returns bool: if fid's presumed-to-be-on devids meet the file class'
202 # replication policy rules. dies on failure to load class, world
203 # info, etc.
204 sub devids_meet_policy {
205 my $self = shift;
206 my $cls = $self->class;
208 my $polobj = $cls->repl_policy_obj;
210 my $alldev = MogileFS::Device->map
211 or die "No global device map";
213 my @devs = $self->devs;
214 # This is a little heavy handed just to fix the 'devcount' cache, but
215 # doing it here ensures we get the error logged.
216 if (@devs != $self->devcount) {
217 return 0;
220 my %rep_args = (
221 fid => $self->id,
222 on_devs => [@devs],
223 all_devs => $alldev,
224 failed => {},
225 min => $cls->mindevcount,
227 my $rr = rr_upgrade($polobj->replicate_to(%rep_args));
228 return $rr->is_happy && ! $rr->too_happy;
231 sub fsck_log {
232 my ($self, $code, $dev) = @_;
233 Mgd::get_store()->fsck_log(
234 code => $code,
235 fid => $self->id,
236 devid => ($dev ? $dev->id : undef),
241 sub forget_cached_devids {
242 my $self = shift;
243 $self->{_devids} = undef;
246 # returns MogileFS::DevFID object, after noting in the db that this fid is on this DB.
247 # it trusts you that it is, and that you've verified it.
248 sub note_on_device {
249 my ($fid, $dev) = @_;
250 my $dfid = MogileFS::DevFID->new($dev, $fid);
251 $dfid->add_to_db;
252 $fid->forget_cached_devids;
253 return $dfid;
256 sub forget_about_device {
257 my ($fid, $dev) = @_;
258 $dev->forget_about($fid);
259 $fid->forget_cached_devids;
260 return 1;
265 __END__
267 =head1 NAME
269 MogileFS::FID - represents a unique, immutable version of a file
271 =head1 ABOUT
273 This class represents a "fid", or "file id", which is a unique
274 revision of a file. If you upload a file with the same key
275 ("filename") a dozen times, each one has a unique "fid". Fids are
276 immutable, and are what are replicated around the MogileFS farm.