@@ -662,10 +662,10 @@ public void onFailure(Exception e) {
662
662
* @param rootBlobs Blobs at the repository root
663
663
* @return RepositoryData
664
664
*/
665
- private RepositoryData safeRepositoryData (long repositoryStateId , Map <String , BlobMetadata > rootBlobs ) throws IOException {
665
+ private RepositoryData safeRepositoryData (long repositoryStateId , Map <String , BlobMetadata > rootBlobs ) {
666
666
final long generation = latestGeneration (rootBlobs .keySet ());
667
667
final long genToLoad ;
668
- final Tuple < Long , BytesReference > cached ;
668
+ final CachedRepositoryData cached ;
669
669
if (bestEffortConsistency ) {
670
670
genToLoad = latestKnownRepoGen .updateAndGet (known -> Math .max (known , repositoryStateId ));
671
671
cached = null ;
@@ -684,8 +684,8 @@ private RepositoryData safeRepositoryData(long repositoryStateId, Map<String, Bl
684
684
throw new RepositoryException (metadata .name (), "concurrent modification of the index-N file, expected current generation [" +
685
685
repositoryStateId + "], actual current generation [" + genToLoad + "]" );
686
686
}
687
- if (cached != null && cached .v1 () == genToLoad ) {
688
- return repositoryDataFromCachedEntry ( cached );
687
+ if (cached != null && cached .generation () == genToLoad && cached . hasData () ) {
688
+ return cached . repositoryData ( );
689
689
}
690
690
return getRepositoryData (genToLoad );
691
691
}
@@ -1272,7 +1272,6 @@ public String startVerification() {
1272
1272
String seed = UUIDs .randomBase64UUID ();
1273
1273
byte [] testBytes = Strings .toUTF8Bytes (seed );
1274
1274
BlobContainer testContainer = blobStore ().blobContainer (basePath ().add (testBlobPrefix (seed )));
1275
- BytesArray bytes = new BytesArray (testBytes );
1276
1275
testContainer .writeBlobAtomic ("master.dat" , new BytesArray (testBytes ), true );
1277
1276
return seed ;
1278
1277
}
@@ -1298,20 +1297,62 @@ public void endVerification(String seed) {
1298
1297
private final AtomicLong latestKnownRepoGen = new AtomicLong (RepositoryData .UNKNOWN_REPO_GEN );
1299
1298
1300
1299
// Best effort cache of the latest known repository data and its generation, cached serialized as compressed json
1301
- private final AtomicReference <Tuple <Long , BytesReference >> latestKnownRepositoryData = new AtomicReference <>();
1300
+ private final AtomicReference <CachedRepositoryData > latestKnownRepositoryData =
1301
+ new AtomicReference <>(new CachedRepositoryData (RepositoryData .EMPTY_REPO_GEN , null ));
1302
+
1303
+ /**
1304
+ * Cached serialized repository data or placeholder to keep track of the fact that data for a generation was too large to be cached.
1305
+ */
1306
+ private static final class CachedRepositoryData {
1307
+
1308
+ private final long generation ;
1309
+
1310
+ @ Nullable
1311
+ private final BytesReference repositoryData ;
1312
+
1313
+ CachedRepositoryData (long generation , @ Nullable BytesReference repositoryData ) {
1314
+ this .generation = generation ;
1315
+ this .repositoryData = repositoryData ;
1316
+ }
1317
+
1318
+ long generation () {
1319
+ return generation ;
1320
+ }
1321
+
1322
+ boolean hasData () {
1323
+ return generation == RepositoryData .EMPTY_REPO_GEN || repositoryData != null ;
1324
+ }
1325
+
1326
+ @ Nullable
1327
+ RepositoryData repositoryData () {
1328
+ if (generation == RepositoryData .EMPTY_REPO_GEN ) {
1329
+ return RepositoryData .EMPTY ;
1330
+ }
1331
+ if (repositoryData == null ) {
1332
+ return null ;
1333
+ }
1334
+ try (InputStream input = CompressorFactory .COMPRESSOR .threadLocalInputStream (repositoryData .streamInput ())) {
1335
+ return RepositoryData .snapshotsFromXContent (
1336
+ XContentType .JSON .xContent ().createParser (NamedXContentRegistry .EMPTY , LoggingDeprecationHandler .INSTANCE , input ),
1337
+ generation , false );
1338
+ } catch (IOException e ) {
1339
+ throw new AssertionError ("no actual IO happens here" , e );
1340
+ }
1341
+ }
1342
+ }
1302
1343
1303
1344
@ Override
1304
1345
public void getRepositoryData (ActionListener <RepositoryData > listener ) {
1305
1346
if (latestKnownRepoGen .get () == RepositoryData .CORRUPTED_REPO_GEN ) {
1306
1347
listener .onFailure (corruptedStateException (null ));
1307
1348
return ;
1308
1349
}
1309
- final Tuple < Long , BytesReference > cached = latestKnownRepositoryData .get ();
1350
+ final CachedRepositoryData cached = latestKnownRepositoryData .get ();
1310
1351
// Fast path loading repository data directly from cache if we're in fully consistent mode and the cache matches up with
1311
1352
// the latest known repository generation
1312
- if (bestEffortConsistency == false && cached != null && cached . v1 () == latestKnownRepoGen .get ()) {
1353
+ if (bestEffortConsistency == false && cached . generation () == latestKnownRepoGen .get () && cached . hasData ()) {
1313
1354
try {
1314
- listener .onResponse (repositoryDataFromCachedEntry ( cached ));
1355
+ listener .onResponse (cached . repositoryData ( ));
1315
1356
} catch (Exception e ) {
1316
1357
listener .onFailure (e );
1317
1358
}
@@ -1341,26 +1382,28 @@ private void doGetRepositoryData(ActionListener<RepositoryData> listener) {
1341
1382
}
1342
1383
genToLoad = latestKnownRepoGen .updateAndGet (known -> Math .max (known , generation ));
1343
1384
if (genToLoad > generation ) {
1344
- logger .info ("Determined repository generation [" + generation
1345
- + "] from repository contents but correct generation must be at least [" + genToLoad + "]" );
1385
+ logger .info ("Determined repository generation [{}] from repository contents but correct generation must be at " +
1386
+ " least [{}]" , generation , genToLoad );
1346
1387
}
1347
1388
} else {
1348
1389
// We only rely on the generation tracked in #latestKnownRepoGen which is exclusively updated from the cluster state
1349
1390
genToLoad = latestKnownRepoGen .get ();
1350
1391
}
1351
1392
try {
1352
- final Tuple < Long , BytesReference > cached = latestKnownRepositoryData .get ();
1393
+ final CachedRepositoryData cached = latestKnownRepositoryData .get ();
1353
1394
final RepositoryData loaded ;
1354
1395
// Caching is not used with #bestEffortConsistency see docs on #cacheRepositoryData for details
1355
- if (bestEffortConsistency == false && cached != null && cached .v1 () == genToLoad ) {
1356
- loaded = repositoryDataFromCachedEntry ( cached );
1396
+ if (bestEffortConsistency == false && cached . generation () == genToLoad && cached .hasData () ) {
1397
+ loaded = cached . repositoryData ( );
1357
1398
} else {
1358
1399
loaded = getRepositoryData (genToLoad );
1359
- // We can cache serialized in the most recent version here without regard to the actual repository metadata version
1360
- // since we're only caching the information that we just wrote and thus won't accidentally cache any information that
1361
- // isn't safe
1362
- cacheRepositoryData (compressRepoDataForCache (BytesReference .bytes (
1363
- loaded .snapshotsToXContent (XContentFactory .jsonBuilder (), Version .CURRENT ))), genToLoad );
1400
+ if (cached == null || cached .generation () < genToLoad ) {
1401
+ // We can cache serialized in the most recent version here without regard to the actual repository metadata version
1402
+ // since we're only caching the information that we just wrote and thus won't accidentally cache any information
1403
+ // that isn't safe
1404
+ cacheRepositoryData (compressRepoDataForCache (BytesReference .bytes (
1405
+ loaded .snapshotsToXContent (XContentFactory .jsonBuilder (), Version .CURRENT ))), genToLoad );
1406
+ }
1364
1407
}
1365
1408
listener .onResponse (loaded );
1366
1409
return ;
@@ -1400,11 +1443,12 @@ private void doGetRepositoryData(ActionListener<RepositoryData> listener) {
1400
1443
* @param generation repository generation of the given repository data
1401
1444
*/
1402
1445
private void cacheRepositoryData (@ Nullable BytesReference serialized , long generation ) {
1446
+ assert generation >= 0 : "No need to cache abstract generations but attempted to cache [" + generation + "]" ;
1403
1447
latestKnownRepositoryData .updateAndGet (known -> {
1404
- if (known != null && known . v1 () > generation ) {
1448
+ if (known . generation () > generation ) {
1405
1449
return known ;
1406
1450
}
1407
- return serialized == null ? null : new Tuple <> (generation , serialized );
1451
+ return new CachedRepositoryData (generation , serialized );
1408
1452
});
1409
1453
}
1410
1454
@@ -1440,14 +1484,6 @@ private BytesReference compressRepoDataForCache(BytesReference uncompressed) {
1440
1484
}
1441
1485
}
1442
1486
1443
- private RepositoryData repositoryDataFromCachedEntry (Tuple <Long , BytesReference > cacheEntry ) throws IOException {
1444
- try (InputStream input = CompressorFactory .COMPRESSOR .threadLocalInputStream (cacheEntry .v2 ().streamInput ())) {
1445
- return RepositoryData .snapshotsFromXContent (
1446
- XContentType .JSON .xContent ().createParser (NamedXContentRegistry .EMPTY ,
1447
- LoggingDeprecationHandler .INSTANCE , input ), cacheEntry .v1 (), false );
1448
- }
1449
- }
1450
-
1451
1487
private RepositoryException corruptedStateException (@ Nullable Exception cause ) {
1452
1488
return new RepositoryException (metadata .name (),
1453
1489
"Could not read repository data because the contents of the repository do not match its " +
0 commit comments