File Coverage

blib/lib/Data/Model/Driver/Cache.pm
Criterion Covered Total %
statement 134 149 89.9
branch 37 52 71.1
condition 11 20 55.0
subroutine 24 29 82.7
pod 0 22 0.0
total 206 272 75.7


line stmt bran cond sub pod time code
1             package Data::Model::Driver::Cache;
2 87     87   971400 use strict;
  87         155  
  87         2828  
3 87     87   461 use warnings;
  87         145  
  87         2275  
4 87     87   416 use base 'Data::Model::Driver';
  87         139  
  87         10631  
5              
6 87     87   470 use Carp ();
  87         159  
  87         2284  
7             $Carp::Internal{(__PACKAGE__)}++;
8              
9 87     87   519 use Storable ();
  87         141  
  87         1705824  
10              
11 27     27 0 447 sub fallback { shift->{fallback} }
12 0     0 0 0 sub cache { shift->{cache} }
13              
14             sub _as_sql_hook {
15 516     516   624 my $self = shift;
16 516         1638 $self->{fallback}->_as_sql_hook(@_);
17             }
18              
19 0     0 0 0 sub add_to_cache { Carp::croak("NOT IMPLEMENTED") }
20 0     0 0 0 sub update_cache { Carp::croak("NOT IMPLEMENTED") }
21 0     0 0 0 sub remove_from_cache { Carp::croak("NOT IMPLEMENTED") }
22 0     0 0 0 sub get_from_cache { Carp::croak("NOT IMPLEMENTED") }
23              
24             sub get_multi_from_cache {
25 24     24 0 47 my($self, $keys) = @_;
26              
27 24         41 my %got;
28 24         41 while (my($key, $id) = each %{ $keys }) {
  74         303  
29 50 100       197 my $obj = $self->get_from_cache($id->[1]) or next;
30 20         50 $got{$key} = $obj;
31             }
32 24         63 \%got;
33             }
34              
35             sub remove_multi_from_cache {
36 5     5 0 23 my($self, $keys) = @_;
37 5         14 $self->remove_from_cache($_) for @{ $keys };
  5         53  
38             }
39              
40             sub init {
41 27     27 0 65 my $self = shift;
42 27         66 my %param = @_;
43 27         258 $self->SUPER::init(@_);
44             # $self->cache($param{cache})
45             # or Carp::croak("cache is required");
46 27 50       239 $self->fallback($param{fallback})
47             or Carp::croak("fallback is required");
48 27         139 $self;
49             }
50              
51             # lookupは真面目にキャッシュする
52             sub lookup {
53 85     85 0 172 my $self = shift;
54 85 100       343 return $self->{fallback}->lookup(@_) if $self->{active_transaction};
55 76         149 my($schema, $id) = @_;
56              
57 76         908 my $cache_key = $self->cache_key($schema, $id);
58 76         1363 my $ret = $self->get_from_cache($cache_key);
59 76 50       246 unless ($ret) {
60 76         471 $ret = $self->{fallback}->lookup(@_);
61 76 100       599 return unless defined $ret;
62 73         556 $self->add_to_cache($cache_key, $ret);
63             }
64 73         267 return $ret;
65             }
66              
67             # 先に get_multi でキャッシュデータを全部取ってきて、キャッシュから取って来れなければfallbackして取得
68             sub lookup_multi {
69 24     24 0 48 my $self = shift;
70 24 50       86 return $self->{fallback}->lookup_multi(@_) if $self->{active_transaction};
71 24         54 my($schema, $ids) = @_;
72              
73 24         43 my %cache_keys = map { join("\0", @{ $_ }) => [ $_, $self->cache_key($schema, $_) ] } @{ $ids };
  50         75  
  50         279  
  24         55  
74 24         155 my $results = $self->get_multi_from_cache(\%cache_keys);
75 24 100       49 if (scalar(keys %cache_keys) == scalar(keys %{ $results })) {
  24         93  
76 6         28 return $results;
77             }
78              
79             # make lookup id list
80 18         43 my @lookup_keys;
81 18         89 while (my($key, $id) = each %cache_keys) {
82 40 100       147 next if $results->{$key};
83 30         115 push @lookup_keys, $id->[0];
84             }
85              
86 18         121 my $fallback_results = $self->{fallback}->lookup_multi($schema, \@lookup_keys);
87 18 100 100     31 return unless scalar(%{ $results }) || scalar(%{ $fallback_results });
  18         91  
  12         80  
88 17 100       33 return $results unless scalar(%{ $fallback_results });
  17         72  
89              
90 13         26 while (my($key, $val) = each %{ $fallback_results }) {
  37         133  
91 24 100       302 $self->add_to_cache($cache_keys{$key}->[1], $val) if defined $val;
92 24         58 $results->{$key} = $val;
93             }
94              
95 13         76 $results;
96             }
97              
98             # key 指定の検索でないならキャッシュ処理しない (未実装)
99             sub get {
100 260     260 0 474 my $self = shift;
101 260 100       1011 return $self->{fallback}->get(@_) if $self->{active_transaction};
102 253         1722 return $self->{fallback}->get(@_);
103 0         0 my($schema, $key, $columns, %args) = @_;
104              
105 0 0 0     0 return $self->{fallback}->get(@_) unless $key && !$columns;
106              
107 0         0 my $cache_key = $self->cache_key($schema, $key);
108 0         0 my $ret = $self->get_from_cache($cache_key);
109 0 0       0 return $self->{fallback}->get(@_) unless $ret;
110 0         0 return $ret;
111             }
112              
113             # insertはキャッシュ処理を通さない
114 252     252 0 1948 sub set { shift->{fallback}->set(@_) }
115              
116             # key で cache を delete するのみ
117             sub replace {
118 4     4 0 12 my $self = shift;
119 4         144 my($schema, $key, $columns, %args) = @_;
120              
121 4 50       7 if (scalar(@{ $key }) == scalar(@{ $schema->key })) {
  4         10  
  4         18  
122 4         27 my $cache_key = $self->cache_key($schema, $key);
123 4         31 $self->remove_cache($cache_key);
124             }
125 4         28 $self->{fallback}->replace(@_);
126             }
127              
128              
129             # delete / update は key を指定した処理を主なターゲットとして
130             # udate_all / delete_all 的なのとかのkeyが判らない物は、いったんその条件でgetしてから、個別のobjectを処理する
131             # なので、直接keyを指定しないと、ここの処理のパフォーマンスはキャッシュ無しのがさらに早くなる
132             sub update {
133 20     20 0 39 my $self = shift;
134 20         74 my($schema, $old_key, $key, $old_columns, $columns, $changed_columns, %args) = @_;
135              
136 20 50       35 if (scalar(@{ $old_key }) == scalar(@{ $schema->key })) {
  20         85  
  20         88  
137 20         170 my $cache_key = $self->cache_key($schema, $old_key);
138 20         148 $self->remove_cache($cache_key);
139             }
140              
141 20         130 $self->{fallback}->update(@_);
142             }
143              
144             sub _delete_cache {
145 22     22   69 my($self, $schema, $key, $columns, %args) = @_;
146              
147 22 100       1204 my($it, $it_opt) = $self->{fallback}->get($schema, $key, $columns ? Storable::dclone($columns) : $columns, %args);
148 22 50       105 if ($it) {
149 22         82 while (my $row = $it->()) {
150 26         154 my $key = $schema->get_key_array_by_hash($row);
151 26         246 my $cache_key = $self->cache_key($schema, $key);
152 26         163 $self->remove_cache($cache_key);
153             }
154 22 100 66     4206 $it_opt->{end}->() if exists $it_opt->{end} && ref($it_opt->{end}) eq 'CODE';
155             }
156 22         237 return 1;
157             }
158              
159             sub update_direct {
160 20     20 0 42 my $self = shift;
161 20         64 my($schema, $key, $query, $columns, %args) = @_;
162              
163 20 50 66     174 if ($key && !$columns && scalar(@{ $key }) == scalar(@{ $schema->key })) {
  0   33     0  
  0         0  
164 0         0 my $cache_key = $self->cache_key($schema, $key);
165 0         0 $self->remove_cache($cache_key);
166             } else {
167 20 50       189 return unless $self->_delete_cache($schema, $key, $query, %args);
168             }
169 20         195 $self->{fallback}->update_direct(@_);
170             }
171              
172             sub delete {
173 44     44 0 100 my $self = shift;
174 44         129 my($schema, $key, $columns, %args) = @_;
175              
176 44 100 66     1491 if ($key && !$columns && scalar(@{ $key }) == scalar(@{ $schema->key })) {
  42   66     95  
  42         1833  
177 42         482 my $cache_key = $self->cache_key($schema, $key);
178 42         257 $self->remove_cache($cache_key);
179             } else {
180 2 50       18 return unless $self->_delete_cache(@_);
181             }
182 44         303 $self->{fallback}->delete($schema, $key, $columns, %args);
183             }
184              
185              
186             sub remove_cache {
187 92     92 0 317 my($self, $cache_key) = @_;
188 92 100       317 if ($self->{active_transaction}) {
189 8         14 push @{ $self->{transaction_delete_queue} }, $cache_key;
  8         44  
190             } else {
191 84         411 $self->remove_from_cache($cache_key);
192             }
193             }
194              
195             # for transactions
196             sub txn_begin {
197 14     14 0 39 my $self = shift;
198 14         49 $self->{active_transaction} = 1;
199 14         72 $self->{transaction_delete_queue} = [];
200 14         94 $self->{fallback}->txn_begin;
201             }
202              
203             sub txn_rollback {
204 9     9 0 20 my $self = shift;
205 9 50       41 return unless $self->{active_transaction};
206 9         46 $self->{fallback}->txn_rollback;
207              
208 9         49 $self->{transaction_delete_queue} = [];
209             }
210              
211             sub txn_commit {
212 5     5 0 12 my $self = shift;
213 5 50       16 return unless $self->{active_transaction};
214 5         26 $self->{fallback}->txn_commit;
215              
216             # apply delete queue
217 5         127 $self->remove_multi_from_cache($self->{transaction_delete_queue});
218              
219 5         30 $self->{transaction_delete_queue} = [];
220             }
221              
222             sub txn_end {
223 14     14 0 29 my $self = shift;
224 14         82 $self->{fallback}->txn_end;
225 14         26 $self->{active_transaction} = 0;
226 14         53 $self->{transaction_delete_queue} = [];
227             }
228              
229             1;