mogstored: fix kqueue usage with daemonization
[MogileFS-Server.git] / lib / MogileFS / FID.pm
blob0e5337b70effcedda590b7249e5fe3259265c3a8
1 package MogileFS::FID;
2 use strict;
3 use warnings;
4 use Carp qw(croak);
5 use MogileFS::ReplicationRequest qw(rr_upgrade);
6 use MogileFS::Server;
7 use overload '""' => \&as_string;
9 BEGIN {
10 my $testing = $ENV{TESTING} ? 1 : 0;
11 eval "sub TESTING () { $testing }";
14 sub 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;
29 sub as_string {
30 my $self = shift;
31 "FID[f=$self->{fidid}]";
34 # mutates/blesses given row.
35 sub 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;
43 # quick port of old API. perhaps not ideal.
44 sub 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);
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)
54 sub 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} || [];
63 # --------------------------------------------------------------------------
65 sub exists {
66 my $self = shift;
67 $self->_tryload;
68 return $self->{_loaded};
71 sub classid {
72 my $self = shift;
73 $self->_load;
74 return $self->{classid};
77 sub dmid {
78 my $self = shift;
79 $self->_load;
80 return $self->{dmid};
83 sub length {
84 my $self = shift;
85 $self->_load;
86 return $self->{length};
89 sub devcount {
90 my $self = shift;
91 $self->_load;
92 return $self->{devcount};
95 sub id { $_[0]{fidid} }
97 # force loading, or die.
98 sub _load {
99 return 1 if $_[0]{_loaded};
100 my $self = shift;
101 croak("FID\#$self->fidid} doesn't exist") unless $self->_tryload;
104 # return 1 if loaded, or 0 if not exist
105 sub _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;
115 sub update_devcount {
116 my ($self, %opts) = @_;
118 my $no_lock = delete $opts{no_lock};
119 croak "Bogus options" if %opts;
121 return 1 if MogileFS::Config->server_setting_cached('skip_devcount');
123 my $fidid = $self->{fidid};
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);
133 sub update_class {
134 my ($self, %opts) = @_;
136 my $classid = delete $opts{classid};
137 croak "Bogus options" if %opts;
139 my $sto = Mgd::get_store();
140 return $sto->update_classid($self->{fidid}, $classid);
143 sub 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);
154 sub mark_unreachable {
155 my $self = shift;
156 # update database table
157 Mgd::get_store()->mark_fidid_unreachable($self->id);
160 sub delete {
161 my $fid = shift;
162 my $sto = Mgd::get_store();
163 my $memc = MogileFS::Config->memcache_client;
164 if ($memc) {
165 $fid->_tryload;
167 $sto->delete_fidid($fid->id);
168 if ($memc && $fid->{_loaded}) {
169 $memc->delete("mogfid:$fid->{dmid}:$fid->{dkey}");
173 # returns 1 on success, 0 on duplicate key error, dies on exception
174 sub rename {
175 my ($fid, $to_key) = @_;
176 my $sto = Mgd::get_store();
177 return $sto->rename_file($fid->id, $to_key);
180 # returns array of devids that this fid is on
181 # NOTE: TODO: by default, this doesn't cache. callers might be surprised from
182 # having an old version later on. before caching is added, auditing needs
183 # to be done.
184 sub devids {
185 my $self = shift;
187 # if it was mass-loaded and stored in _devids arrayref, use
188 # that instead of going to db...
189 return @{$self->{_devids}} if $self->{_devids};
191 # else get it from the database
192 return Mgd::get_store()->read_store->fid_devids($self->id);
195 sub devs {
196 my $self = shift;
197 return map { Mgd::device_factory()->get_by_id($_) } $self->devids;
200 sub devfids {
201 my $self = shift;
202 return map { MogileFS::DevFID->new($_, $self) } $self->devids;
206 # return FID's class
207 sub class {
208 my $self = shift;
209 return Mgd::class_factory()->get_by_id($self->dmid, $self->classid);
212 # Get reloaded the next time we're bothered.
213 sub want_reload {
214 my $self = shift;
215 $self->{_loaded} = 0;
218 # returns bool: if fid's presumed-to-be-on devids meet the file class'
219 # replication policy rules. dies on failure to load class, world
220 # info, etc.
221 sub devids_meet_policy {
222 my $self = shift;
223 my $cls = $self->class;
225 my $polobj = $cls->repl_policy_obj;
227 my $alldev = Mgd::device_factory()->map_by_id
228 or die "No global device map";
230 my @devs = $self->devs;
232 my %rep_args = (
233 fid => $self->id,
234 on_devs => [@devs],
235 all_devs => $alldev,
236 failed => {},
237 min => $cls->mindevcount,
239 my $rr = rr_upgrade($polobj->replicate_to(%rep_args));
240 return $rr->is_happy && ! $rr->too_happy;
243 sub fsck_log {
244 my ($self, $code, $dev) = @_;
245 Mgd::get_store()->fsck_log(
246 code => $code,
247 fid => $self->id,
248 devid => ($dev ? $dev->id : undef),
253 sub forget_cached_devids {
254 my $self = shift;
255 $self->{_devids} = undef;
258 # returns MogileFS::DevFID object, after noting in the db that this fid is on this DB.
259 # it trusts you that it is, and that you've verified it.
260 sub note_on_device {
261 my ($fid, $dev) = @_;
262 my $dfid = MogileFS::DevFID->new($dev, $fid);
263 $dfid->add_to_db;
264 $fid->forget_cached_devids;
265 return $dfid;
268 sub forget_about_device {
269 my ($fid, $dev) = @_;
270 $dev->forget_about($fid);
271 $fid->forget_cached_devids;
272 return 1;
275 # return an FID's checksum object, undef if it's missing
276 sub checksum {
277 my $self = shift;
278 my $row = Mgd::get_store()->get_checksum($self->{fidid}) or return undef;
280 MogileFS::Checksum->new($row);
285 __END__
287 =head1 NAME
289 MogileFS::FID - represents a unique, immutable version of a file
291 =head1 ABOUT
293 This class represents a "fid", or "file id", which is a unique
294 revision of a file. If you upload a file with the same key
295 ("filename") a dozen times, each one has a unique "fid". Fids are
296 immutable, and are what are replicated around the MogileFS farm.