| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Redis::CappedCollection; |
|
2
|
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
=head1 NAME |
|
4
|
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
Redis::CappedCollection - Provides fixed size (determined by 'maxmemory' |
|
6
|
|
|
|
|
|
|
Redis server setting) collections with FIFO data removal. |
|
7
|
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
=head1 VERSION |
|
9
|
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
This documentation refers to C version 1.08 |
|
11
|
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
=cut |
|
13
|
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
#-- Pragmas -------------------------------------------------------------------- |
|
15
|
|
|
|
|
|
|
|
|
16
|
98
|
|
|
98
|
|
8263042
|
use 5.010; |
|
|
98
|
|
|
|
|
238
|
|
|
17
|
98
|
|
|
98
|
|
308
|
use strict; |
|
|
98
|
|
|
|
|
110
|
|
|
|
98
|
|
|
|
|
1492
|
|
|
18
|
98
|
|
|
98
|
|
264
|
use warnings; |
|
|
98
|
|
|
|
|
117
|
|
|
|
98
|
|
|
|
|
1879
|
|
|
19
|
98
|
|
|
98
|
|
12562
|
use bytes; |
|
|
98
|
|
|
|
|
265
|
|
|
|
98
|
|
|
|
|
343
|
|
|
20
|
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
# ENVIRONMENT ------------------------------------------------------------------ |
|
22
|
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
our $VERSION = '1.08'; |
|
24
|
|
|
|
|
|
|
|
|
25
|
98
|
|
|
|
|
4755
|
use Exporter qw( |
|
26
|
|
|
|
|
|
|
import |
|
27
|
98
|
|
|
98
|
|
3124
|
); |
|
|
98
|
|
|
|
|
106
|
|
|
28
|
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
our @EXPORT_OK = qw( |
|
30
|
|
|
|
|
|
|
$DATA_VERSION |
|
31
|
|
|
|
|
|
|
$DEFAULT_CONNECTION_TIMEOUT |
|
32
|
|
|
|
|
|
|
$DEFAULT_OPERATION_TIMEOUT |
|
33
|
|
|
|
|
|
|
$DEFAULT_SERVER |
|
34
|
|
|
|
|
|
|
$DEFAULT_PORT |
|
35
|
|
|
|
|
|
|
$NAMESPACE |
|
36
|
|
|
|
|
|
|
$MIN_MEMORY_RESERVE |
|
37
|
|
|
|
|
|
|
$MAX_MEMORY_RESERVE |
|
38
|
|
|
|
|
|
|
$DEFAULT_MIN_CLEANUP_ITEMS |
|
39
|
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
$E_NO_ERROR |
|
41
|
|
|
|
|
|
|
$E_MISMATCH_ARG |
|
42
|
|
|
|
|
|
|
$E_DATA_TOO_LARGE |
|
43
|
|
|
|
|
|
|
$E_NETWORK |
|
44
|
|
|
|
|
|
|
$E_MAXMEMORY_LIMIT |
|
45
|
|
|
|
|
|
|
$E_MAXMEMORY_POLICY |
|
46
|
|
|
|
|
|
|
$E_COLLECTION_DELETED |
|
47
|
|
|
|
|
|
|
$E_REDIS |
|
48
|
|
|
|
|
|
|
$E_DATA_ID_EXISTS |
|
49
|
|
|
|
|
|
|
$E_OLDER_THAN_ALLOWED |
|
50
|
|
|
|
|
|
|
$E_NONEXISTENT_DATA_ID |
|
51
|
|
|
|
|
|
|
$E_INCOMP_DATA_VERSION |
|
52
|
|
|
|
|
|
|
$E_REDIS_DID_NOT_RETURN_DATA |
|
53
|
|
|
|
|
|
|
$E_UNKNOWN_ERROR |
|
54
|
|
|
|
|
|
|
); |
|
55
|
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
#-- load the modules ----------------------------------------------------------- |
|
57
|
|
|
|
|
|
|
|
|
58
|
98
|
|
|
98
|
|
319
|
use Carp; |
|
|
98
|
|
|
|
|
91
|
|
|
|
98
|
|
|
|
|
3684
|
|
|
59
|
98
|
|
|
98
|
|
32370
|
use Const::Fast; |
|
|
98
|
|
|
|
|
159355
|
|
|
|
98
|
|
|
|
|
494
|
|
|
60
|
98
|
|
|
|
|
4510
|
use Digest::SHA1 qw( |
|
61
|
|
|
|
|
|
|
sha1_hex |
|
62
|
98
|
|
|
98
|
|
39918
|
); |
|
|
98
|
|
|
|
|
42067
|
|
|
63
|
98
|
|
|
|
|
6559
|
use List::Util qw( |
|
64
|
|
|
|
|
|
|
min |
|
65
|
98
|
|
|
98
|
|
473
|
); |
|
|
98
|
|
|
|
|
147
|
|
|
66
|
98
|
|
|
98
|
|
1764
|
use Mouse; |
|
|
98
|
|
|
|
|
41565
|
|
|
|
98
|
|
|
|
|
594
|
|
|
67
|
98
|
|
|
98
|
|
28967
|
use Mouse::Util::TypeConstraints; |
|
|
98
|
|
|
|
|
98
|
|
|
|
98
|
|
|
|
|
571
|
|
|
68
|
98
|
|
|
|
|
6089
|
use Params::Util qw( |
|
69
|
|
|
|
|
|
|
_ARRAY |
|
70
|
|
|
|
|
|
|
_ARRAY0 |
|
71
|
|
|
|
|
|
|
_HASH0 |
|
72
|
|
|
|
|
|
|
_CLASSISA |
|
73
|
|
|
|
|
|
|
_INSTANCE |
|
74
|
|
|
|
|
|
|
_NONNEGINT |
|
75
|
|
|
|
|
|
|
_NUMBER |
|
76
|
|
|
|
|
|
|
_STRING |
|
77
|
98
|
|
|
98
|
|
27488
|
); |
|
|
98
|
|
|
|
|
86363
|
|
|
78
|
98
|
|
|
98
|
|
53567
|
use Redis '1.976'; |
|
|
98
|
|
|
|
|
1629878
|
|
|
|
98
|
|
|
|
|
2706
|
|
|
79
|
98
|
|
|
|
|
4471
|
use Redis::CappedCollection::Util qw( |
|
80
|
|
|
|
|
|
|
format_message |
|
81
|
98
|
|
|
98
|
|
27015
|
); |
|
|
98
|
|
|
|
|
188
|
|
|
82
|
98
|
|
|
98
|
|
421
|
use Try::Tiny; |
|
|
98
|
|
|
|
|
82
|
|
|
|
98
|
|
|
|
|
787853
|
|
|
83
|
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
class_type 'Redis'; |
|
85
|
|
|
|
|
|
|
class_type 'Test::RedisServer'; |
|
86
|
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
#-- declarations --------------------------------------------------------------- |
|
88
|
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
90
|
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
use 5.010; |
|
92
|
|
|
|
|
|
|
use strict; |
|
93
|
|
|
|
|
|
|
use warnings; |
|
94
|
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
#-- Common |
|
96
|
|
|
|
|
|
|
use Redis::CappedCollection qw( |
|
97
|
|
|
|
|
|
|
$DEFAULT_SERVER |
|
98
|
|
|
|
|
|
|
$DEFAULT_PORT |
|
99
|
|
|
|
|
|
|
); |
|
100
|
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
my $server = $DEFAULT_SERVER.':'.$DEFAULT_PORT; |
|
102
|
|
|
|
|
|
|
my $coll = Redis::CappedCollection->create( redis => { server => $server } ); |
|
103
|
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
# Insert new data into collection |
|
105
|
|
|
|
|
|
|
my $list_id = $coll->insert( 'Some List_id', 'Some Data_id', 'Some data' ); |
|
106
|
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
# Change the element of the list with the ID $list_id |
|
108
|
|
|
|
|
|
|
$updated = $coll->update( $list_id, $data_id, 'New data' ); |
|
109
|
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
# Get data from a list with the ID $list_id |
|
111
|
|
|
|
|
|
|
@data = $coll->receive( $list_id ); |
|
112
|
|
|
|
|
|
|
# or to obtain the data ordered from the oldest to the newest |
|
113
|
|
|
|
|
|
|
while ( my ( $list_id, $data ) = $coll->pop_oldest ) { |
|
114
|
|
|
|
|
|
|
say "List '$list_id' had '$data'"; |
|
115
|
|
|
|
|
|
|
} |
|
116
|
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
A brief example of the C usage is provided in |
|
118
|
|
|
|
|
|
|
L"An Example"> section. |
|
119
|
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
The data structures used by C on Redis server |
|
121
|
|
|
|
|
|
|
are explained in L"CappedCollection data structure"> section. |
|
122
|
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
=head1 ABSTRACT |
|
124
|
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
Redis::CappedCollection module provides fixed sized collections that have |
|
126
|
|
|
|
|
|
|
a auto-FIFO age-out feature. |
|
127
|
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
The collection consists of multiple lists containing data items ordered |
|
129
|
|
|
|
|
|
|
by time. Each list must have an unique ID within the collection and each |
|
130
|
|
|
|
|
|
|
data item has unique ID within its list. |
|
131
|
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
Automatic data removal (when size limit is reached) may remove the oldest |
|
133
|
|
|
|
|
|
|
item from any list. |
|
134
|
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
Collection size is determined by 'maxmemory' Redis server setting. |
|
136
|
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
138
|
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
Main features of the package are: |
|
140
|
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
=over 3 |
|
142
|
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
=item * |
|
144
|
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
Support creation of capped collection, status monitoring, |
|
146
|
|
|
|
|
|
|
updating the data set, obtaining consistent data from the collection, |
|
147
|
|
|
|
|
|
|
automatic data removal, error reporting. |
|
148
|
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
=item * |
|
150
|
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
Simple API for inserting and retrieving data and for managing collection. |
|
152
|
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
=back |
|
154
|
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
Capped collections are fixed-size collections that have an auto-FIFO |
|
156
|
|
|
|
|
|
|
age-out feature based on the time of the inserted data. When collection |
|
157
|
|
|
|
|
|
|
size reaches memory limit, the oldest data elements are removed automatically |
|
158
|
|
|
|
|
|
|
to provide space for the new elements. |
|
159
|
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
The lists in capped collection store their data items ordered by item time. |
|
161
|
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
To insert a new data item into the capped collection, provide list ID, data ID, |
|
163
|
|
|
|
|
|
|
data and optional data time (current time is used if not specified). |
|
164
|
|
|
|
|
|
|
If there is a list with the given ID, the data is inserted into the existing list, |
|
165
|
|
|
|
|
|
|
otherwise the new list is created automatically. |
|
166
|
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
You may update the existing data in the collection, providing list ID, data ID and |
|
168
|
|
|
|
|
|
|
optional data time. If no time is specified, the updated data will keep |
|
169
|
|
|
|
|
|
|
its existing time. |
|
170
|
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
Once the space is fully utilized, newly added data will replace |
|
172
|
|
|
|
|
|
|
the oldest data in the collection. |
|
173
|
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
Limits are specified when the collection is created. |
|
175
|
|
|
|
|
|
|
Collection size is determined by 'maxmemory' redis server settings. |
|
176
|
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
The package includes the utilities to dump and restore the collection: |
|
178
|
|
|
|
|
|
|
F, F . |
|
179
|
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
=head2 EXPORT |
|
181
|
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
None by default. |
|
183
|
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
Additional constants are available for import, which can be used |
|
185
|
|
|
|
|
|
|
to define some type of parameters. |
|
186
|
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
These are the defaults: |
|
188
|
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
=head3 $DEFAULT_SERVER |
|
190
|
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
Default Redis local server: C<'localhost'>. |
|
192
|
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
=cut |
|
194
|
|
|
|
|
|
|
const our $DEFAULT_SERVER => 'localhost'; |
|
195
|
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
=head3 $DEFAULT_PORT |
|
197
|
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
Default Redis server port: 6379. |
|
199
|
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
=cut |
|
201
|
|
|
|
|
|
|
const our $DEFAULT_PORT => 6379; |
|
202
|
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
=head3 $DEFAULT_CONNECTION_TIMEOUT |
|
204
|
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
Default socket timeout for connection, number of seconds: 0.1 . |
|
206
|
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
=cut |
|
208
|
|
|
|
|
|
|
const our $DEFAULT_CONNECTION_TIMEOUT => 0.1; |
|
209
|
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
=head3 $DEFAULT_OPERATION_TIMEOUT |
|
211
|
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
Default socket timeout for read and write operations, number of seconds: 1. |
|
213
|
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
=cut |
|
215
|
|
|
|
|
|
|
const our $DEFAULT_OPERATION_TIMEOUT => 1; |
|
216
|
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
=head3 $NAMESPACE |
|
218
|
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
Namespace name used keys on the Redis server: C<'C'>. |
|
220
|
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
=cut |
|
222
|
|
|
|
|
|
|
const our $NAMESPACE => 'C'; |
|
223
|
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
=head3 $MIN_MEMORY_RESERVE, $MAX_MEMORY_RESERVE |
|
225
|
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
Minimum and maximum memory reserve limits based on 'maxmemory' |
|
227
|
|
|
|
|
|
|
configuration of the Redis server. |
|
228
|
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
Not used when C<'maxmemory'> = 0 (not set in the F). |
|
230
|
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
The following values are used by default: |
|
232
|
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
$MIN_MEMORY_RESERVE = 0.05; # 5% |
|
234
|
|
|
|
|
|
|
$MAX_MEMORY_RESERVE = 0.5; # 50% |
|
235
|
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
=cut |
|
237
|
|
|
|
|
|
|
const our $MIN_MEMORY_RESERVE => 0.05; # 5% memory reserve coefficient |
|
238
|
|
|
|
|
|
|
const our $MAX_MEMORY_RESERVE => 0.5; # 50% memory reserve coefficient |
|
239
|
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
=head3 $DEFAULT_MIN_CLEANUP_ITEMS |
|
242
|
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
Number of additional elements to delete from collection during cleanup procedure when collection size |
|
244
|
|
|
|
|
|
|
exceeds 'maxmemory'. |
|
245
|
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
Default 100 elements. 0 means no minimal cleanup required, |
|
247
|
|
|
|
|
|
|
so memory cleanup will be performed only to free up sufficient amount of memory. |
|
248
|
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
=cut |
|
250
|
|
|
|
|
|
|
const our $DEFAULT_MIN_CLEANUP_ITEMS => 100; |
|
251
|
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
=head3 $DATA_VERSION |
|
253
|
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
Current data structure version - 3. |
|
255
|
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
=cut |
|
257
|
|
|
|
|
|
|
const our $DATA_VERSION => 3; # incremented for each incompatible data structure change |
|
258
|
|
|
|
|
|
|
|
|
259
|
|
|
|
|
|
|
=over |
|
260
|
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
=item Error codes |
|
262
|
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
More details about error codes are provided in L section. |
|
264
|
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
=back |
|
266
|
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
Possible error codes: |
|
268
|
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
=cut |
|
270
|
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
=over 3 |
|
272
|
|
|
|
|
|
|
|
|
273
|
|
|
|
|
|
|
=item C<$E_NO_ERROR> |
|
274
|
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
0 - No error |
|
276
|
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
=cut |
|
278
|
|
|
|
|
|
|
const our $E_NO_ERROR => -1000; |
|
279
|
|
|
|
|
|
|
|
|
280
|
|
|
|
|
|
|
=item C<$E_MISMATCH_ARG> |
|
281
|
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
1 - Invalid argument. |
|
283
|
|
|
|
|
|
|
|
|
284
|
|
|
|
|
|
|
Thrown by methods when there is a missing required argument or argument value is invalid. |
|
285
|
|
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
=cut |
|
287
|
|
|
|
|
|
|
const our $E_MISMATCH_ARG => -1001; |
|
288
|
|
|
|
|
|
|
|
|
289
|
|
|
|
|
|
|
=item C<$E_DATA_TOO_LARGE> |
|
290
|
|
|
|
|
|
|
|
|
291
|
|
|
|
|
|
|
2 - Data is too large. |
|
292
|
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
=cut |
|
294
|
|
|
|
|
|
|
const our $E_DATA_TOO_LARGE => -1002; |
|
295
|
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
=item C<$E_NETWORK> |
|
297
|
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
3 - Error in connection to Redis server. |
|
299
|
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
=cut |
|
301
|
|
|
|
|
|
|
const our $E_NETWORK => -1003; |
|
302
|
|
|
|
|
|
|
|
|
303
|
|
|
|
|
|
|
=item C<$E_MAXMEMORY_LIMIT> |
|
304
|
|
|
|
|
|
|
|
|
305
|
|
|
|
|
|
|
4 - Command not allowed when used memory > 'maxmemory'. |
|
306
|
|
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
This means that the command is not allowed when used memory > C |
|
308
|
|
|
|
|
|
|
in the F file. |
|
309
|
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
=cut |
|
311
|
|
|
|
|
|
|
const our $E_MAXMEMORY_LIMIT => -1004; |
|
312
|
|
|
|
|
|
|
|
|
313
|
|
|
|
|
|
|
=item C<$E_MAXMEMORY_POLICY> |
|
314
|
|
|
|
|
|
|
|
|
315
|
|
|
|
|
|
|
5 - Redis server have incompatible C setting. |
|
316
|
|
|
|
|
|
|
|
|
317
|
|
|
|
|
|
|
Thrown when Redis server have incompatible C setting in F. |
|
318
|
|
|
|
|
|
|
|
|
319
|
|
|
|
|
|
|
=cut |
|
320
|
|
|
|
|
|
|
const our $E_MAXMEMORY_POLICY => -1005; |
|
321
|
|
|
|
|
|
|
|
|
322
|
|
|
|
|
|
|
=item C<$E_COLLECTION_DELETED> |
|
323
|
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
6 - Collection elements was removed prior to use. |
|
325
|
|
|
|
|
|
|
|
|
326
|
|
|
|
|
|
|
This means that the system part of the collection was removed prior to use. |
|
327
|
|
|
|
|
|
|
|
|
328
|
|
|
|
|
|
|
=cut |
|
329
|
|
|
|
|
|
|
const our $E_COLLECTION_DELETED => -1006; |
|
330
|
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
=item C<$E_REDIS> |
|
332
|
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
7 - Redis error message. |
|
334
|
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
This means that other Redis error message detected. |
|
336
|
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
=cut |
|
338
|
|
|
|
|
|
|
const our $E_REDIS => -1007; |
|
339
|
|
|
|
|
|
|
|
|
340
|
|
|
|
|
|
|
=item C<$E_DATA_ID_EXISTS> |
|
341
|
|
|
|
|
|
|
|
|
342
|
|
|
|
|
|
|
8 - Attempt to add data with an existing ID |
|
343
|
|
|
|
|
|
|
|
|
344
|
|
|
|
|
|
|
This means that you are trying to insert data with an ID that is already in |
|
345
|
|
|
|
|
|
|
the data list. |
|
346
|
|
|
|
|
|
|
|
|
347
|
|
|
|
|
|
|
=cut |
|
348
|
|
|
|
|
|
|
const our $E_DATA_ID_EXISTS => -1008; |
|
349
|
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
=item C<$E_OLDER_THAN_ALLOWED> |
|
351
|
|
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
9 - Attempt to add outdated data |
|
353
|
|
|
|
|
|
|
|
|
354
|
|
|
|
|
|
|
This means that you are trying to insert the data with the time older than |
|
355
|
|
|
|
|
|
|
the time of the oldest element currently stored in collection. |
|
356
|
|
|
|
|
|
|
|
|
357
|
|
|
|
|
|
|
=cut |
|
358
|
|
|
|
|
|
|
const our $E_OLDER_THAN_ALLOWED => -1009; |
|
359
|
|
|
|
|
|
|
|
|
360
|
|
|
|
|
|
|
=item C<$E_NONEXISTENT_DATA_ID> |
|
361
|
|
|
|
|
|
|
|
|
362
|
|
|
|
|
|
|
10 - Attempt to access the elements missing in the collection. |
|
363
|
|
|
|
|
|
|
|
|
364
|
|
|
|
|
|
|
This means that you are trying to update data which does not exist. |
|
365
|
|
|
|
|
|
|
|
|
366
|
|
|
|
|
|
|
=cut |
|
367
|
|
|
|
|
|
|
const our $E_NONEXISTENT_DATA_ID => -1010; |
|
368
|
|
|
|
|
|
|
|
|
369
|
|
|
|
|
|
|
=item C<$E_INCOMP_DATA_VERSION> |
|
370
|
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
11 - Attempt to access the collection with incompatible data structure, created |
|
372
|
|
|
|
|
|
|
by an older or newer version of this module. |
|
373
|
|
|
|
|
|
|
|
|
374
|
|
|
|
|
|
|
=cut |
|
375
|
|
|
|
|
|
|
const our $E_INCOMP_DATA_VERSION => -1011; |
|
376
|
|
|
|
|
|
|
|
|
377
|
|
|
|
|
|
|
=item C<$E_REDIS_DID_NOT_RETURN_DATA> |
|
378
|
|
|
|
|
|
|
|
|
379
|
|
|
|
|
|
|
12 - The Redis server did not return data. |
|
380
|
|
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
Check the settings in the file F. |
|
382
|
|
|
|
|
|
|
|
|
383
|
|
|
|
|
|
|
=cut |
|
384
|
|
|
|
|
|
|
const our $E_REDIS_DID_NOT_RETURN_DATA => -1012; |
|
385
|
|
|
|
|
|
|
|
|
386
|
|
|
|
|
|
|
=item C<$E_UNKNOWN_ERROR> |
|
387
|
|
|
|
|
|
|
|
|
388
|
|
|
|
|
|
|
13 - Unknown error. |
|
389
|
|
|
|
|
|
|
|
|
390
|
|
|
|
|
|
|
Possibly you should modify the constructor parameters for more intense automatic memory release. |
|
391
|
|
|
|
|
|
|
|
|
392
|
|
|
|
|
|
|
=back |
|
393
|
|
|
|
|
|
|
|
|
394
|
|
|
|
|
|
|
=cut |
|
395
|
|
|
|
|
|
|
const our $E_UNKNOWN_ERROR => -1013; |
|
396
|
|
|
|
|
|
|
|
|
397
|
|
|
|
|
|
|
our %ERROR = ( |
|
398
|
|
|
|
|
|
|
$E_NO_ERROR => 'No error', |
|
399
|
|
|
|
|
|
|
$E_MISMATCH_ARG => 'Invalid argument', |
|
400
|
|
|
|
|
|
|
$E_DATA_TOO_LARGE => 'Data is too large', |
|
401
|
|
|
|
|
|
|
$E_NETWORK => 'Error in connection to Redis server', |
|
402
|
|
|
|
|
|
|
$E_MAXMEMORY_LIMIT => "Command not allowed when used memory > 'maxmemory'", |
|
403
|
|
|
|
|
|
|
$E_MAXMEMORY_POLICY => "Redis server have incompatible 'maxmemory-policy' setting. Use 'noeviction' only.", |
|
404
|
|
|
|
|
|
|
$E_COLLECTION_DELETED => 'Collection elements was removed prior to use', |
|
405
|
|
|
|
|
|
|
$E_REDIS => 'Redis error message', |
|
406
|
|
|
|
|
|
|
$E_DATA_ID_EXISTS => 'Attempt to add data to an existing ID', |
|
407
|
|
|
|
|
|
|
$E_OLDER_THAN_ALLOWED => 'Attempt to add data over outdated', |
|
408
|
|
|
|
|
|
|
$E_NONEXISTENT_DATA_ID => 'Non-existent data id', |
|
409
|
|
|
|
|
|
|
$E_INCOMP_DATA_VERSION => 'Incompatible data version', |
|
410
|
|
|
|
|
|
|
$E_REDIS_DID_NOT_RETURN_DATA => 'The Redis server did not return data', |
|
411
|
|
|
|
|
|
|
$E_UNKNOWN_ERROR => 'Unknown error', |
|
412
|
|
|
|
|
|
|
); |
|
413
|
|
|
|
|
|
|
|
|
414
|
|
|
|
|
|
|
const our $REDIS_ERROR_CODE => 'ERR'; |
|
415
|
|
|
|
|
|
|
const our $REDIS_MEMORY_ERROR_CODE => 'OOM'; |
|
416
|
|
|
|
|
|
|
const our $REDIS_MEMORY_ERROR_MSG => "$REDIS_MEMORY_ERROR_CODE $ERROR{ $E_MAXMEMORY_LIMIT }."; |
|
417
|
|
|
|
|
|
|
const our $MAX_DATASIZE => 512*1024*1024; # A String value can be at max 512 Megabytes in length. |
|
418
|
|
|
|
|
|
|
const my $MAX_REMOVE_RETRIES => 2; # the number of remove retries when memory limit is near |
|
419
|
|
|
|
|
|
|
const my $USED_MEMORY_POLICY => 'noeviction'; |
|
420
|
|
|
|
|
|
|
|
|
421
|
|
|
|
|
|
|
# status field names |
|
422
|
|
|
|
|
|
|
const my $_LISTS => 'lists'; |
|
423
|
|
|
|
|
|
|
const my $_ITEMS => 'items'; |
|
424
|
|
|
|
|
|
|
const my $_OLDER_ALLOWED => 'older_allowed'; |
|
425
|
|
|
|
|
|
|
const my $_MIN_CLEANUP_BYTES => 'min_cleanup_bytes'; |
|
426
|
|
|
|
|
|
|
const my $_MIN_CLEANUP_ITEMS => 'min_cleanup_items'; |
|
427
|
|
|
|
|
|
|
const my $_MEMORY_RESERVE => 'memory_reserve'; |
|
428
|
|
|
|
|
|
|
const my $_DATA_VERSION => 'data_version'; |
|
429
|
|
|
|
|
|
|
const my $_LAST_REMOVED_TIME => 'last_removed_time'; |
|
430
|
|
|
|
|
|
|
const my $_LAST_CLEANUP_BYTES => 'last_cleanup_bytes'; |
|
431
|
|
|
|
|
|
|
# information fields |
|
432
|
|
|
|
|
|
|
const my $_LAST_CLEANUP_ITEMS => 'last_cleanup_items'; |
|
433
|
|
|
|
|
|
|
const my $_LAST_CLEANUP_MAXMEMORY => 'last_cleanup_maxmemory'; |
|
434
|
|
|
|
|
|
|
const my $_LAST_CLEANUP_USED_MEMORY => 'last_cleanup_used_memory'; |
|
435
|
|
|
|
|
|
|
const my $_LAST_CLEANUP_BYTES_MUST_BE_DELETED => 'last_bytes_must_be_deleted'; |
|
436
|
|
|
|
|
|
|
const my $_INSERTS_SINCE_CLEANING => 'inserts_since_cleaning'; |
|
437
|
|
|
|
|
|
|
const my $_UPDATES_SINCE_CLEANING => 'updates_since_cleaning'; |
|
438
|
|
|
|
|
|
|
|
|
439
|
|
|
|
|
|
|
my $_lua_namespace = "local NAMESPACE = '".$NAMESPACE."'"; |
|
440
|
|
|
|
|
|
|
my $_lua_queue_key = "local QUEUE_KEY = NAMESPACE..':Q:'..coll_name"; |
|
441
|
|
|
|
|
|
|
my $_lua_status_key = "local STATUS_KEY = NAMESPACE..':S:'..coll_name"; |
|
442
|
|
|
|
|
|
|
my $_lua_data_keys = "local DATA_KEYS = NAMESPACE..':D:'..coll_name"; |
|
443
|
|
|
|
|
|
|
my $_lua_time_keys = "local TIME_KEYS = NAMESPACE..':T:'..coll_name"; |
|
444
|
|
|
|
|
|
|
my $_lua_data_key = "local DATA_KEY = DATA_KEYS..':'..list_id"; |
|
445
|
|
|
|
|
|
|
my $_lua_time_key = "local TIME_KEY = TIME_KEYS..':'..list_id"; |
|
446
|
|
|
|
|
|
|
|
|
447
|
|
|
|
|
|
|
my %lua_script_body; |
|
448
|
|
|
|
|
|
|
|
|
449
|
|
|
|
|
|
|
my $_lua_clean_data = <<"END_CLEAN_DATA"; |
|
450
|
|
|
|
|
|
|
-- remove the control structures of the collection |
|
451
|
|
|
|
|
|
|
if redis.call( 'EXISTS', QUEUE_KEY ) == 1 then |
|
452
|
|
|
|
|
|
|
ret = ret + redis.call( 'DEL', QUEUE_KEY ) |
|
453
|
|
|
|
|
|
|
end |
|
454
|
|
|
|
|
|
|
|
|
455
|
|
|
|
|
|
|
-- each element of the list are deleted separately, as total number of items may be too large to send commands 'DEL' |
|
456
|
|
|
|
|
|
|
$_lua_data_keys |
|
457
|
|
|
|
|
|
|
$_lua_time_keys |
|
458
|
|
|
|
|
|
|
|
|
459
|
|
|
|
|
|
|
local arr = redis.call( 'KEYS', DATA_KEYS..':*' ) |
|
460
|
|
|
|
|
|
|
if #arr > 0 then |
|
461
|
|
|
|
|
|
|
|
|
462
|
|
|
|
|
|
|
-- remove structures store data lists |
|
463
|
|
|
|
|
|
|
for i = 1, #arr do |
|
464
|
|
|
|
|
|
|
ret = ret + redis.call( 'DEL', arr[i] ) |
|
465
|
|
|
|
|
|
|
end |
|
466
|
|
|
|
|
|
|
|
|
467
|
|
|
|
|
|
|
-- remove structures store time lists |
|
468
|
|
|
|
|
|
|
arr = redis.call( 'KEYS', TIME_KEYS..':*' ) |
|
469
|
|
|
|
|
|
|
for i = 1, #arr do |
|
470
|
|
|
|
|
|
|
ret = ret + redis.call( 'DEL', arr[i] ) |
|
471
|
|
|
|
|
|
|
end |
|
472
|
|
|
|
|
|
|
|
|
473
|
|
|
|
|
|
|
end |
|
474
|
|
|
|
|
|
|
|
|
475
|
|
|
|
|
|
|
return ret |
|
476
|
|
|
|
|
|
|
END_CLEAN_DATA |
|
477
|
|
|
|
|
|
|
|
|
478
|
|
|
|
|
|
|
my $_lua_cleaning = <<"END_CLEANING"; |
|
479
|
|
|
|
|
|
|
local REDIS_USED_MEMORY = 0 |
|
480
|
|
|
|
|
|
|
local REDIS_MAXMEMORY = 0 |
|
481
|
|
|
|
|
|
|
local ROLLBACK = {} |
|
482
|
|
|
|
|
|
|
local TOTAL_BYTES_DELETED = 0 |
|
483
|
|
|
|
|
|
|
local LAST_CLEANUP_BYTES_MUST_BE_DELETED = 0 |
|
484
|
|
|
|
|
|
|
local LAST_CLEANUP_BYTES = 0 |
|
485
|
|
|
|
|
|
|
local LAST_CLEANUP_ITEMS = 0 |
|
486
|
|
|
|
|
|
|
local LAST_OPERATION = '' |
|
487
|
|
|
|
|
|
|
local INSERTS_SINCE_CLEANING = 0 |
|
488
|
|
|
|
|
|
|
local UPDATES_SINCE_CLEANING = 0 |
|
489
|
|
|
|
|
|
|
|
|
490
|
|
|
|
|
|
|
local _DEBUG, _DEBUG_ID, _FUNC_NAME |
|
491
|
|
|
|
|
|
|
|
|
492
|
|
|
|
|
|
|
local table_merge = function ( t1, t2 ) |
|
493
|
|
|
|
|
|
|
for key, val in pairs( t2 ) do |
|
494
|
|
|
|
|
|
|
t1[ key ] = val |
|
495
|
|
|
|
|
|
|
end |
|
496
|
|
|
|
|
|
|
end |
|
497
|
|
|
|
|
|
|
|
|
498
|
|
|
|
|
|
|
local _debug_log = function ( values ) |
|
499
|
|
|
|
|
|
|
table_merge( values, { |
|
500
|
|
|
|
|
|
|
_DEBUG_ID = _DEBUG_ID, |
|
501
|
|
|
|
|
|
|
_FUNC_NAME = _FUNC_NAME, |
|
502
|
|
|
|
|
|
|
REDIS_USED_MEMORY = REDIS_USED_MEMORY, |
|
503
|
|
|
|
|
|
|
list_id = list_id, |
|
504
|
|
|
|
|
|
|
data_id = data_id, |
|
505
|
|
|
|
|
|
|
data_len = #data, |
|
506
|
|
|
|
|
|
|
ROLLBACK = ROLLBACK |
|
507
|
|
|
|
|
|
|
} ) |
|
508
|
|
|
|
|
|
|
|
|
509
|
|
|
|
|
|
|
redis.log( redis.LOG_NOTICE, _FUNC_NAME..': '..cjson.encode( values ) ) |
|
510
|
|
|
|
|
|
|
end |
|
511
|
|
|
|
|
|
|
|
|
512
|
|
|
|
|
|
|
local _setup = function ( argv_idx, func_name, extra_data_len ) |
|
513
|
|
|
|
|
|
|
LAST_OPERATION = func_name |
|
514
|
|
|
|
|
|
|
|
|
515
|
|
|
|
|
|
|
REDIS_MAXMEMORY = tonumber( redis.call( 'CONFIG', 'GET', 'maxmemory' )[2] ) |
|
516
|
|
|
|
|
|
|
local memory_reserve_coefficient = 1 + tonumber( redis.call( 'HGET', STATUS_KEY, '$_MEMORY_RESERVE' ) ) |
|
517
|
|
|
|
|
|
|
|
|
518
|
|
|
|
|
|
|
local redis_used_memory = string.match( |
|
519
|
|
|
|
|
|
|
redis.call( 'INFO', 'memory' ), |
|
520
|
|
|
|
|
|
|
'used_memory:(%d+)' |
|
521
|
|
|
|
|
|
|
) |
|
522
|
|
|
|
|
|
|
REDIS_USED_MEMORY = tonumber( redis_used_memory ) |
|
523
|
|
|
|
|
|
|
LAST_CLEANUP_BYTES_MUST_BE_DELETED = REDIS_USED_MEMORY + extra_data_len - math.floor( REDIS_MAXMEMORY / memory_reserve_coefficient ) |
|
524
|
|
|
|
|
|
|
if LAST_CLEANUP_BYTES_MUST_BE_DELETED < 0 or REDIS_MAXMEMORY == 0 then |
|
525
|
|
|
|
|
|
|
LAST_CLEANUP_BYTES_MUST_BE_DELETED = 0 |
|
526
|
|
|
|
|
|
|
end |
|
527
|
|
|
|
|
|
|
|
|
528
|
|
|
|
|
|
|
LAST_CLEANUP_BYTES = tonumber( redis.call( 'HGET', STATUS_KEY, '$_LAST_CLEANUP_BYTES' ) ) |
|
529
|
|
|
|
|
|
|
if LAST_CLEANUP_BYTES == nil then |
|
530
|
|
|
|
|
|
|
LAST_CLEANUP_BYTES = 0 |
|
531
|
|
|
|
|
|
|
end |
|
532
|
|
|
|
|
|
|
|
|
533
|
|
|
|
|
|
|
INSERTS_SINCE_CLEANING = tonumber( redis.call( 'HGET', STATUS_KEY, '$_INSERTS_SINCE_CLEANING' ) ) |
|
534
|
|
|
|
|
|
|
if INSERTS_SINCE_CLEANING == nil then |
|
535
|
|
|
|
|
|
|
INSERTS_SINCE_CLEANING = 0 |
|
536
|
|
|
|
|
|
|
end |
|
537
|
|
|
|
|
|
|
UPDATES_SINCE_CLEANING = tonumber( redis.call( 'HGET', STATUS_KEY, '$_UPDATES_SINCE_CLEANING' ) ) |
|
538
|
|
|
|
|
|
|
if UPDATES_SINCE_CLEANING == nil then |
|
539
|
|
|
|
|
|
|
UPDATES_SINCE_CLEANING = 0 |
|
540
|
|
|
|
|
|
|
end |
|
541
|
|
|
|
|
|
|
|
|
542
|
|
|
|
|
|
|
_FUNC_NAME = func_name |
|
543
|
|
|
|
|
|
|
_DEBUG_ID = tonumber( ARGV[ argv_idx ] ) |
|
544
|
|
|
|
|
|
|
if _DEBUG_ID ~= 0 then |
|
545
|
|
|
|
|
|
|
_DEBUG = true |
|
546
|
|
|
|
|
|
|
_debug_log( { |
|
547
|
|
|
|
|
|
|
_STEP = '_setup', |
|
548
|
|
|
|
|
|
|
maxmemory = REDIS_MAXMEMORY, |
|
549
|
|
|
|
|
|
|
redis_used_memory = REDIS_USED_MEMORY, |
|
550
|
|
|
|
|
|
|
bytes_must_be_deleted = LAST_CLEANUP_BYTES_MUST_BE_DELETED |
|
551
|
|
|
|
|
|
|
} ) |
|
552
|
|
|
|
|
|
|
else |
|
553
|
|
|
|
|
|
|
_DEBUG = false |
|
554
|
|
|
|
|
|
|
end |
|
555
|
|
|
|
|
|
|
end |
|
556
|
|
|
|
|
|
|
|
|
557
|
|
|
|
|
|
|
local cleaning_error = function ( error_msg ) |
|
558
|
|
|
|
|
|
|
if _DEBUG then |
|
559
|
|
|
|
|
|
|
_debug_log( { |
|
560
|
|
|
|
|
|
|
_STEP = 'cleaning_error', |
|
561
|
|
|
|
|
|
|
error_msg = error_msg |
|
562
|
|
|
|
|
|
|
} ) |
|
563
|
|
|
|
|
|
|
end |
|
564
|
|
|
|
|
|
|
|
|
565
|
|
|
|
|
|
|
for _, rollback_command in ipairs( ROLLBACK ) do |
|
566
|
|
|
|
|
|
|
redis.call( unpack( rollback_command ) ) |
|
567
|
|
|
|
|
|
|
end |
|
568
|
|
|
|
|
|
|
-- Level 2 points the error to where the function that called error was called |
|
569
|
|
|
|
|
|
|
error( error_msg, 2 ) |
|
570
|
|
|
|
|
|
|
end |
|
571
|
|
|
|
|
|
|
|
|
572
|
|
|
|
|
|
|
local table_val_to_str = function ( v ) |
|
573
|
|
|
|
|
|
|
if "string" == type( v ) then |
|
574
|
|
|
|
|
|
|
v = string.gsub( v, "\\n", "\\\\n" ) |
|
575
|
|
|
|
|
|
|
if string.match( string.gsub( v, "[^'\\"]", "" ), '^"+\$' ) then |
|
576
|
|
|
|
|
|
|
return "'" .. v .. "'" |
|
577
|
|
|
|
|
|
|
end |
|
578
|
|
|
|
|
|
|
return '"' .. string.gsub( v, '"', '\\\\"' ) .. '"' |
|
579
|
|
|
|
|
|
|
else |
|
580
|
|
|
|
|
|
|
return "table" == type( v ) and table_tostring( v ) or tostring( v ) |
|
581
|
|
|
|
|
|
|
end |
|
582
|
|
|
|
|
|
|
end |
|
583
|
|
|
|
|
|
|
|
|
584
|
|
|
|
|
|
|
local table_key_to_str = function ( k ) |
|
585
|
|
|
|
|
|
|
if "string" == type( k ) and string.match( k, "^[_%a][_%a%d]*\$" ) then |
|
586
|
|
|
|
|
|
|
return k |
|
587
|
|
|
|
|
|
|
else |
|
588
|
|
|
|
|
|
|
return "[" .. table_val_to_str( k ) .. "]" |
|
589
|
|
|
|
|
|
|
end |
|
590
|
|
|
|
|
|
|
end |
|
591
|
|
|
|
|
|
|
|
|
592
|
|
|
|
|
|
|
local table_tostring = function ( tbl ) |
|
593
|
|
|
|
|
|
|
local result, done = {}, {} |
|
594
|
|
|
|
|
|
|
for k, v in ipairs( tbl ) do |
|
595
|
|
|
|
|
|
|
table.insert( result, table_val_to_str( v ) ) |
|
596
|
|
|
|
|
|
|
done[ k ] = true |
|
597
|
|
|
|
|
|
|
end |
|
598
|
|
|
|
|
|
|
for k, v in pairs( tbl ) do |
|
599
|
|
|
|
|
|
|
if not done[ k ] then |
|
600
|
|
|
|
|
|
|
table.insert( result, table_key_to_str( k ) .. "=" .. table_val_to_str( v ) ) |
|
601
|
|
|
|
|
|
|
end |
|
602
|
|
|
|
|
|
|
end |
|
603
|
|
|
|
|
|
|
return "{" .. table.concat( result, "," ) .. "}" |
|
604
|
|
|
|
|
|
|
end |
|
605
|
|
|
|
|
|
|
|
|
606
|
|
|
|
|
|
|
-- deleting old data to make room for new data |
|
607
|
|
|
|
|
|
|
local cleaning = function ( list_id, data_id, is_cleaning_needed ) |
|
608
|
|
|
|
|
|
|
local coll_items = tonumber( redis.call( 'HGET', STATUS_KEY, '$_ITEMS' ) ) |
|
609
|
|
|
|
|
|
|
|
|
610
|
|
|
|
|
|
|
if coll_items == 0 then |
|
611
|
|
|
|
|
|
|
return |
|
612
|
|
|
|
|
|
|
end |
|
613
|
|
|
|
|
|
|
|
|
614
|
|
|
|
|
|
|
local min_cleanup_bytes = tonumber( redis.call( 'HGET', STATUS_KEY, '$_MIN_CLEANUP_BYTES' ) ) |
|
615
|
|
|
|
|
|
|
local min_cleanup_items = tonumber( redis.call( 'HGET', STATUS_KEY, '$_MIN_CLEANUP_ITEMS' ) ) |
|
616
|
|
|
|
|
|
|
|
|
617
|
|
|
|
|
|
|
local cleanup_bytes = math.max( min_cleanup_bytes, LAST_CLEANUP_BYTES_MUST_BE_DELETED ) |
|
618
|
|
|
|
|
|
|
|
|
619
|
|
|
|
|
|
|
if not ( is_cleaning_needed or LAST_CLEANUP_BYTES_MUST_BE_DELETED > 0 ) then |
|
620
|
|
|
|
|
|
|
return |
|
621
|
|
|
|
|
|
|
end |
|
622
|
|
|
|
|
|
|
|
|
623
|
|
|
|
|
|
|
if _DEBUG then |
|
624
|
|
|
|
|
|
|
_debug_log( { |
|
625
|
|
|
|
|
|
|
_STEP = 'Before cleanings', |
|
626
|
|
|
|
|
|
|
coll_items = coll_items, |
|
627
|
|
|
|
|
|
|
min_cleanup_items = min_cleanup_items, |
|
628
|
|
|
|
|
|
|
min_cleanup_bytes = min_cleanup_bytes, |
|
629
|
|
|
|
|
|
|
} ) |
|
630
|
|
|
|
|
|
|
end |
|
631
|
|
|
|
|
|
|
|
|
632
|
|
|
|
|
|
|
local items_deleted = 0 |
|
633
|
|
|
|
|
|
|
local bytes_deleted = 0 |
|
634
|
|
|
|
|
|
|
local lists_deleted = 0 |
|
635
|
|
|
|
|
|
|
|
|
636
|
|
|
|
|
|
|
repeat |
|
637
|
|
|
|
|
|
|
if redis.call( 'EXISTS', QUEUE_KEY ) ~= 1 then |
|
638
|
|
|
|
|
|
|
-- Level 2 points the error to where the function that called error was called |
|
639
|
|
|
|
|
|
|
error( 'Queue key does not exist', 2 ) |
|
640
|
|
|
|
|
|
|
end |
|
641
|
|
|
|
|
|
|
|
|
642
|
|
|
|
|
|
|
-- continue to work with the to_delete (requiring removal) data and for them using the prefix 'to_delete_' |
|
643
|
|
|
|
|
|
|
local to_delete_list_id, last_removed_time = unpack( redis.call( 'ZRANGE', QUEUE_KEY, 0, 0, 'WITHSCORES' ) ) |
|
644
|
|
|
|
|
|
|
last_removed_time = tonumber( last_removed_time ) |
|
645
|
|
|
|
|
|
|
-- key data structures |
|
646
|
|
|
|
|
|
|
local to_delete_data_key = DATA_KEYS..':'..to_delete_list_id |
|
647
|
|
|
|
|
|
|
local to_delete_time_key = TIME_KEYS..':'..to_delete_list_id |
|
648
|
|
|
|
|
|
|
|
|
649
|
|
|
|
|
|
|
-- looking for the oldest data |
|
650
|
|
|
|
|
|
|
local to_delete_data_id |
|
651
|
|
|
|
|
|
|
local to_delete_data |
|
652
|
|
|
|
|
|
|
local items = redis.call( 'HLEN', to_delete_data_key ) |
|
653
|
|
|
|
|
|
|
-- #FIXME: to_delete_data -> to_delete_data_len |
|
654
|
|
|
|
|
|
|
-- HSTRLEN key field |
|
655
|
|
|
|
|
|
|
-- Available since 3.2.0. |
|
656
|
|
|
|
|
|
|
if items == 1 then |
|
657
|
|
|
|
|
|
|
to_delete_data_id, to_delete_data = unpack( redis.call( 'HGETALL', to_delete_data_key ) ) |
|
658
|
|
|
|
|
|
|
else |
|
659
|
|
|
|
|
|
|
to_delete_data_id = redis.call( 'ZRANGE', to_delete_time_key, 0, 0 )[1] |
|
660
|
|
|
|
|
|
|
to_delete_data = redis.call( 'HGET', to_delete_data_key, to_delete_data_id ) |
|
661
|
|
|
|
|
|
|
end |
|
662
|
|
|
|
|
|
|
local to_delete_data_len = #to_delete_data |
|
663
|
|
|
|
|
|
|
to_delete_data = nil -- free memory |
|
664
|
|
|
|
|
|
|
|
|
665
|
|
|
|
|
|
|
if _DEBUG then |
|
666
|
|
|
|
|
|
|
_debug_log( { |
|
667
|
|
|
|
|
|
|
_STEP = 'Before real cleaning', |
|
668
|
|
|
|
|
|
|
items = items, |
|
669
|
|
|
|
|
|
|
to_delete_list_id = to_delete_list_id, |
|
670
|
|
|
|
|
|
|
to_delete_data_id = to_delete_data_id, |
|
671
|
|
|
|
|
|
|
to_delete_data_len = to_delete_data_len |
|
672
|
|
|
|
|
|
|
} ) |
|
673
|
|
|
|
|
|
|
end |
|
674
|
|
|
|
|
|
|
|
|
675
|
|
|
|
|
|
|
if to_delete_list_id == list_id and to_delete_data_id == data_id then |
|
676
|
|
|
|
|
|
|
if items_deleted == 0 then |
|
677
|
|
|
|
|
|
|
-- Its first attempt to clean the conflicting data, for which the primary operation executed. |
|
678
|
|
|
|
|
|
|
-- In this case, we are roll back operations that have been made before, and immediately return an error, |
|
679
|
|
|
|
|
|
|
-- shifting the handling of such errors on the client. |
|
680
|
|
|
|
|
|
|
cleaning_error( "$REDIS_MEMORY_ERROR_MSG" ) |
|
681
|
|
|
|
|
|
|
end |
|
682
|
|
|
|
|
|
|
break |
|
683
|
|
|
|
|
|
|
end |
|
684
|
|
|
|
|
|
|
|
|
685
|
|
|
|
|
|
|
if _DEBUG then |
|
686
|
|
|
|
|
|
|
_debug_log( { |
|
687
|
|
|
|
|
|
|
_STEP = 'Why it is cleared?', |
|
688
|
|
|
|
|
|
|
coll_items = coll_items, |
|
689
|
|
|
|
|
|
|
min_cleanup_bytes = min_cleanup_bytes, |
|
690
|
|
|
|
|
|
|
min_cleanup_items = min_cleanup_items, |
|
691
|
|
|
|
|
|
|
items_deleted = items_deleted, |
|
692
|
|
|
|
|
|
|
bytes_deleted = bytes_deleted, |
|
693
|
|
|
|
|
|
|
} ) |
|
694
|
|
|
|
|
|
|
end |
|
695
|
|
|
|
|
|
|
|
|
696
|
|
|
|
|
|
|
-- actually remove the oldest item |
|
697
|
|
|
|
|
|
|
redis.call( 'HDEL', to_delete_data_key, to_delete_data_id ) |
|
698
|
|
|
|
|
|
|
items = items - 1 |
|
699
|
|
|
|
|
|
|
coll_items = coll_items - 1 |
|
700
|
|
|
|
|
|
|
|
|
701
|
|
|
|
|
|
|
redis.call( 'HSET', STATUS_KEY, '$_LAST_REMOVED_TIME', last_removed_time ) |
|
702
|
|
|
|
|
|
|
|
|
703
|
|
|
|
|
|
|
if items > 0 then |
|
704
|
|
|
|
|
|
|
-- If the list has more data |
|
705
|
|
|
|
|
|
|
redis.call( 'ZREM', to_delete_time_key, to_delete_data_id ) |
|
706
|
|
|
|
|
|
|
local oldest_item_time = tonumber( redis.call( 'ZRANGE', to_delete_time_key, 0, 0, 'WITHSCORES' )[2] ) |
|
707
|
|
|
|
|
|
|
redis.call( 'ZADD', QUEUE_KEY, oldest_item_time, to_delete_list_id ) |
|
708
|
|
|
|
|
|
|
|
|
709
|
|
|
|
|
|
|
if items == 1 then |
|
710
|
|
|
|
|
|
|
redis.call( 'DEL', to_delete_time_key ) |
|
711
|
|
|
|
|
|
|
end |
|
712
|
|
|
|
|
|
|
else |
|
713
|
|
|
|
|
|
|
-- If the list does not have data |
|
714
|
|
|
|
|
|
|
-- remove the name of the list from the queue collection |
|
715
|
|
|
|
|
|
|
redis.call( 'ZREM', QUEUE_KEY, to_delete_list_id ) |
|
716
|
|
|
|
|
|
|
lists_deleted = lists_deleted + 1 |
|
717
|
|
|
|
|
|
|
end |
|
718
|
|
|
|
|
|
|
|
|
719
|
|
|
|
|
|
|
-- amount of data collection decreased |
|
720
|
|
|
|
|
|
|
items_deleted = items_deleted + 1 |
|
721
|
|
|
|
|
|
|
bytes_deleted = bytes_deleted + to_delete_data_len |
|
722
|
|
|
|
|
|
|
|
|
723
|
|
|
|
|
|
|
if _DEBUG then |
|
724
|
|
|
|
|
|
|
_debug_log( { |
|
725
|
|
|
|
|
|
|
_STEP = 'After real cleaning', |
|
726
|
|
|
|
|
|
|
to_delete_data_key = to_delete_data_key, |
|
727
|
|
|
|
|
|
|
to_delete_data_id = to_delete_data_id, |
|
728
|
|
|
|
|
|
|
items = items, |
|
729
|
|
|
|
|
|
|
items_deleted = items_deleted, |
|
730
|
|
|
|
|
|
|
bytes_deleted = bytes_deleted, |
|
731
|
|
|
|
|
|
|
} ) |
|
732
|
|
|
|
|
|
|
end |
|
733
|
|
|
|
|
|
|
|
|
734
|
|
|
|
|
|
|
until |
|
735
|
|
|
|
|
|
|
coll_items <= 0 |
|
736
|
|
|
|
|
|
|
or ( |
|
737
|
|
|
|
|
|
|
items_deleted >= min_cleanup_items |
|
738
|
|
|
|
|
|
|
and bytes_deleted >= cleanup_bytes |
|
739
|
|
|
|
|
|
|
) |
|
740
|
|
|
|
|
|
|
|
|
741
|
|
|
|
|
|
|
if items_deleted > 0 then |
|
742
|
|
|
|
|
|
|
-- reduce the number of items in the collection |
|
743
|
|
|
|
|
|
|
redis.call( 'HINCRBY', STATUS_KEY, '$_ITEMS', -items_deleted ) |
|
744
|
|
|
|
|
|
|
end |
|
745
|
|
|
|
|
|
|
if lists_deleted > 0 then |
|
746
|
|
|
|
|
|
|
-- reduce the number of lists stored in a collection |
|
747
|
|
|
|
|
|
|
redis.call( 'HINCRBY', STATUS_KEY, '$_LISTS', -lists_deleted ) |
|
748
|
|
|
|
|
|
|
end |
|
749
|
|
|
|
|
|
|
|
|
750
|
|
|
|
|
|
|
if _DEBUG then |
|
751
|
|
|
|
|
|
|
_debug_log( { |
|
752
|
|
|
|
|
|
|
_STEP = 'Cleaning finished', |
|
753
|
|
|
|
|
|
|
items_deleted = items_deleted, |
|
754
|
|
|
|
|
|
|
bytes_deleted = bytes_deleted, |
|
755
|
|
|
|
|
|
|
lists_deleted = lists_deleted, |
|
756
|
|
|
|
|
|
|
min_cleanup_bytes = min_cleanup_bytes, |
|
757
|
|
|
|
|
|
|
min_cleanup_items = min_cleanup_items, |
|
758
|
|
|
|
|
|
|
coll_items = coll_items, |
|
759
|
|
|
|
|
|
|
} ) |
|
760
|
|
|
|
|
|
|
end |
|
761
|
|
|
|
|
|
|
|
|
762
|
|
|
|
|
|
|
if bytes_deleted > 0 then |
|
763
|
|
|
|
|
|
|
if TOTAL_BYTES_DELETED == 0 then -- first cleaning |
|
764
|
|
|
|
|
|
|
LAST_CLEANUP_BYTES = bytes_deleted |
|
765
|
|
|
|
|
|
|
redis.call( 'HSET', STATUS_KEY, '$_LAST_CLEANUP_BYTES', LAST_CLEANUP_BYTES ) |
|
766
|
|
|
|
|
|
|
INSERTS_SINCE_CLEANING = 0 |
|
767
|
|
|
|
|
|
|
redis.call( 'HSET', STATUS_KEY, '$_INSERTS_SINCE_CLEANING', INSERTS_SINCE_CLEANING ) |
|
768
|
|
|
|
|
|
|
UPDATES_SINCE_CLEANING = 0 |
|
769
|
|
|
|
|
|
|
redis.call( 'HSET', STATUS_KEY, '$_UPDATES_SINCE_CLEANING', UPDATES_SINCE_CLEANING ) |
|
770
|
|
|
|
|
|
|
else |
|
771
|
|
|
|
|
|
|
LAST_CLEANUP_BYTES = LAST_CLEANUP_BYTES + bytes_deleted |
|
772
|
|
|
|
|
|
|
redis.call( 'HSET', STATUS_KEY, '$_LAST_CLEANUP_BYTES', LAST_CLEANUP_BYTES ) |
|
773
|
|
|
|
|
|
|
end |
|
774
|
|
|
|
|
|
|
|
|
775
|
|
|
|
|
|
|
TOTAL_BYTES_DELETED = TOTAL_BYTES_DELETED + bytes_deleted |
|
776
|
|
|
|
|
|
|
LAST_CLEANUP_ITEMS = LAST_CLEANUP_ITEMS + items_deleted |
|
777
|
|
|
|
|
|
|
redis.call( 'HSET', STATUS_KEY, '$_LAST_CLEANUP_ITEMS', LAST_CLEANUP_ITEMS ) |
|
778
|
|
|
|
|
|
|
|
|
779
|
|
|
|
|
|
|
-- information values |
|
780
|
|
|
|
|
|
|
redis.call( 'HSET', STATUS_KEY, '$_LAST_CLEANUP_ITEMS', LAST_CLEANUP_ITEMS ) |
|
781
|
|
|
|
|
|
|
redis.call( 'HSET', STATUS_KEY, '$_LAST_CLEANUP_MAXMEMORY', REDIS_MAXMEMORY ) |
|
782
|
|
|
|
|
|
|
redis.call( 'HSET', STATUS_KEY, '$_LAST_CLEANUP_USED_MEMORY', REDIS_USED_MEMORY ) |
|
783
|
|
|
|
|
|
|
redis.call( 'HSET', STATUS_KEY, '$_LAST_CLEANUP_BYTES_MUST_BE_DELETED', LAST_CLEANUP_BYTES_MUST_BE_DELETED ) |
|
784
|
|
|
|
|
|
|
end |
|
785
|
|
|
|
|
|
|
end |
|
786
|
|
|
|
|
|
|
|
|
787
|
|
|
|
|
|
|
local call_with_error_control = function ( list_id, data_id, ... ) |
|
788
|
|
|
|
|
|
|
local retries = $MAX_REMOVE_RETRIES |
|
789
|
|
|
|
|
|
|
local ret |
|
790
|
|
|
|
|
|
|
local error_msg = '' |
|
791
|
|
|
|
|
|
|
repeat |
|
792
|
|
|
|
|
|
|
ret = redis.pcall( ... ) |
|
793
|
|
|
|
|
|
|
if type( ret ) == 'table' and ret.err ~= nil then |
|
794
|
|
|
|
|
|
|
error_msg = "$REDIS_MEMORY_ERROR_MSG - " .. ret.err .. " (call = " .. table_tostring( { ... } ) .. ")" |
|
795
|
|
|
|
|
|
|
if _DEBUG then |
|
796
|
|
|
|
|
|
|
_debug_log( { |
|
797
|
|
|
|
|
|
|
_STEP = 'call_with_error_control', |
|
798
|
|
|
|
|
|
|
error_msg = error_msg, |
|
799
|
|
|
|
|
|
|
retries = retries |
|
800
|
|
|
|
|
|
|
} ) |
|
801
|
|
|
|
|
|
|
end |
|
802
|
|
|
|
|
|
|
|
|
803
|
|
|
|
|
|
|
cleaning( list_id, data_id, true ) |
|
804
|
|
|
|
|
|
|
else |
|
805
|
|
|
|
|
|
|
break |
|
806
|
|
|
|
|
|
|
end |
|
807
|
|
|
|
|
|
|
retries = retries - 1 |
|
808
|
|
|
|
|
|
|
until retries == 0 |
|
809
|
|
|
|
|
|
|
|
|
810
|
|
|
|
|
|
|
if retries == 0 then |
|
811
|
|
|
|
|
|
|
-- Operation returned an error related to insufficient memory. |
|
812
|
|
|
|
|
|
|
-- Start cleaning process and then re-try operation. |
|
813
|
|
|
|
|
|
|
-- Repeat the cycle of operation + memory cleaning a couple of times and return an error / fail, |
|
814
|
|
|
|
|
|
|
-- if it still did not work. |
|
815
|
|
|
|
|
|
|
cleaning_error( error_msg ) |
|
816
|
|
|
|
|
|
|
end |
|
817
|
|
|
|
|
|
|
|
|
818
|
|
|
|
|
|
|
return ret |
|
819
|
|
|
|
|
|
|
end |
|
820
|
|
|
|
|
|
|
END_CLEANING |
|
821
|
|
|
|
|
|
|
|
|
822
|
|
|
|
|
|
|
$lua_script_body{insert} = <<"END_INSERT"; |
|
823
|
|
|
|
|
|
|
-- adding data to a list of collections |
|
824
|
|
|
|
|
|
|
|
|
825
|
|
|
|
|
|
|
local coll_name = ARGV[1] |
|
826
|
|
|
|
|
|
|
local list_id = ARGV[2] |
|
827
|
|
|
|
|
|
|
local data_id = ARGV[3] |
|
828
|
|
|
|
|
|
|
local data = ARGV[4] |
|
829
|
|
|
|
|
|
|
local data_time = tonumber( ARGV[5] ) |
|
830
|
|
|
|
|
|
|
|
|
831
|
|
|
|
|
|
|
-- key data storage structures |
|
832
|
|
|
|
|
|
|
$_lua_namespace |
|
833
|
|
|
|
|
|
|
$_lua_queue_key |
|
834
|
|
|
|
|
|
|
$_lua_status_key |
|
835
|
|
|
|
|
|
|
$_lua_data_keys |
|
836
|
|
|
|
|
|
|
$_lua_time_keys |
|
837
|
|
|
|
|
|
|
$_lua_data_key |
|
838
|
|
|
|
|
|
|
$_lua_time_key |
|
839
|
|
|
|
|
|
|
|
|
840
|
|
|
|
|
|
|
-- determine whether there is a list of data and a collection |
|
841
|
|
|
|
|
|
|
if redis.call( 'EXISTS', STATUS_KEY ) ~= 1 then |
|
842
|
|
|
|
|
|
|
return { $E_COLLECTION_DELETED, 0, 0, 0 } |
|
843
|
|
|
|
|
|
|
end |
|
844
|
|
|
|
|
|
|
|
|
845
|
|
|
|
|
|
|
-- verification of the existence of old data with new data identifier |
|
846
|
|
|
|
|
|
|
if redis.call( 'HEXISTS', DATA_KEY, data_id ) == 1 then |
|
847
|
|
|
|
|
|
|
return { $E_DATA_ID_EXISTS, 0, 0, 0 } |
|
848
|
|
|
|
|
|
|
end |
|
849
|
|
|
|
|
|
|
|
|
850
|
|
|
|
|
|
|
-- Validating the time of new data, if required |
|
851
|
|
|
|
|
|
|
local last_removed_time = tonumber( redis.call( 'HGET', STATUS_KEY, '$_LAST_REMOVED_TIME' ) ) |
|
852
|
|
|
|
|
|
|
|
|
853
|
|
|
|
|
|
|
if redis.call( 'HGET', STATUS_KEY, '$_OLDER_ALLOWED' ) ~= '1' then |
|
854
|
|
|
|
|
|
|
if redis.call( 'EXISTS', QUEUE_KEY ) == 1 then |
|
855
|
|
|
|
|
|
|
if data_time < last_removed_time then |
|
856
|
|
|
|
|
|
|
return { $E_OLDER_THAN_ALLOWED, 0, 0, 0 } |
|
857
|
|
|
|
|
|
|
end |
|
858
|
|
|
|
|
|
|
end |
|
859
|
|
|
|
|
|
|
end |
|
860
|
|
|
|
|
|
|
|
|
861
|
|
|
|
|
|
|
-- deleting obsolete data, if it is necessary |
|
862
|
|
|
|
|
|
|
$_lua_cleaning |
|
863
|
|
|
|
|
|
|
local data_len = #data |
|
864
|
|
|
|
|
|
|
_setup( 6, 'insert', data_len ) -- 6 -> is the index of ARGV[6] |
|
865
|
|
|
|
|
|
|
cleaning( list_id, data_id, false ) |
|
866
|
|
|
|
|
|
|
|
|
867
|
|
|
|
|
|
|
-- add data to the list |
|
868
|
|
|
|
|
|
|
-- Remember that the list and the collection can be automatically deleted after the "crowding out" old data |
|
869
|
|
|
|
|
|
|
|
|
870
|
|
|
|
|
|
|
-- the existing data |
|
871
|
|
|
|
|
|
|
local items = redis.call( 'HLEN', DATA_KEY ) |
|
872
|
|
|
|
|
|
|
local existing_id, existing_time |
|
873
|
|
|
|
|
|
|
if items == 1 then |
|
874
|
|
|
|
|
|
|
existing_id = redis.call( 'HGETALL', DATA_KEY )[1] |
|
875
|
|
|
|
|
|
|
existing_time = tonumber( redis.call( 'ZSCORE', QUEUE_KEY, list_id ) ) |
|
876
|
|
|
|
|
|
|
end |
|
877
|
|
|
|
|
|
|
|
|
878
|
|
|
|
|
|
|
-- actually add data to the list |
|
879
|
|
|
|
|
|
|
call_with_error_control( list_id, data_id, 'HSET', DATA_KEY, data_id, data ) |
|
880
|
|
|
|
|
|
|
data = nil -- free memory |
|
881
|
|
|
|
|
|
|
table.insert( ROLLBACK, 1, { 'HDEL', DATA_KEY, data_id } ) |
|
882
|
|
|
|
|
|
|
|
|
883
|
|
|
|
|
|
|
if redis.call( 'HLEN', DATA_KEY ) == 1 then -- list recreated after cleaning |
|
884
|
|
|
|
|
|
|
redis.call( 'HINCRBY', STATUS_KEY, '$_LISTS', 1 ) |
|
885
|
|
|
|
|
|
|
table.insert( ROLLBACK, 1, { 'HINCRBY', STATUS_KEY, '$_LISTS', -1 } ) |
|
886
|
|
|
|
|
|
|
call_with_error_control( list_id, data_id, 'ZADD', QUEUE_KEY, data_time, list_id ) |
|
887
|
|
|
|
|
|
|
else |
|
888
|
|
|
|
|
|
|
if items == 1 then |
|
889
|
|
|
|
|
|
|
call_with_error_control( list_id, data_id, 'ZADD', TIME_KEY, existing_time, existing_id ) |
|
890
|
|
|
|
|
|
|
table.insert( ROLLBACK, 1, { 'ZREM', TIME_KEY, existing_id } ) |
|
891
|
|
|
|
|
|
|
end |
|
892
|
|
|
|
|
|
|
call_with_error_control( list_id, data_id, 'ZADD', TIME_KEY, data_time, data_id ) |
|
893
|
|
|
|
|
|
|
local oldest_item_time = redis.call( 'ZRANGE', TIME_KEY, 0, 0, 'WITHSCORES' )[2] |
|
894
|
|
|
|
|
|
|
redis.call( 'ZADD', QUEUE_KEY, oldest_item_time, list_id ) |
|
895
|
|
|
|
|
|
|
end |
|
896
|
|
|
|
|
|
|
|
|
897
|
|
|
|
|
|
|
-- reflect the addition of new data |
|
898
|
|
|
|
|
|
|
redis.call( 'HINCRBY', STATUS_KEY, '$_ITEMS', 1 ) |
|
899
|
|
|
|
|
|
|
if data_time < last_removed_time then |
|
900
|
|
|
|
|
|
|
redis.call( 'HSET', STATUS_KEY, '$_LAST_REMOVED_TIME', 0 ) |
|
901
|
|
|
|
|
|
|
end |
|
902
|
|
|
|
|
|
|
|
|
903
|
|
|
|
|
|
|
redis.call( 'HSET', STATUS_KEY, '$_LAST_CLEANUP_BYTES', LAST_CLEANUP_BYTES ) |
|
904
|
|
|
|
|
|
|
INSERTS_SINCE_CLEANING = INSERTS_SINCE_CLEANING + 1 |
|
905
|
|
|
|
|
|
|
redis.call( 'HSET', STATUS_KEY, '$_INSERTS_SINCE_CLEANING', INSERTS_SINCE_CLEANING ) |
|
906
|
|
|
|
|
|
|
|
|
907
|
|
|
|
|
|
|
return { $E_NO_ERROR, LAST_CLEANUP_ITEMS, REDIS_USED_MEMORY, TOTAL_BYTES_DELETED } |
|
908
|
|
|
|
|
|
|
END_INSERT |
|
909
|
|
|
|
|
|
|
|
|
910
|
|
|
|
|
|
|
$lua_script_body{update} = <<"END_UPDATE"; |
|
911
|
|
|
|
|
|
|
-- update the data in the list of collections |
|
912
|
|
|
|
|
|
|
|
|
913
|
|
|
|
|
|
|
local coll_name = ARGV[1] |
|
914
|
|
|
|
|
|
|
local list_id = ARGV[2] |
|
915
|
|
|
|
|
|
|
local data_id = ARGV[3] |
|
916
|
|
|
|
|
|
|
local data = ARGV[4] |
|
917
|
|
|
|
|
|
|
local new_data_time = tonumber( ARGV[5] ) |
|
918
|
|
|
|
|
|
|
|
|
919
|
|
|
|
|
|
|
-- key data storage structures |
|
920
|
|
|
|
|
|
|
$_lua_namespace |
|
921
|
|
|
|
|
|
|
$_lua_queue_key |
|
922
|
|
|
|
|
|
|
$_lua_status_key |
|
923
|
|
|
|
|
|
|
$_lua_data_keys |
|
924
|
|
|
|
|
|
|
$_lua_time_keys |
|
925
|
|
|
|
|
|
|
$_lua_data_key |
|
926
|
|
|
|
|
|
|
$_lua_time_key |
|
927
|
|
|
|
|
|
|
|
|
928
|
|
|
|
|
|
|
-- determine whether there is a list of data and a collection |
|
929
|
|
|
|
|
|
|
if redis.call( 'EXISTS', STATUS_KEY ) ~= 1 then |
|
930
|
|
|
|
|
|
|
return { $E_COLLECTION_DELETED, 0, 0, 0 } |
|
931
|
|
|
|
|
|
|
end |
|
932
|
|
|
|
|
|
|
if redis.call( 'EXISTS', DATA_KEY ) ~= 1 then |
|
933
|
|
|
|
|
|
|
return { $E_NONEXISTENT_DATA_ID, 0, 0, 0 } |
|
934
|
|
|
|
|
|
|
end |
|
935
|
|
|
|
|
|
|
local extra_data_len |
|
936
|
|
|
|
|
|
|
local data_len = #data |
|
937
|
|
|
|
|
|
|
if redis.call( 'HEXISTS', DATA_KEY, data_id ) == 1 then |
|
938
|
|
|
|
|
|
|
-- #FIXME: existed_data -> existed_data_len |
|
939
|
|
|
|
|
|
|
-- HSTRLEN key field |
|
940
|
|
|
|
|
|
|
-- Available since 3.2.0. |
|
941
|
|
|
|
|
|
|
local existed_data = redis.call( 'HGET', DATA_KEY, data_id ) |
|
942
|
|
|
|
|
|
|
extra_data_len = data_len - #existed_data |
|
943
|
|
|
|
|
|
|
existed_data = nil -- free memory |
|
944
|
|
|
|
|
|
|
else |
|
945
|
|
|
|
|
|
|
return { $E_NONEXISTENT_DATA_ID, 0, 0, 0 } |
|
946
|
|
|
|
|
|
|
end |
|
947
|
|
|
|
|
|
|
|
|
948
|
|
|
|
|
|
|
local last_removed_time = tonumber( redis.call( 'HGET', STATUS_KEY, '$_LAST_REMOVED_TIME' ) ) |
|
949
|
|
|
|
|
|
|
if redis.call( 'HGET', STATUS_KEY, '$_OLDER_ALLOWED' ) ~= '1' then |
|
950
|
|
|
|
|
|
|
if new_data_time ~= 0 and new_data_time < last_removed_time then |
|
951
|
|
|
|
|
|
|
return { $E_OLDER_THAN_ALLOWED, 0, 0, 0 } |
|
952
|
|
|
|
|
|
|
end |
|
953
|
|
|
|
|
|
|
end |
|
954
|
|
|
|
|
|
|
|
|
955
|
|
|
|
|
|
|
-- deleting obsolete data, if it can be necessary |
|
956
|
|
|
|
|
|
|
$_lua_cleaning |
|
957
|
|
|
|
|
|
|
_setup( 6, 'update', extra_data_len ) -- 6 is the index of ARGV[6] |
|
958
|
|
|
|
|
|
|
cleaning( list_id, data_id, false ) |
|
959
|
|
|
|
|
|
|
|
|
960
|
|
|
|
|
|
|
-- data change |
|
961
|
|
|
|
|
|
|
-- Remember that the list and the collection can be automatically deleted after the "crowding out" old data |
|
962
|
|
|
|
|
|
|
if redis.call( 'HEXISTS', DATA_KEY, data_id ) ~= 1 then |
|
963
|
|
|
|
|
|
|
return { $E_NONEXISTENT_DATA_ID, 0, 0, 0 } |
|
964
|
|
|
|
|
|
|
end |
|
965
|
|
|
|
|
|
|
|
|
966
|
|
|
|
|
|
|
-- data to be changed were not removed |
|
967
|
|
|
|
|
|
|
|
|
968
|
|
|
|
|
|
|
-- actually change |
|
969
|
|
|
|
|
|
|
call_with_error_control( list_id, data_id, 'HSET', DATA_KEY, data_id, data ) |
|
970
|
|
|
|
|
|
|
data = nil -- free memory |
|
971
|
|
|
|
|
|
|
|
|
972
|
|
|
|
|
|
|
if new_data_time ~= 0 then |
|
973
|
|
|
|
|
|
|
if redis.call( 'HLEN', DATA_KEY ) == 1 then |
|
974
|
|
|
|
|
|
|
redis.call( 'ZADD', QUEUE_KEY, new_data_time, list_id ) |
|
975
|
|
|
|
|
|
|
else |
|
976
|
|
|
|
|
|
|
redis.call( 'ZADD', TIME_KEY, new_data_time, data_id ) |
|
977
|
|
|
|
|
|
|
local oldest_item_time = tonumber( redis.call( 'ZRANGE', TIME_KEY, 0, 0, 'WITHSCORES' )[2] ) |
|
978
|
|
|
|
|
|
|
redis.call( 'ZADD', QUEUE_KEY, oldest_item_time, list_id ) |
|
979
|
|
|
|
|
|
|
end |
|
980
|
|
|
|
|
|
|
|
|
981
|
|
|
|
|
|
|
if new_data_time < last_removed_time then |
|
982
|
|
|
|
|
|
|
redis.call( 'HSET', STATUS_KEY, '$_LAST_REMOVED_TIME', 0 ) |
|
983
|
|
|
|
|
|
|
end |
|
984
|
|
|
|
|
|
|
end |
|
985
|
|
|
|
|
|
|
|
|
986
|
|
|
|
|
|
|
UPDATES_SINCE_CLEANING = UPDATES_SINCE_CLEANING + 1 |
|
987
|
|
|
|
|
|
|
redis.call( 'HSET', STATUS_KEY, '$_UPDATES_SINCE_CLEANING', UPDATES_SINCE_CLEANING ) |
|
988
|
|
|
|
|
|
|
|
|
989
|
|
|
|
|
|
|
return { $E_NO_ERROR, LAST_CLEANUP_ITEMS, REDIS_USED_MEMORY, TOTAL_BYTES_DELETED } |
|
990
|
|
|
|
|
|
|
END_UPDATE |
|
991
|
|
|
|
|
|
|
|
|
992
|
|
|
|
|
|
|
$lua_script_body{upsert} = <<"END_UPSERT"; |
|
993
|
|
|
|
|
|
|
-- update or insert the data in the list of collections |
|
994
|
|
|
|
|
|
|
|
|
995
|
|
|
|
|
|
|
local coll_name = ARGV[1] |
|
996
|
|
|
|
|
|
|
local list_id = ARGV[2] |
|
997
|
|
|
|
|
|
|
local data_id = ARGV[3] |
|
998
|
|
|
|
|
|
|
local data_time = tonumber( ARGV[5] ) |
|
999
|
|
|
|
|
|
|
local start_time = ARGV[7] |
|
1000
|
|
|
|
|
|
|
|
|
1001
|
|
|
|
|
|
|
-- key data storage structures |
|
1002
|
|
|
|
|
|
|
$_lua_namespace |
|
1003
|
|
|
|
|
|
|
$_lua_data_keys |
|
1004
|
|
|
|
|
|
|
$_lua_data_key |
|
1005
|
|
|
|
|
|
|
|
|
1006
|
|
|
|
|
|
|
-- verification of the existence of old data with new data identifier |
|
1007
|
|
|
|
|
|
|
if redis.call( 'HEXISTS', DATA_KEY, data_id ) == 1 then |
|
1008
|
|
|
|
|
|
|
if data_time == -1 then |
|
1009
|
|
|
|
|
|
|
ARGV[5] = '0' |
|
1010
|
|
|
|
|
|
|
end |
|
1011
|
|
|
|
|
|
|
$lua_script_body{update} |
|
1012
|
|
|
|
|
|
|
else |
|
1013
|
|
|
|
|
|
|
if data_time == -1 then |
|
1014
|
|
|
|
|
|
|
ARGV[5] = start_time |
|
1015
|
|
|
|
|
|
|
end |
|
1016
|
|
|
|
|
|
|
$lua_script_body{insert} |
|
1017
|
|
|
|
|
|
|
end |
|
1018
|
|
|
|
|
|
|
END_UPSERT |
|
1019
|
|
|
|
|
|
|
|
|
1020
|
|
|
|
|
|
|
$lua_script_body{receive} = <<"END_RECEIVE"; |
|
1021
|
|
|
|
|
|
|
-- returns the data from the list |
|
1022
|
|
|
|
|
|
|
|
|
1023
|
|
|
|
|
|
|
local coll_name = ARGV[1] |
|
1024
|
|
|
|
|
|
|
local list_id = ARGV[2] |
|
1025
|
|
|
|
|
|
|
local mode = ARGV[3] |
|
1026
|
|
|
|
|
|
|
local data_id = ARGV[4] |
|
1027
|
|
|
|
|
|
|
|
|
1028
|
|
|
|
|
|
|
-- key data storage structures |
|
1029
|
|
|
|
|
|
|
$_lua_namespace |
|
1030
|
|
|
|
|
|
|
$_lua_status_key |
|
1031
|
|
|
|
|
|
|
$_lua_data_keys |
|
1032
|
|
|
|
|
|
|
$_lua_data_key |
|
1033
|
|
|
|
|
|
|
|
|
1034
|
|
|
|
|
|
|
-- determine whether there is a list of data and a collection |
|
1035
|
|
|
|
|
|
|
if redis.call( 'EXISTS', STATUS_KEY ) ~= 1 then |
|
1036
|
|
|
|
|
|
|
-- sort of a mistake |
|
1037
|
|
|
|
|
|
|
return nil |
|
1038
|
|
|
|
|
|
|
end |
|
1039
|
|
|
|
|
|
|
|
|
1040
|
|
|
|
|
|
|
if mode == 'val' then |
|
1041
|
|
|
|
|
|
|
-- returns the specified element of the data list |
|
1042
|
|
|
|
|
|
|
return redis.call( 'HGET', DATA_KEY, data_id ) |
|
1043
|
|
|
|
|
|
|
elseif mode == 'len' then |
|
1044
|
|
|
|
|
|
|
-- returns the length of the data list |
|
1045
|
|
|
|
|
|
|
return redis.call( 'HLEN', DATA_KEY ) |
|
1046
|
|
|
|
|
|
|
elseif mode == 'vals' then |
|
1047
|
|
|
|
|
|
|
-- returns all the data from the list |
|
1048
|
|
|
|
|
|
|
return redis.call( 'HVALS', DATA_KEY ) |
|
1049
|
|
|
|
|
|
|
elseif mode == 'all' then |
|
1050
|
|
|
|
|
|
|
-- returns all data IDs and data values of the data list |
|
1051
|
|
|
|
|
|
|
return redis.call( 'HGETALL', DATA_KEY ) |
|
1052
|
|
|
|
|
|
|
else |
|
1053
|
|
|
|
|
|
|
-- sort of a mistake |
|
1054
|
|
|
|
|
|
|
return nil |
|
1055
|
|
|
|
|
|
|
end |
|
1056
|
|
|
|
|
|
|
|
|
1057
|
|
|
|
|
|
|
END_RECEIVE |
|
1058
|
|
|
|
|
|
|
|
|
1059
|
|
|
|
|
|
|
$lua_script_body{pop_oldest} = <<"END_POP_OLDEST"; |
|
1060
|
|
|
|
|
|
|
-- retrieve the oldest data stored in the collection |
|
1061
|
|
|
|
|
|
|
|
|
1062
|
|
|
|
|
|
|
local coll_name = ARGV[1] |
|
1063
|
|
|
|
|
|
|
|
|
1064
|
|
|
|
|
|
|
-- key data storage structures |
|
1065
|
|
|
|
|
|
|
$_lua_namespace |
|
1066
|
|
|
|
|
|
|
$_lua_queue_key |
|
1067
|
|
|
|
|
|
|
$_lua_status_key |
|
1068
|
|
|
|
|
|
|
|
|
1069
|
|
|
|
|
|
|
-- determine whether there is a list of data and a collection |
|
1070
|
|
|
|
|
|
|
if redis.call( 'EXISTS', STATUS_KEY ) ~= 1 then |
|
1071
|
|
|
|
|
|
|
-- sort of a mistake |
|
1072
|
|
|
|
|
|
|
return { $E_COLLECTION_DELETED, nil, nil, nil } |
|
1073
|
|
|
|
|
|
|
end |
|
1074
|
|
|
|
|
|
|
if redis.call( 'EXISTS', QUEUE_KEY ) ~= 1 then |
|
1075
|
|
|
|
|
|
|
return { $E_NO_ERROR, false, nil, nil } |
|
1076
|
|
|
|
|
|
|
end |
|
1077
|
|
|
|
|
|
|
|
|
1078
|
|
|
|
|
|
|
-- initialize the data returned from the script |
|
1079
|
|
|
|
|
|
|
local list_exist = 0 |
|
1080
|
|
|
|
|
|
|
local list_id = false |
|
1081
|
|
|
|
|
|
|
local data = false |
|
1082
|
|
|
|
|
|
|
|
|
1083
|
|
|
|
|
|
|
-- identifier of the list with the oldest data |
|
1084
|
|
|
|
|
|
|
list_id = redis.call( 'ZRANGE', QUEUE_KEY, 0, 0 )[1] |
|
1085
|
|
|
|
|
|
|
|
|
1086
|
|
|
|
|
|
|
-- key data storage structures |
|
1087
|
|
|
|
|
|
|
$_lua_data_keys |
|
1088
|
|
|
|
|
|
|
$_lua_time_keys |
|
1089
|
|
|
|
|
|
|
$_lua_data_key |
|
1090
|
|
|
|
|
|
|
$_lua_time_key |
|
1091
|
|
|
|
|
|
|
|
|
1092
|
|
|
|
|
|
|
-- determine whether there is a list of data and a collection |
|
1093
|
|
|
|
|
|
|
if redis.call( 'EXISTS', DATA_KEY ) ~= 1 then |
|
1094
|
|
|
|
|
|
|
return { $E_COLLECTION_DELETED, nil, nil, nil } |
|
1095
|
|
|
|
|
|
|
end |
|
1096
|
|
|
|
|
|
|
|
|
1097
|
|
|
|
|
|
|
-- Features the oldest data |
|
1098
|
|
|
|
|
|
|
local items = redis.call( 'HLEN', DATA_KEY ) |
|
1099
|
|
|
|
|
|
|
local data_id |
|
1100
|
|
|
|
|
|
|
if items == 1 then |
|
1101
|
|
|
|
|
|
|
data_id = redis.call( 'HGETALL', DATA_KEY )[1] |
|
1102
|
|
|
|
|
|
|
else |
|
1103
|
|
|
|
|
|
|
data_id = redis.call( 'ZRANGE', TIME_KEY, 0, 0 )[1] |
|
1104
|
|
|
|
|
|
|
end |
|
1105
|
|
|
|
|
|
|
local last_removed_time = tonumber( redis.call( 'ZRANGE', QUEUE_KEY, 0, 0, 'WITHSCORES' )[2] ) |
|
1106
|
|
|
|
|
|
|
|
|
1107
|
|
|
|
|
|
|
-- get data |
|
1108
|
|
|
|
|
|
|
|
|
1109
|
|
|
|
|
|
|
-- actually get data |
|
1110
|
|
|
|
|
|
|
data = redis.call( 'HGET', DATA_KEY, data_id ) |
|
1111
|
|
|
|
|
|
|
|
|
1112
|
|
|
|
|
|
|
-- delete the data from the list |
|
1113
|
|
|
|
|
|
|
redis.call( 'HDEL', DATA_KEY, data_id ) |
|
1114
|
|
|
|
|
|
|
items = items - 1 |
|
1115
|
|
|
|
|
|
|
|
|
1116
|
|
|
|
|
|
|
-- obtain information about the data that has become the oldest |
|
1117
|
|
|
|
|
|
|
local oldest_item_time = tonumber( redis.call( 'ZRANGE', TIME_KEY, 0, 0, 'WITHSCORES' )[2] ) |
|
1118
|
|
|
|
|
|
|
|
|
1119
|
|
|
|
|
|
|
if items > 0 then |
|
1120
|
|
|
|
|
|
|
-- If the list has more data |
|
1121
|
|
|
|
|
|
|
|
|
1122
|
|
|
|
|
|
|
-- delete the information about the time of the data |
|
1123
|
|
|
|
|
|
|
redis.call( 'ZREM', TIME_KEY, data_id ) |
|
1124
|
|
|
|
|
|
|
|
|
1125
|
|
|
|
|
|
|
redis.call( 'ZADD', QUEUE_KEY, oldest_item_time, list_id ) |
|
1126
|
|
|
|
|
|
|
|
|
1127
|
|
|
|
|
|
|
if items == 1 then |
|
1128
|
|
|
|
|
|
|
-- delete the list data structure 'zset' |
|
1129
|
|
|
|
|
|
|
redis.call( 'DEL', TIME_KEY ) |
|
1130
|
|
|
|
|
|
|
end |
|
1131
|
|
|
|
|
|
|
else |
|
1132
|
|
|
|
|
|
|
-- if the list is no more data |
|
1133
|
|
|
|
|
|
|
-- delete the list data structure 'zset' |
|
1134
|
|
|
|
|
|
|
redis.call( 'DEL', TIME_KEY ) |
|
1135
|
|
|
|
|
|
|
|
|
1136
|
|
|
|
|
|
|
-- reduce the number of lists stored in a collection |
|
1137
|
|
|
|
|
|
|
redis.call( 'HINCRBY', STATUS_KEY, '$_LISTS', -1 ) |
|
1138
|
|
|
|
|
|
|
-- remove the name of the list from the queue collection |
|
1139
|
|
|
|
|
|
|
redis.call( 'ZREM', QUEUE_KEY, list_id ) |
|
1140
|
|
|
|
|
|
|
end |
|
1141
|
|
|
|
|
|
|
|
|
1142
|
|
|
|
|
|
|
redis.call( 'HINCRBY', STATUS_KEY, '$_ITEMS', -1 ) |
|
1143
|
|
|
|
|
|
|
redis.call( 'HSET', STATUS_KEY, '$_LAST_REMOVED_TIME', last_removed_time ) |
|
1144
|
|
|
|
|
|
|
|
|
1145
|
|
|
|
|
|
|
return { $E_NO_ERROR, true, list_id, data } |
|
1146
|
|
|
|
|
|
|
END_POP_OLDEST |
|
1147
|
|
|
|
|
|
|
|
|
1148
|
|
|
|
|
|
|
$lua_script_body{collection_info} = <<"END_COLLECTION_INFO"; |
|
1149
|
|
|
|
|
|
|
-- to obtain information on the status of the collection |
|
1150
|
|
|
|
|
|
|
|
|
1151
|
|
|
|
|
|
|
local coll_name = ARGV[1] |
|
1152
|
|
|
|
|
|
|
|
|
1153
|
|
|
|
|
|
|
-- key data storage structures |
|
1154
|
|
|
|
|
|
|
$_lua_namespace |
|
1155
|
|
|
|
|
|
|
$_lua_queue_key |
|
1156
|
|
|
|
|
|
|
$_lua_status_key |
|
1157
|
|
|
|
|
|
|
|
|
1158
|
|
|
|
|
|
|
-- determine whether there is a collection |
|
1159
|
|
|
|
|
|
|
if redis.call( 'EXISTS', STATUS_KEY ) ~= 1 then |
|
1160
|
|
|
|
|
|
|
return { $E_COLLECTION_DELETED, false, false, false, false, false, false, false } |
|
1161
|
|
|
|
|
|
|
end |
|
1162
|
|
|
|
|
|
|
|
|
1163
|
|
|
|
|
|
|
local oldest_item_time = redis.call( 'ZRANGE', QUEUE_KEY, 0, 0, 'WITHSCORES' )[2] |
|
1164
|
|
|
|
|
|
|
local lists, items, older_allowed, min_cleanup_bytes, min_cleanup_items, memory_reserve, data_version, last_removed_time = unpack( redis.call( 'HMGET', STATUS_KEY, |
|
1165
|
|
|
|
|
|
|
'$_LISTS', |
|
1166
|
|
|
|
|
|
|
'$_ITEMS', |
|
1167
|
|
|
|
|
|
|
'$_OLDER_ALLOWED', |
|
1168
|
|
|
|
|
|
|
'$_MIN_CLEANUP_BYTES', |
|
1169
|
|
|
|
|
|
|
'$_MIN_CLEANUP_ITEMS', |
|
1170
|
|
|
|
|
|
|
'$_MEMORY_RESERVE', |
|
1171
|
|
|
|
|
|
|
'$_DATA_VERSION', |
|
1172
|
|
|
|
|
|
|
'$_LAST_REMOVED_TIME' |
|
1173
|
|
|
|
|
|
|
) ) |
|
1174
|
|
|
|
|
|
|
|
|
1175
|
|
|
|
|
|
|
if type( data_version ) ~= 'string' then data_version = '0' end |
|
1176
|
|
|
|
|
|
|
|
|
1177
|
|
|
|
|
|
|
return { |
|
1178
|
|
|
|
|
|
|
$E_NO_ERROR, |
|
1179
|
|
|
|
|
|
|
lists, |
|
1180
|
|
|
|
|
|
|
items, |
|
1181
|
|
|
|
|
|
|
older_allowed, |
|
1182
|
|
|
|
|
|
|
min_cleanup_bytes, |
|
1183
|
|
|
|
|
|
|
min_cleanup_items, |
|
1184
|
|
|
|
|
|
|
memory_reserve, |
|
1185
|
|
|
|
|
|
|
data_version, |
|
1186
|
|
|
|
|
|
|
last_removed_time, |
|
1187
|
|
|
|
|
|
|
oldest_item_time |
|
1188
|
|
|
|
|
|
|
} |
|
1189
|
|
|
|
|
|
|
END_COLLECTION_INFO |
|
1190
|
|
|
|
|
|
|
|
|
1191
|
|
|
|
|
|
|
$lua_script_body{oldest_time} = <<"END_OLDEST_TIME"; |
|
1192
|
|
|
|
|
|
|
-- to obtain time corresponding to the oldest data in the collection |
|
1193
|
|
|
|
|
|
|
|
|
1194
|
|
|
|
|
|
|
local coll_name = ARGV[1] |
|
1195
|
|
|
|
|
|
|
|
|
1196
|
|
|
|
|
|
|
-- key data storage structures |
|
1197
|
|
|
|
|
|
|
$_lua_namespace |
|
1198
|
|
|
|
|
|
|
$_lua_queue_key |
|
1199
|
|
|
|
|
|
|
$_lua_status_key |
|
1200
|
|
|
|
|
|
|
|
|
1201
|
|
|
|
|
|
|
-- determine whe, falther there is a collection |
|
1202
|
|
|
|
|
|
|
if redis.call( 'EXISTS', STATUS_KEY ) ~= 1 then |
|
1203
|
|
|
|
|
|
|
return { $E_COLLECTION_DELETED, false } |
|
1204
|
|
|
|
|
|
|
end |
|
1205
|
|
|
|
|
|
|
|
|
1206
|
|
|
|
|
|
|
local oldest_item_time = redis.call( 'ZRANGE', QUEUE_KEY, 0, 0, 'WITHSCORES' )[2] |
|
1207
|
|
|
|
|
|
|
return { $E_NO_ERROR, oldest_item_time } |
|
1208
|
|
|
|
|
|
|
END_OLDEST_TIME |
|
1209
|
|
|
|
|
|
|
|
|
1210
|
|
|
|
|
|
|
$lua_script_body{list_info} = <<"END_LIST_INFO"; |
|
1211
|
|
|
|
|
|
|
-- to obtain information on the status of the data list |
|
1212
|
|
|
|
|
|
|
|
|
1213
|
|
|
|
|
|
|
local coll_name = ARGV[1] |
|
1214
|
|
|
|
|
|
|
local list_id = ARGV[2] |
|
1215
|
|
|
|
|
|
|
|
|
1216
|
|
|
|
|
|
|
-- key data storage structures |
|
1217
|
|
|
|
|
|
|
$_lua_namespace |
|
1218
|
|
|
|
|
|
|
$_lua_queue_key |
|
1219
|
|
|
|
|
|
|
$_lua_status_key |
|
1220
|
|
|
|
|
|
|
$_lua_data_keys |
|
1221
|
|
|
|
|
|
|
$_lua_data_key |
|
1222
|
|
|
|
|
|
|
|
|
1223
|
|
|
|
|
|
|
-- determine whether there is a list of data and a collection |
|
1224
|
|
|
|
|
|
|
if redis.call( 'EXISTS', STATUS_KEY ) ~= 1 then |
|
1225
|
|
|
|
|
|
|
return { $E_COLLECTION_DELETED, false, nil } |
|
1226
|
|
|
|
|
|
|
end |
|
1227
|
|
|
|
|
|
|
if redis.call( 'EXISTS', DATA_KEY ) ~= 1 then |
|
1228
|
|
|
|
|
|
|
return { $E_NO_ERROR, false, nil } |
|
1229
|
|
|
|
|
|
|
end |
|
1230
|
|
|
|
|
|
|
|
|
1231
|
|
|
|
|
|
|
-- the length of the data list |
|
1232
|
|
|
|
|
|
|
local items = redis.call( 'HLEN', DATA_KEY ) |
|
1233
|
|
|
|
|
|
|
|
|
1234
|
|
|
|
|
|
|
-- the second data |
|
1235
|
|
|
|
|
|
|
local oldest_item_time = redis.call( 'ZSCORE', QUEUE_KEY, list_id ) |
|
1236
|
|
|
|
|
|
|
|
|
1237
|
|
|
|
|
|
|
return { $E_NO_ERROR, items, oldest_item_time } |
|
1238
|
|
|
|
|
|
|
END_LIST_INFO |
|
1239
|
|
|
|
|
|
|
|
|
1240
|
|
|
|
|
|
|
$lua_script_body{drop_collection} = <<"END_DROP_COLLECTION"; |
|
1241
|
|
|
|
|
|
|
-- to remove the entire collection |
|
1242
|
|
|
|
|
|
|
|
|
1243
|
|
|
|
|
|
|
local coll_name = ARGV[1] |
|
1244
|
|
|
|
|
|
|
|
|
1245
|
|
|
|
|
|
|
-- key data storage structures |
|
1246
|
|
|
|
|
|
|
$_lua_namespace |
|
1247
|
|
|
|
|
|
|
$_lua_queue_key |
|
1248
|
|
|
|
|
|
|
$_lua_status_key |
|
1249
|
|
|
|
|
|
|
|
|
1250
|
|
|
|
|
|
|
-- initialize the data returned from the script |
|
1251
|
|
|
|
|
|
|
local ret = 0 -- the number of deleted items |
|
1252
|
|
|
|
|
|
|
|
|
1253
|
|
|
|
|
|
|
if redis.call( 'EXISTS', STATUS_KEY ) == 1 then |
|
1254
|
|
|
|
|
|
|
ret = ret + redis.call( 'DEL', STATUS_KEY ) |
|
1255
|
|
|
|
|
|
|
end |
|
1256
|
|
|
|
|
|
|
|
|
1257
|
|
|
|
|
|
|
$_lua_clean_data |
|
1258
|
|
|
|
|
|
|
END_DROP_COLLECTION |
|
1259
|
|
|
|
|
|
|
|
|
1260
|
|
|
|
|
|
|
$lua_script_body{clear_collection} = <<"END_CLEAR_COLLECTION"; |
|
1261
|
|
|
|
|
|
|
-- to remove the entire collection data |
|
1262
|
|
|
|
|
|
|
|
|
1263
|
|
|
|
|
|
|
local coll_name = ARGV[1] |
|
1264
|
|
|
|
|
|
|
|
|
1265
|
|
|
|
|
|
|
-- key data storage structures |
|
1266
|
|
|
|
|
|
|
$_lua_namespace |
|
1267
|
|
|
|
|
|
|
$_lua_queue_key |
|
1268
|
|
|
|
|
|
|
$_lua_status_key |
|
1269
|
|
|
|
|
|
|
|
|
1270
|
|
|
|
|
|
|
-- initialize the data returned from the script |
|
1271
|
|
|
|
|
|
|
local ret = 0 -- the number of deleted items |
|
1272
|
|
|
|
|
|
|
|
|
1273
|
|
|
|
|
|
|
redis.call( 'HMSET', STATUS_KEY, |
|
1274
|
|
|
|
|
|
|
'$_LISTS', 0, |
|
1275
|
|
|
|
|
|
|
'$_ITEMS', 0, |
|
1276
|
|
|
|
|
|
|
'$_LAST_REMOVED_TIME', 0 |
|
1277
|
|
|
|
|
|
|
); |
|
1278
|
|
|
|
|
|
|
|
|
1279
|
|
|
|
|
|
|
$_lua_clean_data |
|
1280
|
|
|
|
|
|
|
END_CLEAR_COLLECTION |
|
1281
|
|
|
|
|
|
|
|
|
1282
|
|
|
|
|
|
|
$lua_script_body{drop_list} = <<"END_DROP_LIST"; |
|
1283
|
|
|
|
|
|
|
-- to remove the data_list |
|
1284
|
|
|
|
|
|
|
|
|
1285
|
|
|
|
|
|
|
local coll_name = ARGV[1] |
|
1286
|
|
|
|
|
|
|
local list_id = ARGV[2] |
|
1287
|
|
|
|
|
|
|
|
|
1288
|
|
|
|
|
|
|
-- key data storage structures |
|
1289
|
|
|
|
|
|
|
$_lua_namespace |
|
1290
|
|
|
|
|
|
|
$_lua_queue_key |
|
1291
|
|
|
|
|
|
|
$_lua_status_key |
|
1292
|
|
|
|
|
|
|
$_lua_data_keys |
|
1293
|
|
|
|
|
|
|
$_lua_data_key |
|
1294
|
|
|
|
|
|
|
|
|
1295
|
|
|
|
|
|
|
-- determine whether there is a list of data and a collection |
|
1296
|
|
|
|
|
|
|
if redis.call( 'EXISTS', STATUS_KEY ) ~= 1 then |
|
1297
|
|
|
|
|
|
|
return { $E_COLLECTION_DELETED, 0 } |
|
1298
|
|
|
|
|
|
|
end |
|
1299
|
|
|
|
|
|
|
if redis.call( 'EXISTS', DATA_KEY ) ~= 1 then |
|
1300
|
|
|
|
|
|
|
return { $E_NO_ERROR, 0 } |
|
1301
|
|
|
|
|
|
|
end |
|
1302
|
|
|
|
|
|
|
|
|
1303
|
|
|
|
|
|
|
-- initialize the data returned from the script |
|
1304
|
|
|
|
|
|
|
local ret = 0 -- the number of deleted items |
|
1305
|
|
|
|
|
|
|
|
|
1306
|
|
|
|
|
|
|
-- key data storage structures |
|
1307
|
|
|
|
|
|
|
$_lua_time_keys |
|
1308
|
|
|
|
|
|
|
$_lua_time_key |
|
1309
|
|
|
|
|
|
|
|
|
1310
|
|
|
|
|
|
|
-- determine the size of the data in the list and delete the list structure |
|
1311
|
|
|
|
|
|
|
local bytes_deleted = 0 |
|
1312
|
|
|
|
|
|
|
local vals = redis.call( 'HVALS', DATA_KEY ) |
|
1313
|
|
|
|
|
|
|
local list_items = #vals |
|
1314
|
|
|
|
|
|
|
for i = 1, list_items do |
|
1315
|
|
|
|
|
|
|
bytes_deleted = bytes_deleted + #vals[ i ] |
|
1316
|
|
|
|
|
|
|
end |
|
1317
|
|
|
|
|
|
|
redis.call( 'DEL', DATA_KEY, TIME_KEY ) |
|
1318
|
|
|
|
|
|
|
|
|
1319
|
|
|
|
|
|
|
-- reduce the number of items in the collection |
|
1320
|
|
|
|
|
|
|
redis.call( 'HINCRBY', STATUS_KEY, '$_ITEMS', -list_items ) |
|
1321
|
|
|
|
|
|
|
-- reduce the number of lists stored in a collection |
|
1322
|
|
|
|
|
|
|
redis.call( 'HINCRBY', STATUS_KEY, '$_LISTS', -1 ) |
|
1323
|
|
|
|
|
|
|
-- remove the name of the list from the queue collection |
|
1324
|
|
|
|
|
|
|
redis.call( 'ZREM', QUEUE_KEY, list_id ) |
|
1325
|
|
|
|
|
|
|
|
|
1326
|
|
|
|
|
|
|
return { $E_NO_ERROR, 1 } |
|
1327
|
|
|
|
|
|
|
END_DROP_LIST |
|
1328
|
|
|
|
|
|
|
|
|
1329
|
|
|
|
|
|
|
$lua_script_body{verify_collection} = <<"END_VERIFY_COLLECTION"; |
|
1330
|
|
|
|
|
|
|
-- creation of the collection and characterization of the collection by accessing existing collection |
|
1331
|
|
|
|
|
|
|
|
|
1332
|
|
|
|
|
|
|
local coll_name = ARGV[1]; |
|
1333
|
|
|
|
|
|
|
local older_allowed = ARGV[2]; |
|
1334
|
|
|
|
|
|
|
local min_cleanup_bytes = ARGV[3]; |
|
1335
|
|
|
|
|
|
|
local min_cleanup_items = ARGV[4]; |
|
1336
|
|
|
|
|
|
|
local memory_reserve = ARGV[5]; |
|
1337
|
|
|
|
|
|
|
|
|
1338
|
|
|
|
|
|
|
local data_version = '$DATA_VERSION' |
|
1339
|
|
|
|
|
|
|
|
|
1340
|
|
|
|
|
|
|
-- key data storage structures |
|
1341
|
|
|
|
|
|
|
$_lua_namespace |
|
1342
|
|
|
|
|
|
|
$_lua_status_key |
|
1343
|
|
|
|
|
|
|
|
|
1344
|
|
|
|
|
|
|
-- determine whether there is a collection |
|
1345
|
|
|
|
|
|
|
local status_exist = redis.call( 'EXISTS', STATUS_KEY ); |
|
1346
|
|
|
|
|
|
|
|
|
1347
|
|
|
|
|
|
|
if status_exist == 1 then |
|
1348
|
|
|
|
|
|
|
-- if there is a collection |
|
1349
|
|
|
|
|
|
|
older_allowed, min_cleanup_bytes, min_cleanup_items, memory_reserve, data_version = unpack( redis.call( 'HMGET', STATUS_KEY, |
|
1350
|
|
|
|
|
|
|
'$_OLDER_ALLOWED', |
|
1351
|
|
|
|
|
|
|
'$_MIN_CLEANUP_BYTES', |
|
1352
|
|
|
|
|
|
|
'$_MIN_CLEANUP_ITEMS', |
|
1353
|
|
|
|
|
|
|
'$_MEMORY_RESERVE', |
|
1354
|
|
|
|
|
|
|
'$_DATA_VERSION' |
|
1355
|
|
|
|
|
|
|
) ); |
|
1356
|
|
|
|
|
|
|
|
|
1357
|
|
|
|
|
|
|
if type( data_version ) ~= 'string' then data_version = '0' end |
|
1358
|
|
|
|
|
|
|
else |
|
1359
|
|
|
|
|
|
|
-- if you want to create a new collection |
|
1360
|
|
|
|
|
|
|
redis.call( 'HMSET', STATUS_KEY, |
|
1361
|
|
|
|
|
|
|
'$_LISTS', 0, |
|
1362
|
|
|
|
|
|
|
'$_ITEMS', 0, |
|
1363
|
|
|
|
|
|
|
'$_OLDER_ALLOWED', older_allowed, |
|
1364
|
|
|
|
|
|
|
'$_MIN_CLEANUP_BYTES', min_cleanup_bytes, |
|
1365
|
|
|
|
|
|
|
'$_MIN_CLEANUP_ITEMS', min_cleanup_items, |
|
1366
|
|
|
|
|
|
|
'$_MEMORY_RESERVE', memory_reserve, |
|
1367
|
|
|
|
|
|
|
'$_DATA_VERSION', data_version, |
|
1368
|
|
|
|
|
|
|
'$_LAST_REMOVED_TIME', 0, |
|
1369
|
|
|
|
|
|
|
'$_LAST_CLEANUP_BYTES', 0, |
|
1370
|
|
|
|
|
|
|
'$_LAST_CLEANUP_ITEMS', 0 |
|
1371
|
|
|
|
|
|
|
); |
|
1372
|
|
|
|
|
|
|
end |
|
1373
|
|
|
|
|
|
|
|
|
1374
|
|
|
|
|
|
|
return { |
|
1375
|
|
|
|
|
|
|
status_exist, |
|
1376
|
|
|
|
|
|
|
older_allowed, |
|
1377
|
|
|
|
|
|
|
min_cleanup_bytes, |
|
1378
|
|
|
|
|
|
|
min_cleanup_items, |
|
1379
|
|
|
|
|
|
|
memory_reserve, |
|
1380
|
|
|
|
|
|
|
data_version |
|
1381
|
|
|
|
|
|
|
}; |
|
1382
|
|
|
|
|
|
|
END_VERIFY_COLLECTION |
|
1383
|
|
|
|
|
|
|
|
|
1384
|
|
|
|
|
|
|
$lua_script_body{_long_term_operation} = <<"END_LONG_TERM_OPERATION"; |
|
1385
|
|
|
|
|
|
|
local coll_name = ARGV[1]; |
|
1386
|
|
|
|
|
|
|
local return_as_insert = tonumber( ARGV[2] ); |
|
1387
|
|
|
|
|
|
|
|
|
1388
|
|
|
|
|
|
|
local STATUS_KEY = 'C:S:'..coll_name; |
|
1389
|
|
|
|
|
|
|
local DATA_VERSION_KEY = '$_DATA_VERSION'; |
|
1390
|
|
|
|
|
|
|
|
|
1391
|
|
|
|
|
|
|
local LIST = 'Test_list'; |
|
1392
|
|
|
|
|
|
|
local DATA = 'Data'; |
|
1393
|
|
|
|
|
|
|
local MAX_WORKING_CYCLES = 5000000; |
|
1394
|
|
|
|
|
|
|
|
|
1395
|
|
|
|
|
|
|
redis.call( 'DEL', LIST ); |
|
1396
|
|
|
|
|
|
|
|
|
1397
|
|
|
|
|
|
|
local ret; |
|
1398
|
|
|
|
|
|
|
local i = 1; |
|
1399
|
|
|
|
|
|
|
while i < MAX_WORKING_CYCLES do |
|
1400
|
|
|
|
|
|
|
-- simple active actions |
|
1401
|
|
|
|
|
|
|
local data_version = redis.call( 'HGET', STATUS_KEY, DATA_VERSION_KEY ); |
|
1402
|
|
|
|
|
|
|
ret = redis.call( 'HSET', LIST, i, DATA ); |
|
1403
|
|
|
|
|
|
|
|
|
1404
|
|
|
|
|
|
|
i = i + 1; |
|
1405
|
|
|
|
|
|
|
end |
|
1406
|
|
|
|
|
|
|
|
|
1407
|
|
|
|
|
|
|
if return_as_insert == 1 then |
|
1408
|
|
|
|
|
|
|
return { $E_NO_ERROR, 0, 0, 0 }; |
|
1409
|
|
|
|
|
|
|
else |
|
1410
|
|
|
|
|
|
|
return { $E_NO_ERROR, ret, '_long_term_operation' }; |
|
1411
|
|
|
|
|
|
|
end |
|
1412
|
|
|
|
|
|
|
END_LONG_TERM_OPERATION |
|
1413
|
|
|
|
|
|
|
|
|
1414
|
|
|
|
|
|
|
subtype __PACKAGE__.'::NonNegInt', |
|
1415
|
|
|
|
|
|
|
as 'Int', |
|
1416
|
|
|
|
|
|
|
where { $_ >= 0 }, |
|
1417
|
|
|
|
|
|
|
message { format_message( '%s is not a non-negative integer!', $_ ) } |
|
1418
|
|
|
|
|
|
|
; |
|
1419
|
|
|
|
|
|
|
|
|
1420
|
|
|
|
|
|
|
subtype __PACKAGE__.'::NonNegNum', |
|
1421
|
|
|
|
|
|
|
as 'Num', |
|
1422
|
|
|
|
|
|
|
where { $_ >= 0 }, |
|
1423
|
|
|
|
|
|
|
message { format_message( '%s is not a non-negative number!', $_ ) } |
|
1424
|
|
|
|
|
|
|
; |
|
1425
|
|
|
|
|
|
|
|
|
1426
|
|
|
|
|
|
|
subtype __PACKAGE__.'::NonEmptNameStr', |
|
1427
|
|
|
|
|
|
|
as 'Str', |
|
1428
|
|
|
|
|
|
|
where { $_ ne '' && $_ !~ /:/ }, |
|
1429
|
|
|
|
|
|
|
message { format_message( '%s is not a non-empty string!', $_ ) } |
|
1430
|
|
|
|
|
|
|
; |
|
1431
|
|
|
|
|
|
|
|
|
1432
|
|
|
|
|
|
|
subtype __PACKAGE__.'::DataStr', |
|
1433
|
|
|
|
|
|
|
as 'Str', |
|
1434
|
|
|
|
|
|
|
where { bytes::length( $_ ) <= $MAX_DATASIZE }, |
|
1435
|
|
|
|
|
|
|
message { format_message( "'%s' is not a valid data string!", $_ ) } |
|
1436
|
|
|
|
|
|
|
; |
|
1437
|
|
|
|
|
|
|
|
|
1438
|
|
|
|
|
|
|
#-- constructor ---------------------------------------------------------------- |
|
1439
|
|
|
|
|
|
|
|
|
1440
|
|
|
|
|
|
|
=head2 CONSTRUCTOR |
|
1441
|
|
|
|
|
|
|
|
|
1442
|
|
|
|
|
|
|
=head3 create |
|
1443
|
|
|
|
|
|
|
|
|
1444
|
|
|
|
|
|
|
create( redis => $server, name => $name, ... ) |
|
1445
|
|
|
|
|
|
|
|
|
1446
|
|
|
|
|
|
|
Create a new collection on the Redis server and return an C |
|
1447
|
|
|
|
|
|
|
object to access it. Must be called as a class method only. |
|
1448
|
|
|
|
|
|
|
|
|
1449
|
|
|
|
|
|
|
The C creates and returns a C object that is configured |
|
1450
|
|
|
|
|
|
|
to work with the default settings if the corresponding arguments were not given. |
|
1451
|
|
|
|
|
|
|
|
|
1452
|
|
|
|
|
|
|
C argument can be either an existing object of L class |
|
1453
|
|
|
|
|
|
|
(which is then used for all communication with Redis server) or a hash reference used to create a |
|
1454
|
|
|
|
|
|
|
new internal Redis object. See documentation of L module for details. |
|
1455
|
|
|
|
|
|
|
|
|
1456
|
|
|
|
|
|
|
C takes arguments in key-value pairs. |
|
1457
|
|
|
|
|
|
|
|
|
1458
|
|
|
|
|
|
|
This example illustrates a C call with all the valid arguments: |
|
1459
|
|
|
|
|
|
|
|
|
1460
|
|
|
|
|
|
|
my $coll = Redis::CappedCollection->create( |
|
1461
|
|
|
|
|
|
|
redis => { server => "$server:$port" }, # Redis object |
|
1462
|
|
|
|
|
|
|
# or hash reference to parameters to create a new Redis object. |
|
1463
|
|
|
|
|
|
|
name => 'Some name', # Redis::CappedCollection collection name. |
|
1464
|
|
|
|
|
|
|
min_cleanup_bytes => 50_000, # The minimum size, in bytes, |
|
1465
|
|
|
|
|
|
|
# of the data to be released when performing memory cleanup. |
|
1466
|
|
|
|
|
|
|
min_cleanup_items => 1_000, # The minimum number of the collection |
|
1467
|
|
|
|
|
|
|
# elements to be realesed when performing memory cleanup. |
|
1468
|
|
|
|
|
|
|
max_datasize => 1_000_000, # Maximum size, in bytes, of the data. |
|
1469
|
|
|
|
|
|
|
# Default 512MB. |
|
1470
|
|
|
|
|
|
|
older_allowed => 0, # Allow adding an element to collection that's older |
|
1471
|
|
|
|
|
|
|
# than the last element removed from collection. |
|
1472
|
|
|
|
|
|
|
# Default 0. |
|
1473
|
|
|
|
|
|
|
check_maxmemory => 1, # Controls if collection should try to find out maximum |
|
1474
|
|
|
|
|
|
|
# available memory from Redis. |
|
1475
|
|
|
|
|
|
|
# In some cases Redis implementation forbids such request, |
|
1476
|
|
|
|
|
|
|
# but setting 'check_maxmemory' to false can be used |
|
1477
|
|
|
|
|
|
|
# as a workaround. |
|
1478
|
|
|
|
|
|
|
memory_reserve => 0.05, # Reserve coefficient of 'maxmemory'. |
|
1479
|
|
|
|
|
|
|
# Not used when 'maxmemory' == 0 (it is not set in the redis.conf). |
|
1480
|
|
|
|
|
|
|
# When you add or modify the data trying to ensure |
|
1481
|
|
|
|
|
|
|
# reserve of free memory for metadata and bookkeeping. |
|
1482
|
|
|
|
|
|
|
reconnect_on_error => 0, # Controls ability to force re-connection with Redis on error. |
|
1483
|
|
|
|
|
|
|
connection_timeout => $DEFAULT_CONNECTION_TIMEOUT, # Socket timeout for connection, |
|
1484
|
|
|
|
|
|
|
# number of seconds (can be fractional). |
|
1485
|
|
|
|
|
|
|
# NOTE: Changes external socket configuration. |
|
1486
|
|
|
|
|
|
|
operation_timeout => $DEFAULT_OPERATION_TIMEOUT, # Socket timeout for read and write operations, |
|
1487
|
|
|
|
|
|
|
# number of seconds (can be fractional). |
|
1488
|
|
|
|
|
|
|
# NOTE: Changes external socket configuration. |
|
1489
|
|
|
|
|
|
|
); |
|
1490
|
|
|
|
|
|
|
|
|
1491
|
|
|
|
|
|
|
The C and C arguments are required. |
|
1492
|
|
|
|
|
|
|
Do not use the symbol C<':'> in C. |
|
1493
|
|
|
|
|
|
|
|
|
1494
|
|
|
|
|
|
|
The following examples illustrate other uses of the C method: |
|
1495
|
|
|
|
|
|
|
|
|
1496
|
|
|
|
|
|
|
my $redis = Redis->new( server => "$server:$port" ); |
|
1497
|
|
|
|
|
|
|
my $coll = Redis::CappedCollection->create( redis => $redis, name => 'Next collection' ); |
|
1498
|
|
|
|
|
|
|
my $next_coll = Redis::CappedCollection->create( redis => $coll, name => 'Some name' ); |
|
1499
|
|
|
|
|
|
|
|
|
1500
|
|
|
|
|
|
|
An error exception is thrown (C) if an argument is not valid or the collection with |
|
1501
|
|
|
|
|
|
|
same name already exists. |
|
1502
|
|
|
|
|
|
|
|
|
1503
|
|
|
|
|
|
|
=cut |
|
1504
|
|
|
|
|
|
|
sub create { |
|
1505
|
0
|
0
|
|
0
|
1
|
|
my $class = _CLASSISA( shift, __PACKAGE__ ) or confess 'Must be called as a class method only'; |
|
1506
|
0
|
|
|
|
|
|
return $class->new( @_, _create_from_naked_new => 0 ); |
|
1507
|
|
|
|
|
|
|
} |
|
1508
|
|
|
|
|
|
|
|
|
1509
|
|
|
|
|
|
|
sub BUILD { |
|
1510
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
|
1511
|
|
|
|
|
|
|
|
|
1512
|
0
|
|
|
|
|
|
my $redis = $self->redis; |
|
1513
|
0
|
0
|
|
|
|
|
if ( _INSTANCE( $redis, 'Redis' ) ) { |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
1514
|
|
|
|
|
|
|
# have to look into the Redis object ... |
|
1515
|
0
|
|
|
|
|
|
$self->_server( $redis->{server} ); |
|
1516
|
0
|
|
|
|
|
|
$self->_redis( $redis ); |
|
1517
|
|
|
|
|
|
|
} elsif ( _INSTANCE( $redis, 'Test::RedisServer' ) ) { |
|
1518
|
|
|
|
|
|
|
# to test only |
|
1519
|
|
|
|
|
|
|
# have to look into the Test::RedisServer object ... |
|
1520
|
0
|
|
|
|
|
|
my $conf = $redis->conf; |
|
1521
|
0
|
0
|
|
|
|
|
$conf->{server} = '127.0.0.1:'.$conf->{port} unless exists $conf->{server}; |
|
1522
|
0
|
|
|
|
|
|
$self->_server( $conf->{server} ); |
|
1523
|
0
|
|
|
|
|
|
$self->_redis( Redis->new( server => $conf->{server} ) ); |
|
1524
|
|
|
|
|
|
|
} elsif ( _INSTANCE( $redis, __PACKAGE__ ) ) { |
|
1525
|
0
|
|
|
|
|
|
$self->_server( $redis->_server ); |
|
1526
|
0
|
|
|
|
|
|
$self->_redis( $self->_redis ); |
|
1527
|
|
|
|
|
|
|
} else { # $redis is hash ref |
|
1528
|
0
|
|
0
|
|
|
|
$self->_server( $redis->{server} // "$DEFAULT_SERVER:$DEFAULT_PORT" ); |
|
1529
|
|
|
|
|
|
|
|
|
1530
|
|
|
|
|
|
|
# defaults for the case when the Redis object we create |
|
1531
|
0
|
0
|
|
|
|
|
$redis->{reconnect} = 0 unless exists $redis->{reconnect}; |
|
1532
|
0
|
0
|
|
|
|
|
$redis->{every} = 1000 unless exists $redis->{every}; # 1 ms |
|
1533
|
0
|
0
|
|
|
|
|
$redis->{conservative_reconnect} = 0 unless exists $redis->{conservative_reconnect}; |
|
1534
|
0
|
0
|
|
|
|
|
$redis->{cnx_timeout} = $DEFAULT_CONNECTION_TIMEOUT unless exists $redis->{cnx_timeout}; |
|
1535
|
0
|
0
|
|
|
|
|
$redis->{read_timeout} = $DEFAULT_OPERATION_TIMEOUT unless exists $redis->{read_timeout}; |
|
1536
|
0
|
0
|
|
|
|
|
$redis->{write_timeout} = $DEFAULT_OPERATION_TIMEOUT unless exists $redis->{write_timeout}; |
|
1537
|
|
|
|
|
|
|
|
|
1538
|
0
|
|
|
|
|
|
$self->_redis( $self->_redis_constructor( $redis ) ); |
|
1539
|
0
|
|
|
|
|
|
$self->_use_external_connection( 0 ); |
|
1540
|
|
|
|
|
|
|
} |
|
1541
|
|
|
|
|
|
|
|
|
1542
|
0
|
|
|
|
|
|
$self->_connection_timeout_trigger( $self->connection_timeout ); |
|
1543
|
0
|
|
|
|
|
|
$self->_operation_timeout_trigger( $self->operation_timeout ); |
|
1544
|
|
|
|
|
|
|
|
|
1545
|
0
|
0
|
|
|
|
|
if ( $self->_create_from_naked_new ) { |
|
1546
|
0
|
|
|
|
|
|
warn 'Redis::CappedCollection->new() is deprecated and will be removed in future. Please use either create() or open() instead.'; |
|
1547
|
|
|
|
|
|
|
} else { |
|
1548
|
0
|
0
|
0
|
|
|
|
confess format_message( "Collection '%s' already exists", $self->name ) |
|
1549
|
|
|
|
|
|
|
if !$self->_create_from_open && $self->collection_exists( name => $self->name ); |
|
1550
|
|
|
|
|
|
|
} |
|
1551
|
|
|
|
|
|
|
|
|
1552
|
0
|
|
|
|
|
|
my $maxmemory; |
|
1553
|
0
|
0
|
|
|
|
|
if ( $self->_check_maxmemory ) { |
|
1554
|
0
|
|
|
|
|
|
( undef, $maxmemory ) = $self->_call_redis( 'CONFIG', 'GET', 'maxmemory' ); |
|
1555
|
0
|
0
|
|
|
|
|
defined( _NONNEGINT( $maxmemory ) ) |
|
1556
|
|
|
|
|
|
|
or $self->_throw( $E_NETWORK ); |
|
1557
|
|
|
|
|
|
|
} else { |
|
1558
|
|
|
|
|
|
|
# 0 means all system memory |
|
1559
|
0
|
|
|
|
|
|
$maxmemory = 0; |
|
1560
|
|
|
|
|
|
|
} |
|
1561
|
|
|
|
|
|
|
|
|
1562
|
0
|
|
|
|
|
|
my ( $major, $minor ) = $self->_redis->info->{redis_version} =~ /^(\d+)\.(\d+)/; |
|
1563
|
0
|
0
|
0
|
|
|
|
if ( $major < 2 || ( $major == 2 && $minor < 8 ) ) { |
|
|
|
|
0
|
|
|
|
|
|
1564
|
0
|
|
|
|
|
|
$self->_set_last_errorcode( $E_REDIS ); |
|
1565
|
0
|
|
|
|
|
|
confess "Need a Redis server version 2.8 or higher"; |
|
1566
|
|
|
|
|
|
|
} |
|
1567
|
|
|
|
|
|
|
|
|
1568
|
0
|
0
|
|
|
|
|
$self->_throw( $E_MAXMEMORY_POLICY ) |
|
1569
|
|
|
|
|
|
|
unless $self->_maxmemory_policy_ok; |
|
1570
|
|
|
|
|
|
|
|
|
1571
|
0
|
|
|
|
|
|
$self->_maxmemory( $maxmemory ); |
|
1572
|
0
|
0
|
|
|
|
|
$self->max_datasize( min $self->_maxmemory, $self->max_datasize ) |
|
1573
|
|
|
|
|
|
|
if $self->_maxmemory; |
|
1574
|
|
|
|
|
|
|
|
|
1575
|
0
|
|
|
|
|
|
$self->_queue_key( $NAMESPACE.':Q:'.$self->name ); |
|
1576
|
0
|
|
|
|
|
|
$self->_status_key( _make_status_key( $self->name ) ); |
|
1577
|
0
|
|
|
|
|
|
$self->_data_keys( _make_data_key( $self->name ) ); |
|
1578
|
0
|
|
|
|
|
|
$self->_time_keys( $NAMESPACE.':T:'.$self->name ); |
|
1579
|
|
|
|
|
|
|
|
|
1580
|
0
|
0
|
|
|
|
|
$self->_verify_collection unless $self->_create_from_open; |
|
1581
|
|
|
|
|
|
|
} |
|
1582
|
|
|
|
|
|
|
|
|
1583
|
|
|
|
|
|
|
#-- public attributes ---------------------------------------------------------- |
|
1584
|
|
|
|
|
|
|
|
|
1585
|
|
|
|
|
|
|
=head3 open |
|
1586
|
|
|
|
|
|
|
|
|
1587
|
|
|
|
|
|
|
open( redis => $server, name => $name, ... ) |
|
1588
|
|
|
|
|
|
|
|
|
1589
|
|
|
|
|
|
|
Example: |
|
1590
|
|
|
|
|
|
|
|
|
1591
|
|
|
|
|
|
|
my $redis = Redis->new( server => "$server:$port" ); |
|
1592
|
|
|
|
|
|
|
my $coll = Redis::CappedCollection::open( redis => $redis, name => 'Some name' ); |
|
1593
|
|
|
|
|
|
|
|
|
1594
|
|
|
|
|
|
|
Create a C object to work with an existing collection |
|
1595
|
|
|
|
|
|
|
(created by L). It must be called as a class method only. |
|
1596
|
|
|
|
|
|
|
|
|
1597
|
|
|
|
|
|
|
C takes optional arguments. These arguments are in key-value pairs. |
|
1598
|
|
|
|
|
|
|
Arguments description is the same as for L method. |
|
1599
|
|
|
|
|
|
|
|
|
1600
|
|
|
|
|
|
|
=over 3 |
|
1601
|
|
|
|
|
|
|
|
|
1602
|
|
|
|
|
|
|
=item I |
|
1603
|
|
|
|
|
|
|
|
|
1604
|
|
|
|
|
|
|
=item I |
|
1605
|
|
|
|
|
|
|
|
|
1606
|
|
|
|
|
|
|
=item I |
|
1607
|
|
|
|
|
|
|
|
|
1608
|
|
|
|
|
|
|
=item I |
|
1609
|
|
|
|
|
|
|
|
|
1610
|
|
|
|
|
|
|
=item I |
|
1611
|
|
|
|
|
|
|
|
|
1612
|
|
|
|
|
|
|
=item I |
|
1613
|
|
|
|
|
|
|
|
|
1614
|
|
|
|
|
|
|
=item I |
|
1615
|
|
|
|
|
|
|
|
|
1616
|
|
|
|
|
|
|
=back |
|
1617
|
|
|
|
|
|
|
|
|
1618
|
|
|
|
|
|
|
The C and C arguments are mandatory. |
|
1619
|
|
|
|
|
|
|
|
|
1620
|
|
|
|
|
|
|
The C creates and returns a C object that is configured |
|
1621
|
|
|
|
|
|
|
to work with the default settings if the corresponding arguments are not given. |
|
1622
|
|
|
|
|
|
|
|
|
1623
|
|
|
|
|
|
|
If C argument is not a L object, a new connection to Redis is established using |
|
1624
|
|
|
|
|
|
|
passed hash reference to create a new L object. |
|
1625
|
|
|
|
|
|
|
|
|
1626
|
|
|
|
|
|
|
An error exception is thrown (C) if an argument is not valid. |
|
1627
|
|
|
|
|
|
|
|
|
1628
|
|
|
|
|
|
|
=cut |
|
1629
|
|
|
|
|
|
|
my @_asked_parameters = qw( |
|
1630
|
|
|
|
|
|
|
redis |
|
1631
|
|
|
|
|
|
|
name |
|
1632
|
|
|
|
|
|
|
max_datasize |
|
1633
|
|
|
|
|
|
|
check_maxmemory |
|
1634
|
|
|
|
|
|
|
reconnect_on_error |
|
1635
|
|
|
|
|
|
|
connection_timeout |
|
1636
|
|
|
|
|
|
|
operation_timeout |
|
1637
|
|
|
|
|
|
|
); |
|
1638
|
|
|
|
|
|
|
my @_status_parameters = qw( |
|
1639
|
|
|
|
|
|
|
older_allowed |
|
1640
|
|
|
|
|
|
|
min_cleanup_bytes |
|
1641
|
|
|
|
|
|
|
min_cleanup_items |
|
1642
|
|
|
|
|
|
|
memory_reserve |
|
1643
|
|
|
|
|
|
|
); |
|
1644
|
|
|
|
|
|
|
|
|
1645
|
|
|
|
|
|
|
sub open { |
|
1646
|
0
|
0
|
|
0
|
1
|
|
my $class = _CLASSISA( shift, __PACKAGE__ ) or confess 'Must be called as a class method only'; |
|
1647
|
|
|
|
|
|
|
|
|
1648
|
0
|
|
|
|
|
|
my %params = @_; |
|
1649
|
0
|
|
|
|
|
|
_check_arguments_acceptability( \%params, \@_asked_parameters ); |
|
1650
|
|
|
|
|
|
|
|
|
1651
|
0
|
0
|
|
|
|
|
confess "'redis' argument is required" unless exists $params{redis}; |
|
1652
|
0
|
0
|
|
|
|
|
confess "'name' argument is required" unless exists $params{name}; |
|
1653
|
|
|
|
|
|
|
|
|
1654
|
0
|
|
|
|
|
|
my $use_external_connection = ref( $params{redis} ) ne 'HASH'; |
|
1655
|
0
|
|
|
|
|
|
my $redis = $params{redis} = _get_redis( $params{redis} ); |
|
1656
|
0
|
|
|
|
|
|
my $name = $params{name}; |
|
1657
|
0
|
0
|
|
|
|
|
if ( collection_exists( redis => $redis, name => $name ) ) { |
|
1658
|
0
|
|
|
|
|
|
my $info = collection_info( redis => $redis, name => $name ); |
|
1659
|
0
|
0
|
|
|
|
|
$info->{data_version} == $DATA_VERSION or confess $ERROR{ $E_INCOMP_DATA_VERSION }; |
|
1660
|
0
|
|
|
|
|
|
$params{ $_ } = $info->{ $_ } foreach @_status_parameters; |
|
1661
|
0
|
|
|
|
|
|
return $class->new( %params, |
|
1662
|
|
|
|
|
|
|
_create_from_naked_new => 0, |
|
1663
|
|
|
|
|
|
|
_create_from_open => 1, |
|
1664
|
|
|
|
|
|
|
_use_external_connection => $use_external_connection, |
|
1665
|
|
|
|
|
|
|
); |
|
1666
|
|
|
|
|
|
|
} else { |
|
1667
|
0
|
|
|
|
|
|
confess format_message( "Collection '%s' does not exist", $name ); |
|
1668
|
|
|
|
|
|
|
}; |
|
1669
|
|
|
|
|
|
|
} |
|
1670
|
|
|
|
|
|
|
|
|
1671
|
|
|
|
|
|
|
=head2 METHODS |
|
1672
|
|
|
|
|
|
|
|
|
1673
|
|
|
|
|
|
|
An exception is thrown (C) if any method argument is not valid or |
|
1674
|
|
|
|
|
|
|
if a required argument is missing. |
|
1675
|
|
|
|
|
|
|
|
|
1676
|
|
|
|
|
|
|
ATTENTION: In the L module the synchronous commands throw an |
|
1677
|
|
|
|
|
|
|
exception on receipt of an error reply, or return a non-error reply directly. |
|
1678
|
|
|
|
|
|
|
|
|
1679
|
|
|
|
|
|
|
=cut |
|
1680
|
|
|
|
|
|
|
|
|
1681
|
|
|
|
|
|
|
=head3 name |
|
1682
|
|
|
|
|
|
|
|
|
1683
|
|
|
|
|
|
|
Get collection C attribute (collection ID). |
|
1684
|
|
|
|
|
|
|
The method returns the current value of the attribute. |
|
1685
|
|
|
|
|
|
|
The C attribute value is used in the L. |
|
1686
|
|
|
|
|
|
|
|
|
1687
|
|
|
|
|
|
|
=cut |
|
1688
|
|
|
|
|
|
|
has name => ( |
|
1689
|
|
|
|
|
|
|
is => 'ro', |
|
1690
|
|
|
|
|
|
|
clearer => '_clear_name', |
|
1691
|
|
|
|
|
|
|
isa => __PACKAGE__.'::NonEmptNameStr', |
|
1692
|
|
|
|
|
|
|
required => 1, |
|
1693
|
|
|
|
|
|
|
); |
|
1694
|
|
|
|
|
|
|
|
|
1695
|
|
|
|
|
|
|
=head3 redis |
|
1696
|
|
|
|
|
|
|
|
|
1697
|
|
|
|
|
|
|
Existing L object or a hash reference with parameters to create a new one. |
|
1698
|
|
|
|
|
|
|
|
|
1699
|
|
|
|
|
|
|
=cut |
|
1700
|
|
|
|
|
|
|
has redis => ( |
|
1701
|
|
|
|
|
|
|
is => 'ro', |
|
1702
|
|
|
|
|
|
|
isa => 'Redis|Test::RedisServer|HashRef', |
|
1703
|
|
|
|
|
|
|
required => 1, |
|
1704
|
|
|
|
|
|
|
); |
|
1705
|
|
|
|
|
|
|
|
|
1706
|
|
|
|
|
|
|
=head3 reconnect_on_error |
|
1707
|
|
|
|
|
|
|
|
|
1708
|
|
|
|
|
|
|
Controls ability to force re-connection with Redis on error. |
|
1709
|
|
|
|
|
|
|
|
|
1710
|
|
|
|
|
|
|
=cut |
|
1711
|
|
|
|
|
|
|
has reconnect_on_error => ( |
|
1712
|
|
|
|
|
|
|
is => 'rw', |
|
1713
|
|
|
|
|
|
|
isa => 'Bool', |
|
1714
|
|
|
|
|
|
|
default => 0, |
|
1715
|
|
|
|
|
|
|
); |
|
1716
|
|
|
|
|
|
|
|
|
1717
|
|
|
|
|
|
|
=head3 connection_timeout |
|
1718
|
|
|
|
|
|
|
|
|
1719
|
|
|
|
|
|
|
Controls socket timeout for Redis server connection, number of seconds (can be fractional). |
|
1720
|
|
|
|
|
|
|
|
|
1721
|
|
|
|
|
|
|
NOTE: Changes external socket configuration. |
|
1722
|
|
|
|
|
|
|
|
|
1723
|
|
|
|
|
|
|
=cut |
|
1724
|
|
|
|
|
|
|
has connection_timeout => ( |
|
1725
|
|
|
|
|
|
|
is => 'rw', |
|
1726
|
|
|
|
|
|
|
isa => 'Maybe['.__PACKAGE__.'::NonNegNum]', |
|
1727
|
|
|
|
|
|
|
default => undef, |
|
1728
|
|
|
|
|
|
|
trigger => \&_connection_timeout_trigger, |
|
1729
|
|
|
|
|
|
|
); |
|
1730
|
|
|
|
|
|
|
|
|
1731
|
|
|
|
|
|
|
sub _connection_timeout_trigger { |
|
1732
|
0
|
|
|
0
|
|
|
my ( $self, $timeout, $old_timeout ) = @_; |
|
1733
|
|
|
|
|
|
|
|
|
1734
|
0
|
0
|
0
|
|
|
|
return if scalar( @_ ) == 2 && ( !defined( $timeout ) && !defined( $old_timeout ) ); |
|
|
|
|
0
|
|
|
|
|
|
1735
|
|
|
|
|
|
|
|
|
1736
|
0
|
0
|
|
|
|
|
if ( my $redis = $self->_redis ) { |
|
1737
|
0
|
0
|
|
|
|
|
my $socket = _INSTANCE( $redis->{sock}, 'IO::Socket' ) or confess 'Bad socket object'; |
|
1738
|
|
|
|
|
|
|
# IO::Socket provides a way to set a timeout on the socket, |
|
1739
|
|
|
|
|
|
|
# but the timeout will be used only for connection, |
|
1740
|
|
|
|
|
|
|
# not for reading / writing operations. |
|
1741
|
0
|
|
|
|
|
|
$socket->timeout( $redis->{cnx_timeout} = $timeout ); |
|
1742
|
|
|
|
|
|
|
} |
|
1743
|
|
|
|
|
|
|
} |
|
1744
|
|
|
|
|
|
|
|
|
1745
|
|
|
|
|
|
|
=head3 operation_timeout |
|
1746
|
|
|
|
|
|
|
|
|
1747
|
|
|
|
|
|
|
Controls socket timeout for Redis server read and write operations, number of seconds (can be fractional). |
|
1748
|
|
|
|
|
|
|
|
|
1749
|
|
|
|
|
|
|
NOTE: Changes external socket configuration. |
|
1750
|
|
|
|
|
|
|
|
|
1751
|
|
|
|
|
|
|
=cut |
|
1752
|
|
|
|
|
|
|
has operation_timeout => ( |
|
1753
|
|
|
|
|
|
|
is => 'rw', |
|
1754
|
|
|
|
|
|
|
isa => 'Maybe['.__PACKAGE__.'::NonNegNum]', |
|
1755
|
|
|
|
|
|
|
default => undef, |
|
1756
|
|
|
|
|
|
|
trigger => \&_operation_timeout_trigger, |
|
1757
|
|
|
|
|
|
|
); |
|
1758
|
|
|
|
|
|
|
|
|
1759
|
|
|
|
|
|
|
sub _operation_timeout_trigger { |
|
1760
|
0
|
|
|
0
|
|
|
my ( $self, $timeout, $old_timeout ) = @_; |
|
1761
|
|
|
|
|
|
|
|
|
1762
|
0
|
0
|
0
|
|
|
|
return if scalar( @_ ) == 2 && ( !defined( $timeout ) && !defined( $old_timeout ) ); |
|
|
|
|
0
|
|
|
|
|
|
1763
|
|
|
|
|
|
|
|
|
1764
|
0
|
0
|
|
|
|
|
if ( my $redis = $self->_redis ) { |
|
1765
|
0
|
0
|
|
|
|
|
my $socket = _INSTANCE( $redis->{sock}, 'IO::Socket' ) or confess 'Bad socket object'; |
|
1766
|
|
|
|
|
|
|
# IO::Socket::Timeout provides a way to set a timeout |
|
1767
|
|
|
|
|
|
|
# on read / write operations on an IO::Socket instance, |
|
1768
|
|
|
|
|
|
|
# or any IO::Socket::* modules, like IO::Socket::INET. |
|
1769
|
0
|
0
|
|
|
|
|
if ( defined $timeout ) { |
|
1770
|
0
|
|
|
|
|
|
$redis->{write_timeout} = $redis->{read_timeout} = $timeout; |
|
1771
|
0
|
|
|
|
|
|
$redis->_maybe_enable_timeouts( $socket ); |
|
1772
|
0
|
|
|
|
|
|
$socket->enable_timeout; |
|
1773
|
|
|
|
|
|
|
} else { |
|
1774
|
0
|
|
|
|
|
|
$redis->{write_timeout} = $redis->{read_timeout} = 0; |
|
1775
|
0
|
|
|
|
|
|
$redis->_maybe_enable_timeouts( $socket ); |
|
1776
|
0
|
|
|
|
|
|
$socket->disable_timeout; |
|
1777
|
|
|
|
|
|
|
} |
|
1778
|
|
|
|
|
|
|
} |
|
1779
|
|
|
|
|
|
|
} |
|
1780
|
|
|
|
|
|
|
|
|
1781
|
|
|
|
|
|
|
=head3 min_cleanup_bytes |
|
1782
|
|
|
|
|
|
|
|
|
1783
|
|
|
|
|
|
|
Accessor for C attribute - The minimum size, in bytes, |
|
1784
|
|
|
|
|
|
|
of the data to be released when performing memory cleanup. |
|
1785
|
|
|
|
|
|
|
Default 0. |
|
1786
|
|
|
|
|
|
|
|
|
1787
|
|
|
|
|
|
|
The C attribute is designed to reduce the release of memory |
|
1788
|
|
|
|
|
|
|
operations with frequent data changes. |
|
1789
|
|
|
|
|
|
|
|
|
1790
|
|
|
|
|
|
|
The C attribute value can be provided to L. |
|
1791
|
|
|
|
|
|
|
The method returns and sets the current value of the attribute. |
|
1792
|
|
|
|
|
|
|
|
|
1793
|
|
|
|
|
|
|
The C value must be less than or equal to C<'maxmemory'>. Otherwise |
|
1794
|
|
|
|
|
|
|
an error exception is thrown (C). |
|
1795
|
|
|
|
|
|
|
|
|
1796
|
|
|
|
|
|
|
=cut |
|
1797
|
|
|
|
|
|
|
has min_cleanup_bytes => ( |
|
1798
|
|
|
|
|
|
|
is => 'rw', |
|
1799
|
|
|
|
|
|
|
writer => '_set_min_cleanup_bytes', |
|
1800
|
|
|
|
|
|
|
isa => __PACKAGE__.'::NonNegInt', |
|
1801
|
|
|
|
|
|
|
default => 0, |
|
1802
|
|
|
|
|
|
|
trigger => sub { |
|
1803
|
|
|
|
|
|
|
my $self = shift; |
|
1804
|
|
|
|
|
|
|
!$self->_maxmemory || ( $self->min_cleanup_bytes <= $self->maxmemory || $self->_throw( $E_MISMATCH_ARG, 'min_cleanup_bytes' ) ); |
|
1805
|
|
|
|
|
|
|
}, |
|
1806
|
|
|
|
|
|
|
); |
|
1807
|
|
|
|
|
|
|
|
|
1808
|
|
|
|
|
|
|
=head3 min_cleanup_items |
|
1809
|
|
|
|
|
|
|
|
|
1810
|
|
|
|
|
|
|
The minimum number of the collection elements to be realesed |
|
1811
|
|
|
|
|
|
|
when performing memory cleanup. Default 100. |
|
1812
|
|
|
|
|
|
|
|
|
1813
|
|
|
|
|
|
|
The C attribute is designed to reduce number of times collection cleanup takes place. |
|
1814
|
|
|
|
|
|
|
Setting value too high may result in unwanted delays during operations with Redis. |
|
1815
|
|
|
|
|
|
|
|
|
1816
|
|
|
|
|
|
|
The C attribute value can be used in the L. |
|
1817
|
|
|
|
|
|
|
The method returns and sets the current value of the attribute. |
|
1818
|
|
|
|
|
|
|
|
|
1819
|
|
|
|
|
|
|
=cut |
|
1820
|
|
|
|
|
|
|
has min_cleanup_items => ( |
|
1821
|
|
|
|
|
|
|
is => 'rw', |
|
1822
|
|
|
|
|
|
|
writer => '_set_min_cleanup_items', |
|
1823
|
|
|
|
|
|
|
isa => __PACKAGE__.'::NonNegInt', |
|
1824
|
|
|
|
|
|
|
default => 100, |
|
1825
|
|
|
|
|
|
|
); |
|
1826
|
|
|
|
|
|
|
|
|
1827
|
|
|
|
|
|
|
=head3 max_datasize |
|
1828
|
|
|
|
|
|
|
|
|
1829
|
|
|
|
|
|
|
Accessor for the C attribute. |
|
1830
|
|
|
|
|
|
|
|
|
1831
|
|
|
|
|
|
|
The method returns the current value of the attribute if called without arguments. |
|
1832
|
|
|
|
|
|
|
|
|
1833
|
|
|
|
|
|
|
Non-negative integer value can be used to specify a new value to |
|
1834
|
|
|
|
|
|
|
the maximum size of the data introduced into the collection |
|
1835
|
|
|
|
|
|
|
(methods L and L). |
|
1836
|
|
|
|
|
|
|
|
|
1837
|
|
|
|
|
|
|
The C attribute value is used in the L |
|
1838
|
|
|
|
|
|
|
and operations data entry on the Redis server. |
|
1839
|
|
|
|
|
|
|
|
|
1840
|
|
|
|
|
|
|
The L uses the smaller of the values of 512MB and |
|
1841
|
|
|
|
|
|
|
C<'maxmemory'> limit from a F file. |
|
1842
|
|
|
|
|
|
|
|
|
1843
|
|
|
|
|
|
|
=cut |
|
1844
|
|
|
|
|
|
|
has max_datasize => ( |
|
1845
|
|
|
|
|
|
|
is => 'rw', |
|
1846
|
|
|
|
|
|
|
isa => __PACKAGE__.'::NonNegInt', |
|
1847
|
|
|
|
|
|
|
default => $MAX_DATASIZE, |
|
1848
|
|
|
|
|
|
|
lazy => 1, |
|
1849
|
|
|
|
|
|
|
trigger => sub { |
|
1850
|
|
|
|
|
|
|
my $self = shift; |
|
1851
|
|
|
|
|
|
|
$self->max_datasize <= ( $self->_maxmemory ? min( $self->_maxmemory, $MAX_DATASIZE ) : $MAX_DATASIZE ) |
|
1852
|
|
|
|
|
|
|
|| $self->_throw( $E_MISMATCH_ARG, 'max_datasize' ); |
|
1853
|
|
|
|
|
|
|
}, |
|
1854
|
|
|
|
|
|
|
); |
|
1855
|
|
|
|
|
|
|
|
|
1856
|
|
|
|
|
|
|
=head3 older_allowed |
|
1857
|
|
|
|
|
|
|
|
|
1858
|
|
|
|
|
|
|
Accessor for the C attribute which controls if adding an element |
|
1859
|
|
|
|
|
|
|
that is older than the last element removed from collection is allowed. |
|
1860
|
|
|
|
|
|
|
Default is C<0> (not allowed). |
|
1861
|
|
|
|
|
|
|
|
|
1862
|
|
|
|
|
|
|
The method returns the current value of the attribute. |
|
1863
|
|
|
|
|
|
|
The C attribute value is used in the L. |
|
1864
|
|
|
|
|
|
|
|
|
1865
|
|
|
|
|
|
|
=cut |
|
1866
|
|
|
|
|
|
|
has older_allowed => ( |
|
1867
|
|
|
|
|
|
|
is => 'rw', |
|
1868
|
|
|
|
|
|
|
isa => 'Bool', |
|
1869
|
|
|
|
|
|
|
default => 0, |
|
1870
|
|
|
|
|
|
|
); |
|
1871
|
|
|
|
|
|
|
|
|
1872
|
|
|
|
|
|
|
=head3 memory_reserve |
|
1873
|
|
|
|
|
|
|
|
|
1874
|
|
|
|
|
|
|
Accessor for the C attribute which specifies the amount of additional |
|
1875
|
|
|
|
|
|
|
memory reserved for metadata and bookkeeping. |
|
1876
|
|
|
|
|
|
|
Default C<0.05> (5%) of 'maxmemory'. |
|
1877
|
|
|
|
|
|
|
Not used when C<'maxmemory'> == 0 (it is not set in the F). |
|
1878
|
|
|
|
|
|
|
|
|
1879
|
|
|
|
|
|
|
Valid values must be between C<$MIN_MEMORY_RESERVE> and C<$MAX_MEMORY_RESERVE>. |
|
1880
|
|
|
|
|
|
|
|
|
1881
|
|
|
|
|
|
|
The method returns the current value of the attribute. |
|
1882
|
|
|
|
|
|
|
The C attribute value is used in the L. |
|
1883
|
|
|
|
|
|
|
|
|
1884
|
|
|
|
|
|
|
=cut |
|
1885
|
|
|
|
|
|
|
has memory_reserve => ( |
|
1886
|
|
|
|
|
|
|
is => 'rw', |
|
1887
|
|
|
|
|
|
|
writer => '_set_memory_reserve', |
|
1888
|
|
|
|
|
|
|
isa => 'Num', |
|
1889
|
|
|
|
|
|
|
default => $MIN_MEMORY_RESERVE, |
|
1890
|
|
|
|
|
|
|
trigger => sub { |
|
1891
|
|
|
|
|
|
|
my $self = shift; |
|
1892
|
|
|
|
|
|
|
my $memory_reserve = $self->memory_reserve; |
|
1893
|
|
|
|
|
|
|
( _NUMBER( $memory_reserve ) && $memory_reserve >= $MIN_MEMORY_RESERVE && $memory_reserve <= $MAX_MEMORY_RESERVE ) |
|
1894
|
|
|
|
|
|
|
|| $self->_throw( $E_MISMATCH_ARG, 'memory_reserve' ); |
|
1895
|
|
|
|
|
|
|
}, |
|
1896
|
|
|
|
|
|
|
); |
|
1897
|
|
|
|
|
|
|
|
|
1898
|
|
|
|
|
|
|
=head3 last_errorcode |
|
1899
|
|
|
|
|
|
|
|
|
1900
|
|
|
|
|
|
|
Get code of the last error. |
|
1901
|
|
|
|
|
|
|
|
|
1902
|
|
|
|
|
|
|
See the list of supported error codes in L section. |
|
1903
|
|
|
|
|
|
|
|
|
1904
|
|
|
|
|
|
|
=cut |
|
1905
|
|
|
|
|
|
|
has last_errorcode => ( |
|
1906
|
|
|
|
|
|
|
reader => 'last_errorcode', |
|
1907
|
|
|
|
|
|
|
writer => '_set_last_errorcode', |
|
1908
|
|
|
|
|
|
|
isa => 'Int', |
|
1909
|
|
|
|
|
|
|
default => 0, |
|
1910
|
|
|
|
|
|
|
); |
|
1911
|
|
|
|
|
|
|
|
|
1912
|
|
|
|
|
|
|
#-- public methods ------------------------------------------------------------- |
|
1913
|
|
|
|
|
|
|
|
|
1914
|
|
|
|
|
|
|
=head3 insert |
|
1915
|
|
|
|
|
|
|
|
|
1916
|
|
|
|
|
|
|
insert( $list_id, $data_id, $data, $data_time ) |
|
1917
|
|
|
|
|
|
|
|
|
1918
|
|
|
|
|
|
|
Example: |
|
1919
|
|
|
|
|
|
|
|
|
1920
|
|
|
|
|
|
|
$list_id = $coll->insert( 'Some List_id', 'Some Data_id', 'Some data' ); |
|
1921
|
|
|
|
|
|
|
|
|
1922
|
|
|
|
|
|
|
$list_id = $coll->insert( 'Another List_id', 'Data ID', 'More data', Time::HiRes::time() ); |
|
1923
|
|
|
|
|
|
|
|
|
1924
|
|
|
|
|
|
|
Insert data into the capped collection on the Redis server. |
|
1925
|
|
|
|
|
|
|
|
|
1926
|
|
|
|
|
|
|
Arguments: |
|
1927
|
|
|
|
|
|
|
|
|
1928
|
|
|
|
|
|
|
=over 3 |
|
1929
|
|
|
|
|
|
|
|
|
1930
|
|
|
|
|
|
|
=item C<$list_id> |
|
1931
|
|
|
|
|
|
|
|
|
1932
|
|
|
|
|
|
|
Mandatory, non-empty string: list ID. Must not contain C<':'>. |
|
1933
|
|
|
|
|
|
|
|
|
1934
|
|
|
|
|
|
|
The data will be inserted into the list with given ID, and the list |
|
1935
|
|
|
|
|
|
|
is created automatically if it does not exist yet. |
|
1936
|
|
|
|
|
|
|
|
|
1937
|
|
|
|
|
|
|
=item C<$data_id> |
|
1938
|
|
|
|
|
|
|
|
|
1939
|
|
|
|
|
|
|
Mandatory, non-empty string: data ID, unique within the list identified by C<$list_id> |
|
1940
|
|
|
|
|
|
|
argument. |
|
1941
|
|
|
|
|
|
|
|
|
1942
|
|
|
|
|
|
|
=item C<$data> |
|
1943
|
|
|
|
|
|
|
|
|
1944
|
|
|
|
|
|
|
Data value: a string. Data length should not exceed value of L attribute. |
|
1945
|
|
|
|
|
|
|
|
|
1946
|
|
|
|
|
|
|
=item C<$data_time> |
|
1947
|
|
|
|
|
|
|
|
|
1948
|
|
|
|
|
|
|
Optional data time, a non-negative number. If not specified, the current |
|
1949
|
|
|
|
|
|
|
value returned by C |
|
1950
|
|
|
|
|
|
|
returned by L module) are supported to have time |
|
1951
|
|
|
|
|
|
|
granularity of less than 1 second and stored with 4 decimal places. |
|
1952
|
|
|
|
|
|
|
|
|
1953
|
|
|
|
|
|
|
=back |
|
1954
|
|
|
|
|
|
|
|
|
1955
|
|
|
|
|
|
|
If collection is set to C and C<$data_time> less than time of the last removed |
|
1956
|
|
|
|
|
|
|
element (C - see C) then C is set to 0. |
|
1957
|
|
|
|
|
|
|
The L attribute value is used in the L. |
|
1958
|
|
|
|
|
|
|
|
|
1959
|
|
|
|
|
|
|
The method returns the ID of the data list to which the data was inserted (value of |
|
1960
|
|
|
|
|
|
|
the C<$list_id> argument). |
|
1961
|
|
|
|
|
|
|
|
|
1962
|
|
|
|
|
|
|
=cut |
|
1963
|
|
|
|
|
|
|
sub insert { |
|
1964
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
|
1965
|
0
|
|
|
|
|
|
my $list_id = shift; |
|
1966
|
0
|
|
|
|
|
|
my $data_id = shift; |
|
1967
|
0
|
|
|
|
|
|
my $data = shift; |
|
1968
|
0
|
|
0
|
|
|
|
my $data_time = shift // time; |
|
1969
|
|
|
|
|
|
|
|
|
1970
|
0
|
|
0
|
|
|
|
$data // $self->_throw( $E_MISMATCH_ARG, 'data' ); |
|
1971
|
0
|
0
|
0
|
|
|
|
( defined( _STRING( $data ) ) || $data eq '' ) || $self->_throw( $E_MISMATCH_ARG, 'data' ); |
|
1972
|
0
|
|
0
|
|
|
|
_STRING( $list_id ) // $self->_throw( $E_MISMATCH_ARG, 'list_id' ); |
|
1973
|
0
|
0
|
|
|
|
|
$list_id !~ /:/ || $self->_throw( $E_MISMATCH_ARG, 'list_id' ); |
|
1974
|
0
|
0
|
|
|
|
|
defined( _STRING( $data_id ) ) || $self->_throw( $E_MISMATCH_ARG, 'data_id' ); |
|
1975
|
0
|
0
|
0
|
|
|
|
( defined( _NUMBER( $data_time ) ) && $data_time > 0 ) || $self->_throw( $E_MISMATCH_ARG, 'data_time' ); |
|
1976
|
|
|
|
|
|
|
|
|
1977
|
0
|
|
|
|
|
|
my $data_len = bytes::length( $data ); |
|
1978
|
0
|
0
|
|
|
|
|
( $data_len <= $self->max_datasize ) || $self->_throw( $E_DATA_TOO_LARGE ); |
|
1979
|
|
|
|
|
|
|
|
|
1980
|
0
|
|
|
|
|
|
$self->_set_last_errorcode( $E_NO_ERROR ); |
|
1981
|
|
|
|
|
|
|
|
|
1982
|
0
|
|
|
|
|
|
my @ret = $self->_call_redis( |
|
1983
|
|
|
|
|
|
|
$self->_lua_script_cmd( 'insert' ), |
|
1984
|
|
|
|
|
|
|
0, |
|
1985
|
|
|
|
|
|
|
$self->name, |
|
1986
|
|
|
|
|
|
|
$list_id, |
|
1987
|
|
|
|
|
|
|
$data_id, |
|
1988
|
|
|
|
|
|
|
$data, |
|
1989
|
|
|
|
|
|
|
$data_time, |
|
1990
|
|
|
|
|
|
|
# Recommend the inclusion of this option in the case of incomprehensible errors |
|
1991
|
|
|
|
|
|
|
$self->_DEBUG, |
|
1992
|
|
|
|
|
|
|
); |
|
1993
|
|
|
|
|
|
|
|
|
1994
|
0
|
|
|
|
|
|
my ( $error, $_last_cleanup_items, $_used_memory, $_total_bytes_deleted ) = @ret; |
|
1995
|
|
|
|
|
|
|
|
|
1996
|
0
|
0
|
0
|
|
|
|
if ( scalar( @ret ) == 4 && exists( $ERROR{ $error } ) && defined( _NONNEGINT( $_last_cleanup_items ) ) ) { |
|
|
|
|
0
|
|
|
|
|
|
1997
|
0
|
0
|
0
|
|
|
|
if ( $error == $E_NO_ERROR ) { |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
1998
|
|
|
|
|
|
|
# Normal result: Nothing to do |
|
1999
|
|
|
|
|
|
|
} elsif ( $error == $E_COLLECTION_DELETED ) { |
|
2000
|
0
|
|
|
|
|
|
$self->_clear_sha1; |
|
2001
|
0
|
|
|
|
|
|
$self->_throw( $error ); |
|
2002
|
|
|
|
|
|
|
} elsif ( |
|
2003
|
|
|
|
|
|
|
$error == $E_DATA_ID_EXISTS |
|
2004
|
|
|
|
|
|
|
|| $error == $E_OLDER_THAN_ALLOWED |
|
2005
|
|
|
|
|
|
|
) { |
|
2006
|
0
|
|
|
|
|
|
$self->_throw( $error ); |
|
2007
|
|
|
|
|
|
|
} else { |
|
2008
|
0
|
|
|
|
|
|
$self->_throw( $error, 'Unexpected error' ); |
|
2009
|
|
|
|
|
|
|
} |
|
2010
|
|
|
|
|
|
|
} else { |
|
2011
|
0
|
|
|
|
|
|
$self->_process_unknown_error( @ret ); |
|
2012
|
|
|
|
|
|
|
} |
|
2013
|
|
|
|
|
|
|
|
|
2014
|
0
|
0
|
|
|
|
|
return wantarray ? ( $list_id, $_last_cleanup_items, $_used_memory, $_total_bytes_deleted ) : $list_id; |
|
2015
|
|
|
|
|
|
|
} |
|
2016
|
|
|
|
|
|
|
|
|
2017
|
|
|
|
|
|
|
=head3 update |
|
2018
|
|
|
|
|
|
|
|
|
2019
|
|
|
|
|
|
|
update( $list_id, $data_id, $data, $new_data_time ) |
|
2020
|
|
|
|
|
|
|
|
|
2021
|
|
|
|
|
|
|
Example: |
|
2022
|
|
|
|
|
|
|
|
|
2023
|
|
|
|
|
|
|
if ( $coll->update( $list_id, $data_id, 'New data' ) ) { |
|
2024
|
|
|
|
|
|
|
say "Data updated successfully"; |
|
2025
|
|
|
|
|
|
|
} else { |
|
2026
|
|
|
|
|
|
|
say "The data is not updated"; |
|
2027
|
|
|
|
|
|
|
} |
|
2028
|
|
|
|
|
|
|
|
|
2029
|
|
|
|
|
|
|
Updates existing data item. |
|
2030
|
|
|
|
|
|
|
|
|
2031
|
|
|
|
|
|
|
Arguments: |
|
2032
|
|
|
|
|
|
|
|
|
2033
|
|
|
|
|
|
|
=over 3 |
|
2034
|
|
|
|
|
|
|
|
|
2035
|
|
|
|
|
|
|
=item C<$list_id> |
|
2036
|
|
|
|
|
|
|
|
|
2037
|
|
|
|
|
|
|
Mandatory, non-empty string: list ID. Must not contain C<':'>. |
|
2038
|
|
|
|
|
|
|
|
|
2039
|
|
|
|
|
|
|
=item C<$data_id> |
|
2040
|
|
|
|
|
|
|
|
|
2041
|
|
|
|
|
|
|
Mandatory, non-empty string: data ID, unique within the list identified by C<$list_id> |
|
2042
|
|
|
|
|
|
|
argument. |
|
2043
|
|
|
|
|
|
|
|
|
2044
|
|
|
|
|
|
|
=item C<$data> |
|
2045
|
|
|
|
|
|
|
|
|
2046
|
|
|
|
|
|
|
New data value: a string. Data length should not exceed value of L attribute. |
|
2047
|
|
|
|
|
|
|
|
|
2048
|
|
|
|
|
|
|
=item C<$new_data_time> |
|
2049
|
|
|
|
|
|
|
|
|
2050
|
|
|
|
|
|
|
Optional new data time, a non-negative number. If not specified, the existing |
|
2051
|
|
|
|
|
|
|
data time is preserved. |
|
2052
|
|
|
|
|
|
|
|
|
2053
|
|
|
|
|
|
|
=back |
|
2054
|
|
|
|
|
|
|
|
|
2055
|
|
|
|
|
|
|
If the collection is set to C and C<$new_data_time> less than time of the last |
|
2056
|
|
|
|
|
|
|
removed element (C - see L) then C is set to 0. |
|
2057
|
|
|
|
|
|
|
The L attribute value is used in the L. |
|
2058
|
|
|
|
|
|
|
|
|
2059
|
|
|
|
|
|
|
Method returns true if the data is updated or false if the list with the given ID does not exist or |
|
2060
|
|
|
|
|
|
|
is used an invalid data ID. |
|
2061
|
|
|
|
|
|
|
|
|
2062
|
|
|
|
|
|
|
Throws an exception on other errors. |
|
2063
|
|
|
|
|
|
|
|
|
2064
|
|
|
|
|
|
|
=cut |
|
2065
|
|
|
|
|
|
|
sub update { |
|
2066
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
|
2067
|
0
|
|
|
|
|
|
my $list_id = shift; |
|
2068
|
0
|
|
|
|
|
|
my $data_id = shift; |
|
2069
|
0
|
|
|
|
|
|
my $data = shift; |
|
2070
|
|
|
|
|
|
|
|
|
2071
|
0
|
|
0
|
|
|
|
$data // $self->_throw( $E_MISMATCH_ARG, 'data' ); |
|
2072
|
0
|
0
|
0
|
|
|
|
( defined( _STRING( $data ) ) || $data eq '' ) || $self->_throw( $E_MISMATCH_ARG, 'data' ); |
|
2073
|
0
|
|
0
|
|
|
|
_STRING( $list_id ) // $self->_throw( $E_MISMATCH_ARG, 'list_id' ); |
|
2074
|
0
|
0
|
|
|
|
|
defined( _STRING( $data_id ) ) || $self->_throw( $E_MISMATCH_ARG, 'data_id' ); |
|
2075
|
|
|
|
|
|
|
|
|
2076
|
0
|
|
|
|
|
|
my $new_data_time; |
|
2077
|
0
|
0
|
|
|
|
|
if ( @_ ) { |
|
2078
|
0
|
|
|
|
|
|
$new_data_time = shift; |
|
2079
|
0
|
0
|
0
|
|
|
|
( defined( _NUMBER( $new_data_time ) ) && $new_data_time > 0 ) || $self->_throw( $E_MISMATCH_ARG, 'new_data_time' ); |
|
2080
|
|
|
|
|
|
|
} |
|
2081
|
|
|
|
|
|
|
|
|
2082
|
0
|
|
|
|
|
|
my $data_len = bytes::length( $data ); |
|
2083
|
0
|
0
|
|
|
|
|
( $data_len <= $self->max_datasize ) || $self->_throw( $E_DATA_TOO_LARGE ); |
|
2084
|
|
|
|
|
|
|
|
|
2085
|
0
|
|
|
|
|
|
$self->_set_last_errorcode( $E_NO_ERROR ); |
|
2086
|
|
|
|
|
|
|
|
|
2087
|
0
|
|
0
|
|
|
|
my @ret = $self->_call_redis( |
|
2088
|
|
|
|
|
|
|
$self->_lua_script_cmd( 'update' ), |
|
2089
|
|
|
|
|
|
|
0, |
|
2090
|
|
|
|
|
|
|
$self->name, |
|
2091
|
|
|
|
|
|
|
$list_id, |
|
2092
|
|
|
|
|
|
|
$data_id, |
|
2093
|
|
|
|
|
|
|
$data, |
|
2094
|
|
|
|
|
|
|
$new_data_time // 0, |
|
2095
|
|
|
|
|
|
|
# Recommend the inclusion of this option in the case of incomprehensible errors |
|
2096
|
|
|
|
|
|
|
$self->_DEBUG, |
|
2097
|
|
|
|
|
|
|
); |
|
2098
|
|
|
|
|
|
|
|
|
2099
|
0
|
|
|
|
|
|
my ( $error, $_last_cleanup_items, $_used_memory, $_total_bytes_deleted ) = @ret; |
|
2100
|
|
|
|
|
|
|
|
|
2101
|
0
|
0
|
0
|
|
|
|
if ( scalar( @ret ) == 4 && exists( $ERROR{ $error } ) && defined( _NONNEGINT( $_last_cleanup_items ) ) ) { |
|
|
|
|
0
|
|
|
|
|
|
2102
|
0
|
0
|
0
|
|
|
|
if ( $error == $E_NO_ERROR ) { |
|
|
|
0
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
2103
|
0
|
0
|
|
|
|
|
return wantarray ? ( 1, $_last_cleanup_items, $_used_memory, $_total_bytes_deleted ) : 1; |
|
2104
|
|
|
|
|
|
|
} elsif ( $error == $E_NONEXISTENT_DATA_ID ) { |
|
2105
|
0
|
|
|
|
|
|
return 0; |
|
2106
|
|
|
|
|
|
|
} elsif ( |
|
2107
|
|
|
|
|
|
|
$error == $E_COLLECTION_DELETED |
|
2108
|
|
|
|
|
|
|
|| $error == $E_DATA_ID_EXISTS |
|
2109
|
|
|
|
|
|
|
|| $error == $E_OLDER_THAN_ALLOWED |
|
2110
|
|
|
|
|
|
|
) { |
|
2111
|
0
|
|
|
|
|
|
$self->_clear_sha1; |
|
2112
|
0
|
|
|
|
|
|
$self->_throw( $error ); |
|
2113
|
|
|
|
|
|
|
} else { |
|
2114
|
0
|
|
|
|
|
|
$self->_throw( $error, 'Unexpected error' ); |
|
2115
|
|
|
|
|
|
|
} |
|
2116
|
|
|
|
|
|
|
} else { |
|
2117
|
0
|
|
|
|
|
|
$self->_process_unknown_error( @ret ); |
|
2118
|
|
|
|
|
|
|
} |
|
2119
|
|
|
|
|
|
|
} |
|
2120
|
|
|
|
|
|
|
|
|
2121
|
|
|
|
|
|
|
=head3 upsert |
|
2122
|
|
|
|
|
|
|
|
|
2123
|
|
|
|
|
|
|
upsert( $list_id, $data_id, $data, $data_time ) |
|
2124
|
|
|
|
|
|
|
|
|
2125
|
|
|
|
|
|
|
Example: |
|
2126
|
|
|
|
|
|
|
|
|
2127
|
|
|
|
|
|
|
$list_id = $coll->upsert( 'Some List_id', 'Some Data_id', 'Some data' ); |
|
2128
|
|
|
|
|
|
|
|
|
2129
|
|
|
|
|
|
|
$list_id = $coll->upsert( 'Another List_id', 'Data ID', 'More data', Time::HiRes::time() ); |
|
2130
|
|
|
|
|
|
|
|
|
2131
|
|
|
|
|
|
|
If the list C<$list_id> does not contain data with C<$data_id>, |
|
2132
|
|
|
|
|
|
|
then it behaves like an L, |
|
2133
|
|
|
|
|
|
|
otherwise behaves like an L. |
|
2134
|
|
|
|
|
|
|
|
|
2135
|
|
|
|
|
|
|
The method returns the ID of the data list to which the data was inserted (value of |
|
2136
|
|
|
|
|
|
|
the C<$list_id> argument) as the L method. |
|
2137
|
|
|
|
|
|
|
|
|
2138
|
|
|
|
|
|
|
=cut |
|
2139
|
|
|
|
|
|
|
sub upsert { |
|
2140
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
|
2141
|
0
|
|
|
|
|
|
my $list_id = shift; |
|
2142
|
0
|
|
|
|
|
|
my $data_id = shift; |
|
2143
|
0
|
|
|
|
|
|
my $data = shift; |
|
2144
|
0
|
|
|
|
|
|
my $data_time = shift; |
|
2145
|
|
|
|
|
|
|
|
|
2146
|
0
|
|
0
|
|
|
|
$data // $self->_throw( $E_MISMATCH_ARG, 'data' ); |
|
2147
|
0
|
0
|
0
|
|
|
|
( defined( _STRING( $data ) ) || $data eq '' ) || $self->_throw( $E_MISMATCH_ARG, 'data' ); |
|
2148
|
0
|
|
0
|
|
|
|
_STRING( $list_id ) // $self->_throw( $E_MISMATCH_ARG, 'list_id' ); |
|
2149
|
0
|
0
|
|
|
|
|
$list_id !~ /:/ || $self->_throw( $E_MISMATCH_ARG, 'list_id' ); |
|
2150
|
0
|
0
|
|
|
|
|
defined( _STRING( $data_id ) ) || $self->_throw( $E_MISMATCH_ARG, 'data_id' ); |
|
2151
|
0
|
0
|
0
|
|
|
|
!defined( $data_time ) || ( defined( _NUMBER( $data_time ) ) && $data_time > 0 ) || $self->_throw( $E_MISMATCH_ARG, 'data_time' ); |
|
|
|
|
0
|
|
|
|
|
|
2152
|
|
|
|
|
|
|
|
|
2153
|
0
|
|
|
|
|
|
my $data_len = bytes::length( $data ); |
|
2154
|
0
|
0
|
|
|
|
|
( $data_len <= $self->max_datasize ) || $self->_throw( $E_DATA_TOO_LARGE ); |
|
2155
|
|
|
|
|
|
|
|
|
2156
|
0
|
|
|
|
|
|
$self->_set_last_errorcode( $E_NO_ERROR ); |
|
2157
|
|
|
|
|
|
|
|
|
2158
|
0
|
|
0
|
|
|
|
my @ret = $self->_call_redis( |
|
2159
|
|
|
|
|
|
|
$self->_lua_script_cmd( 'upsert' ), |
|
2160
|
|
|
|
|
|
|
0, |
|
2161
|
|
|
|
|
|
|
$self->name, |
|
2162
|
|
|
|
|
|
|
$list_id, |
|
2163
|
|
|
|
|
|
|
$data_id, |
|
2164
|
|
|
|
|
|
|
$data, |
|
2165
|
|
|
|
|
|
|
$data_time // -1, |
|
2166
|
|
|
|
|
|
|
# Recommend the inclusion of this option in the case of incomprehensible errors |
|
2167
|
|
|
|
|
|
|
$self->_DEBUG, |
|
2168
|
|
|
|
|
|
|
time, |
|
2169
|
|
|
|
|
|
|
); |
|
2170
|
|
|
|
|
|
|
|
|
2171
|
0
|
|
|
|
|
|
my ( $error, $cleanings ) = @ret; |
|
2172
|
|
|
|
|
|
|
|
|
2173
|
0
|
0
|
0
|
|
|
|
if ( scalar( @ret ) == 4 && exists( $ERROR{ $error } ) && defined( _NONNEGINT( $cleanings ) ) ) { |
|
|
|
|
0
|
|
|
|
|
|
2174
|
0
|
0
|
0
|
|
|
|
if ( $error == $E_NO_ERROR ) { |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
2175
|
|
|
|
|
|
|
# Normal result: Nothing to do |
|
2176
|
|
|
|
|
|
|
} elsif ( $error == $E_COLLECTION_DELETED ) { |
|
2177
|
0
|
|
|
|
|
|
$self->_clear_sha1; |
|
2178
|
0
|
|
|
|
|
|
$self->_throw( $error ); |
|
2179
|
|
|
|
|
|
|
} elsif ( |
|
2180
|
|
|
|
|
|
|
$error == $E_DATA_ID_EXISTS |
|
2181
|
|
|
|
|
|
|
|| $error == $E_OLDER_THAN_ALLOWED |
|
2182
|
|
|
|
|
|
|
) { |
|
2183
|
0
|
|
|
|
|
|
$self->_throw( $error ); |
|
2184
|
|
|
|
|
|
|
} elsif ( $error == $E_NONEXISTENT_DATA_ID ) { |
|
2185
|
|
|
|
|
|
|
# Nothing to do |
|
2186
|
|
|
|
|
|
|
} else { |
|
2187
|
0
|
|
|
|
|
|
$self->_throw( $error, 'Unexpected error' ); |
|
2188
|
|
|
|
|
|
|
} |
|
2189
|
|
|
|
|
|
|
} else { |
|
2190
|
0
|
|
|
|
|
|
$self->_process_unknown_error( @ret ); |
|
2191
|
|
|
|
|
|
|
} |
|
2192
|
|
|
|
|
|
|
|
|
2193
|
0
|
0
|
|
|
|
|
return wantarray ? ( $list_id, $cleanings ) : $list_id; # as insert |
|
2194
|
|
|
|
|
|
|
} |
|
2195
|
|
|
|
|
|
|
|
|
2196
|
|
|
|
|
|
|
=head3 receive |
|
2197
|
|
|
|
|
|
|
|
|
2198
|
|
|
|
|
|
|
receive( $list_id, $data_id ) |
|
2199
|
|
|
|
|
|
|
|
|
2200
|
|
|
|
|
|
|
Example: |
|
2201
|
|
|
|
|
|
|
|
|
2202
|
|
|
|
|
|
|
my @data = $coll->receive( $list_id ); |
|
2203
|
|
|
|
|
|
|
say "List '$list_id' has '$_'" foreach @data; |
|
2204
|
|
|
|
|
|
|
# or |
|
2205
|
|
|
|
|
|
|
my $list_len = $coll->receive( $list_id ); |
|
2206
|
|
|
|
|
|
|
say "List '$list_id' has '$list_len' item(s)"; |
|
2207
|
|
|
|
|
|
|
# or |
|
2208
|
|
|
|
|
|
|
my $data = $coll->receive( $list_id, $data_id ); |
|
2209
|
|
|
|
|
|
|
say "List '$list_id' has '$data_id'" if defined $data; |
|
2210
|
|
|
|
|
|
|
|
|
2211
|
|
|
|
|
|
|
If the C<$data_id> argument is not specified or is an empty string: |
|
2212
|
|
|
|
|
|
|
|
|
2213
|
|
|
|
|
|
|
=over 3 |
|
2214
|
|
|
|
|
|
|
|
|
2215
|
|
|
|
|
|
|
=item * |
|
2216
|
|
|
|
|
|
|
|
|
2217
|
|
|
|
|
|
|
In a list context, the method returns all the data from the list given by |
|
2218
|
|
|
|
|
|
|
the C<$list_id> identifier. |
|
2219
|
|
|
|
|
|
|
|
|
2220
|
|
|
|
|
|
|
Method returns an empty list if the list with the given ID does not exist. |
|
2221
|
|
|
|
|
|
|
|
|
2222
|
|
|
|
|
|
|
=item * |
|
2223
|
|
|
|
|
|
|
|
|
2224
|
|
|
|
|
|
|
In a scalar context, the method returns the length of the data list given by |
|
2225
|
|
|
|
|
|
|
the C<$list_id> identifier. |
|
2226
|
|
|
|
|
|
|
|
|
2227
|
|
|
|
|
|
|
=back |
|
2228
|
|
|
|
|
|
|
|
|
2229
|
|
|
|
|
|
|
If the C<$data_id> argument is specified: |
|
2230
|
|
|
|
|
|
|
|
|
2231
|
|
|
|
|
|
|
=over 3 |
|
2232
|
|
|
|
|
|
|
|
|
2233
|
|
|
|
|
|
|
=item * |
|
2234
|
|
|
|
|
|
|
|
|
2235
|
|
|
|
|
|
|
The method returns the specified element of the data list. |
|
2236
|
|
|
|
|
|
|
If the data with C<$data_id> ID does not exist, C is returned. |
|
2237
|
|
|
|
|
|
|
|
|
2238
|
|
|
|
|
|
|
=back |
|
2239
|
|
|
|
|
|
|
|
|
2240
|
|
|
|
|
|
|
=cut |
|
2241
|
|
|
|
|
|
|
sub receive { |
|
2242
|
0
|
|
|
0
|
1
|
|
my ( $self, $list_id, $data_id ) = @_; |
|
2243
|
|
|
|
|
|
|
|
|
2244
|
0
|
|
0
|
|
|
|
_STRING( $list_id ) // $self->_throw( $E_MISMATCH_ARG, 'list_id' ); |
|
2245
|
|
|
|
|
|
|
|
|
2246
|
0
|
|
|
|
|
|
$self->_set_last_errorcode( $E_NO_ERROR ); |
|
2247
|
|
|
|
|
|
|
|
|
2248
|
0
|
0
|
|
|
|
|
return unless $self->list_exists( $list_id ); |
|
2249
|
|
|
|
|
|
|
|
|
2250
|
0
|
0
|
0
|
|
|
|
if ( defined( $data_id ) && $data_id ne '' ) { |
|
2251
|
0
|
|
0
|
|
|
|
_STRING( $data_id ) // $self->_throw( $E_MISMATCH_ARG, 'data_id' ); |
|
2252
|
0
|
|
|
|
|
|
return $self->_call_redis( |
|
2253
|
|
|
|
|
|
|
$self->_lua_script_cmd( 'receive' ), |
|
2254
|
|
|
|
|
|
|
0, |
|
2255
|
|
|
|
|
|
|
$self->name, |
|
2256
|
|
|
|
|
|
|
$list_id, |
|
2257
|
|
|
|
|
|
|
'val', |
|
2258
|
|
|
|
|
|
|
$data_id, |
|
2259
|
|
|
|
|
|
|
); |
|
2260
|
|
|
|
|
|
|
} else { |
|
2261
|
0
|
0
|
|
|
|
|
if ( wantarray ) { |
|
2262
|
0
|
0
|
|
|
|
|
return $self->_call_redis( |
|
2263
|
|
|
|
|
|
|
$self->_lua_script_cmd( 'receive' ), |
|
2264
|
|
|
|
|
|
|
0, |
|
2265
|
|
|
|
|
|
|
$self->name, |
|
2266
|
|
|
|
|
|
|
$list_id, |
|
2267
|
|
|
|
|
|
|
defined( $data_id ) ? 'all' : 'vals', |
|
2268
|
|
|
|
|
|
|
'', |
|
2269
|
|
|
|
|
|
|
); |
|
2270
|
|
|
|
|
|
|
} else { |
|
2271
|
0
|
|
|
|
|
|
return $self->_call_redis( |
|
2272
|
|
|
|
|
|
|
$self->_lua_script_cmd( 'receive' ), |
|
2273
|
|
|
|
|
|
|
0, |
|
2274
|
|
|
|
|
|
|
$self->name, |
|
2275
|
|
|
|
|
|
|
$list_id, |
|
2276
|
|
|
|
|
|
|
'len', |
|
2277
|
|
|
|
|
|
|
'', |
|
2278
|
|
|
|
|
|
|
); |
|
2279
|
|
|
|
|
|
|
} |
|
2280
|
|
|
|
|
|
|
} |
|
2281
|
|
|
|
|
|
|
} |
|
2282
|
|
|
|
|
|
|
|
|
2283
|
|
|
|
|
|
|
=head3 pop_oldest |
|
2284
|
|
|
|
|
|
|
|
|
2285
|
|
|
|
|
|
|
The method retrieves the oldest data stored in the collection and removes it from |
|
2286
|
|
|
|
|
|
|
the collection. |
|
2287
|
|
|
|
|
|
|
|
|
2288
|
|
|
|
|
|
|
Returns a list of two elements. |
|
2289
|
|
|
|
|
|
|
The first element contains the identifier of the list from which the data was retrieved. |
|
2290
|
|
|
|
|
|
|
The second element contains the extracted data. |
|
2291
|
|
|
|
|
|
|
|
|
2292
|
|
|
|
|
|
|
The returned data item is removed from the collection. |
|
2293
|
|
|
|
|
|
|
|
|
2294
|
|
|
|
|
|
|
Method returns an empty list if the collection does not contain any data. |
|
2295
|
|
|
|
|
|
|
|
|
2296
|
|
|
|
|
|
|
The following examples illustrate uses of the C method: |
|
2297
|
|
|
|
|
|
|
|
|
2298
|
|
|
|
|
|
|
while ( my ( $list_id, $data ) = $coll->pop_oldest ) { |
|
2299
|
|
|
|
|
|
|
say "List '$list_id' had '$data'"; |
|
2300
|
|
|
|
|
|
|
} |
|
2301
|
|
|
|
|
|
|
|
|
2302
|
|
|
|
|
|
|
=cut |
|
2303
|
|
|
|
|
|
|
sub pop_oldest { |
|
2304
|
0
|
|
|
0
|
1
|
|
my ( $self ) = @_; |
|
2305
|
|
|
|
|
|
|
|
|
2306
|
0
|
|
|
|
|
|
$self->_set_last_errorcode( $E_NO_ERROR ); |
|
2307
|
|
|
|
|
|
|
|
|
2308
|
0
|
|
|
|
|
|
my @ret = $self->_call_redis( |
|
2309
|
|
|
|
|
|
|
$self->_lua_script_cmd( 'pop_oldest' ), |
|
2310
|
|
|
|
|
|
|
0, |
|
2311
|
|
|
|
|
|
|
$self->name, |
|
2312
|
|
|
|
|
|
|
); |
|
2313
|
|
|
|
|
|
|
|
|
2314
|
0
|
|
|
|
|
|
my ( $error, $queue_exist, $to_delete_id, $to_delete_data ) = @ret; |
|
2315
|
|
|
|
|
|
|
|
|
2316
|
0
|
0
|
|
|
|
|
if ( exists $ERROR{ $error } ) { |
|
2317
|
0
|
0
|
|
|
|
|
$self->_clear_sha1 if $error == $E_COLLECTION_DELETED; |
|
2318
|
0
|
0
|
|
|
|
|
$self->_throw( $error ) if $error != $E_NO_ERROR; |
|
2319
|
|
|
|
|
|
|
} else { |
|
2320
|
0
|
|
|
|
|
|
$self->_process_unknown_error( @ret ); |
|
2321
|
|
|
|
|
|
|
} |
|
2322
|
|
|
|
|
|
|
|
|
2323
|
0
|
0
|
|
|
|
|
if ( $queue_exist ) { |
|
2324
|
0
|
|
|
|
|
|
return( $to_delete_id, $to_delete_data ); |
|
2325
|
|
|
|
|
|
|
} else { |
|
2326
|
0
|
|
|
|
|
|
return; |
|
2327
|
|
|
|
|
|
|
} |
|
2328
|
|
|
|
|
|
|
} |
|
2329
|
|
|
|
|
|
|
|
|
2330
|
|
|
|
|
|
|
=head3 redis_config_ok |
|
2331
|
|
|
|
|
|
|
|
|
2332
|
|
|
|
|
|
|
redis_config_ok( redis => $server ) |
|
2333
|
|
|
|
|
|
|
|
|
2334
|
|
|
|
|
|
|
Example: |
|
2335
|
|
|
|
|
|
|
|
|
2336
|
|
|
|
|
|
|
say 'Redis server config ', $coll->redis_config_ok ? 'OK' : 'NOT OK'; |
|
2337
|
|
|
|
|
|
|
my $redis = Redis->new( server => "$server:$port" ); |
|
2338
|
|
|
|
|
|
|
say 'Redis server config ', |
|
2339
|
|
|
|
|
|
|
Redis::CappedCollection::redis_config_ok( redis => $redis ) |
|
2340
|
|
|
|
|
|
|
? 'OK' |
|
2341
|
|
|
|
|
|
|
: 'NOT OK' |
|
2342
|
|
|
|
|
|
|
; |
|
2343
|
|
|
|
|
|
|
|
|
2344
|
|
|
|
|
|
|
Check whether there is a Redis server config correct, |
|
2345
|
|
|
|
|
|
|
now that the 'maxmemory-policy' setting is 'noeviction'. |
|
2346
|
|
|
|
|
|
|
Returns true if config correct and false otherwise. |
|
2347
|
|
|
|
|
|
|
|
|
2348
|
|
|
|
|
|
|
It can be called as either the existing C object method or a class function. |
|
2349
|
|
|
|
|
|
|
|
|
2350
|
|
|
|
|
|
|
If invoked as the object method, C uses the C |
|
2351
|
|
|
|
|
|
|
attribute from the object as default. |
|
2352
|
|
|
|
|
|
|
|
|
2353
|
|
|
|
|
|
|
If invoked as the class function, C requires mandatory C |
|
2354
|
|
|
|
|
|
|
argument. |
|
2355
|
|
|
|
|
|
|
|
|
2356
|
|
|
|
|
|
|
This argument are in key-value pair as described for L method. |
|
2357
|
|
|
|
|
|
|
|
|
2358
|
|
|
|
|
|
|
An error exception is thrown (C) if an argument is not valid. |
|
2359
|
|
|
|
|
|
|
|
|
2360
|
|
|
|
|
|
|
=cut |
|
2361
|
|
|
|
|
|
|
sub redis_config_ok { |
|
2362
|
0
|
|
|
0
|
1
|
|
return _maxmemory_policy_ok( @_ ); |
|
2363
|
|
|
|
|
|
|
} |
|
2364
|
|
|
|
|
|
|
|
|
2365
|
|
|
|
|
|
|
=head3 collection_info |
|
2366
|
|
|
|
|
|
|
|
|
2367
|
|
|
|
|
|
|
collection_info( redis => $server, name => $name ) |
|
2368
|
|
|
|
|
|
|
|
|
2369
|
|
|
|
|
|
|
Example: |
|
2370
|
|
|
|
|
|
|
|
|
2371
|
|
|
|
|
|
|
my $info = $coll->collection_info; |
|
2372
|
|
|
|
|
|
|
say 'An existing collection uses ', $info->{min_cleanup_bytes}, " byte of 'min_cleanup_bytes', ", |
|
2373
|
|
|
|
|
|
|
$info->{items}, ' items are stored in ', $info->{lists}, ' lists'; |
|
2374
|
|
|
|
|
|
|
# or |
|
2375
|
|
|
|
|
|
|
my $info = Redis::CappedCollection::collection_info( |
|
2376
|
|
|
|
|
|
|
redis => $redis, # or redis => { server => "$server:$port" } |
|
2377
|
|
|
|
|
|
|
name => 'Collection name', |
|
2378
|
|
|
|
|
|
|
); |
|
2379
|
|
|
|
|
|
|
|
|
2380
|
|
|
|
|
|
|
Get collection information and status. |
|
2381
|
|
|
|
|
|
|
It can be called as either an existing C object method or a class function. |
|
2382
|
|
|
|
|
|
|
|
|
2383
|
|
|
|
|
|
|
C arguments are in key-value pairs. |
|
2384
|
|
|
|
|
|
|
Arguments description match the arguments description for L method: |
|
2385
|
|
|
|
|
|
|
|
|
2386
|
|
|
|
|
|
|
=over 3 |
|
2387
|
|
|
|
|
|
|
|
|
2388
|
|
|
|
|
|
|
=item C |
|
2389
|
|
|
|
|
|
|
|
|
2390
|
|
|
|
|
|
|
=item C |
|
2391
|
|
|
|
|
|
|
|
|
2392
|
|
|
|
|
|
|
=back |
|
2393
|
|
|
|
|
|
|
|
|
2394
|
|
|
|
|
|
|
If invoked as the object method, C, arguments are optional and |
|
2395
|
|
|
|
|
|
|
use corresponding object attributes as defaults. |
|
2396
|
|
|
|
|
|
|
|
|
2397
|
|
|
|
|
|
|
If called as a class methods, the arguments are mandatory. |
|
2398
|
|
|
|
|
|
|
|
|
2399
|
|
|
|
|
|
|
Returns a reference to a hash with the following elements: |
|
2400
|
|
|
|
|
|
|
|
|
2401
|
|
|
|
|
|
|
=over 3 |
|
2402
|
|
|
|
|
|
|
|
|
2403
|
|
|
|
|
|
|
=item * |
|
2404
|
|
|
|
|
|
|
|
|
2405
|
|
|
|
|
|
|
C - Number of lists in a collection. |
|
2406
|
|
|
|
|
|
|
|
|
2407
|
|
|
|
|
|
|
=item * |
|
2408
|
|
|
|
|
|
|
|
|
2409
|
|
|
|
|
|
|
C - Number of data items stored in the collection. |
|
2410
|
|
|
|
|
|
|
|
|
2411
|
|
|
|
|
|
|
=item * |
|
2412
|
|
|
|
|
|
|
|
|
2413
|
|
|
|
|
|
|
C - Time of the oldest data in the collection. |
|
2414
|
|
|
|
|
|
|
C if the collection does not contain data. |
|
2415
|
|
|
|
|
|
|
|
|
2416
|
|
|
|
|
|
|
=item * |
|
2417
|
|
|
|
|
|
|
|
|
2418
|
|
|
|
|
|
|
C - True if it is allowed to put data in collection that is older than the last element |
|
2419
|
|
|
|
|
|
|
removed from collection. |
|
2420
|
|
|
|
|
|
|
|
|
2421
|
|
|
|
|
|
|
=item * |
|
2422
|
|
|
|
|
|
|
|
|
2423
|
|
|
|
|
|
|
C - Memory reserve coefficient. |
|
2424
|
|
|
|
|
|
|
|
|
2425
|
|
|
|
|
|
|
=item * |
|
2426
|
|
|
|
|
|
|
|
|
2427
|
|
|
|
|
|
|
C - The minimum size, in bytes, |
|
2428
|
|
|
|
|
|
|
of the data to be released when performing memory cleanup. |
|
2429
|
|
|
|
|
|
|
|
|
2430
|
|
|
|
|
|
|
=item * |
|
2431
|
|
|
|
|
|
|
|
|
2432
|
|
|
|
|
|
|
C - The minimum number of the collection elements |
|
2433
|
|
|
|
|
|
|
to be realesed when performing memory cleanup. |
|
2434
|
|
|
|
|
|
|
|
|
2435
|
|
|
|
|
|
|
=item * |
|
2436
|
|
|
|
|
|
|
|
|
2437
|
|
|
|
|
|
|
C - Data structure version. |
|
2438
|
|
|
|
|
|
|
|
|
2439
|
|
|
|
|
|
|
=item * |
|
2440
|
|
|
|
|
|
|
|
|
2441
|
|
|
|
|
|
|
C - time of the last removed element from collection |
|
2442
|
|
|
|
|
|
|
or 0 if nothing was removed from collection yet. |
|
2443
|
|
|
|
|
|
|
|
|
2444
|
|
|
|
|
|
|
=back |
|
2445
|
|
|
|
|
|
|
|
|
2446
|
|
|
|
|
|
|
An error will cause the program to throw an exception (C) if an argument is not valid |
|
2447
|
|
|
|
|
|
|
or the collection does not exist. |
|
2448
|
|
|
|
|
|
|
|
|
2449
|
|
|
|
|
|
|
=cut |
|
2450
|
|
|
|
|
|
|
my @_collection_info_result_keys = qw( |
|
2451
|
|
|
|
|
|
|
error |
|
2452
|
|
|
|
|
|
|
lists |
|
2453
|
|
|
|
|
|
|
items |
|
2454
|
|
|
|
|
|
|
older_allowed |
|
2455
|
|
|
|
|
|
|
min_cleanup_bytes |
|
2456
|
|
|
|
|
|
|
min_cleanup_items |
|
2457
|
|
|
|
|
|
|
memory_reserve |
|
2458
|
|
|
|
|
|
|
data_version |
|
2459
|
|
|
|
|
|
|
last_removed_time |
|
2460
|
|
|
|
|
|
|
oldest_time |
|
2461
|
|
|
|
|
|
|
); |
|
2462
|
|
|
|
|
|
|
|
|
2463
|
|
|
|
|
|
|
sub collection_info { |
|
2464
|
0
|
|
|
0
|
1
|
|
my $results = {}; |
|
2465
|
0
|
|
|
|
|
|
my @ret; |
|
2466
|
0
|
0
|
0
|
|
|
|
if ( @_ && _INSTANCE( $_[0], __PACKAGE__ ) ) { # allow calling $obj->bar |
|
2467
|
0
|
|
|
|
|
|
my $self = shift; |
|
2468
|
|
|
|
|
|
|
|
|
2469
|
0
|
|
|
|
|
|
my %arguments = @_; |
|
2470
|
0
|
|
|
|
|
|
_check_arguments_acceptability( \%arguments, [] ); |
|
2471
|
|
|
|
|
|
|
|
|
2472
|
0
|
|
|
|
|
|
$self->_set_last_errorcode( $E_NO_ERROR ); |
|
2473
|
|
|
|
|
|
|
|
|
2474
|
0
|
|
|
|
|
|
@ret = $self->_call_redis( |
|
2475
|
|
|
|
|
|
|
$self->_lua_script_cmd( 'collection_info' ), |
|
2476
|
|
|
|
|
|
|
0, |
|
2477
|
|
|
|
|
|
|
$self->name, |
|
2478
|
|
|
|
|
|
|
); |
|
2479
|
0
|
|
|
|
|
|
$results = _lists2hash( \@_collection_info_result_keys, \@ret ); |
|
2480
|
|
|
|
|
|
|
|
|
2481
|
0
|
|
|
|
|
|
my $error = $results->{error}; |
|
2482
|
|
|
|
|
|
|
|
|
2483
|
0
|
0
|
|
|
|
|
if ( exists $ERROR{ $error } ) { |
|
2484
|
0
|
0
|
|
|
|
|
$self->_clear_sha1 if $error == $E_COLLECTION_DELETED; |
|
2485
|
0
|
0
|
|
|
|
|
$self->_throw( $error ) if $error != $E_NO_ERROR; |
|
2486
|
|
|
|
|
|
|
} else { |
|
2487
|
0
|
|
|
|
|
|
$self->_process_unknown_error( @ret ); |
|
2488
|
|
|
|
|
|
|
} |
|
2489
|
|
|
|
|
|
|
} else { |
|
2490
|
0
|
0
|
|
|
|
|
shift if _CLASSISA( $_[0], __PACKAGE__ ); # allow calling Foo->bar as well as Foo::bar |
|
2491
|
|
|
|
|
|
|
|
|
2492
|
0
|
|
|
|
|
|
my %arguments = @_; |
|
2493
|
0
|
|
|
|
|
|
_check_arguments_acceptability( \%arguments, [ 'redis', 'name' ] ); |
|
2494
|
|
|
|
|
|
|
|
|
2495
|
0
|
0
|
|
|
|
|
confess "'redis' argument is required" unless defined $arguments{redis}; |
|
2496
|
0
|
0
|
|
|
|
|
confess "'name' argument is required" unless defined $arguments{name}; |
|
2497
|
|
|
|
|
|
|
|
|
2498
|
0
|
|
|
|
|
|
my $redis = _get_redis( delete $arguments{redis} ); |
|
2499
|
0
|
|
|
|
|
|
my $name = delete $arguments{name}; |
|
2500
|
|
|
|
|
|
|
|
|
2501
|
0
|
0
|
|
|
|
|
confess( 'Unknown arguments: ', join( ', ', keys %arguments ) ) if %arguments; |
|
2502
|
|
|
|
|
|
|
|
|
2503
|
0
|
|
|
|
|
|
@ret = _call_redis( |
|
2504
|
|
|
|
|
|
|
$redis, |
|
2505
|
|
|
|
|
|
|
_lua_script_cmd( $redis, 'collection_info' ), |
|
2506
|
|
|
|
|
|
|
0, |
|
2507
|
|
|
|
|
|
|
$name, |
|
2508
|
|
|
|
|
|
|
); |
|
2509
|
0
|
|
|
|
|
|
$results = _lists2hash( \@_collection_info_result_keys, \@ret ); |
|
2510
|
|
|
|
|
|
|
|
|
2511
|
0
|
|
|
|
|
|
my $error = $results->{error}; |
|
2512
|
|
|
|
|
|
|
|
|
2513
|
0
|
0
|
|
|
|
|
if ( exists $ERROR{ $error } ) { |
|
2514
|
0
|
0
|
|
|
|
|
_confess( format_message( "Collection '%s' info not received (%s)", $name, $ERROR{ $error } ) ) |
|
2515
|
|
|
|
|
|
|
if $error != $E_NO_ERROR; |
|
2516
|
|
|
|
|
|
|
} else { |
|
2517
|
0
|
|
|
|
|
|
_unknown_error( @ret ); |
|
2518
|
|
|
|
|
|
|
} |
|
2519
|
|
|
|
|
|
|
} |
|
2520
|
|
|
|
|
|
|
|
|
2521
|
0
|
|
|
|
|
|
my $oldest_time = $results->{oldest_time}; |
|
2522
|
0
|
0
|
0
|
|
|
|
!$oldest_time || defined( _NUMBER( $oldest_time ) ) || warn( format_message( 'oldest_time is not a number: %s', $oldest_time ) ); |
|
2523
|
|
|
|
|
|
|
|
|
2524
|
0
|
|
|
|
|
|
delete $results->{error}; |
|
2525
|
0
|
|
|
|
|
|
return $results; |
|
2526
|
|
|
|
|
|
|
} |
|
2527
|
|
|
|
|
|
|
|
|
2528
|
|
|
|
|
|
|
=head3 list_info |
|
2529
|
|
|
|
|
|
|
|
|
2530
|
|
|
|
|
|
|
list_info( $list_id ) |
|
2531
|
|
|
|
|
|
|
|
|
2532
|
|
|
|
|
|
|
Get data list information and status. |
|
2533
|
|
|
|
|
|
|
|
|
2534
|
|
|
|
|
|
|
C<$list_id> must be a non-empty string. |
|
2535
|
|
|
|
|
|
|
|
|
2536
|
|
|
|
|
|
|
Returns a reference to a hash with the following elements: |
|
2537
|
|
|
|
|
|
|
|
|
2538
|
|
|
|
|
|
|
=over 3 |
|
2539
|
|
|
|
|
|
|
|
|
2540
|
|
|
|
|
|
|
=item * |
|
2541
|
|
|
|
|
|
|
|
|
2542
|
|
|
|
|
|
|
C - Number of data items stored in the data list. |
|
2543
|
|
|
|
|
|
|
|
|
2544
|
|
|
|
|
|
|
=item * |
|
2545
|
|
|
|
|
|
|
|
|
2546
|
|
|
|
|
|
|
C - The time of the oldest data in the list. |
|
2547
|
|
|
|
|
|
|
C if the data list does not exist. |
|
2548
|
|
|
|
|
|
|
|
|
2549
|
|
|
|
|
|
|
=back |
|
2550
|
|
|
|
|
|
|
|
|
2551
|
|
|
|
|
|
|
=cut |
|
2552
|
|
|
|
|
|
|
my @_list_info_result_keys = qw( |
|
2553
|
|
|
|
|
|
|
error |
|
2554
|
|
|
|
|
|
|
items |
|
2555
|
|
|
|
|
|
|
oldest_time |
|
2556
|
|
|
|
|
|
|
); |
|
2557
|
|
|
|
|
|
|
|
|
2558
|
|
|
|
|
|
|
sub list_info { |
|
2559
|
0
|
|
|
0
|
1
|
|
my ( $self, $list_id ) = @_; |
|
2560
|
|
|
|
|
|
|
|
|
2561
|
0
|
|
0
|
|
|
|
_STRING( $list_id ) // $self->_throw( $E_MISMATCH_ARG, 'list_id' ); |
|
2562
|
|
|
|
|
|
|
|
|
2563
|
0
|
|
|
|
|
|
$self->_set_last_errorcode( $E_NO_ERROR ); |
|
2564
|
|
|
|
|
|
|
|
|
2565
|
0
|
|
|
|
|
|
my @ret = $self->_call_redis( |
|
2566
|
|
|
|
|
|
|
$self->_lua_script_cmd( 'list_info' ), |
|
2567
|
|
|
|
|
|
|
0, |
|
2568
|
|
|
|
|
|
|
$self->name, |
|
2569
|
|
|
|
|
|
|
$list_id, |
|
2570
|
|
|
|
|
|
|
); |
|
2571
|
0
|
|
|
|
|
|
my $results = _lists2hash( \@_list_info_result_keys, \@ret ); |
|
2572
|
|
|
|
|
|
|
|
|
2573
|
0
|
|
|
|
|
|
my $error = $results->{error}; |
|
2574
|
|
|
|
|
|
|
|
|
2575
|
0
|
0
|
|
|
|
|
if ( exists $ERROR{ $error } ) { |
|
2576
|
0
|
0
|
|
|
|
|
$self->_clear_sha1 if $error == $E_COLLECTION_DELETED; |
|
2577
|
0
|
0
|
|
|
|
|
$self->_throw( $error ) if $error != $E_NO_ERROR; |
|
2578
|
|
|
|
|
|
|
} else { |
|
2579
|
0
|
|
|
|
|
|
$self->_process_unknown_error( @ret ); |
|
2580
|
|
|
|
|
|
|
} |
|
2581
|
|
|
|
|
|
|
|
|
2582
|
0
|
|
|
|
|
|
my $oldest_time = $results->{oldest_time}; |
|
2583
|
0
|
0
|
0
|
|
|
|
!$oldest_time || defined( _NUMBER( $oldest_time ) ) || warn( format_message( 'oldest_time is not a number: %s', $oldest_time ) ); |
|
2584
|
|
|
|
|
|
|
|
|
2585
|
0
|
|
|
|
|
|
delete $results->{error}; |
|
2586
|
0
|
|
|
|
|
|
return $results; |
|
2587
|
|
|
|
|
|
|
} |
|
2588
|
|
|
|
|
|
|
|
|
2589
|
|
|
|
|
|
|
=head3 oldest_time |
|
2590
|
|
|
|
|
|
|
|
|
2591
|
|
|
|
|
|
|
my $oldest_time = $coll->oldest_time; |
|
2592
|
|
|
|
|
|
|
|
|
2593
|
|
|
|
|
|
|
Get the time of the oldest data in the collection. |
|
2594
|
|
|
|
|
|
|
Returns C if the collection does not contain data. |
|
2595
|
|
|
|
|
|
|
|
|
2596
|
|
|
|
|
|
|
An error exception is thrown (C) if the collection does not exist. |
|
2597
|
|
|
|
|
|
|
|
|
2598
|
|
|
|
|
|
|
=cut |
|
2599
|
|
|
|
|
|
|
my @_oldest_time_result_keys = qw( |
|
2600
|
|
|
|
|
|
|
error |
|
2601
|
|
|
|
|
|
|
oldest_time |
|
2602
|
|
|
|
|
|
|
); |
|
2603
|
|
|
|
|
|
|
|
|
2604
|
|
|
|
|
|
|
sub oldest_time { |
|
2605
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
|
2606
|
|
|
|
|
|
|
|
|
2607
|
0
|
|
|
|
|
|
$self->_set_last_errorcode( $E_NO_ERROR ); |
|
2608
|
|
|
|
|
|
|
|
|
2609
|
0
|
|
|
|
|
|
my @ret = $self->_call_redis( |
|
2610
|
|
|
|
|
|
|
$self->_lua_script_cmd( 'oldest_time' ), |
|
2611
|
|
|
|
|
|
|
0, |
|
2612
|
|
|
|
|
|
|
$self->name, |
|
2613
|
|
|
|
|
|
|
); |
|
2614
|
0
|
|
|
|
|
|
my $results = _lists2hash( \@_oldest_time_result_keys, \@ret ); |
|
2615
|
|
|
|
|
|
|
|
|
2616
|
0
|
|
|
|
|
|
my $error = $results->{error}; |
|
2617
|
|
|
|
|
|
|
|
|
2618
|
0
|
0
|
|
|
|
|
if ( exists $ERROR{ $error } ) { |
|
2619
|
0
|
0
|
|
|
|
|
$self->_clear_sha1 if $error == $E_COLLECTION_DELETED; |
|
2620
|
0
|
0
|
|
|
|
|
$self->_throw( $error ) if $error != $E_NO_ERROR; |
|
2621
|
|
|
|
|
|
|
} else { |
|
2622
|
0
|
|
|
|
|
|
$self->_process_unknown_error( @ret ); |
|
2623
|
|
|
|
|
|
|
} |
|
2624
|
|
|
|
|
|
|
|
|
2625
|
0
|
|
|
|
|
|
my $oldest_time = $results->{oldest_time}; |
|
2626
|
0
|
0
|
0
|
|
|
|
!$oldest_time || defined( _NUMBER( $oldest_time ) ) || warn( format_message( 'oldest_time is not a number: %s', $oldest_time ) ); |
|
2627
|
|
|
|
|
|
|
|
|
2628
|
0
|
|
|
|
|
|
return $oldest_time; |
|
2629
|
|
|
|
|
|
|
} |
|
2630
|
|
|
|
|
|
|
|
|
2631
|
|
|
|
|
|
|
=head3 list_exists |
|
2632
|
|
|
|
|
|
|
|
|
2633
|
|
|
|
|
|
|
list_exists( $list_id ) |
|
2634
|
|
|
|
|
|
|
|
|
2635
|
|
|
|
|
|
|
Example: |
|
2636
|
|
|
|
|
|
|
|
|
2637
|
|
|
|
|
|
|
say "The collection has '$list_id' list" if $coll->list_exists( 'Some_id' ); |
|
2638
|
|
|
|
|
|
|
|
|
2639
|
|
|
|
|
|
|
Check whether there is a list in the collection with given |
|
2640
|
|
|
|
|
|
|
ID C<$list_id>. |
|
2641
|
|
|
|
|
|
|
|
|
2642
|
|
|
|
|
|
|
Returns true if the list exists and false otherwise. |
|
2643
|
|
|
|
|
|
|
|
|
2644
|
|
|
|
|
|
|
=cut |
|
2645
|
|
|
|
|
|
|
sub list_exists { |
|
2646
|
0
|
|
|
0
|
1
|
|
my ( $self, $list_id ) = @_; |
|
2647
|
|
|
|
|
|
|
|
|
2648
|
0
|
|
0
|
|
|
|
_STRING( $list_id ) // $self->_throw( $E_MISMATCH_ARG, 'list_id' ); |
|
2649
|
|
|
|
|
|
|
|
|
2650
|
0
|
|
|
|
|
|
$self->_set_last_errorcode( $E_NO_ERROR ); |
|
2651
|
|
|
|
|
|
|
|
|
2652
|
0
|
|
|
|
|
|
return $self->_call_redis( 'EXISTS', $self->_data_list_key( $list_id ) ); |
|
2653
|
|
|
|
|
|
|
} |
|
2654
|
|
|
|
|
|
|
|
|
2655
|
|
|
|
|
|
|
=head3 collection_exists |
|
2656
|
|
|
|
|
|
|
|
|
2657
|
|
|
|
|
|
|
collection_exists( redis => $server, name => $name ) |
|
2658
|
|
|
|
|
|
|
|
|
2659
|
|
|
|
|
|
|
Example: |
|
2660
|
|
|
|
|
|
|
|
|
2661
|
|
|
|
|
|
|
say 'The collection ', $coll->name, ' exists' if $coll->collection_exists; |
|
2662
|
|
|
|
|
|
|
my $redis = Redis->new( server => "$server:$port" ); |
|
2663
|
|
|
|
|
|
|
say "The collection 'Some name' exists" |
|
2664
|
|
|
|
|
|
|
if Redis::CappedCollection::collection_exists( redis => $redis, name => 'Some name' ); |
|
2665
|
|
|
|
|
|
|
|
|
2666
|
|
|
|
|
|
|
Check whether there is a collection with given name. |
|
2667
|
|
|
|
|
|
|
Returns true if the collection exists and false otherwise. |
|
2668
|
|
|
|
|
|
|
|
|
2669
|
|
|
|
|
|
|
It can be called as either the existing C object method or a class function. |
|
2670
|
|
|
|
|
|
|
|
|
2671
|
|
|
|
|
|
|
If invoked as the object method, C uses C and C |
|
2672
|
|
|
|
|
|
|
attributes from the object as defaults. |
|
2673
|
|
|
|
|
|
|
|
|
2674
|
|
|
|
|
|
|
If invoked as the class function, C requires mandatory C and C |
|
2675
|
|
|
|
|
|
|
arguments. |
|
2676
|
|
|
|
|
|
|
|
|
2677
|
|
|
|
|
|
|
These arguments are in key-value pairs as described for L method. |
|
2678
|
|
|
|
|
|
|
|
|
2679
|
|
|
|
|
|
|
An error exception is thrown (C) if an argument is not valid. |
|
2680
|
|
|
|
|
|
|
|
|
2681
|
|
|
|
|
|
|
=cut |
|
2682
|
|
|
|
|
|
|
sub collection_exists { |
|
2683
|
0
|
|
|
0
|
1
|
|
my ( $self, $redis, $name ); |
|
2684
|
0
|
0
|
0
|
|
|
|
if ( @_ && _INSTANCE( $_[0], __PACKAGE__ ) ) { # allow calling $obj->bar |
|
2685
|
0
|
|
|
|
|
|
$self = shift; |
|
2686
|
0
|
|
|
|
|
|
$redis = $self->_redis; |
|
2687
|
0
|
|
|
|
|
|
$name = $self->name; |
|
2688
|
|
|
|
|
|
|
} else { |
|
2689
|
0
|
0
|
|
|
|
|
shift if _CLASSISA( $_[0], __PACKAGE__ ); # allow calling Foo->bar as well as Foo::bar |
|
2690
|
|
|
|
|
|
|
} |
|
2691
|
|
|
|
|
|
|
|
|
2692
|
0
|
|
|
|
|
|
my %arguments = @_; |
|
2693
|
0
|
|
|
|
|
|
_check_arguments_acceptability( \%arguments, [ 'redis', 'name' ] ); |
|
2694
|
|
|
|
|
|
|
|
|
2695
|
0
|
0
|
|
|
|
|
unless ( $self ) { |
|
2696
|
0
|
0
|
|
|
|
|
confess "'redis' argument is required" unless defined $arguments{redis}; |
|
2697
|
0
|
0
|
|
|
|
|
confess "'name' argument is required" unless defined $arguments{name}; |
|
2698
|
|
|
|
|
|
|
} |
|
2699
|
|
|
|
|
|
|
|
|
2700
|
0
|
0
|
|
|
|
|
$redis = _get_redis( $arguments{redis} ) unless $self; |
|
2701
|
0
|
0
|
|
|
|
|
$name = $arguments{name} if exists $arguments{name}; |
|
2702
|
|
|
|
|
|
|
|
|
2703
|
0
|
0
|
|
|
|
|
if ( $self ) { |
|
2704
|
0
|
|
|
|
|
|
return $self->_call_redis( 'EXISTS', _make_status_key( $name ) ); |
|
2705
|
|
|
|
|
|
|
} else { |
|
2706
|
0
|
|
|
|
|
|
return _call_redis( $redis, 'EXISTS', _make_status_key( $name ) ); |
|
2707
|
|
|
|
|
|
|
} |
|
2708
|
|
|
|
|
|
|
} |
|
2709
|
|
|
|
|
|
|
|
|
2710
|
|
|
|
|
|
|
=head3 lists |
|
2711
|
|
|
|
|
|
|
|
|
2712
|
|
|
|
|
|
|
lists( $pattern ) |
|
2713
|
|
|
|
|
|
|
|
|
2714
|
|
|
|
|
|
|
Example: |
|
2715
|
|
|
|
|
|
|
|
|
2716
|
|
|
|
|
|
|
say "The collection has '$_' list" foreach $coll->lists; |
|
2717
|
|
|
|
|
|
|
|
|
2718
|
|
|
|
|
|
|
Returns an array of list ID of lists stored in a collection. |
|
2719
|
|
|
|
|
|
|
Returns all list IDs matching C<$pattern> if C<$pattern> is not empty. |
|
2720
|
|
|
|
|
|
|
C<$patten> must be a non-empty string. |
|
2721
|
|
|
|
|
|
|
|
|
2722
|
|
|
|
|
|
|
Supported glob-style patterns: |
|
2723
|
|
|
|
|
|
|
|
|
2724
|
|
|
|
|
|
|
=over 3 |
|
2725
|
|
|
|
|
|
|
|
|
2726
|
|
|
|
|
|
|
=item * |
|
2727
|
|
|
|
|
|
|
|
|
2728
|
|
|
|
|
|
|
C matches C, C and C |
|
2729
|
|
|
|
|
|
|
|
|
2730
|
|
|
|
|
|
|
=item * |
|
2731
|
|
|
|
|
|
|
|
|
2732
|
|
|
|
|
|
|
C matches C and C |
|
2733
|
|
|
|
|
|
|
|
|
2734
|
|
|
|
|
|
|
=item * |
|
2735
|
|
|
|
|
|
|
|
|
2736
|
|
|
|
|
|
|
C matches C and C, but not C |
|
2737
|
|
|
|
|
|
|
|
|
2738
|
|
|
|
|
|
|
=back |
|
2739
|
|
|
|
|
|
|
|
|
2740
|
|
|
|
|
|
|
Use C<'\'> to escape special characters if you want to match them verbatim. |
|
2741
|
|
|
|
|
|
|
|
|
2742
|
|
|
|
|
|
|
Warning: consider C as a command that should only be used in production |
|
2743
|
|
|
|
|
|
|
environments with extreme care. Its performance is not optimal for large collections. |
|
2744
|
|
|
|
|
|
|
This command is intended for debugging and special operations. |
|
2745
|
|
|
|
|
|
|
Don't use C in your regular application code. |
|
2746
|
|
|
|
|
|
|
|
|
2747
|
|
|
|
|
|
|
In addition, it may cause an exception (C) if |
|
2748
|
|
|
|
|
|
|
the collection contains a very large number of lists |
|
2749
|
|
|
|
|
|
|
(C<'Error while reading from Redis server'>). |
|
2750
|
|
|
|
|
|
|
|
|
2751
|
|
|
|
|
|
|
=cut |
|
2752
|
|
|
|
|
|
|
sub lists { |
|
2753
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
|
2754
|
0
|
|
0
|
|
|
|
my $pattern = shift // '*'; |
|
2755
|
|
|
|
|
|
|
|
|
2756
|
0
|
|
0
|
|
|
|
_STRING( $pattern ) // $self->_throw( $E_MISMATCH_ARG, 'pattern' ); |
|
2757
|
|
|
|
|
|
|
|
|
2758
|
0
|
|
|
|
|
|
$self->_set_last_errorcode( $E_NO_ERROR ); |
|
2759
|
|
|
|
|
|
|
|
|
2760
|
0
|
|
|
|
|
|
my @keys; |
|
2761
|
|
|
|
|
|
|
try { |
|
2762
|
0
|
|
|
0
|
|
|
@keys = $self->_call_redis( 'KEYS', $self->_data_list_key( $pattern ) ); |
|
2763
|
|
|
|
|
|
|
} catch { |
|
2764
|
0
|
|
|
0
|
|
|
my $error = $_; |
|
2765
|
0
|
0
|
|
|
|
|
_confess( $error ) unless $self->last_errorcode == $E_REDIS_DID_NOT_RETURN_DATA; |
|
2766
|
0
|
|
|
|
|
|
}; |
|
2767
|
|
|
|
|
|
|
|
|
2768
|
0
|
|
|
|
|
|
return map { ( $_ =~ /:([^:]+)$/ )[0] } @keys; |
|
|
0
|
|
|
|
|
|
|
|
2769
|
|
|
|
|
|
|
} |
|
2770
|
|
|
|
|
|
|
|
|
2771
|
|
|
|
|
|
|
=head3 resize |
|
2772
|
|
|
|
|
|
|
|
|
2773
|
|
|
|
|
|
|
resize( redis => $server, name => $name, ... ) |
|
2774
|
|
|
|
|
|
|
|
|
2775
|
|
|
|
|
|
|
Example: |
|
2776
|
|
|
|
|
|
|
|
|
2777
|
|
|
|
|
|
|
$coll->resize( min_cleanup_bytes => 100_000 ); |
|
2778
|
|
|
|
|
|
|
my $redis = Redis->new( server => "$server:$port" ); |
|
2779
|
|
|
|
|
|
|
Redis::CappedCollection::resize( redis => $redis, name => 'Some name', older_allowed => 1 ); |
|
2780
|
|
|
|
|
|
|
|
|
2781
|
|
|
|
|
|
|
Use the C to change the values of the parameters of the collection. |
|
2782
|
|
|
|
|
|
|
It can be called as either the existing C object method or a class function. |
|
2783
|
|
|
|
|
|
|
|
|
2784
|
|
|
|
|
|
|
If invoked as the object method, C uses C and C attributes |
|
2785
|
|
|
|
|
|
|
from the object as defaults. |
|
2786
|
|
|
|
|
|
|
If invoked as the class function, C requires mandatory C and C |
|
2787
|
|
|
|
|
|
|
arguments. |
|
2788
|
|
|
|
|
|
|
|
|
2789
|
|
|
|
|
|
|
These arguments are in key-value pairs as described for L method. |
|
2790
|
|
|
|
|
|
|
|
|
2791
|
|
|
|
|
|
|
It is possible to change the following parameters: C, C, |
|
2792
|
|
|
|
|
|
|
C, C. One or more parameters are required. |
|
2793
|
|
|
|
|
|
|
|
|
2794
|
|
|
|
|
|
|
Returns the number of completed changes. |
|
2795
|
|
|
|
|
|
|
|
|
2796
|
|
|
|
|
|
|
An error exception is thrown (C) if an argument is not valid or the |
|
2797
|
|
|
|
|
|
|
collection does not exist. |
|
2798
|
|
|
|
|
|
|
|
|
2799
|
|
|
|
|
|
|
=cut |
|
2800
|
|
|
|
|
|
|
sub resize { |
|
2801
|
0
|
|
|
0
|
1
|
|
my ( $self, $redis, $name ); |
|
2802
|
0
|
0
|
0
|
|
|
|
if ( @_ && _INSTANCE( $_[0], __PACKAGE__ ) ) { # allow calling $obj->bar |
|
2803
|
0
|
|
|
|
|
|
$self = shift; |
|
2804
|
0
|
|
|
|
|
|
$redis = $self->_redis; |
|
2805
|
0
|
|
|
|
|
|
$name = $self->name; |
|
2806
|
|
|
|
|
|
|
} else { |
|
2807
|
0
|
0
|
|
|
|
|
shift if _CLASSISA( $_[0], __PACKAGE__ ); # allow calling Foo->bar as well as Foo::bar |
|
2808
|
|
|
|
|
|
|
} |
|
2809
|
|
|
|
|
|
|
|
|
2810
|
0
|
|
|
|
|
|
my %arguments = @_; |
|
2811
|
0
|
|
|
|
|
|
_check_arguments_acceptability( \%arguments, [ 'redis', 'name', @_status_parameters ] ); |
|
2812
|
|
|
|
|
|
|
|
|
2813
|
0
|
0
|
|
|
|
|
unless ( $self ) { |
|
2814
|
0
|
0
|
|
|
|
|
confess "'redis' argument is required" unless defined $arguments{redis}; |
|
2815
|
0
|
0
|
|
|
|
|
confess "'name' argument is required" unless defined $arguments{name}; |
|
2816
|
|
|
|
|
|
|
} |
|
2817
|
|
|
|
|
|
|
|
|
2818
|
0
|
0
|
|
|
|
|
$redis = _get_redis( $arguments{redis} ) unless $self; |
|
2819
|
0
|
0
|
|
|
|
|
$name = $arguments{name} if $arguments{name}; |
|
2820
|
|
|
|
|
|
|
|
|
2821
|
0
|
|
|
|
|
|
my $requested_changes = 0; |
|
2822
|
0
|
|
|
|
|
|
foreach my $parameter ( @_status_parameters ) { |
|
2823
|
0
|
0
|
|
|
|
|
++$requested_changes if exists $arguments{ $parameter }; |
|
2824
|
|
|
|
|
|
|
} |
|
2825
|
0
|
0
|
|
|
|
|
unless ( $requested_changes ) { |
|
2826
|
0
|
|
|
|
|
|
my $error = 'One or more parameters are required'; |
|
2827
|
0
|
0
|
|
|
|
|
if ( $self ) { |
|
2828
|
0
|
|
|
|
|
|
$self->_throw( $E_MISMATCH_ARG, $error ); |
|
2829
|
|
|
|
|
|
|
} else { |
|
2830
|
0
|
|
|
|
|
|
confess format_message( '%s : %s', $error, $ERROR{ $E_MISMATCH_ARG } ); |
|
2831
|
|
|
|
|
|
|
} |
|
2832
|
|
|
|
|
|
|
} |
|
2833
|
|
|
|
|
|
|
|
|
2834
|
0
|
|
|
|
|
|
my $resized = 0; |
|
2835
|
0
|
|
|
|
|
|
foreach my $parameter ( @_status_parameters ) { |
|
2836
|
0
|
0
|
|
|
|
|
if ( exists $arguments{ $parameter } ) { |
|
2837
|
0
|
0
|
0
|
|
|
|
if ( $parameter eq 'min_cleanup_bytes' || $parameter eq 'min_cleanup_items' ) { |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
2838
|
|
|
|
|
|
|
confess "'$parameter' must be nonnegative integer" |
|
2839
|
0
|
0
|
|
|
|
|
unless _NONNEGINT( $arguments{ $parameter } ); |
|
2840
|
|
|
|
|
|
|
} elsif ( $parameter eq 'memory_reserve' ) { |
|
2841
|
0
|
|
|
|
|
|
my $memory_reserve = $arguments{ $parameter }; |
|
2842
|
0
|
0
|
0
|
|
|
|
confess format_message( "'%s' must have a valid value", $parameter ) |
|
|
|
|
0
|
|
|
|
|
|
2843
|
|
|
|
|
|
|
unless _NUMBER( $memory_reserve ) && $memory_reserve >= $MIN_MEMORY_RESERVE && $memory_reserve <= $MAX_MEMORY_RESERVE; |
|
2844
|
|
|
|
|
|
|
} elsif ( $parameter eq 'older_allowed' ) { |
|
2845
|
0
|
0
|
|
|
|
|
$arguments{ $parameter } = $arguments{ $parameter } ? 1 :0; |
|
2846
|
|
|
|
|
|
|
} |
|
2847
|
|
|
|
|
|
|
|
|
2848
|
0
|
|
|
|
|
|
my $ret = 0; |
|
2849
|
0
|
|
|
|
|
|
my $new_val = $arguments{ $parameter }; |
|
2850
|
0
|
0
|
|
|
|
|
if ( $self ) { |
|
2851
|
0
|
|
|
|
|
|
$ret = $self->_call_redis( 'HSET', _make_status_key( $self->name ), $parameter, $new_val ); |
|
2852
|
|
|
|
|
|
|
} else { |
|
2853
|
0
|
|
|
|
|
|
$ret = _call_redis( $redis, 'HSET', _make_status_key( $name ), $parameter, $new_val ); |
|
2854
|
|
|
|
|
|
|
} |
|
2855
|
|
|
|
|
|
|
|
|
2856
|
0
|
0
|
|
|
|
|
if ( $ret == 0 ) { # 0 if field already exists in the hash and the value was updated |
|
2857
|
0
|
0
|
|
|
|
|
if ( $self ) { |
|
2858
|
0
|
0
|
|
|
|
|
if ( $parameter eq 'min_cleanup_bytes' ) { |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
2859
|
0
|
|
|
|
|
|
$self->_set_min_cleanup_bytes( $new_val ); |
|
2860
|
|
|
|
|
|
|
} elsif ( $parameter eq 'min_cleanup_items' ) { |
|
2861
|
0
|
|
|
|
|
|
$self->_set_min_cleanup_items( $new_val ); |
|
2862
|
|
|
|
|
|
|
} elsif ( $parameter eq 'memory_reserve' ) { |
|
2863
|
0
|
|
|
|
|
|
$self->_set_memory_reserve( $new_val ); |
|
2864
|
|
|
|
|
|
|
} else { |
|
2865
|
0
|
|
|
|
|
|
$self->$parameter( $new_val ); |
|
2866
|
|
|
|
|
|
|
} |
|
2867
|
|
|
|
|
|
|
} |
|
2868
|
0
|
|
|
|
|
|
++$resized; |
|
2869
|
|
|
|
|
|
|
} else { |
|
2870
|
0
|
|
|
|
|
|
my $msg = format_message( "Parameter %s not updated to '%s' for collection '%s'", $parameter, $new_val, $name ); |
|
2871
|
0
|
0
|
|
|
|
|
if ( $self ) { |
|
2872
|
0
|
|
|
|
|
|
$self->_throw( $E_COLLECTION_DELETED, $msg ); |
|
2873
|
|
|
|
|
|
|
} else { |
|
2874
|
0
|
|
|
|
|
|
_confess( "$msg (".$ERROR{ $E_COLLECTION_DELETED }.')' ); |
|
2875
|
|
|
|
|
|
|
} |
|
2876
|
|
|
|
|
|
|
} |
|
2877
|
|
|
|
|
|
|
} |
|
2878
|
|
|
|
|
|
|
} |
|
2879
|
|
|
|
|
|
|
|
|
2880
|
0
|
|
|
|
|
|
return $resized; |
|
2881
|
|
|
|
|
|
|
} |
|
2882
|
|
|
|
|
|
|
|
|
2883
|
|
|
|
|
|
|
=head3 drop_collection |
|
2884
|
|
|
|
|
|
|
|
|
2885
|
|
|
|
|
|
|
drop_collection( redis => $server, name => $name ) |
|
2886
|
|
|
|
|
|
|
|
|
2887
|
|
|
|
|
|
|
Example: |
|
2888
|
|
|
|
|
|
|
|
|
2889
|
|
|
|
|
|
|
$coll->drop_collection; |
|
2890
|
|
|
|
|
|
|
my $redis = Redis->new( server => "$server:$port" ); |
|
2891
|
|
|
|
|
|
|
Redis::CappedCollection::drop_collection( redis => $redis, name => 'Some name' ); |
|
2892
|
|
|
|
|
|
|
|
|
2893
|
|
|
|
|
|
|
Use the C to remove the entire collection from the redis server, |
|
2894
|
|
|
|
|
|
|
including all its data and metadata. |
|
2895
|
|
|
|
|
|
|
|
|
2896
|
|
|
|
|
|
|
Before using this method, make sure that the collection is not being used by other customers. |
|
2897
|
|
|
|
|
|
|
|
|
2898
|
|
|
|
|
|
|
It can be called as either the existing C object method or a class function. |
|
2899
|
|
|
|
|
|
|
If invoked as the class function, C requires mandatory C and C |
|
2900
|
|
|
|
|
|
|
arguments. |
|
2901
|
|
|
|
|
|
|
These arguments are in key-value pairs as described for L method. |
|
2902
|
|
|
|
|
|
|
|
|
2903
|
|
|
|
|
|
|
Warning: consider C as a command that should only be used in production |
|
2904
|
|
|
|
|
|
|
environments with extreme care. Its performance is not optimal for large collections. |
|
2905
|
|
|
|
|
|
|
This command is intended for debugging and special operations. |
|
2906
|
|
|
|
|
|
|
Avoid using C in your regular application code. |
|
2907
|
|
|
|
|
|
|
|
|
2908
|
|
|
|
|
|
|
C mat throw an exception (C) if |
|
2909
|
|
|
|
|
|
|
the collection contains a very large number of lists |
|
2910
|
|
|
|
|
|
|
(C<'Error while reading from Redis server'>). |
|
2911
|
|
|
|
|
|
|
|
|
2912
|
|
|
|
|
|
|
An error exception is thrown (C) if an argument is not valid. |
|
2913
|
|
|
|
|
|
|
|
|
2914
|
|
|
|
|
|
|
=cut |
|
2915
|
|
|
|
|
|
|
sub drop_collection { |
|
2916
|
0
|
|
|
0
|
1
|
|
my $ret; |
|
2917
|
0
|
0
|
0
|
|
|
|
if ( @_ && _INSTANCE( $_[0], __PACKAGE__ ) ) { # allow calling $obj->bar |
|
2918
|
0
|
|
|
|
|
|
my $self = shift; |
|
2919
|
|
|
|
|
|
|
|
|
2920
|
0
|
|
|
|
|
|
my %arguments = @_; |
|
2921
|
0
|
|
|
|
|
|
_check_arguments_acceptability( \%arguments, [] ); |
|
2922
|
|
|
|
|
|
|
|
|
2923
|
0
|
|
|
|
|
|
$self->_set_last_errorcode( $E_NO_ERROR ); |
|
2924
|
|
|
|
|
|
|
|
|
2925
|
0
|
|
|
|
|
|
$ret = $self->_call_redis( |
|
2926
|
|
|
|
|
|
|
$self->_lua_script_cmd( 'drop_collection' ), |
|
2927
|
|
|
|
|
|
|
0, |
|
2928
|
|
|
|
|
|
|
$self->name, |
|
2929
|
|
|
|
|
|
|
); |
|
2930
|
|
|
|
|
|
|
|
|
2931
|
0
|
|
|
|
|
|
$self->_clear_name; |
|
2932
|
0
|
|
|
|
|
|
$self->_clear_sha1; |
|
2933
|
|
|
|
|
|
|
} else { |
|
2934
|
0
|
0
|
|
|
|
|
shift if _CLASSISA( $_[0], __PACKAGE__ ); # allow calling Foo->bar as well as Foo::bar |
|
2935
|
|
|
|
|
|
|
|
|
2936
|
0
|
|
|
|
|
|
my %arguments = @_; |
|
2937
|
0
|
|
|
|
|
|
_check_arguments_acceptability( \%arguments, [ 'redis', 'name' ] ); |
|
2938
|
|
|
|
|
|
|
|
|
2939
|
0
|
0
|
|
|
|
|
confess "'redis' argument is required" unless defined $arguments{redis}; |
|
2940
|
0
|
0
|
|
|
|
|
confess "'name' argument is required" unless defined $arguments{name}; |
|
2941
|
|
|
|
|
|
|
|
|
2942
|
0
|
|
|
|
|
|
my $redis = _get_redis( $arguments{redis} ); |
|
2943
|
0
|
|
|
|
|
|
my $name = $arguments{name}; |
|
2944
|
|
|
|
|
|
|
|
|
2945
|
0
|
|
|
|
|
|
$ret = _call_redis( |
|
2946
|
|
|
|
|
|
|
$redis, |
|
2947
|
|
|
|
|
|
|
_lua_script_cmd( $redis, 'drop_collection' ), |
|
2948
|
|
|
|
|
|
|
0, |
|
2949
|
|
|
|
|
|
|
$name, |
|
2950
|
|
|
|
|
|
|
); |
|
2951
|
|
|
|
|
|
|
} |
|
2952
|
|
|
|
|
|
|
|
|
2953
|
0
|
|
|
|
|
|
return $ret; |
|
2954
|
|
|
|
|
|
|
} |
|
2955
|
|
|
|
|
|
|
|
|
2956
|
|
|
|
|
|
|
=head3 drop_list |
|
2957
|
|
|
|
|
|
|
|
|
2958
|
|
|
|
|
|
|
drop_list( $list_id ) |
|
2959
|
|
|
|
|
|
|
|
|
2960
|
|
|
|
|
|
|
Use the C method to remove the entire specified list. |
|
2961
|
|
|
|
|
|
|
Method removes all the structures on the Redis server associated with |
|
2962
|
|
|
|
|
|
|
the specified list. |
|
2963
|
|
|
|
|
|
|
|
|
2964
|
|
|
|
|
|
|
C<$list_id> must be a non-empty string. |
|
2965
|
|
|
|
|
|
|
|
|
2966
|
|
|
|
|
|
|
Method returns true if the list is removed, or false otherwise. |
|
2967
|
|
|
|
|
|
|
|
|
2968
|
|
|
|
|
|
|
=cut |
|
2969
|
|
|
|
|
|
|
my @_drop_list_result_keys = qw( |
|
2970
|
|
|
|
|
|
|
error |
|
2971
|
|
|
|
|
|
|
list_removed |
|
2972
|
|
|
|
|
|
|
); |
|
2973
|
|
|
|
|
|
|
|
|
2974
|
|
|
|
|
|
|
sub drop_list { |
|
2975
|
0
|
|
|
0
|
1
|
|
my ( $self, $list_id ) = @_; |
|
2976
|
|
|
|
|
|
|
|
|
2977
|
0
|
|
0
|
|
|
|
_STRING( $list_id ) // $self->_throw( $E_MISMATCH_ARG, 'list_id' ); |
|
2978
|
|
|
|
|
|
|
|
|
2979
|
0
|
|
|
|
|
|
$self->_set_last_errorcode( $E_NO_ERROR ); |
|
2980
|
|
|
|
|
|
|
|
|
2981
|
0
|
|
|
|
|
|
my @ret = $self->_call_redis( |
|
2982
|
|
|
|
|
|
|
$self->_lua_script_cmd( 'drop_list' ), |
|
2983
|
|
|
|
|
|
|
0, |
|
2984
|
|
|
|
|
|
|
$self->name, |
|
2985
|
|
|
|
|
|
|
$list_id, |
|
2986
|
|
|
|
|
|
|
); |
|
2987
|
0
|
|
|
|
|
|
my $results = _lists2hash( \@_drop_list_result_keys, \@ret ); |
|
2988
|
|
|
|
|
|
|
|
|
2989
|
0
|
|
|
|
|
|
my $error = $results->{error}; |
|
2990
|
|
|
|
|
|
|
|
|
2991
|
0
|
0
|
|
|
|
|
if ( exists $ERROR{ $error } ) { |
|
2992
|
0
|
0
|
|
|
|
|
$self->_clear_sha1 if $error == $E_COLLECTION_DELETED; |
|
2993
|
0
|
0
|
|
|
|
|
$self->_throw( $error ) if $error != $E_NO_ERROR; |
|
2994
|
|
|
|
|
|
|
} else { |
|
2995
|
0
|
|
|
|
|
|
$self->_process_unknown_error( @ret ); |
|
2996
|
|
|
|
|
|
|
} |
|
2997
|
|
|
|
|
|
|
|
|
2998
|
0
|
|
|
|
|
|
return $results->{list_removed}; |
|
2999
|
|
|
|
|
|
|
} |
|
3000
|
|
|
|
|
|
|
|
|
3001
|
|
|
|
|
|
|
=head3 clear_collection |
|
3002
|
|
|
|
|
|
|
|
|
3003
|
|
|
|
|
|
|
$coll->clear_collection; |
|
3004
|
|
|
|
|
|
|
|
|
3005
|
|
|
|
|
|
|
Use the C to remove the entire collection data from the redis server, |
|
3006
|
|
|
|
|
|
|
|
|
3007
|
|
|
|
|
|
|
Before using this method, make sure that the collection is not being used by other customers. |
|
3008
|
|
|
|
|
|
|
|
|
3009
|
|
|
|
|
|
|
Warning: consider C as a command that should only be used in production |
|
3010
|
|
|
|
|
|
|
environments with extreme care. Its performance is not optimal for large collections. |
|
3011
|
|
|
|
|
|
|
This command is intended for debugging and special operations. |
|
3012
|
|
|
|
|
|
|
Avoid using C in your regular application code. |
|
3013
|
|
|
|
|
|
|
|
|
3014
|
|
|
|
|
|
|
C mat throw an exception (C) if |
|
3015
|
|
|
|
|
|
|
the collection contains a very large number of lists |
|
3016
|
|
|
|
|
|
|
(C<'Error while reading from Redis server'>). |
|
3017
|
|
|
|
|
|
|
|
|
3018
|
|
|
|
|
|
|
=cut |
|
3019
|
|
|
|
|
|
|
sub clear_collection { |
|
3020
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
|
3021
|
|
|
|
|
|
|
|
|
3022
|
0
|
|
|
|
|
|
my $ret; |
|
3023
|
|
|
|
|
|
|
|
|
3024
|
0
|
|
|
|
|
|
$self->_set_last_errorcode( $E_NO_ERROR ); |
|
3025
|
|
|
|
|
|
|
|
|
3026
|
0
|
|
|
|
|
|
$ret = $self->_call_redis( |
|
3027
|
|
|
|
|
|
|
$self->_lua_script_cmd( 'clear_collection' ), |
|
3028
|
|
|
|
|
|
|
0, |
|
3029
|
|
|
|
|
|
|
$self->name, |
|
3030
|
|
|
|
|
|
|
); |
|
3031
|
|
|
|
|
|
|
|
|
3032
|
0
|
|
|
|
|
|
return $ret; |
|
3033
|
|
|
|
|
|
|
} |
|
3034
|
|
|
|
|
|
|
|
|
3035
|
|
|
|
|
|
|
=head3 ping |
|
3036
|
|
|
|
|
|
|
|
|
3037
|
|
|
|
|
|
|
$is_alive = $coll->ping; |
|
3038
|
|
|
|
|
|
|
|
|
3039
|
|
|
|
|
|
|
This command is used to test if a connection is still alive. |
|
3040
|
|
|
|
|
|
|
|
|
3041
|
|
|
|
|
|
|
Returns 1 if a connection is still alive or 0 otherwise. |
|
3042
|
|
|
|
|
|
|
|
|
3043
|
|
|
|
|
|
|
External connections to the server object (eg, C <$redis = Redis->new( ... );>), |
|
3044
|
|
|
|
|
|
|
and the collection object can continue to work after calling ping only if the method returned 1. |
|
3045
|
|
|
|
|
|
|
|
|
3046
|
|
|
|
|
|
|
If there is no connection to the Redis server (methods return 0), the connection to the server closes. |
|
3047
|
|
|
|
|
|
|
In this case, to continue working with the collection, |
|
3048
|
|
|
|
|
|
|
you must re-create the C object with the L method. |
|
3049
|
|
|
|
|
|
|
When using an external connection to the server, |
|
3050
|
|
|
|
|
|
|
to check the connection to the server you can use the C<$redis->echo( ... )> call. |
|
3051
|
|
|
|
|
|
|
This is useful to avoid closing the connection to the Redis server unintentionally. |
|
3052
|
|
|
|
|
|
|
|
|
3053
|
|
|
|
|
|
|
=cut |
|
3054
|
|
|
|
|
|
|
sub ping { |
|
3055
|
0
|
|
|
0
|
1
|
|
my ( $self ) = @_; |
|
3056
|
|
|
|
|
|
|
|
|
3057
|
0
|
|
|
|
|
|
$self->_set_last_errorcode( $E_NO_ERROR ); |
|
3058
|
|
|
|
|
|
|
|
|
3059
|
0
|
|
|
|
|
|
my $ret; |
|
3060
|
|
|
|
|
|
|
try { |
|
3061
|
0
|
|
|
0
|
|
|
$ret = $self->_redis->ping; |
|
3062
|
|
|
|
|
|
|
} catch { |
|
3063
|
0
|
|
|
0
|
|
|
$self->_redis_exception( $_ ); |
|
3064
|
0
|
|
|
|
|
|
}; |
|
3065
|
|
|
|
|
|
|
|
|
3066
|
0
|
0
|
0
|
|
|
|
return( ( $ret // '' ) eq 'PONG' ? 1 : 0 ); |
|
3067
|
|
|
|
|
|
|
} |
|
3068
|
|
|
|
|
|
|
|
|
3069
|
|
|
|
|
|
|
=head3 quit |
|
3070
|
|
|
|
|
|
|
|
|
3071
|
|
|
|
|
|
|
$coll->quit; |
|
3072
|
|
|
|
|
|
|
|
|
3073
|
|
|
|
|
|
|
Close the connection with the redis server. |
|
3074
|
|
|
|
|
|
|
|
|
3075
|
|
|
|
|
|
|
It does not close the connection to the Redis server if it is an external connection provided |
|
3076
|
|
|
|
|
|
|
to collection constructor as existing L object. |
|
3077
|
|
|
|
|
|
|
When using an external connection (eg, C<$redis = Redis-> new (...);>), |
|
3078
|
|
|
|
|
|
|
to close the connection to the Redis server, call C<$redis->quit> after calling this method. |
|
3079
|
|
|
|
|
|
|
|
|
3080
|
|
|
|
|
|
|
=cut |
|
3081
|
|
|
|
|
|
|
sub quit { |
|
3082
|
0
|
|
|
0
|
1
|
|
my ( $self ) = @_; |
|
3083
|
|
|
|
|
|
|
|
|
3084
|
0
|
0
|
0
|
|
|
|
return if $] >= 5.14 && ${^GLOBAL_PHASE} eq 'DESTRUCT'; |
|
3085
|
|
|
|
|
|
|
|
|
3086
|
0
|
|
|
|
|
|
$self->_set_last_errorcode( $E_NO_ERROR ); |
|
3087
|
0
|
|
|
|
|
|
$self->_clear_sha1; |
|
3088
|
0
|
0
|
|
|
|
|
unless ( $self->_use_external_connection ) { |
|
3089
|
|
|
|
|
|
|
try { |
|
3090
|
0
|
|
|
0
|
|
|
$self->_redis->quit; |
|
3091
|
|
|
|
|
|
|
} catch { |
|
3092
|
0
|
|
|
0
|
|
|
$self->_redis_exception( $_ ); |
|
3093
|
0
|
|
|
|
|
|
}; |
|
3094
|
|
|
|
|
|
|
} |
|
3095
|
|
|
|
|
|
|
|
|
3096
|
0
|
|
|
|
|
|
return; |
|
3097
|
|
|
|
|
|
|
} |
|
3098
|
|
|
|
|
|
|
|
|
3099
|
|
|
|
|
|
|
#-- private attributes --------------------------------------------------------- |
|
3100
|
|
|
|
|
|
|
|
|
3101
|
|
|
|
|
|
|
has _DEBUG => ( |
|
3102
|
|
|
|
|
|
|
is => 'rw', |
|
3103
|
|
|
|
|
|
|
init_arg => undef, |
|
3104
|
|
|
|
|
|
|
isa => 'Num', |
|
3105
|
|
|
|
|
|
|
default => 0, |
|
3106
|
|
|
|
|
|
|
); |
|
3107
|
|
|
|
|
|
|
|
|
3108
|
|
|
|
|
|
|
has _check_maxmemory => ( |
|
3109
|
|
|
|
|
|
|
is => 'ro', |
|
3110
|
|
|
|
|
|
|
init_arg => 'check_maxmemory', |
|
3111
|
|
|
|
|
|
|
isa => 'Bool', |
|
3112
|
|
|
|
|
|
|
default => 1, |
|
3113
|
|
|
|
|
|
|
); |
|
3114
|
|
|
|
|
|
|
|
|
3115
|
|
|
|
|
|
|
has _create_from_naked_new => ( |
|
3116
|
|
|
|
|
|
|
is => 'ro', |
|
3117
|
|
|
|
|
|
|
isa => 'Bool', |
|
3118
|
|
|
|
|
|
|
default => 1, |
|
3119
|
|
|
|
|
|
|
); |
|
3120
|
|
|
|
|
|
|
|
|
3121
|
|
|
|
|
|
|
has _create_from_open => ( |
|
3122
|
|
|
|
|
|
|
is => 'ro', |
|
3123
|
|
|
|
|
|
|
isa => 'Bool', |
|
3124
|
|
|
|
|
|
|
default => 0, |
|
3125
|
|
|
|
|
|
|
); |
|
3126
|
|
|
|
|
|
|
|
|
3127
|
|
|
|
|
|
|
has _use_external_connection => ( |
|
3128
|
|
|
|
|
|
|
is => 'rw', |
|
3129
|
|
|
|
|
|
|
isa => 'Bool', |
|
3130
|
|
|
|
|
|
|
default => 1, |
|
3131
|
|
|
|
|
|
|
); |
|
3132
|
|
|
|
|
|
|
|
|
3133
|
|
|
|
|
|
|
has _server => ( |
|
3134
|
|
|
|
|
|
|
is => 'rw', |
|
3135
|
|
|
|
|
|
|
isa => 'Str', |
|
3136
|
|
|
|
|
|
|
default => $DEFAULT_SERVER.':'.$DEFAULT_PORT, |
|
3137
|
|
|
|
|
|
|
trigger => sub { |
|
3138
|
|
|
|
|
|
|
my $self = shift; |
|
3139
|
|
|
|
|
|
|
$self->_server( $self->_server.':'.$DEFAULT_PORT ) |
|
3140
|
|
|
|
|
|
|
unless $self->_server =~ /:/; |
|
3141
|
|
|
|
|
|
|
}, |
|
3142
|
|
|
|
|
|
|
); |
|
3143
|
|
|
|
|
|
|
|
|
3144
|
|
|
|
|
|
|
has _redis => ( |
|
3145
|
|
|
|
|
|
|
is => 'rw', |
|
3146
|
|
|
|
|
|
|
# 'Maybe[Test::RedisServer]' to test only |
|
3147
|
|
|
|
|
|
|
isa => 'Maybe[Redis] | Maybe[Test::RedisServer]', |
|
3148
|
|
|
|
|
|
|
); |
|
3149
|
|
|
|
|
|
|
|
|
3150
|
|
|
|
|
|
|
has _maxmemory => ( |
|
3151
|
|
|
|
|
|
|
is => 'rw', |
|
3152
|
|
|
|
|
|
|
isa => __PACKAGE__.'::NonNegInt', |
|
3153
|
|
|
|
|
|
|
init_arg => undef, |
|
3154
|
|
|
|
|
|
|
); |
|
3155
|
|
|
|
|
|
|
|
|
3156
|
|
|
|
|
|
|
foreach my $attr_name ( qw( |
|
3157
|
|
|
|
|
|
|
_queue_key |
|
3158
|
|
|
|
|
|
|
_status_key |
|
3159
|
|
|
|
|
|
|
_data_keys |
|
3160
|
|
|
|
|
|
|
_time_keys |
|
3161
|
|
|
|
|
|
|
) ) { |
|
3162
|
|
|
|
|
|
|
has $attr_name => ( |
|
3163
|
|
|
|
|
|
|
is => 'rw', |
|
3164
|
|
|
|
|
|
|
isa => 'Str', |
|
3165
|
|
|
|
|
|
|
init_arg => undef, |
|
3166
|
|
|
|
|
|
|
); |
|
3167
|
|
|
|
|
|
|
} |
|
3168
|
|
|
|
|
|
|
|
|
3169
|
|
|
|
|
|
|
my $_lua_scripts = {}; |
|
3170
|
|
|
|
|
|
|
|
|
3171
|
|
|
|
|
|
|
#-- private functions ---------------------------------------------------------- |
|
3172
|
|
|
|
|
|
|
|
|
3173
|
|
|
|
|
|
|
sub _check_arguments_acceptability { |
|
3174
|
0
|
|
|
0
|
|
|
my ( $received_arguments, $acceptable_arguments ) = @_; |
|
3175
|
|
|
|
|
|
|
|
|
3176
|
0
|
|
|
|
|
|
my ( %legal_arguments, @unlegal_arguments ); |
|
3177
|
0
|
|
|
|
|
|
$legal_arguments{ $_ } = 1 foreach @$acceptable_arguments; |
|
3178
|
0
|
|
|
|
|
|
foreach my $argument ( keys %$received_arguments ) { |
|
3179
|
0
|
0
|
|
|
|
|
push @unlegal_arguments, $argument unless exists $legal_arguments{ $argument }; |
|
3180
|
|
|
|
|
|
|
} |
|
3181
|
|
|
|
|
|
|
|
|
3182
|
0
|
0
|
|
|
|
|
confess( format_message( 'Unknown arguments: %s', \@unlegal_arguments ) ) if @unlegal_arguments; |
|
3183
|
|
|
|
|
|
|
|
|
3184
|
0
|
|
|
|
|
|
return; |
|
3185
|
|
|
|
|
|
|
} |
|
3186
|
|
|
|
|
|
|
|
|
3187
|
|
|
|
|
|
|
sub _maxmemory_policy_ok { |
|
3188
|
0
|
|
|
0
|
|
|
my ( $self, $redis ); |
|
3189
|
0
|
0
|
0
|
|
|
|
if ( @_ && _INSTANCE( $_[0], __PACKAGE__ ) ) { # allow calling $obj->bar |
|
3190
|
0
|
|
|
|
|
|
$self = shift; |
|
3191
|
0
|
|
|
|
|
|
$redis = $self->_redis; |
|
3192
|
|
|
|
|
|
|
} else { |
|
3193
|
0
|
0
|
|
|
|
|
shift if _CLASSISA( $_[0], __PACKAGE__ ); # allow calling Foo->bar as well as Foo::bar |
|
3194
|
|
|
|
|
|
|
} |
|
3195
|
|
|
|
|
|
|
|
|
3196
|
0
|
|
|
|
|
|
my %arguments = @_; |
|
3197
|
0
|
|
|
|
|
|
_check_arguments_acceptability( \%arguments, [ 'redis' ] ); |
|
3198
|
|
|
|
|
|
|
|
|
3199
|
0
|
|
|
|
|
|
my $maxmemory_policy; |
|
3200
|
0
|
0
|
|
|
|
|
if ( $self ) { |
|
3201
|
0
|
|
|
|
|
|
( undef, $maxmemory_policy ) = $self->_call_redis( 'CONFIG', 'GET', 'maxmemory-policy' ); |
|
3202
|
|
|
|
|
|
|
} else { |
|
3203
|
0
|
|
|
|
|
|
my $redis_argument = $arguments{redis}; |
|
3204
|
0
|
0
|
|
|
|
|
confess "'redis' argument is required" unless defined( $redis_argument ); |
|
3205
|
0
|
|
|
|
|
|
( undef, $maxmemory_policy ) = _call_redis( _get_redis( $redis_argument ), 'CONFIG', 'GET', 'maxmemory-policy' ) |
|
3206
|
|
|
|
|
|
|
} |
|
3207
|
|
|
|
|
|
|
|
|
3208
|
0
|
|
0
|
|
|
|
return( defined( $maxmemory_policy ) && $maxmemory_policy eq $USED_MEMORY_POLICY ); |
|
3209
|
|
|
|
|
|
|
} |
|
3210
|
|
|
|
|
|
|
|
|
3211
|
|
|
|
|
|
|
sub _lists2hash { |
|
3212
|
0
|
|
|
0
|
|
|
my ( $keys, $vals ) = @_; |
|
3213
|
|
|
|
|
|
|
|
|
3214
|
0
|
0
|
0
|
|
|
|
confess $ERROR{ $E_MISMATCH_ARG }." for internal function '_lists2hash'" |
|
|
|
|
0
|
|
|
|
|
|
3215
|
|
|
|
|
|
|
unless _ARRAY( $keys ) && _ARRAY0( $vals ) && scalar( @$keys ) >= scalar( @$vals ); |
|
3216
|
|
|
|
|
|
|
|
|
3217
|
0
|
|
|
|
|
|
my %hash; |
|
3218
|
0
|
|
|
|
|
|
for ( my $idx = 0; $idx < @$keys; $idx++ ) { |
|
3219
|
0
|
|
|
|
|
|
$hash{ $keys->[ $idx ] } = $vals->[ $idx ]; |
|
3220
|
|
|
|
|
|
|
} |
|
3221
|
|
|
|
|
|
|
|
|
3222
|
0
|
|
|
|
|
|
return \%hash; |
|
3223
|
|
|
|
|
|
|
} |
|
3224
|
|
|
|
|
|
|
|
|
3225
|
|
|
|
|
|
|
sub _process_unknown_error { |
|
3226
|
0
|
|
|
0
|
|
|
my ( $self, @args ) = @_; |
|
3227
|
|
|
|
|
|
|
|
|
3228
|
0
|
|
|
|
|
|
$self->_set_last_errorcode( $E_UNKNOWN_ERROR ); |
|
3229
|
0
|
0
|
|
|
|
|
_unknown_error( @args, $self->reconnect_on_error ? $self->_reconnect( $E_UNKNOWN_ERROR ) : () ); |
|
3230
|
|
|
|
|
|
|
} |
|
3231
|
|
|
|
|
|
|
|
|
3232
|
|
|
|
|
|
|
sub _unknown_error { |
|
3233
|
0
|
|
|
0
|
|
|
my @args = @_; |
|
3234
|
|
|
|
|
|
|
|
|
3235
|
0
|
|
|
|
|
|
_confess( format_message( '%s: %s', $ERROR{ $E_UNKNOWN_ERROR }, \@args ) ); |
|
3236
|
|
|
|
|
|
|
} |
|
3237
|
|
|
|
|
|
|
|
|
3238
|
|
|
|
|
|
|
sub _confess { |
|
3239
|
0
|
|
|
0
|
|
|
my @args = @_; |
|
3240
|
|
|
|
|
|
|
|
|
3241
|
0
|
|
|
|
|
|
confess @args; |
|
3242
|
|
|
|
|
|
|
} |
|
3243
|
|
|
|
|
|
|
|
|
3244
|
|
|
|
|
|
|
sub _make_data_key { |
|
3245
|
0
|
|
|
0
|
|
|
my ( $name ) = @_; |
|
3246
|
0
|
|
|
|
|
|
return( $NAMESPACE.':D:'.$name ); |
|
3247
|
|
|
|
|
|
|
} |
|
3248
|
|
|
|
|
|
|
|
|
3249
|
|
|
|
|
|
|
sub _make_time_key { |
|
3250
|
0
|
|
|
0
|
|
|
my ( $name ) = @_; |
|
3251
|
0
|
|
|
|
|
|
return( $NAMESPACE.':T:'.$name ); |
|
3252
|
|
|
|
|
|
|
} |
|
3253
|
|
|
|
|
|
|
|
|
3254
|
|
|
|
|
|
|
sub _make_status_key { |
|
3255
|
0
|
|
|
0
|
|
|
my ( $name ) = @_; |
|
3256
|
0
|
|
|
|
|
|
return( $NAMESPACE.':S:'.$name ); |
|
3257
|
|
|
|
|
|
|
} |
|
3258
|
|
|
|
|
|
|
|
|
3259
|
|
|
|
|
|
|
sub _get_redis { |
|
3260
|
0
|
|
|
0
|
|
|
my ( $redis ) = @_; |
|
3261
|
|
|
|
|
|
|
|
|
3262
|
0
|
0
|
|
|
|
|
$redis = _redis_constructor( $redis ) |
|
3263
|
|
|
|
|
|
|
unless _INSTANCE( $redis, 'Redis' ); |
|
3264
|
|
|
|
|
|
|
|
|
3265
|
0
|
|
|
|
|
|
return $redis; |
|
3266
|
|
|
|
|
|
|
} |
|
3267
|
|
|
|
|
|
|
|
|
3268
|
|
|
|
|
|
|
#-- private methods ------------------------------------------------------------ |
|
3269
|
|
|
|
|
|
|
|
|
3270
|
|
|
|
|
|
|
# for testing only |
|
3271
|
|
|
|
|
|
|
sub _long_term_operation { |
|
3272
|
0
|
|
|
0
|
|
|
my ( $self, $return_as_insert ) = @_; |
|
3273
|
|
|
|
|
|
|
|
|
3274
|
0
|
|
|
|
|
|
$self->_set_last_errorcode( $E_NO_ERROR ); |
|
3275
|
|
|
|
|
|
|
|
|
3276
|
0
|
0
|
|
|
|
|
my @ret = $self->_call_redis( |
|
3277
|
|
|
|
|
|
|
$self->_lua_script_cmd( '_long_term_operation' ), |
|
3278
|
|
|
|
|
|
|
0, |
|
3279
|
|
|
|
|
|
|
$self->name, |
|
3280
|
|
|
|
|
|
|
$return_as_insert ? 1 : 0, |
|
3281
|
|
|
|
|
|
|
); |
|
3282
|
|
|
|
|
|
|
|
|
3283
|
0
|
|
|
|
|
|
my ( $error ) = @ret; |
|
3284
|
|
|
|
|
|
|
|
|
3285
|
0
|
0
|
|
|
|
|
if ( $return_as_insert ) { |
|
3286
|
0
|
0
|
0
|
|
|
|
if ( scalar( @ret ) == 1 && exists( $ERROR{ $error } ) ) { |
|
3287
|
0
|
0
|
0
|
|
|
|
if ( $error == $E_NO_ERROR ) { |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
3288
|
|
|
|
|
|
|
# Normal result: Nothing to do |
|
3289
|
|
|
|
|
|
|
} elsif ( $error == $E_COLLECTION_DELETED ) { |
|
3290
|
0
|
|
|
|
|
|
$self->_clear_sha1; |
|
3291
|
0
|
|
|
|
|
|
$self->_throw( $error ); |
|
3292
|
|
|
|
|
|
|
} elsif ( |
|
3293
|
|
|
|
|
|
|
$error == $E_DATA_ID_EXISTS |
|
3294
|
|
|
|
|
|
|
|| $error == $E_OLDER_THAN_ALLOWED |
|
3295
|
|
|
|
|
|
|
) { |
|
3296
|
0
|
|
|
|
|
|
$self->_throw( $error ); |
|
3297
|
|
|
|
|
|
|
} else { |
|
3298
|
0
|
|
|
|
|
|
$self->_throw( $error, 'Unexpected error' ); |
|
3299
|
|
|
|
|
|
|
} |
|
3300
|
|
|
|
|
|
|
} else { |
|
3301
|
0
|
|
|
|
|
|
$self->_process_unknown_error( @ret ); |
|
3302
|
|
|
|
|
|
|
} |
|
3303
|
|
|
|
|
|
|
} else { |
|
3304
|
0
|
0
|
0
|
|
|
|
if ( scalar( @ret ) == 3 && exists( $ERROR{ $error } ) && $ret[2] eq '_long_term_operation' ) { |
|
|
|
|
0
|
|
|
|
|
|
3305
|
0
|
0
|
|
|
|
|
if ( $error == $E_NO_ERROR ) { |
|
3306
|
|
|
|
|
|
|
# Normal result: Nothing to do |
|
3307
|
|
|
|
|
|
|
} else { |
|
3308
|
0
|
|
|
|
|
|
$self->_throw( $error, 'Unexpected error' ); |
|
3309
|
|
|
|
|
|
|
} |
|
3310
|
|
|
|
|
|
|
} else { |
|
3311
|
0
|
|
|
|
|
|
$self->_process_unknown_error( @ret ); |
|
3312
|
|
|
|
|
|
|
} |
|
3313
|
|
|
|
|
|
|
} |
|
3314
|
|
|
|
|
|
|
|
|
3315
|
0
|
|
|
|
|
|
return \@ret; |
|
3316
|
|
|
|
|
|
|
} |
|
3317
|
|
|
|
|
|
|
|
|
3318
|
|
|
|
|
|
|
sub _data_list_key { |
|
3319
|
0
|
|
|
0
|
|
|
my ( $self, $list_id ) = @_; |
|
3320
|
|
|
|
|
|
|
|
|
3321
|
0
|
|
|
|
|
|
return( $self->_data_keys.':'.$list_id ); |
|
3322
|
|
|
|
|
|
|
} |
|
3323
|
|
|
|
|
|
|
|
|
3324
|
|
|
|
|
|
|
sub _time_list_key { |
|
3325
|
0
|
|
|
0
|
|
|
my ( $self, $list_id ) = @_; |
|
3326
|
|
|
|
|
|
|
|
|
3327
|
0
|
|
|
|
|
|
return( $self->_time_keys.':'.$list_id ); |
|
3328
|
|
|
|
|
|
|
} |
|
3329
|
|
|
|
|
|
|
|
|
3330
|
|
|
|
|
|
|
sub _verify_collection { |
|
3331
|
0
|
|
|
0
|
|
|
my ( $self ) = @_; |
|
3332
|
|
|
|
|
|
|
|
|
3333
|
0
|
|
|
|
|
|
$self->_set_last_errorcode( $E_NO_ERROR ); |
|
3334
|
|
|
|
|
|
|
|
|
3335
|
0
|
0
|
0
|
|
|
|
my ( $status_exist, $older_allowed, $min_cleanup_bytes, $min_cleanup_items, $memory_reserve, $data_version ) = $self->_call_redis( |
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
3336
|
|
|
|
|
|
|
$self->_lua_script_cmd( 'verify_collection' ), |
|
3337
|
|
|
|
|
|
|
0, |
|
3338
|
|
|
|
|
|
|
$self->name, |
|
3339
|
|
|
|
|
|
|
$self->older_allowed ? 1 : 0, |
|
3340
|
|
|
|
|
|
|
$self->min_cleanup_bytes || 0, |
|
3341
|
|
|
|
|
|
|
$self->min_cleanup_items || 0, |
|
3342
|
|
|
|
|
|
|
$self->memory_reserve || $MIN_MEMORY_RESERVE, |
|
3343
|
|
|
|
|
|
|
); |
|
3344
|
|
|
|
|
|
|
|
|
3345
|
0
|
0
|
|
|
|
|
if ( $status_exist ) { |
|
3346
|
0
|
0
|
|
|
|
|
$self->min_cleanup_bytes( $min_cleanup_bytes ) unless $self->min_cleanup_bytes; |
|
3347
|
0
|
0
|
|
|
|
|
$self->min_cleanup_items( $min_cleanup_items ) unless $self->min_cleanup_items; |
|
3348
|
0
|
0
|
|
|
|
|
$older_allowed == $self->older_allowed or $self->_throw( $E_MISMATCH_ARG, 'older_allowed' ); |
|
3349
|
0
|
0
|
|
|
|
|
$min_cleanup_bytes == $self->min_cleanup_bytes or $self->_throw( $E_MISMATCH_ARG, 'min_cleanup_bytes' ); |
|
3350
|
0
|
0
|
|
|
|
|
$min_cleanup_items == $self->min_cleanup_items or $self->_throw( $E_MISMATCH_ARG, 'min_cleanup_items' ); |
|
3351
|
0
|
0
|
|
|
|
|
$memory_reserve == $self->memory_reserve or $self->_throw( $E_MISMATCH_ARG, 'memory_reserve' ); |
|
3352
|
0
|
0
|
|
|
|
|
$data_version == $DATA_VERSION or $self->_throw( $E_INCOMP_DATA_VERSION ); |
|
3353
|
|
|
|
|
|
|
} |
|
3354
|
|
|
|
|
|
|
} |
|
3355
|
|
|
|
|
|
|
|
|
3356
|
|
|
|
|
|
|
sub _reconnect { |
|
3357
|
0
|
|
|
0
|
|
|
my $self = shift; |
|
3358
|
0
|
|
0
|
|
|
|
my $err = shift // 0; |
|
3359
|
0
|
|
|
|
|
|
my $msg = shift; |
|
3360
|
|
|
|
|
|
|
|
|
3361
|
0
|
|
|
|
|
|
my $err_msg = ''; |
|
3362
|
0
|
0
|
0
|
|
|
|
if ( |
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
3363
|
|
|
|
|
|
|
!$err || ( |
|
3364
|
|
|
|
|
|
|
$err != $E_MISMATCH_ARG |
|
3365
|
|
|
|
|
|
|
&& $err != $E_DATA_TOO_LARGE |
|
3366
|
|
|
|
|
|
|
&& $err != $E_MAXMEMORY_LIMIT |
|
3367
|
|
|
|
|
|
|
&& $err != $E_MAXMEMORY_POLICY |
|
3368
|
|
|
|
|
|
|
) |
|
3369
|
|
|
|
|
|
|
) { |
|
3370
|
|
|
|
|
|
|
try { |
|
3371
|
0
|
|
|
0
|
|
|
$self->_redis->connect; |
|
3372
|
|
|
|
|
|
|
} catch { |
|
3373
|
0
|
|
|
0
|
|
|
my $error = $_; |
|
3374
|
0
|
|
|
|
|
|
$err_msg = "(Not reconnected: $error)"; |
|
3375
|
0
|
|
|
|
|
|
}; |
|
3376
|
|
|
|
|
|
|
} |
|
3377
|
|
|
|
|
|
|
|
|
3378
|
0
|
0
|
|
|
|
|
if ( $err_msg ) { |
|
3379
|
0
|
0
|
|
|
|
|
$msg = defined( $msg ) |
|
|
|
0
|
|
|
|
|
|
|
3380
|
|
|
|
|
|
|
? ( $msg ? "$msg " : '' )."($err_msg)" |
|
3381
|
|
|
|
|
|
|
: $err_msg; |
|
3382
|
|
|
|
|
|
|
} |
|
3383
|
|
|
|
|
|
|
|
|
3384
|
0
|
|
|
|
|
|
return $msg; |
|
3385
|
|
|
|
|
|
|
} |
|
3386
|
|
|
|
|
|
|
|
|
3387
|
|
|
|
|
|
|
sub _throw { |
|
3388
|
0
|
|
|
0
|
|
|
my ( $self, $err, $prefix ) = @_; |
|
3389
|
|
|
|
|
|
|
|
|
3390
|
0
|
0
|
|
|
|
|
$prefix = $self->_reconnect( $err, $prefix ) if $self->reconnect_on_error; |
|
3391
|
|
|
|
|
|
|
|
|
3392
|
0
|
0
|
|
|
|
|
if ( exists $ERROR{ $err } ) { |
|
3393
|
0
|
|
|
|
|
|
$self->_set_last_errorcode( $err ); |
|
3394
|
0
|
0
|
|
|
|
|
_confess( format_message( '%s%s', ( $prefix ? "$prefix : " : '' ), $ERROR{ $err } ) ); |
|
3395
|
|
|
|
|
|
|
} else { |
|
3396
|
0
|
|
|
|
|
|
$self->_set_last_errorcode( $E_UNKNOWN_ERROR ); |
|
3397
|
0
|
0
|
|
|
|
|
_confess( format_message( '%s: %s%s', $ERROR{ $E_UNKNOWN_ERROR }, ( $prefix ? "$prefix : " : '' ), format_message( '%s', $err ) ) ); |
|
3398
|
|
|
|
|
|
|
} |
|
3399
|
|
|
|
|
|
|
} |
|
3400
|
|
|
|
|
|
|
|
|
3401
|
|
|
|
|
|
|
{ |
|
3402
|
|
|
|
|
|
|
my ( $_running_script_name, $_running_script_body ); |
|
3403
|
|
|
|
|
|
|
|
|
3404
|
|
|
|
|
|
|
sub _lua_script_cmd { |
|
3405
|
0
|
|
|
0
|
|
|
my ( $self, $redis ); |
|
3406
|
0
|
0
|
|
|
|
|
if ( _INSTANCE( $_[0], __PACKAGE__ ) ) { # allow calling $obj->bar |
|
3407
|
0
|
|
|
|
|
|
$self = shift; |
|
3408
|
0
|
|
|
|
|
|
$redis = $self->_redis; |
|
3409
|
|
|
|
|
|
|
} else { # allow calling Foo::bar |
|
3410
|
0
|
|
|
|
|
|
$redis = shift; |
|
3411
|
|
|
|
|
|
|
} |
|
3412
|
|
|
|
|
|
|
|
|
3413
|
0
|
|
|
|
|
|
$_running_script_name = shift; |
|
3414
|
0
|
|
|
|
|
|
$_running_script_body = $lua_script_body{ $_running_script_name }; |
|
3415
|
|
|
|
|
|
|
|
|
3416
|
0
|
|
|
|
|
|
my $sha1 = $_lua_scripts->{ $redis }->{ $_running_script_name }; |
|
3417
|
0
|
0
|
|
|
|
|
unless ( $sha1 ) { |
|
3418
|
0
|
|
|
|
|
|
$sha1 = $_lua_scripts->{ $redis }->{ $_running_script_name } = sha1_hex( $_running_script_body ); |
|
3419
|
0
|
|
|
|
|
|
my $ret; |
|
3420
|
0
|
0
|
|
|
|
|
if ( $self ) { |
|
3421
|
0
|
|
|
|
|
|
$ret = ( $self->_call_redis( 'SCRIPT', 'EXISTS', $sha1 ) )[0]; |
|
3422
|
|
|
|
|
|
|
} else { |
|
3423
|
0
|
|
|
|
|
|
$ret = ( _call_redis( $redis, 'SCRIPT', 'EXISTS', $sha1 ) )[0]; |
|
3424
|
|
|
|
|
|
|
} |
|
3425
|
0
|
0
|
|
|
|
|
return( 'EVAL', $_running_script_body ) |
|
3426
|
|
|
|
|
|
|
unless $ret; |
|
3427
|
|
|
|
|
|
|
} |
|
3428
|
0
|
|
|
|
|
|
return( 'EVALSHA', $sha1 ); |
|
3429
|
|
|
|
|
|
|
} |
|
3430
|
|
|
|
|
|
|
|
|
3431
|
|
|
|
|
|
|
sub _redis_exception { |
|
3432
|
0
|
|
|
0
|
|
|
my $self; |
|
3433
|
0
|
0
|
|
|
|
|
$self = shift if _INSTANCE( $_[0], __PACKAGE__ ); # allow calling $obj->bar |
|
3434
|
0
|
|
|
|
|
|
my ( $error ) = @_; # allow calling Foo::bar |
|
3435
|
|
|
|
|
|
|
|
|
3436
|
0
|
|
|
|
|
|
my $err_msg = ''; |
|
3437
|
0
|
0
|
|
|
|
|
if ( $self ) { |
|
3438
|
|
|
|
|
|
|
# Use the error messages from Redis.pm |
|
3439
|
0
|
0
|
0
|
|
|
|
if ( |
|
|
|
0
|
0
|
|
|
|
|
|
|
|
0
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
3440
|
|
|
|
|
|
|
$error =~ /^Could not connect to Redis server at / |
|
3441
|
|
|
|
|
|
|
|| $error =~ /^Can't close socket: / |
|
3442
|
|
|
|
|
|
|
|| $error =~ /^Not connected to any server/ |
|
3443
|
|
|
|
|
|
|
# Maybe for pub/sub only |
|
3444
|
|
|
|
|
|
|
|| $error =~ /^Error while reading from Redis server: / |
|
3445
|
|
|
|
|
|
|
|| $error =~ /^Redis server closed connection/ |
|
3446
|
|
|
|
|
|
|
) { |
|
3447
|
0
|
|
|
|
|
|
$self->_set_last_errorcode( $E_NETWORK ); |
|
3448
|
|
|
|
|
|
|
|
|
3449
|
|
|
|
|
|
|
# For connection problem |
|
3450
|
0
|
0
|
|
|
|
|
$err_msg = $self->_reconnect( $E_UNKNOWN_ERROR, $err_msg ) if $self->reconnect_on_error; |
|
3451
|
|
|
|
|
|
|
} elsif ( |
|
3452
|
|
|
|
|
|
|
$error =~ /^\[[^]]+\]\s+-?\Q$REDIS_MEMORY_ERROR_MSG\E/i |
|
3453
|
|
|
|
|
|
|
|| $error =~ /^\[[^]]+\]\s+-?\Q$REDIS_ERROR_CODE $ERROR{ $E_MAXMEMORY_LIMIT }\E/i |
|
3454
|
|
|
|
|
|
|
|| $error =~ /^\[[^]]+\]\s+-NOSCRIPT No matching script. Please use EVAL./ |
|
3455
|
|
|
|
|
|
|
) { |
|
3456
|
0
|
|
|
|
|
|
$self->_set_last_errorcode( $E_MAXMEMORY_LIMIT ); |
|
3457
|
0
|
|
|
|
|
|
$self->_clear_sha1; |
|
3458
|
|
|
|
|
|
|
|
|
3459
|
|
|
|
|
|
|
# No connection problem |
|
3460
|
|
|
|
|
|
|
} elsif ( $error =~ /^\[[^]]+\]\s+BUSY Redis is busy running a script/ ){ |
|
3461
|
0
|
|
|
|
|
|
$self->_set_last_errorcode( $E_UNKNOWN_ERROR ); |
|
3462
|
|
|
|
|
|
|
|
|
3463
|
|
|
|
|
|
|
# No connection problem - must wait... |
|
3464
|
|
|
|
|
|
|
} else { # external ALRM processing here |
|
3465
|
0
|
|
|
|
|
|
$self->_set_last_errorcode( $E_REDIS ); |
|
3466
|
|
|
|
|
|
|
|
|
3467
|
|
|
|
|
|
|
# For possible connection problems |
|
3468
|
0
|
0
|
|
|
|
|
$err_msg = $self->_reconnect( $E_UNKNOWN_ERROR, $err_msg ) if $self->reconnect_on_error; |
|
3469
|
|
|
|
|
|
|
} |
|
3470
|
|
|
|
|
|
|
} else { |
|
3471
|
|
|
|
|
|
|
# nothing to do now |
|
3472
|
|
|
|
|
|
|
} |
|
3473
|
|
|
|
|
|
|
|
|
3474
|
0
|
0
|
|
|
|
|
if ( $error =~ /\] ERR Error (?:running|compiling) script/ ) { |
|
3475
|
0
|
|
|
|
|
|
$error .= "\nLua script '$_running_script_name':\n$_running_script_body"; |
|
3476
|
|
|
|
|
|
|
} |
|
3477
|
0
|
|
|
|
|
|
_confess( format_message( '%s %s', $error, $err_msg ) ); |
|
3478
|
|
|
|
|
|
|
} |
|
3479
|
|
|
|
|
|
|
} |
|
3480
|
|
|
|
|
|
|
|
|
3481
|
|
|
|
|
|
|
sub _clear_sha1 { |
|
3482
|
0
|
|
|
0
|
|
|
my ( $self ) = @_; |
|
3483
|
|
|
|
|
|
|
|
|
3484
|
0
|
0
|
|
|
|
|
delete( $_lua_scripts->{ $self->_redis } ) if $self->_redis; |
|
3485
|
|
|
|
|
|
|
} |
|
3486
|
|
|
|
|
|
|
|
|
3487
|
|
|
|
|
|
|
sub _redis_constructor { |
|
3488
|
0
|
|
|
0
|
|
|
my ( $self, $redis, $redis_parameters ); |
|
3489
|
0
|
0
|
0
|
|
|
|
if ( @_ && _INSTANCE( $_[0], __PACKAGE__ ) ) { # allow calling $obj->bar |
|
3490
|
0
|
|
|
|
|
|
$self = shift; |
|
3491
|
0
|
|
|
|
|
|
$redis_parameters = shift; |
|
3492
|
|
|
|
|
|
|
|
|
3493
|
0
|
0
|
|
|
|
|
if ( _HASH0( $redis_parameters ) ) { |
|
3494
|
0
|
|
|
|
|
|
$self->_set_last_errorcode( $E_NO_ERROR ); |
|
3495
|
|
|
|
|
|
|
$redis = try { |
|
3496
|
0
|
|
|
0
|
|
|
Redis->new( %$redis_parameters ); |
|
3497
|
|
|
|
|
|
|
} catch { |
|
3498
|
0
|
|
|
0
|
|
|
my $error = $_; |
|
3499
|
0
|
|
|
|
|
|
$self->_redis_exception( format_message( '%s; (redis_parameters = %s)', $error, _parameters_2_str( $redis_parameters ) ) ); |
|
3500
|
0
|
|
|
|
|
|
}; |
|
3501
|
|
|
|
|
|
|
} else { |
|
3502
|
0
|
|
|
|
|
|
$redis = $self->_redis; |
|
3503
|
|
|
|
|
|
|
} |
|
3504
|
|
|
|
|
|
|
} else { # allow calling Foo::bar |
|
3505
|
0
|
0
|
|
|
|
|
$redis_parameters = _HASH0( shift ) or confess $ERROR{ $E_MISMATCH_ARG }; |
|
3506
|
|
|
|
|
|
|
$redis = try { |
|
3507
|
0
|
|
|
0
|
|
|
Redis->new( %$redis_parameters ); |
|
3508
|
|
|
|
|
|
|
} catch { |
|
3509
|
0
|
|
|
0
|
|
|
my $error = $_; |
|
3510
|
0
|
|
|
|
|
|
confess format_message( "'Redis' exception: %s; (redis_parameters = %s)", $error, _parameters_2_str( $redis_parameters ) ); |
|
3511
|
0
|
|
|
|
|
|
}; |
|
3512
|
|
|
|
|
|
|
} |
|
3513
|
|
|
|
|
|
|
|
|
3514
|
0
|
|
|
|
|
|
return $redis; |
|
3515
|
|
|
|
|
|
|
} |
|
3516
|
|
|
|
|
|
|
|
|
3517
|
|
|
|
|
|
|
sub _parameters_2_str { |
|
3518
|
0
|
|
|
0
|
|
|
my ( $parameters_hash_ref ) = @_; |
|
3519
|
|
|
|
|
|
|
|
|
3520
|
0
|
|
|
|
|
|
my %parameters_hash = ( %$parameters_hash_ref ); |
|
3521
|
0
|
0
|
|
|
|
|
$parameters_hash{password} =~ s/./*/g if defined $parameters_hash{password}; |
|
3522
|
|
|
|
|
|
|
|
|
3523
|
0
|
|
|
|
|
|
return format_message( '%s', \%parameters_hash ); |
|
3524
|
|
|
|
|
|
|
} |
|
3525
|
|
|
|
|
|
|
|
|
3526
|
|
|
|
|
|
|
# Keep in mind the default 'redis.conf' values: |
|
3527
|
|
|
|
|
|
|
# Close the connection after a client is idle for N seconds (0 to disable) |
|
3528
|
|
|
|
|
|
|
# timeout 300 |
|
3529
|
|
|
|
|
|
|
|
|
3530
|
|
|
|
|
|
|
# Send a request to Redis |
|
3531
|
|
|
|
|
|
|
sub _call_redis { |
|
3532
|
0
|
|
|
0
|
|
|
my ( $self, $redis ); |
|
3533
|
0
|
0
|
|
|
|
|
if ( _INSTANCE( $_[0], __PACKAGE__ ) ) { # allow calling $obj->bar |
|
3534
|
0
|
|
|
|
|
|
$self = shift; |
|
3535
|
|
|
|
|
|
|
|
|
3536
|
0
|
0
|
0
|
|
|
|
if ( $self->reconnect_on_error && !$self->ping ) { |
|
3537
|
0
|
|
|
|
|
|
my $err_msg = $self->_reconnect(); |
|
3538
|
0
|
0
|
|
|
|
|
_confess( format_message( '%s: %s', $ERROR{$E_REDIS}, $err_msg ) ) if $err_msg; |
|
3539
|
|
|
|
|
|
|
} |
|
3540
|
|
|
|
|
|
|
|
|
3541
|
0
|
|
|
|
|
|
$redis = $self->_redis; |
|
3542
|
|
|
|
|
|
|
} else { # allow calling Foo::bar |
|
3543
|
0
|
|
|
|
|
|
$redis = shift; |
|
3544
|
|
|
|
|
|
|
} |
|
3545
|
0
|
|
|
|
|
|
my $method = shift; |
|
3546
|
|
|
|
|
|
|
|
|
3547
|
0
|
0
|
|
|
|
|
$self->_set_last_errorcode( $E_NO_ERROR ) if $self; |
|
3548
|
|
|
|
|
|
|
|
|
3549
|
0
|
|
|
|
|
|
my @return; |
|
3550
|
0
|
|
|
|
|
|
my @args = @_; |
|
3551
|
|
|
|
|
|
|
try { |
|
3552
|
0
|
0
|
|
0
|
|
|
@return = $redis->$method( map { ref( $_ ) ? $$_ : $_ } @args ); |
|
|
0
|
|
|
|
|
|
|
|
3553
|
|
|
|
|
|
|
} catch { |
|
3554
|
0
|
|
|
0
|
|
|
my $error = $_; |
|
3555
|
0
|
0
|
|
|
|
|
if ( $self ) { |
|
3556
|
0
|
|
|
|
|
|
$self->_redis_exception( $error ); |
|
3557
|
|
|
|
|
|
|
} else { |
|
3558
|
0
|
|
|
|
|
|
_redis_exception( $error ); |
|
3559
|
|
|
|
|
|
|
} |
|
3560
|
0
|
|
|
|
|
|
}; |
|
3561
|
|
|
|
|
|
|
|
|
3562
|
0
|
0
|
|
|
|
|
unless ( scalar @return ) { |
|
3563
|
0
|
0
|
|
|
|
|
$self->_set_last_errorcode( $E_REDIS_DID_NOT_RETURN_DATA ) |
|
3564
|
|
|
|
|
|
|
if $self; |
|
3565
|
0
|
|
|
|
|
|
confess $ERROR{ $E_REDIS_DID_NOT_RETURN_DATA }; |
|
3566
|
|
|
|
|
|
|
} |
|
3567
|
|
|
|
|
|
|
|
|
3568
|
0
|
0
|
|
|
|
|
return wantarray ? @return : $return[0]; |
|
3569
|
|
|
|
|
|
|
} |
|
3570
|
|
|
|
|
|
|
|
|
3571
|
|
|
|
|
|
|
sub DESTROY { |
|
3572
|
|
|
|
|
|
|
my ( $self ) = @_; |
|
3573
|
|
|
|
|
|
|
|
|
3574
|
|
|
|
|
|
|
$self->clear_sha1; |
|
3575
|
|
|
|
|
|
|
} |
|
3576
|
|
|
|
|
|
|
|
|
3577
|
|
|
|
|
|
|
#-- Closes and cleans up ------------------------------------------------------- |
|
3578
|
|
|
|
|
|
|
|
|
3579
|
98
|
|
|
98
|
|
579
|
no Mouse::Util::TypeConstraints; |
|
|
98
|
|
|
|
|
154
|
|
|
|
98
|
|
|
|
|
924
|
|
|
3580
|
98
|
|
|
98
|
|
11666
|
no Mouse; # keywords are removed from the package |
|
|
98
|
|
|
|
|
118
|
|
|
|
98
|
|
|
|
|
381
|
|
|
3581
|
|
|
|
|
|
|
__PACKAGE__->meta->make_immutable(); |
|
3582
|
|
|
|
|
|
|
|
|
3583
|
|
|
|
|
|
|
=head2 DIAGNOSTICS |
|
3584
|
|
|
|
|
|
|
|
|
3585
|
|
|
|
|
|
|
All recognizable errors in C set corresponding value |
|
3586
|
|
|
|
|
|
|
into the L and throw an exception (C). |
|
3587
|
|
|
|
|
|
|
Unidentified errors also throw exceptions but L is not set. |
|
3588
|
|
|
|
|
|
|
|
|
3589
|
|
|
|
|
|
|
In addition to errors in the L module, detected errors are |
|
3590
|
|
|
|
|
|
|
L$E_MISMATCH_ARG>, L$E_DATA_TOO_LARGE>, L$E_MAXMEMORY_POLICY>, L$E_COLLECTION_DELETED>, |
|
3591
|
|
|
|
|
|
|
L$E_DATA_ID_EXISTS>, L$E_OLDER_THAN_ALLOWED>, L$E_NONEXISTENT_DATA_ID>, |
|
3592
|
|
|
|
|
|
|
L$E_INCOMP_DATA_VERSION>, L$E_REDIS_DID_NOT_RETURN_DATA>, L$E_UNKNOWN_ERROR>. |
|
3593
|
|
|
|
|
|
|
|
|
3594
|
|
|
|
|
|
|
The user has the choice: |
|
3595
|
|
|
|
|
|
|
|
|
3596
|
|
|
|
|
|
|
=over 3 |
|
3597
|
|
|
|
|
|
|
|
|
3598
|
|
|
|
|
|
|
=item * |
|
3599
|
|
|
|
|
|
|
|
|
3600
|
|
|
|
|
|
|
Use the module methods and independently analyze the situation without the use |
|
3601
|
|
|
|
|
|
|
of L. |
|
3602
|
|
|
|
|
|
|
|
|
3603
|
|
|
|
|
|
|
=item * |
|
3604
|
|
|
|
|
|
|
|
|
3605
|
|
|
|
|
|
|
Piece of code wrapped in C and analyze L |
|
3606
|
|
|
|
|
|
|
(look at the L"An Example"> section). |
|
3607
|
|
|
|
|
|
|
|
|
3608
|
|
|
|
|
|
|
=back |
|
3609
|
|
|
|
|
|
|
|
|
3610
|
|
|
|
|
|
|
=head2 An Example |
|
3611
|
|
|
|
|
|
|
|
|
3612
|
|
|
|
|
|
|
An example of error handling. |
|
3613
|
|
|
|
|
|
|
|
|
3614
|
|
|
|
|
|
|
use 5.010; |
|
3615
|
|
|
|
|
|
|
use strict; |
|
3616
|
|
|
|
|
|
|
use warnings; |
|
3617
|
|
|
|
|
|
|
|
|
3618
|
|
|
|
|
|
|
#-- Common --------------------------------------------------------- |
|
3619
|
|
|
|
|
|
|
use Redis::CappedCollection qw( |
|
3620
|
|
|
|
|
|
|
$DEFAULT_SERVER |
|
3621
|
|
|
|
|
|
|
$DEFAULT_PORT |
|
3622
|
|
|
|
|
|
|
|
|
3623
|
|
|
|
|
|
|
$E_NO_ERROR |
|
3624
|
|
|
|
|
|
|
$E_MISMATCH_ARG |
|
3625
|
|
|
|
|
|
|
$E_DATA_TOO_LARGE |
|
3626
|
|
|
|
|
|
|
$E_NETWORK |
|
3627
|
|
|
|
|
|
|
$E_MAXMEMORY_LIMIT |
|
3628
|
|
|
|
|
|
|
$E_MAXMEMORY_POLICY |
|
3629
|
|
|
|
|
|
|
$E_COLLECTION_DELETED |
|
3630
|
|
|
|
|
|
|
$E_REDIS |
|
3631
|
|
|
|
|
|
|
); |
|
3632
|
|
|
|
|
|
|
|
|
3633
|
|
|
|
|
|
|
# Error handling |
|
3634
|
|
|
|
|
|
|
sub exception { |
|
3635
|
|
|
|
|
|
|
my $coll = shift; |
|
3636
|
|
|
|
|
|
|
my $err = shift; |
|
3637
|
|
|
|
|
|
|
|
|
3638
|
|
|
|
|
|
|
die $err unless $coll; |
|
3639
|
|
|
|
|
|
|
if ( $coll->last_errorcode == $E_NO_ERROR ) { |
|
3640
|
|
|
|
|
|
|
# For example, to ignore |
|
3641
|
|
|
|
|
|
|
return unless $err; |
|
3642
|
|
|
|
|
|
|
} elsif ( $coll->last_errorcode == $E_MISMATCH_ARG ) { |
|
3643
|
|
|
|
|
|
|
# Necessary to correct the code |
|
3644
|
|
|
|
|
|
|
} elsif ( $coll->last_errorcode == $E_DATA_TOO_LARGE ) { |
|
3645
|
|
|
|
|
|
|
# Limit data length |
|
3646
|
|
|
|
|
|
|
} elsif ( $coll->last_errorcode == $E_NETWORK ) { |
|
3647
|
|
|
|
|
|
|
# For example, sleep |
|
3648
|
|
|
|
|
|
|
#sleep 60; |
|
3649
|
|
|
|
|
|
|
# and return code to repeat the operation |
|
3650
|
|
|
|
|
|
|
#return 'to repeat'; |
|
3651
|
|
|
|
|
|
|
} elsif ( $coll->last_errorcode == $E_MAXMEMORY_LIMIT ) { |
|
3652
|
|
|
|
|
|
|
# For example, return code to restart the server |
|
3653
|
|
|
|
|
|
|
#return 'to restart the redis server'; |
|
3654
|
|
|
|
|
|
|
} elsif ( $coll->last_errorcode == $E_MAXMEMORY_POLICY ) { |
|
3655
|
|
|
|
|
|
|
# Correct Redis server 'maxmemory-policy' setting |
|
3656
|
|
|
|
|
|
|
} elsif ( $coll->last_errorcode == $E_COLLECTION_DELETED ) { |
|
3657
|
|
|
|
|
|
|
# For example, return code to ignore |
|
3658
|
|
|
|
|
|
|
#return "to ignore $err"; |
|
3659
|
|
|
|
|
|
|
} elsif ( $coll->last_errorcode == $E_REDIS ) { |
|
3660
|
|
|
|
|
|
|
# Independently analyze the $err |
|
3661
|
|
|
|
|
|
|
} elsif ( $coll->last_errorcode == $E_DATA_ID_EXISTS ) { |
|
3662
|
|
|
|
|
|
|
# For example, return code to reinsert the data |
|
3663
|
|
|
|
|
|
|
#return "to reinsert with new data ID"; |
|
3664
|
|
|
|
|
|
|
} elsif ( $coll->last_errorcode == $E_OLDER_THAN_ALLOWED ) { |
|
3665
|
|
|
|
|
|
|
# Independently analyze the situation |
|
3666
|
|
|
|
|
|
|
} else { |
|
3667
|
|
|
|
|
|
|
# Unknown error code |
|
3668
|
|
|
|
|
|
|
} |
|
3669
|
|
|
|
|
|
|
die $err if $err; |
|
3670
|
|
|
|
|
|
|
} |
|
3671
|
|
|
|
|
|
|
|
|
3672
|
|
|
|
|
|
|
my ( $list_id, $coll, @data ); |
|
3673
|
|
|
|
|
|
|
|
|
3674
|
|
|
|
|
|
|
eval { |
|
3675
|
|
|
|
|
|
|
$coll = Redis::CappedCollection->create( |
|
3676
|
|
|
|
|
|
|
redis => $DEFAULT_SERVER.':'.$DEFAULT_PORT, |
|
3677
|
|
|
|
|
|
|
name => 'Some name', |
|
3678
|
|
|
|
|
|
|
); |
|
3679
|
|
|
|
|
|
|
}; |
|
3680
|
|
|
|
|
|
|
exception( $coll, $@ ) if $@; |
|
3681
|
|
|
|
|
|
|
say "'", $coll->name, "' collection created."; |
|
3682
|
|
|
|
|
|
|
|
|
3683
|
|
|
|
|
|
|
#-- Producer ------------------------------------------------------- |
|
3684
|
|
|
|
|
|
|
#-- New data |
|
3685
|
|
|
|
|
|
|
|
|
3686
|
|
|
|
|
|
|
eval { |
|
3687
|
|
|
|
|
|
|
$list_id = $coll->insert( |
|
3688
|
|
|
|
|
|
|
'Some List_id', # list id |
|
3689
|
|
|
|
|
|
|
123, # data id |
|
3690
|
|
|
|
|
|
|
'Some data', |
|
3691
|
|
|
|
|
|
|
); |
|
3692
|
|
|
|
|
|
|
say "Added data in a list with '", $list_id, "' id" ); |
|
3693
|
|
|
|
|
|
|
|
|
3694
|
|
|
|
|
|
|
# Change the "zero" element of the list with the ID $list_id |
|
3695
|
|
|
|
|
|
|
if ( $coll->update( $list_id, 0, 'New data' ) ) { |
|
3696
|
|
|
|
|
|
|
say 'Data updated successfully'; |
|
3697
|
|
|
|
|
|
|
} else { |
|
3698
|
|
|
|
|
|
|
say 'Failed to update element'; |
|
3699
|
|
|
|
|
|
|
} |
|
3700
|
|
|
|
|
|
|
}; |
|
3701
|
|
|
|
|
|
|
exception( $coll, $@ ) if $@; |
|
3702
|
|
|
|
|
|
|
|
|
3703
|
|
|
|
|
|
|
#-- Consumer ------------------------------------------------------- |
|
3704
|
|
|
|
|
|
|
#-- Fetching the data |
|
3705
|
|
|
|
|
|
|
|
|
3706
|
|
|
|
|
|
|
eval { |
|
3707
|
|
|
|
|
|
|
@data = $coll->receive( $list_id ); |
|
3708
|
|
|
|
|
|
|
say "List '$list_id' has '$_'" foreach @data; |
|
3709
|
|
|
|
|
|
|
# or to obtain records in the order they were placed |
|
3710
|
|
|
|
|
|
|
while ( my ( $list_id, $data ) = $coll->pop_oldest ) { |
|
3711
|
|
|
|
|
|
|
say "List '$list_id' had '$data'"; |
|
3712
|
|
|
|
|
|
|
} |
|
3713
|
|
|
|
|
|
|
}; |
|
3714
|
|
|
|
|
|
|
exception( $coll, $@ ) if $@; |
|
3715
|
|
|
|
|
|
|
|
|
3716
|
|
|
|
|
|
|
#-- Utility -------------------------------------------------------- |
|
3717
|
|
|
|
|
|
|
#-- Getting statistics |
|
3718
|
|
|
|
|
|
|
|
|
3719
|
|
|
|
|
|
|
my ( $lists, $items ); |
|
3720
|
|
|
|
|
|
|
eval { |
|
3721
|
|
|
|
|
|
|
my $info = $coll->collection_info; |
|
3722
|
|
|
|
|
|
|
say 'An existing collection uses ', $info->{min_cleanup_bytes}, " byte of 'min_cleanup_bytes', ", |
|
3723
|
|
|
|
|
|
|
'in ', $info->{items}, ' items are placed in ', |
|
3724
|
|
|
|
|
|
|
$info->{lists}, ' lists'; |
|
3725
|
|
|
|
|
|
|
|
|
3726
|
|
|
|
|
|
|
say "The collection has '$list_id' list" |
|
3727
|
|
|
|
|
|
|
if $coll->list_exists( 'Some_id' ); |
|
3728
|
|
|
|
|
|
|
}; |
|
3729
|
|
|
|
|
|
|
exception( $coll, $@ ) if $@; |
|
3730
|
|
|
|
|
|
|
|
|
3731
|
|
|
|
|
|
|
#-- Closes and cleans up ------------------------------------------- |
|
3732
|
|
|
|
|
|
|
|
|
3733
|
|
|
|
|
|
|
eval { |
|
3734
|
|
|
|
|
|
|
$coll->quit; |
|
3735
|
|
|
|
|
|
|
|
|
3736
|
|
|
|
|
|
|
# Before use, make sure that the collection |
|
3737
|
|
|
|
|
|
|
# is not being used by other clients |
|
3738
|
|
|
|
|
|
|
#$coll->drop_collection; |
|
3739
|
|
|
|
|
|
|
}; |
|
3740
|
|
|
|
|
|
|
exception( $coll, $@ ) if $@; |
|
3741
|
|
|
|
|
|
|
|
|
3742
|
|
|
|
|
|
|
=head2 CappedCollection data structure |
|
3743
|
|
|
|
|
|
|
|
|
3744
|
|
|
|
|
|
|
Using currently selected database (default = 0). |
|
3745
|
|
|
|
|
|
|
|
|
3746
|
|
|
|
|
|
|
CappedCollection package creates the following data structures on Redis: |
|
3747
|
|
|
|
|
|
|
|
|
3748
|
|
|
|
|
|
|
#-- To store collection status: |
|
3749
|
|
|
|
|
|
|
# HASH Namespace:S:Collection_id |
|
3750
|
|
|
|
|
|
|
# For example: |
|
3751
|
|
|
|
|
|
|
$ redis-cli |
|
3752
|
|
|
|
|
|
|
redis 127.0.0.1:6379> KEYS C:S:* |
|
3753
|
|
|
|
|
|
|
1) "C:S:Some collection name" |
|
3754
|
|
|
|
|
|
|
# | | | |
|
3755
|
|
|
|
|
|
|
# | +-------+ +------------+ |
|
3756
|
|
|
|
|
|
|
# | | | |
|
3757
|
|
|
|
|
|
|
# Namespace | | |
|
3758
|
|
|
|
|
|
|
# Fixed symbol of a properties hash | |
|
3759
|
|
|
|
|
|
|
# Capped Collection id |
|
3760
|
|
|
|
|
|
|
... |
|
3761
|
|
|
|
|
|
|
redis 127.0.0.1:6379> HGETALL "C:S:Some collection name" |
|
3762
|
|
|
|
|
|
|
1) "lists" # hash key |
|
3763
|
|
|
|
|
|
|
2) "1" # the key value |
|
3764
|
|
|
|
|
|
|
3) "items" # hash key |
|
3765
|
|
|
|
|
|
|
4) "1" # the key value |
|
3766
|
|
|
|
|
|
|
5) "older_allowed" # hash key |
|
3767
|
|
|
|
|
|
|
6) "0" # the key value |
|
3768
|
|
|
|
|
|
|
7) "min_cleanup_bytes" # hash key |
|
3769
|
|
|
|
|
|
|
8) "0" # the key value |
|
3770
|
|
|
|
|
|
|
9) "min_cleanup_items" # hash key |
|
3771
|
|
|
|
|
|
|
10) "100" # the key value |
|
3772
|
|
|
|
|
|
|
11) "memory_reserve" # hash key |
|
3773
|
|
|
|
|
|
|
12) "0.05" # the key value |
|
3774
|
|
|
|
|
|
|
13) "data_version" # hash key |
|
3775
|
|
|
|
|
|
|
14) "3" # the key value |
|
3776
|
|
|
|
|
|
|
15) "last_removed_time" # hash key |
|
3777
|
|
|
|
|
|
|
16) "0" # the key value |
|
3778
|
|
|
|
|
|
|
... |
|
3779
|
|
|
|
|
|
|
|
|
3780
|
|
|
|
|
|
|
#-- To store collection queue: |
|
3781
|
|
|
|
|
|
|
# ZSET Namespace:Q:Collection_id |
|
3782
|
|
|
|
|
|
|
# For example: |
|
3783
|
|
|
|
|
|
|
redis 127.0.0.1:6379> KEYS C:Q:* |
|
3784
|
|
|
|
|
|
|
1) "C:Q:Some collection name" |
|
3785
|
|
|
|
|
|
|
# | | | |
|
3786
|
|
|
|
|
|
|
# | +------+ +-----------+ |
|
3787
|
|
|
|
|
|
|
# | | | |
|
3788
|
|
|
|
|
|
|
# Namespace | | |
|
3789
|
|
|
|
|
|
|
# Fixed symbol of a queue | |
|
3790
|
|
|
|
|
|
|
# Capped Collection id |
|
3791
|
|
|
|
|
|
|
... |
|
3792
|
|
|
|
|
|
|
redis 127.0.0.1:6379> ZRANGE "C:Q:Some collection name" 0 -1 WITHSCORES |
|
3793
|
|
|
|
|
|
|
1) "Some list id" ----------+ |
|
3794
|
|
|
|
|
|
|
2) "1348252575.6651001" | |
|
3795
|
|
|
|
|
|
|
# | | |
|
3796
|
|
|
|
|
|
|
# Score: oldest data_time | |
|
3797
|
|
|
|
|
|
|
# Member: Data List id |
|
3798
|
|
|
|
|
|
|
... |
|
3799
|
|
|
|
|
|
|
|
|
3800
|
|
|
|
|
|
|
#-- To store CappedCollection data: |
|
3801
|
|
|
|
|
|
|
# HASH Namespace:I:Collection_id:DataList_id |
|
3802
|
|
|
|
|
|
|
# HASH Namespace:D:Collection_id:DataList_id |
|
3803
|
|
|
|
|
|
|
# If the amount of data in the list is greater than 1 |
|
3804
|
|
|
|
|
|
|
# ZSET Namespace:T:Collection_id:DataList_id |
|
3805
|
|
|
|
|
|
|
# For example: |
|
3806
|
|
|
|
|
|
|
redis 127.0.0.1:6379> KEYS C:[DT]:* |
|
3807
|
|
|
|
|
|
|
1) "C:D:Some collection name:Some list id" |
|
3808
|
|
|
|
|
|
|
# If the amount of data in the list is greater than 1 |
|
3809
|
|
|
|
|
|
|
2) "C:T:Some collection name:Some list id" |
|
3810
|
|
|
|
|
|
|
# | | | | |
|
3811
|
|
|
|
|
|
|
# | +-----+ +-------+ + ---------+ |
|
3812
|
|
|
|
|
|
|
# | | | | |
|
3813
|
|
|
|
|
|
|
# Namespace | | | |
|
3814
|
|
|
|
|
|
|
# Fixed symbol of a list of data | | |
|
3815
|
|
|
|
|
|
|
# Capped Collection id | |
|
3816
|
|
|
|
|
|
|
# Data list id |
|
3817
|
|
|
|
|
|
|
... |
|
3818
|
|
|
|
|
|
|
redis 127.0.0.1:6379> HGETALL "C:D:Some collection name:Some list id" |
|
3819
|
|
|
|
|
|
|
1) "0" # hash key: Data id |
|
3820
|
|
|
|
|
|
|
2) "Some stuff" # the key value: Data |
|
3821
|
|
|
|
|
|
|
... |
|
3822
|
|
|
|
|
|
|
# If the amount of data in the list is greater than 1 |
|
3823
|
|
|
|
|
|
|
redis 127.0.0.1:6379> ZRANGE "C:T:Some collection name:Some list id" 0 -1 WITHSCORES |
|
3824
|
|
|
|
|
|
|
1) "0" ---------------+ |
|
3825
|
|
|
|
|
|
|
2) "1348252575.5906" | |
|
3826
|
|
|
|
|
|
|
# | | |
|
3827
|
|
|
|
|
|
|
# Score: data_time | |
|
3828
|
|
|
|
|
|
|
# Member: Data id |
|
3829
|
|
|
|
|
|
|
... |
|
3830
|
|
|
|
|
|
|
|
|
3831
|
|
|
|
|
|
|
=head1 DEPENDENCIES |
|
3832
|
|
|
|
|
|
|
|
|
3833
|
|
|
|
|
|
|
In order to install and use this package Perl version 5.010 or better is |
|
3834
|
|
|
|
|
|
|
required. Redis::CappedCollection module depends on other packages |
|
3835
|
|
|
|
|
|
|
that are distributed separately from Perl. We recommend the following packages |
|
3836
|
|
|
|
|
|
|
to be installed before installing Redis::CappedCollection : |
|
3837
|
|
|
|
|
|
|
|
|
3838
|
|
|
|
|
|
|
Const::Fast |
|
3839
|
|
|
|
|
|
|
Digest::SHA1 |
|
3840
|
|
|
|
|
|
|
Mouse |
|
3841
|
|
|
|
|
|
|
Params::Util |
|
3842
|
|
|
|
|
|
|
Redis |
|
3843
|
|
|
|
|
|
|
Try::Tiny |
|
3844
|
|
|
|
|
|
|
|
|
3845
|
|
|
|
|
|
|
The Redis::CappedCollection module has the following optional dependencies: |
|
3846
|
|
|
|
|
|
|
|
|
3847
|
|
|
|
|
|
|
Data::UUID |
|
3848
|
|
|
|
|
|
|
JSON::XS |
|
3849
|
|
|
|
|
|
|
Net::EmptyPort |
|
3850
|
|
|
|
|
|
|
Test::Exception |
|
3851
|
|
|
|
|
|
|
Test::NoWarnings |
|
3852
|
|
|
|
|
|
|
Test::RedisServer |
|
3853
|
|
|
|
|
|
|
|
|
3854
|
|
|
|
|
|
|
If the optional modules are missing, some "prereq" tests are skipped. |
|
3855
|
|
|
|
|
|
|
|
|
3856
|
|
|
|
|
|
|
The installation of the missing dependencies can either be accomplished |
|
3857
|
|
|
|
|
|
|
through your OS package manager or through CPAN (or downloading the source |
|
3858
|
|
|
|
|
|
|
for all dependencies and compiling them manually). |
|
3859
|
|
|
|
|
|
|
|
|
3860
|
|
|
|
|
|
|
=head1 BUGS AND LIMITATIONS |
|
3861
|
|
|
|
|
|
|
|
|
3862
|
|
|
|
|
|
|
Redis server version 2.8 or higher is required. |
|
3863
|
|
|
|
|
|
|
|
|
3864
|
|
|
|
|
|
|
The use of C in the F file could lead to |
|
3865
|
|
|
|
|
|
|
a serious (and hard to detect) problem as Redis server may delete |
|
3866
|
|
|
|
|
|
|
the collection element. Therefore the C does not work with |
|
3867
|
|
|
|
|
|
|
mode C in the F. |
|
3868
|
|
|
|
|
|
|
|
|
3869
|
|
|
|
|
|
|
It may not be possible to use this module with the cluster of Redis servers |
|
3870
|
|
|
|
|
|
|
because full name of some Redis keys may not be known at the time of the call |
|
3871
|
|
|
|
|
|
|
the Redis Lua script (C<'EVAL'> or C<'EVALSHA'> command). |
|
3872
|
|
|
|
|
|
|
So the Redis server may not be able to correctly forward the request |
|
3873
|
|
|
|
|
|
|
to the appropriate node in the cluster. |
|
3874
|
|
|
|
|
|
|
|
|
3875
|
|
|
|
|
|
|
We strongly recommend setting C option in the F file. |
|
3876
|
|
|
|
|
|
|
|
|
3877
|
|
|
|
|
|
|
Old data with the same time will be forced out in no specific order. |
|
3878
|
|
|
|
|
|
|
|
|
3879
|
|
|
|
|
|
|
The collection API does not support deleting a single data item. |
|
3880
|
|
|
|
|
|
|
|
|
3881
|
|
|
|
|
|
|
UTF-8 data should be serialized before passing to C for storing in Redis. |
|
3882
|
|
|
|
|
|
|
|
|
3883
|
|
|
|
|
|
|
According to L documentation: |
|
3884
|
|
|
|
|
|
|
|
|
3885
|
|
|
|
|
|
|
=over 3 |
|
3886
|
|
|
|
|
|
|
|
|
3887
|
|
|
|
|
|
|
=item * |
|
3888
|
|
|
|
|
|
|
|
|
3889
|
|
|
|
|
|
|
This module consider that any data sent to the Redis server is a raw octets string, |
|
3890
|
|
|
|
|
|
|
even if it has utf8 flag set. |
|
3891
|
|
|
|
|
|
|
And it doesn't do anything when getting data from the Redis server. |
|
3892
|
|
|
|
|
|
|
|
|
3893
|
|
|
|
|
|
|
TODO: implement tests for |
|
3894
|
|
|
|
|
|
|
|
|
3895
|
|
|
|
|
|
|
=over 3 |
|
3896
|
|
|
|
|
|
|
|
|
3897
|
|
|
|
|
|
|
=item * |
|
3898
|
|
|
|
|
|
|
|
|
3899
|
|
|
|
|
|
|
memory errors (working with internal ROLLBACK commands) |
|
3900
|
|
|
|
|
|
|
|
|
3901
|
|
|
|
|
|
|
=item * |
|
3902
|
|
|
|
|
|
|
|
|
3903
|
|
|
|
|
|
|
working when maxmemory = 0 (in the F file) |
|
3904
|
|
|
|
|
|
|
|
|
3905
|
|
|
|
|
|
|
=back |
|
3906
|
|
|
|
|
|
|
|
|
3907
|
|
|
|
|
|
|
WARNING: According to C function in F : |
|
3908
|
|
|
|
|
|
|
|
|
3909
|
|
|
|
|
|
|
/* 32 bit instances are limited to 4GB of address space, so if there is |
|
3910
|
|
|
|
|
|
|
* no explicit limit in the user provided configuration we set a limit |
|
3911
|
|
|
|
|
|
|
* at 3 GB using maxmemory with 'noeviction' policy'. This avoids |
|
3912
|
|
|
|
|
|
|
* useless crashes of the Redis instance for out of memory. */ |
|
3913
|
|
|
|
|
|
|
|
|
3914
|
|
|
|
|
|
|
The C module was written, tested, and found working |
|
3915
|
|
|
|
|
|
|
on recent Linux distributions. |
|
3916
|
|
|
|
|
|
|
|
|
3917
|
|
|
|
|
|
|
There are no known bugs in this package. |
|
3918
|
|
|
|
|
|
|
|
|
3919
|
|
|
|
|
|
|
Please report problems to the L"AUTHOR">. |
|
3920
|
|
|
|
|
|
|
|
|
3921
|
|
|
|
|
|
|
Patches are welcome. |
|
3922
|
|
|
|
|
|
|
|
|
3923
|
|
|
|
|
|
|
=back |
|
3924
|
|
|
|
|
|
|
|
|
3925
|
|
|
|
|
|
|
=head1 MORE DOCUMENTATION |
|
3926
|
|
|
|
|
|
|
|
|
3927
|
|
|
|
|
|
|
All modules contain detailed information on the interfaces they provide. |
|
3928
|
|
|
|
|
|
|
|
|
3929
|
|
|
|
|
|
|
=head1 SEE ALSO |
|
3930
|
|
|
|
|
|
|
|
|
3931
|
|
|
|
|
|
|
The basic operation of the Redis::CappedCollection package module: |
|
3932
|
|
|
|
|
|
|
|
|
3933
|
|
|
|
|
|
|
L - Object interface to create |
|
3934
|
|
|
|
|
|
|
a collection, addition of data and data manipulation. |
|
3935
|
|
|
|
|
|
|
|
|
3936
|
|
|
|
|
|
|
L - String manipulation utilities. |
|
3937
|
|
|
|
|
|
|
|
|
3938
|
|
|
|
|
|
|
L - Perl binding for Redis database. |
|
3939
|
|
|
|
|
|
|
|
|
3940
|
|
|
|
|
|
|
=head1 SOURCE CODE |
|
3941
|
|
|
|
|
|
|
|
|
3942
|
|
|
|
|
|
|
Redis::CappedCollection is hosted on GitHub: |
|
3943
|
|
|
|
|
|
|
L |
|
3944
|
|
|
|
|
|
|
|
|
3945
|
|
|
|
|
|
|
=head1 AUTHOR |
|
3946
|
|
|
|
|
|
|
|
|
3947
|
|
|
|
|
|
|
Sergey Gladkov, Esgladkov@trackingsoft.comE |
|
3948
|
|
|
|
|
|
|
|
|
3949
|
|
|
|
|
|
|
=head1 CONTRIBUTORS |
|
3950
|
|
|
|
|
|
|
|
|
3951
|
|
|
|
|
|
|
Alexander Solovey |
|
3952
|
|
|
|
|
|
|
|
|
3953
|
|
|
|
|
|
|
Jeremy Jordan |
|
3954
|
|
|
|
|
|
|
|
|
3955
|
|
|
|
|
|
|
Vlad Marchenko |
|
3956
|
|
|
|
|
|
|
|
|
3957
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
|
3958
|
|
|
|
|
|
|
|
|
3959
|
|
|
|
|
|
|
Copyright (C) 2012-2016 by TrackingSoft LLC. |
|
3960
|
|
|
|
|
|
|
|
|
3961
|
|
|
|
|
|
|
This package is free software; you can redistribute it and/or modify it under |
|
3962
|
|
|
|
|
|
|
the same terms as Perl itself. See I at |
|
3963
|
|
|
|
|
|
|
L. |
|
3964
|
|
|
|
|
|
|
|
|
3965
|
|
|
|
|
|
|
This program is |
|
3966
|
|
|
|
|
|
|
distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; |
|
3967
|
|
|
|
|
|
|
without even the implied warranty of MERCHANTABILITY or FITNESS FOR A |
|
3968
|
|
|
|
|
|
|
PARTICULAR PURPOSE. |
|
3969
|
|
|
|
|
|
|
|
|
3970
|
|
|
|
|
|
|
=cut |
|
3971
|
|
|
|
|
|
|
|
|
3972
|
|
|
|
|
|
|
__DATA__ |