-
Notifications
You must be signed in to change notification settings - Fork 33
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Caching: add global cache based on Redis (experimental) #1201
Conversation
my $redis; | ||
our $object_cache = \%Zonemaster::Engine::Nameserver::Cache::object_cache; | ||
|
||
my $REDIS_EXPIRE = 3600; # seconds |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does that mean that TTL in the DNS record is ignored?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With this implementation yes. Rethinking about it, it might indeed be a good idea to set an expiry time to min(TTL, REDIS_EXPIRE)
. I've improved the logic to use the resource record TTL if available.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do not understand with "if available". TTL is always set in a DNS record.
It is legal to use a shorter TLL than set in DNS, but what is the reason for for having such a limit?
If there is such a limit it should be configurable (but such configuration could be added later).
@pnax, what are your plans for this? |
Don't you have to start the redis service too? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Documentation of how to run Redis is needed too.
sub set_key { | ||
my ( $self, $hash, $packet ) = @_; | ||
my $key = "ns:" . $self->address . ":" . $hash; | ||
|
||
my $ttl = $REDIS_EXPIRE; | ||
|
||
$self->data->{$hash} = $packet; | ||
if ( defined $packet ) { | ||
my $msg = $mp->pack({ | ||
data => $packet->data, | ||
answerfrom => $packet->answerfrom, | ||
timestamp => $packet->timestamp, | ||
querytime => $packet->querytime, | ||
}); | ||
if ( $packet->answer ) { | ||
my @rr = $packet->answer; | ||
$ttl = min( map { $_->ttl } @rr ); | ||
$ttl = $ttl < $REDIS_EXPIRE ? $ttl : $REDIS_EXPIRE; | ||
} | ||
$self->redis->set( $key, $msg, 'EX', $ttl ); | ||
} else { | ||
$self->redis->set( $key, '', 'EX', $ttl ); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I understand the code correctly, the TTL will always be REDIS_EXPIRE
if there is no response. If REDIS_EXPIRE
is configuratble that seems reasonable.
If there is a response then the lowest TTL of the DNS records in the answer section, if any, will be used for TTL of that response. That seems reasonable.
If the answer section is empty it could be an NXDOMAIN or NODATA response, and then it would be reasonable to respect the TTL of the response by using the TTL of the SOA record in the authority section, which is the way of signaling the TTL for such responses.
If the answer section is empty and the response is a referral then the TTL of the NS RRset in the authority section would be reasonable to use.
If the RCODE is neither NOERROR nor NXDOMAIN then using REDIS_EXPIRE
could be reasonable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've updated the logic to take your suggestion in consideration.
"redis": { | ||
"server": "127.0.0.1:6379" | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems like such configuration should rather be in the application (Backend, CLI) rather than in the library (Engine). I guess the model requires this to be in Engine. A positive side is that the global cache can be used for multiple executions of CLI.
Todo:
|
if ( $rcode eq 'NXDOMAIN' ) { | ||
$ttl = min( map { $_->minimum } @rr ); | ||
} | ||
elsif ( $rcode eq 'NOERROR' ) { | ||
$ttl = min( map { $_->ttl } @rr ); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Negative caching is set to be the TTL value of SOA record (not the minimum field in the SOA RDATA) in the authority section.
If there are any NSEC or RRSIG records in the authority section, strictly they should be disregarded. Only the SOA record should be considered.
Please re-review. |
eval( <<EOS | ||
use Data::MessagePack; | ||
use Redis; | ||
EOS | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have you tried eval
with a block instead of a string?
eval( <<EOS | |
use Data::MessagePack; | |
use Redis; | |
EOS | |
); | |
eval { | |
use Data::MessagePack; | |
use Redis; | |
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion applied.
|
||
|
||
if ( $@ ) { | ||
die "Cant' use the Redis cache. Make sure the Data::MessagePack and Redis module are installed.\n"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
die "Cant' use the Redis cache. Make sure the Data::MessagePack and Redis module are installed.\n"; | |
die "Can't use the Redis cache. Make sure the Data::MessagePack and Redis modules are installed.\n"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
our $object_cache = \%Zonemaster::Engine::Nameserver::Cache::object_cache; | ||
|
||
my $REDIS_EXPIRE = 3600; # seconds | ||
my $REDIS_EXPIRE = 5; # seconds |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why no longer 3600 seconds? Besides, it isn’t sufficiently clear from the variable’s name that it’s merely a default value.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why no longer 3600 seconds?
I recall it being discussed during the F2F. @matsduf wanted a lower value. We agreed on 5 seconds. This is also the value suggested in the additional profile.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice work.
I think it would be good to have documentation of the available cache engines, e.g. in lib/Zonemaster/Engine/Profile.pm
, even if its in experimental state.
The unitary tests of t/profiles.t
should be updated as well for the new profile key. By the way, for future-proof reasons, what do you think of making that part generic? E.g:
"cache" : {
"name" : "redis",
"server": "127.0.0.1:6379",
"expire": 5
}
@tgreenx I should have addressed all your comments/suggestions. Good idea to have a generic entry in the profile for the cache layer. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make room for other caching solution. Move default cache into new LocalCache module.
Require Perl modules: Redis and Data::MessagePack
* use an eval block * fix a typo * make it clear variable is a default value
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have tested it and have no comment, it seems to be working correctly.
Can you edit the description so that the profile section uses the new |
Done. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@pnax @blacksponge please re-review. I just added a commit (08f88cd) which adds testing for the provided subkeys of profile entry cache.redis
.
We need the documentation on how to use this new feature to be available. We also want the users to see that it exists. I suggest that a short section is included in the installation instructions for Zonemaster-Engine, at the end of that document as an experimental feature. As in "How to test this PR" in this PR it can be limited to one OS and very brief. The same text can probably be used. |
I installed redis and the other packages on FreeBSD. Created an updated profile. Ran zonemaster-cli with the updated profile. But the redis cache is empty and rerunning the test does not speed it up. How could I check?
|
After discussion it appears 5 seconds is to low when testing this feature.
With the new default, 300 instead of 5 seconds, I get the expected result. Checking "ripe.net" took 50 seconds first time and less than 10 seconds the second time. The |
Purpose
Improve our cache implementation to make room to integrate other caching solutions. Provide an optional and experimental global cache layer based on Redis. This can be handy when multiple Zonemaster instances run concurrently (when dealing with batches for instance).
Context
https://gitlab.rd.nic.fr/zonemaster/zonemaster-engine/-/commit/b96181e460b5824dcd4ac913a3210c458970126a
and
https://gitlab.rd.nic.fr/zonemaster/zonemaster-engine/-/commit/0073398f8a4c47bb6f7f7d8dae8f8a2ddd2694a6
Credits to @blacksponge for the initial proposal
Changes
How to test this PR
share/profile_additional_properties.json
) :