-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathREADME
477 lines (385 loc) · 21.1 KB
/
README
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
NAME
CHI::Cascade - a cache dependencies (cache and like 'make' utility
concept)
SYNOPSIS
use CHI;
use CHI::Cascade;
$cascade = CHI::Cascade->new(chi => CHI->new(...));
$cascade->rule(
target => 'unique_name',
depends => ['unique_name_other1', 'unique_name_other2'],
code => sub {
my ($rule, $target_name, $values_of_depends) = @_;
# $values_of_depends == {
# unique_name_other1 => $value_1,
# unique_name_other2 => $value_2
# }
# $rule->target eq $target_name
# $rule->depends === ['unique_name_other1', 'unique_name_other2']
# $rule->dep_values == $values_of_depends
# $rule->params == { a => 1, b => 2 }
# Now we can calcualte $value
return $value;
},
params => { a => 1, b => 2 }
);
$cascade->rule(
target => 'unique_name_other1',
depends => 'unique_name_other3',
code => sub {
my ($rule, $target_name, $values_of_depends) = @_;
# $values_of_depends == {
# unique_name_other3 => $value_3
# }
# computing here
return $value;
}
);
$value_of_this_target = $cascade->run('unique_name');
DESCRIPTION
This module is the attempt to use a benefits of caching and 'make'
concept. If we have many an expensive tasks (a *computations* or
sometimes here used term as a *recomputing*) and want to cache it we can
split its to small expsnsive tasks and to describe dependencies for
cache items.
This module is experimental yet. I plan to improve it near time but some
things already work. You can take a look for t/* tests as examples.
CONSTRUCTOR
$cascade = CHI::Cascade->new( %options )
This method constructs a new "CHI::Cascade" object and returns it.
Key/value pair arguments may be provided to set up the initial state.
Options are:
chi Required. Instance of CHI object. The CHI::Cascade doesn't construct
this object for you. Please create instance of "CHI" yourself.
busy_lock
Optional. Default is *never*. *This is not "busy_lock" option of
CHI!* This is amount of time (to see "DURATION EXPRESSIONS" in CHI)
until all target locks expire. When a target is to being computing
it is locked. If process which is to be computing target and it will
die or OS will be hangs up we can dead locks and locked target will
never recomputed again. This option helps to avoid it. You can set
up a special busy_lock for rules too.
target_chi
Optional. This is CHI cache for target markers. Default value is
value of "chi" option. It can be useful if you use a "l1_cache" in
CHI option. So you can separate data of targets from target markers
- data will be kept in a file cache and a marker in memory cache for
example.
METHODS
rule( %options )
To add new rule to "CHI::Cascade" object. All rules should be added
before first "run" method
The keys of %options are (options are passed directly in
CHI::Cascade::Rule constructor):
target
Required. A target for "run" and for searching of "depends". It
can be as scalar text or "Regexp" object created through "qr//"
depends
Optional. The scalar, arrayref or coderef values of
dependencies. This is the definition of target(s) from which
this current rule is dependent. If *depends* is:
scalar
It should be plain text of single dependence of this target.
arrayref
An each item of list can be scalar value (exactly matched
target) or code reference. If item is coderef it will be
executed once as $coderef->( $rule, $rule->qr_params ) and
should return a scalar value as current dependence for this
target at runtime (the API for coderef parameters was
changed since v0.16)
coderef
This subroutine will be executed once inside *run* method if
necessary with parameters as: $coderef->( $rule,
<$rule-qr_params|CHI::Cascade::Rule/qr_params >> ) (API was
changed since v0.16). It should return scalar or arrayref.
The returned value is *scalar* it will be considered as
single dependence of this target and the behavior will be
exactly as described for *scalar* in this paragraph. If the
returned value is *arrayref* it will be considered as list
of dependencies for this target and the behavior will be
exactly as described for *arrayref* in this paragraph.
depends_catch
Optional. This is coderef for dependence exceptions. If any
dependence from list of "depends"'s option throws an exception
of type CHI::Cascade::Value by "die" (for example like this
code: "die CHI::Cascade::Value->new->value( { i_have_problem =>
1 } )" ) then the $cascade will execute this code as
"$rule->{depends_catch}->( $this_rule_obj,
$exception_of_dependence, $rule_obj_of_dependence,
$plain_text_target_of_dependence )" and you can do into inside a
following:
re-"die" new exception of any type
If your new exception will be type of CHI::Cascade::Value
you will get the value of this object from "run" method
immediately (please to see "code" below) without saving in
cache.
If exception will be other type this will be propogated
onward beyond the "run" method
to do something
You can make something in this code. After execution of your
code the cascade re-throws original exception of dependence
like described above in "re-"die"" section.
But please notice that original exception has a status of
"thrown from code" so it can be catched later by other
"depends_catch" callback from other rule located closer to
the call hierarchy of "run".
Please notice that there no way to continue a "code" of current
rule if any dependence throws an exception!. It because that the
main concept of execution code of rules is to have all valid
values (cached or recomputed) of all dependencies before
execution of dependent code.
code
Required. The code reference for computing a value of this
target (a *computational code*). Will be executed if no value in
cache for this target or any dependence or dependences of
dependences and so on will be recomputed. Will be executed as
"$code->( $rule, $target, $hashref_to_value_of_dependencies )"
*(The API of running this code was changed since v0.10)*
If you want to terminate a code and to return immediately from
"run" method and don't want to save a value in cache you can
throw an exception from "code" of type CHI::Cascade::Value. Your
instance of CHI::Cascade::Value can have a value or cannot (a
valid value can be even "undef"!). A "run" method returns either
a value is set by you (through "value" in CHI::Cascade::Value
method) or value from cache or "undef" in other cases. Please to
see CHI::Cascade::Value
If "run" method will have a "defer" option as true this code
will not be executed and you will get a set bit CASCADE_DEFERRED
in "state" bit mask variable. This may useful when you want to
control a target execution.
$rule
An instance of CHI::Cascade::Rule object. You can use it
object as accessor for some current executed target data
(plain text of target, for getting of parameters and so on).
Please to see CHI::Cascade::Rule
$target
The current executed target as plain text for this "code"
$hashref_to_value_of_dependencies
A hash reference of values (values are cleaned values not
CHI::Cascade::Value objects!) of all dependencies for
current target. Keys in this hash are flat strings of
dependecies and values are computed or cached ones.
This module should guarantee that values of dependencies
will be valid values even if value is "undef". This code can
return "undef" value as a valid code return but author
doesn't recommend it. If "CHI::Cascade" could not get a
valid values of all dependencies of current target before
execution of this code the last will not be executed (The
"run" will return "undef").
params
Optional. You can pass in your code any additional parameters by
this option. These parameters are accessed in your rule's code
through "params" in CHI::Cascade::Rule method of
CHI::Cascade::Rule instance object.
busy_lock
Optional. Default is "busy_lock" of constructor or *never* if
first is not defined. *This is not "busy_lock" option of CHI!*
This is amount of time (to see "DURATION EXPRESSIONS" in CHI)
until target lock expires. When a target is to being computed it
is locked. If process which to be recomputing a target and it
will die or OS will be hangs up we can dead locks and locked
target will never recomputed again. This option helps to avoid
it.
recomputed
Optional. This is a computational callback (coderef). If target
of this rule was recomputed this callback will be executed right
away after a recomputed value has been saved in cache. The
callback will be executed as $coderef->( $rule, $target, $value
) where passed parameters are:
$rule
An instance of CHI::Cascade::Rule class. This instance is
recreated for every target searching and recomputing if
need.
$target
A current target as string
$value
The instance of CHI::Cascade::Value class. You can use a
computed value as $value->value
For example you can use this callback for notifying of other
sites that your target's value has been changed and is already
in cache.
value_expires
Optional. Sets an CHI's cache expire value for all future target
markers are created by this rule in notation described in
"DURATION EXPRESSIONS" in CHI. The default is 'never'. It can be
coderef or string scalar format as "DURATION EXPRESSIONS" in
CHI. A coderef should return value in same format.
ttl Optional. An arrayref for min & max intervals of TTL. Example:
"[ 60, 3600 ]" - where the minimum ttl is seconds and the
maximum is 3600 seconds. Targets of this rule will be recomputed
during from 60 up to 3600 seconds from touched time of any
dependence this rule. Please read "CASCADE_TTL_INVOLVED" in
CHI::Cascade::Value too.
run( $target, %options )
This method makes a cascade computation if need and returns value
(value is cleaned value not CHI::Cascade::Value object!) for this
target If any dependence of this target of any dependencies of
dependencies were (re)computed this target will be (re)computed too.
The run method of instance of cascade can be called from other run
method of same instance and from "callref" function inside "depends"
rule's option. This was made possible by creating a separate data
instance for each root call of run method. This can come in handy
when you compute dependencies on the go, which are computed by the
same object (instance) of "cascade".
$target
Required. Plain text string of target.
%options
Optional. And all options are optional too A hash of options.
Valid keys and values are:
state
A scalarref of variable where will be stored a state of
"run". Value will be a bit mask.
defer
If value will be a true then computational code will not be
run if there is a need. After "run" you can test status of
returned value - it should be (re)computed or not by bit
"CASCADE_DEFERRED" in saved "state" variable. If the
CASCADE_DEFERRED bit is set you can recall "run" method
again or re-execute target in other process for a
non-blocking execution of current process.
actual_term
The value in seconds (a floating point value) of actual
term. The actual term is period when dependencies to be
checked for $target in "run" method. If this option is not
defined then the "run" method checks a dependencies of
$target every time in runtime. But sometimes (when a target
has many dependencies) we could want to reduce an amount of
dependencies checks. For example if "actual_term" will be
defined as 2.5 this will mean to check a dependencies only
every 2.5 seconds. So recomputing in this example can be
recomputed only one time in every 2.5 seconds (even if one
from dependencies will be updated). But if value of $target
is missing in cache a recomputing can be run regardless of
this option.
ttl A scalarref for getting current TTL for value of 'run'
target. The TTL is "time to live" as TTL in DNS. If any rule
in a path of following to dependencies has ttl parameter
then the cascade will do there:
1. will look up a time of this retouched dependence;
2. if rule's target marker already has a upper time and
this time in future the target will be recomputed in
this time in future and before this moment you will get
a old data from cache for 'run' target. If this time is
there and has elapsed cascade will use a standard
algorithm.
3. will look up the rule's ttl parameter (min & max ttl
values) and will generate upper time of computation of
this rule's target and will return from "run" method old
data of 'run' target. Next "run"s executions will return
old values of any targets where this TTL-marked target
is as dependence.
4. In any case if old value misses in cache the cascade
will recompute codes.
This feature was made for *reset* situation. For example if
we have 'reset' rule and all rules depend from this one rule
the better way will be to have 'ttl' parameter in every rule
except 'reset' rule. So if rule 'reset' will be retouched
(or deleted) other targets will be recomputed during time
from 'min' and 'max' intervals from 'reset' touched time. It
reduce a server's load. Later i will add examples for this
and will document this feature more details. Please read
"CASCADE_TTL_INVOLVED" in CHI::Cascade::Value too.
stash
A *hashref* to stash - temporary data container between
rule's codes. Please see "stash ()" method for details.
touch( $target )
This method refreshes the time of this target. Here is analogy with
touch utility of Unix and behaviour as make(1) after it. After
"touch" all targets are dependent from this target will be
recomputed at next "run" with an appropriate ones.
target_remove ( $target )
It's like a removing of target file in make. You can force to
recompute target by this method. It will remove target marker if one
exists and once when cascade will need target value it will be
recomputed. In a during recomputing of course cascade will return an
old value if one exists in cache.
stash()
Deprecated! It returns *hashref* to a stash. A stash is hash for
temporary data between rule's codes. It can be used only from inside
"run". Example:
$cascade->run( 'target', stash => { key1 => value1 } )
and into rule's code:
# DEPRECATED - OLD METHOD! It's supported and works but please don't use it
$rule->cascade->stash->{key1}
# NEW METHOD:
$rule->stash->{key1}
If a "run" method didn't get stash hashref the default stash will be
as empty hash. You can pass a data between rule's codes but it's
recommended only in special cases. For example when run's target
cannot get a full data from its target's name.
STATUS
This module is experimental and not finished for new features ;-) Please
send me issues through <https://github.com/Perlover/CHI-Cascade> page
ANALOGIES WITH make
Here simple example how it works. Here is a direct analogy to Unix make
utility:
In CHI::Cascade: In make:
rule rule
depends prerequisites
code commands
run( rule_name ) make target_name
FEATURES
The features of this module are following:
Computing inside process
If module needs to compute item for cache we compute inside process
(no forks) For web applications it means that one process for one
request could take a some time for computing. But other processes
will not wait and will get either old previous computed value or
*undef* value.
Non-blocking computing for concurrent processes
If other process want to get data from cache we should not block it.
So concurrent process can get an old data if new computing is run or
can get *undef* value. A concurrent process should decide itself
what it should do after it - try again after few time or print some
message like 'Please wait and try again' to user.
Each target is splitted is two items in cache
For optimization this module keeps target's info by separately from
value item. A target item has lock & timestamp fields. A value item
has a computed value.
EXAMPLE
For example please to see the SYNOPSIS
When we prepared a rules and a depends we can:
If unique_name_other1 and/or unique_name_other2 are(is) more newer than
unique_name the unique_name will be recomputed. If in this example
unique_name_other1 and unique_name_other2 are older than unique_name but
the unique_name_other3 is newer than unique_name_other1 then
unique_name_other1 will be recomputed and after the unique_name will be
recomputed.
And even we can have a same rule:
$cascade->rule(
target => qr/^unique_name_(.*)$/,
depends => sub { 'unique_name_other_' . $_[1] },
code => sub {
my ($rule, $target_name, $values_of_depends) = @_;
# $rule->qr_params === ( 3 )
# $target_name == 'unique_name_3' if $cascade->run('unique_name_3') was
# $values_of_depends == {
# unique_name_other_3 => $value_ref_3
# }
}
);
$cascade->rule(
target => qr/unique_name_other_(.*)/,
code => sub {
my ($rule, $target_name, $values_of_depends) = @_;
...
}
);
When we will do:
$cascade->run('unique_name_52');
$cascade will find rule with qr/^unique_name_(.*)$/, will make =~ and
will find a depend as unique_name_other_52
AUTHOR
This module has been written by Perlover <[email protected]>
LICENSE
This module is free software and is published under the same terms as
Perl itself.
SEE ALSO
CHI::Cascade::Rule
An instance of this object can be used in your target codes.
CHI This object is used for cache.
CHI::Driver::Memcached::Fast
Recommended if you have the Memcached
CHI::Driver::File
Recommended if you want to use the file caching instead the
Memcached for example