1111
1212import com .maxmind .geoip2 .model .AbstractResponse ;
1313
14+ import org .elasticsearch .cluster .metadata .ProjectId ;
1415import org .elasticsearch .core .TimeValue ;
1516import org .elasticsearch .ingest .geoip .stats .CacheStats ;
1617import org .elasticsearch .test .ESTestCase ;
1718
19+ import java .nio .file .Path ;
1820import java .util .concurrent .atomic .AtomicInteger ;
1921import java .util .concurrent .atomic .AtomicLong ;
2022import java .util .function .Function ;
@@ -26,21 +28,22 @@ public class GeoIpCacheTests extends ESTestCase {
2628
2729 public void testCachesAndEvictsResults () {
2830 GeoIpCache cache = new GeoIpCache (1 );
31+ ProjectId projectId = randomProjectIdOrDefault ();
2932 AbstractResponse response1 = mock (AbstractResponse .class );
3033 AbstractResponse response2 = mock (AbstractResponse .class );
3134
3235 // add a key
33- AbstractResponse cachedResponse = cache .putIfAbsent ("127.0.0.1" , "path/to/db" , ip -> response1 );
36+ AbstractResponse cachedResponse = cache .putIfAbsent (projectId , "127.0.0.1" , "path/to/db" , ip -> response1 );
3437 assertSame (cachedResponse , response1 );
35- assertSame (cachedResponse , cache .putIfAbsent ("127.0.0.1" , "path/to/db" , ip -> response1 ));
36- assertSame (cachedResponse , cache .get ("127.0.0.1" , "path/to/db" ));
38+ assertSame (cachedResponse , cache .putIfAbsent (projectId , "127.0.0.1" , "path/to/db" , ip -> response1 ));
39+ assertSame (cachedResponse , cache .get (projectId , "127.0.0.1" , "path/to/db" ));
3740
3841 // evict old key by adding another value
39- cachedResponse = cache .putIfAbsent ("127.0.0.2" , "path/to/db" , ip -> response2 );
42+ cachedResponse = cache .putIfAbsent (projectId , "127.0.0.2" , "path/to/db" , ip -> response2 );
4043 assertSame (cachedResponse , response2 );
41- assertSame (cachedResponse , cache .putIfAbsent ("127.0.0.2" , "path/to/db" , ip -> response2 ));
42- assertSame (cachedResponse , cache .get ("127.0.0.2" , "path/to/db" ));
43- assertNotSame (response1 , cache .get ("127.0.0.1" , "path/to/db" ));
44+ assertSame (cachedResponse , cache .putIfAbsent (projectId , "127.0.0.2" , "path/to/db" , ip -> response2 ));
45+ assertSame (cachedResponse , cache .get (projectId , "127.0.0.2" , "path/to/db" ));
46+ assertNotSame (response1 , cache .get (projectId , "127.0.0.1" , "path/to/db" ));
4447 }
4548
4649 public void testCachesNoResult () {
@@ -51,31 +54,47 @@ public void testCachesNoResult() {
5154 return null ;
5255 };
5356
54- AbstractResponse response = cache .putIfAbsent ("127.0.0.1" , "path/to/db" , countAndReturnNull );
57+ ProjectId projectId = randomProjectIdOrDefault ();
58+ AbstractResponse response = cache .putIfAbsent (projectId , "127.0.0.1" , "path/to/db" , countAndReturnNull );
5559 assertNull (response );
56- assertNull (cache .putIfAbsent ("127.0.0.1" , "path/to/db" , countAndReturnNull ));
60+ assertNull (cache .putIfAbsent (projectId , "127.0.0.1" , "path/to/db" , countAndReturnNull ));
5761 assertEquals (1 , count .get ());
5862
5963 // the cached value is not actually *null*, it's the NO_RESULT sentinel
60- assertSame (GeoIpCache .NO_RESULT , cache .get ("127.0.0.1" , "path/to/db" ));
64+ assertSame (GeoIpCache .NO_RESULT , cache .get (projectId , "127.0.0.1" , "path/to/db" ));
6165 }
6266
63- public void testCacheKey () {
67+ public void testCacheDoesNotCollideForDifferentDatabases () {
6468 GeoIpCache cache = new GeoIpCache (2 );
6569 AbstractResponse response1 = mock (AbstractResponse .class );
6670 AbstractResponse response2 = mock (AbstractResponse .class );
6771
68- assertSame (response1 , cache .putIfAbsent ("127.0.0.1" , "path/to/db1" , ip -> response1 ));
69- assertSame (response2 , cache .putIfAbsent ("127.0.0.1" , "path/to/db2" , ip -> response2 ));
70- assertSame (response1 , cache .get ("127.0.0.1" , "path/to/db1" ));
71- assertSame (response2 , cache .get ("127.0.0.1" , "path/to/db2" ));
72+ ProjectId projectId = randomProjectIdOrDefault ();
73+ assertSame (response1 , cache .putIfAbsent (projectId , "127.0.0.1" , "path/to/db1" , ip -> response1 ));
74+ assertSame (response2 , cache .putIfAbsent (projectId , "127.0.0.1" , "path/to/db2" , ip -> response2 ));
75+ assertSame (response1 , cache .get (projectId , "127.0.0.1" , "path/to/db1" ));
76+ assertSame (response2 , cache .get (projectId , "127.0.0.1" , "path/to/db2" ));
77+ }
78+
79+ public void testCacheDoesNotCollideForDifferentProjects () {
80+ GeoIpCache cache = new GeoIpCache (2 );
81+ AbstractResponse response1 = mock (AbstractResponse .class );
82+ AbstractResponse response2 = mock (AbstractResponse .class );
83+
84+ ProjectId projectId1 = randomUniqueProjectId ();
85+ ProjectId projectId2 = randomUniqueProjectId ();
86+ assertSame (response1 , cache .putIfAbsent (projectId1 , "127.0.0.1" , "path/to/db1" , ip -> response1 ));
87+ assertSame (response2 , cache .putIfAbsent (projectId2 , "127.0.0.1" , "path/to/db1" , ip -> response2 ));
88+ assertSame (response1 , cache .get (projectId1 , "127.0.0.1" , "path/to/db1" ));
89+ assertSame (response2 , cache .get (projectId2 , "127.0.0.1" , "path/to/db1" ));
7290 }
7391
7492 public void testThrowsFunctionsException () {
7593 GeoIpCache cache = new GeoIpCache (1 );
94+ ProjectId projectId = randomProjectIdOrDefault ();
7695 IllegalArgumentException ex = expectThrows (
7796 IllegalArgumentException .class ,
78- () -> cache .putIfAbsent ("127.0.0.1" , "path/to/db" , ip -> {
97+ () -> cache .putIfAbsent (projectId , "127.0.0.1" , "path/to/db" , ip -> {
7998 throw new IllegalArgumentException ("bad" );
8099 })
81100 );
@@ -92,19 +111,20 @@ public void testGetCacheStats() {
92111 final AtomicLong testNanoTime = new AtomicLong (0 );
93112 // We use a relative time provider that increments 1ms every time it is called. So each operation appears to take 1ms
94113 GeoIpCache cache = new GeoIpCache (maxCacheSize , () -> testNanoTime .addAndGet (TimeValue .timeValueMillis (1 ).getNanos ()));
114+ ProjectId projectId = randomProjectIdOrDefault ();
95115 AbstractResponse response = mock (AbstractResponse .class );
96116 String databasePath = "path/to/db1" ;
97117 String key1 = "127.0.0.1" ;
98118 String key2 = "127.0.0.2" ;
99119 String key3 = "127.0.0.3" ;
100120
101- cache .putIfAbsent (key1 , databasePath , ip -> response ); // cache miss
102- cache .putIfAbsent (key2 , databasePath , ip -> response ); // cache miss
103- cache .putIfAbsent (key1 , databasePath , ip -> response ); // cache hit
104- cache .putIfAbsent (key1 , databasePath , ip -> response ); // cache hit
105- cache .putIfAbsent (key1 , databasePath , ip -> response ); // cache hit
106- cache .putIfAbsent (key3 , databasePath , ip -> response ); // cache miss, key2 will be evicted
107- cache .putIfAbsent (key2 , databasePath , ip -> response ); // cache miss, key1 will be evicted
121+ cache .putIfAbsent (projectId , key1 , databasePath , ip -> response ); // cache miss
122+ cache .putIfAbsent (projectId , key2 , databasePath , ip -> response ); // cache miss
123+ cache .putIfAbsent (projectId , key1 , databasePath , ip -> response ); // cache hit
124+ cache .putIfAbsent (projectId , key1 , databasePath , ip -> response ); // cache hit
125+ cache .putIfAbsent (projectId , key1 , databasePath , ip -> response ); // cache hit
126+ cache .putIfAbsent (projectId , key3 , databasePath , ip -> response ); // cache miss, key2 will be evicted
127+ cache .putIfAbsent (projectId , key2 , databasePath , ip -> response ); // cache miss, key1 will be evicted
108128 CacheStats cacheStats = cache .getCacheStats ();
109129 assertThat (cacheStats .count (), equalTo (maxCacheSize ));
110130 assertThat (cacheStats .hits (), equalTo (3L ));
@@ -115,4 +135,28 @@ public void testGetCacheStats() {
115135 // There are 4 misses. Each is made up of a cache query, and a database query, each being 1ms:
116136 assertThat (cacheStats .missesTimeInMillis (), equalTo (8L ));
117137 }
138+
139+ public void testPurgeCacheEntriesForDatabase () {
140+ GeoIpCache cache = new GeoIpCache (100 );
141+ ProjectId projectId1 = randomUniqueProjectId ();
142+ ProjectId projectId2 = randomUniqueProjectId ();
143+ String databasePath1 = "path/to/db1" ;
144+ String databasePath2 = "path/to/db2" ;
145+ String ip1 = "127.0.0.1" ;
146+ String ip2 = "127.0.0.2" ;
147+
148+ AbstractResponse response = mock (AbstractResponse .class );
149+ cache .putIfAbsent (projectId1 , ip1 , databasePath1 , ip -> response ); // cache miss
150+ cache .putIfAbsent (projectId1 , ip2 , databasePath1 , ip -> response ); // cache miss
151+ cache .putIfAbsent (projectId2 , ip1 , databasePath1 , ip -> response ); // cache miss
152+ cache .putIfAbsent (projectId1 , ip1 , databasePath2 , ip -> response ); // cache miss
153+ cache .purgeCacheEntriesForDatabase (projectId1 , Path .of (databasePath1 ));
154+ // should have purged entries for projectId1 and databasePath1...
155+ assertNull (cache .get (projectId1 , ip1 , databasePath1 ));
156+ assertNull (cache .get (projectId1 , ip2 , databasePath1 ));
157+ // ...but left the one for projectId2...
158+ assertSame (response , cache .get (projectId2 , ip1 , databasePath1 ));
159+ // ...and for databasePath2:
160+ assertSame (response , cache .get (projectId1 , ip1 , databasePath2 ));
161+ }
118162}
0 commit comments