fix fake md5sum import objects
[cloudstore.git] / lib / CloudStore / Couchbase.pm
1 package CloudStore::Couchbase;
2 use warnings;
3 use strict;
4
5 use autodie;
6 use JSON::XS;
7 use File::Path qw(make_path);
8 use File::Slurp qw();
9 use Cache::Memcached;
10 use Digest::MD5 qw(md5_base64);
11 use Data::Dump qw(dump);
12
13 my $buckets = {
14         files => 5800,
15         usage => 5801,
16 };
17
18 sub new {
19         my ($class) = @_;
20
21         my $self = {};
22         bless $self, $class;
23
24         foreach my $bucket ( keys %$buckets ) {
25                 my $port = $buckets->{$bucket};
26                 my $server = new Cache::Memcached {
27                         'servers' => [ "127.0.0.1:$port" ],
28                         'debug' => $ENV{DEBUG},
29                 #       'compress_threshold' => 10_000,
30                 };
31                 #$server->set_servers($array_ref);
32                 #$server->set_compress_threshold(10_000);
33                 $server->enable_compress(0);
34                 $self->{$bucket} = $server;
35
36         }
37
38         warn "# new ",dump $self;
39
40         return $self;
41 }
42
43 sub usage_decr {
44         my ($self,$data) = @_;
45         $self->{usage}->decr( $data->{login} => $data->{size} );
46 }
47
48 sub usage_incr {
49         my ($self,$data) = @_;
50         $self->{usage}->incr( $data->{login} => $data->{size} );
51 }
52
53 sub usage {
54         my ($self,$data) = @_;
55         $self->{usage}->get( $data->{login} );
56 }
57
58 sub file_set {
59         my ($self,$data) = @_;
60         my $k = md5_base64( $data->{login} . '/' . $data->{file} );
61         my $json = encode_json $data;
62         $self->{files}->set( $k => $json );
63         return $json;
64 }
65
66 sub file_get {
67         my ($self,$data) = @_;
68         my $k = md5_base64( $data->{login} . '/' . $data->{file} );
69         if ( my $json = $self->{files}->get($k) ) {
70                 return decode_json $json;
71         }
72 }
73
74 sub modify_file {
75         my ( $self,$data ) = @_;
76
77         if ( my $old = $self->file_get( $data ) ) {
78                 $self->usage_decr( $data );
79         }
80
81         $self->new_file($data);
82 }
83
84 sub new_file {
85         my ( $self,$data ) = @_;
86         $self->file_set($data);
87         $self->usage_incr($data);
88 }
89
90 sub remove_file {
91         my ( $self, $data ) = @_;
92         $self->usage_decr( $data );
93         my $k = md5_base64( $data->{login} . '/' . $data->{file} );
94         $self->{files}->delete( $k );
95 }
96
97 sub make_dir {
98         my ( $self, $data ) = @_;
99
100 }
101
102 sub transfer {
103         my ( $self,$data ) = @_;
104
105         my $blob = "users/$data->{login}/blob";
106         my $path = "$blob/$data->{file}";
107
108         if ( $data->{itemize} =~ m/^[c>]([fd])/ ) { # received change/create
109                 my $type = $1;
110
111                 if ( $type eq 'f' ) {
112                         $self->modify_file( $data );
113                         $self->dedup( $data, $path );
114                 } elsif ( $type eq 'd' ) {
115                         $self->make_dir( $data );
116                 } else {
117                         die "unknown type $type ", dump $data;
118                 }
119         } elsif ( $data->{itemize} =~ m/\*deleting/ ) {
120                 $self->remove_file($data);
121         }
122         return $data;
123 }
124
125 sub md5pool {
126         my ( $path, $md5 ) = @_;
127
128         my $pool = 'md5'; # FIXME sharding?
129         mkdir $pool unless -e $pool;
130
131         if ( -e "$pool/$md5" ) {
132                 warn "dedup hit $md5 $path\n";
133                 my $dedup = $path . '.dedup';
134                 rename $path, $dedup;
135                 link "$pool/$md5", $path;
136                 unlink $dedup;
137         } else {
138                 link $path, "$pool/$md5";
139         }
140 }
141
142 my $empty_md5 = " " x 32;
143
144 sub dedup {
145         my ( $self, $data, $path ) = @_;
146
147         if ( $data->{file} =~ /^(.+\/)?md5sum$/ ) {
148                 my $dir = $1;
149                 my $imported = 0;
150                 warn "IMPORT ", $data->{file}, "\n";
151                 open(my $md5sum, '<', $path);
152                 while(<$md5sum>) {
153                         chomp;
154                         my ( $md5, $file ) = split(/\s+/,$_,2);
155                         if ( ! -e "md5/$md5" ) {
156                                 warn "MISSING $md5 $file\n";
157                                 next;
158                         }
159                         my $new = "users/$data->{login}/blob/$dir$file";
160                         if ( ! -e $new ) {
161                                 # create path from md5sum file
162                                 my $only_dir = $1 if $new =~ m{^(.+)/[^/]+$};
163                                 make_path $only_dir unless -d $only_dir;
164                                 $imported += link "md5/$md5", $new;
165                                 my $fake = {
166                                         login => $data->{login},
167                                         host => $data->{host},
168                                         file => $dir . $file,
169                                         md5 => $md5,
170                                         size => -s $new,
171                                 };
172                                 $self->new_file($fake);
173                                 warn "fake ",dump($fake);
174                         } else {
175                                 md5pool $new => $md5;
176                         }
177                 }
178                 print "INFO imported $imported files from ",dump($data);
179         }
180
181         if ( $data->{md5} ne $empty_md5 ) {
182                 md5pool $path => $data->{md5};
183         } else {
184                 
185         }
186 }
187
188 1;