NAME
Geo::Coder::Free::MissCache - process-level memory of lookups that found nothing
VERSION
Version 0.51
SYNOPSIS
use Geo::Coder::Free::MissCache;
my $scope = Geo::Coder::Free::MissCache->scope_for('OpenAddresses', '/data/openaddresses');
my $digest = 'AB12CD34';
# A lookup found nothing: remember that, so the next caller does not
# have to read the same database to learn it again.
Geo::Coder::Free::MissCache->remember($scope, $digest);
# A caller about to do the work asks first.
if(Geo::Coder::Free::MissCache->missed($scope, $digest)) {
print "already looked for $digest\n";
}
# The data behind the cache was rebuilt, so forget everything.
Geo::Coder::Free->clear_miss_cache();
DESCRIPTION
Every backend in Geo::Coder::Free has the same problem: an address that is not in its data is not found on every call, and each call pays for the whole search again. Geo::Coder::Free::MissCache is the one place where the backends record those misses, so the process remembers the absence once and every later call - through any instance of any backend - answers without touching a database.
Entries are held in memory, in the process, and are not shared between processes. They expire after an hour by default. That is long enough to spare a batch, a web request or a test run the repeated work, and short enough that a process which stays up after its data is rebuilt does not keep answering from a stale memory forever; Geo::Coder::Free-clear_miss_cache()> empties the cache at once for that case. Expired entries are discarded when they are next asked about, and a sweep runs at most once a minute as new misses are recorded, so the cache does not grow without bound.
A miss is only ever remembered, never inferred: nothing here decides that an answer will be missing, it only stores that a backend has already found that it is.
METHODS
scope_for
my $scope = Geo::Coder::Free::MissCache->scope_for('OpenAddresses', $dir);
Builds the name a backend stores its misses under. The parts are joined with a zero byte, so the first part names the backend and the rest say which data set is being read - two OpenAddresses directories hold different data, and a miss in one says nothing about the other. clear() uses the same convention: giving it a backend name clears every data set that backend opened.
API SPECIFICATION
input
backend => { type => 'string' } # Local, MaxMind or OpenAddresses
dataset => { type => 'string', optional => 1 } # zero or more, which data set
output
{ type => 'string' }
missed
my $seen = Geo::Coder::Free::MissCache->missed($scope, $key);
True if $key was remembered as a miss in $scope and has not expired. An expired entry is deleted as it is found. A missing entry is false and is not an error.
API SPECIFICATION
input
scope => { type => 'string' } # from scope_for()
key => { type => 'string' }
output
{ type => 'boolean' }
remember
Geo::Coder::Free::MissCache->remember($scope, $key);
Records that $key found nothing in $scope, for as long as "expires_after" says. Recording the same miss again refreshes it. Returns true.
API SPECIFICATION
input
scope => { type => 'string' } # from scope_for()
key => { type => 'string' }
output
{ type => 'boolean', value => 1 }
forget
Geo::Coder::Free::MissCache->forget($scope, $key);
Forgets one miss, leaving the rest of $scope alone. Returns true if there was an entry to forget, and false if $scope or $key held none, so forgetting the same miss twice returns false the second time. A backend calls this when a lookup that had been remembered as a miss now succeeds, so a hit is never skipped on the strength of an older miss.
API SPECIFICATION
input
scope => { type => 'string' } # from scope_for()
key => { type => 'string' }
output
{ type => 'boolean' }
clear
Geo::Coder::Free::MissCache->clear();
Geo::Coder::Free::MissCache->clear('OpenAddresses');
With no argument, forgets every miss in every scope. With a scope, forgets the misses in that scope and in every scope below it: clear('OpenAddresses') empties every OpenAddresses data set this process has opened. A scope that holds nothing, or names no backend, forgets nothing. Returns true.
API SPECIFICATION
input
scope => { type => 'string', optional => 1 } # a backend name or a scope_for() string
output
{ type => 'boolean', value => 1 }
size
my $count = Geo::Coder::Free::MissCache->size();
my $count = Geo::Coder::Free::MissCache->size('Local');
How many misses are currently remembered, in all scopes or in one scope. Expired entries are discarded first, so this is the number of live entries.
API SPECIFICATION
input
scope => { type => 'string', optional => 1 } # a backend name or a scope_for() string
output
{ type => 'integer', min => 0 }
expires_after
my $seconds = Geo::Coder::Free::MissCache->expires_after();
Geo::Coder::Free::MissCache->expires_after(600);
How long a remembered miss lasts, in seconds. Without an argument it answers with the current setting, which is one hour. Setting it to 0 makes every miss expire as soon as it is recorded, which turns the cache off. It is a setting of the process, not of an object, like the cache itself.
The lifetime is a whole number of seconds. A negative number, a fraction or anything that is not a number croaks expires_after() expects a non-negative whole number of seconds, and the setting is left as it was. Returns the setting in force after the call.
API SPECIFICATION
input
seconds => { type => 'integer', min => 0, optional => 1 }
output
{ type => 'integer', min => 0 }
AUTHOR
Nigel Horne <njh@nigelhorne.com>
SEE ALSO
LICENSE AND COPYRIGHT
Copyright 2026 Nigel Horne.
The program code is released under the following licence: GPL2 for personal use on a single computer. All other users (including Commercial, Charity, Educational, and Government) must apply in writing for a licence for use from Nigel Horne at <njh at nigelhorne.com>.