2222import com .sun .net .httpserver .HttpExchange ;
2323import com .sun .net .httpserver .HttpHandler ;
2424import fixture .s3 .S3HttpHandler ;
25+ import org .elasticsearch .action .ActionRunnable ;
26+ import org .elasticsearch .action .support .PlainActionFuture ;
2527import org .elasticsearch .cluster .metadata .RepositoryMetaData ;
2628import org .elasticsearch .cluster .service .ClusterService ;
2729import org .elasticsearch .common .SuppressForbidden ;
2830import org .elasticsearch .common .blobstore .BlobContainer ;
2931import org .elasticsearch .common .blobstore .BlobPath ;
3032import org .elasticsearch .common .blobstore .BlobStore ;
33+ import org .elasticsearch .common .bytes .BytesReference ;
3134import org .elasticsearch .common .settings .MockSecureSettings ;
3235import org .elasticsearch .common .settings .Setting ;
3336import org .elasticsearch .common .settings .Settings ;
3437import org .elasticsearch .common .unit .ByteSizeUnit ;
38+ import org .elasticsearch .common .unit .TimeValue ;
3539import org .elasticsearch .common .xcontent .NamedXContentRegistry ;
40+ import org .elasticsearch .common .xcontent .XContentFactory ;
3641import org .elasticsearch .plugins .Plugin ;
42+ import org .elasticsearch .repositories .RepositoriesService ;
43+ import org .elasticsearch .repositories .RepositoryData ;
44+ import org .elasticsearch .repositories .blobstore .BlobStoreRepository ;
3745import org .elasticsearch .repositories .blobstore .ESMockAPIBasedRepositoryIntegTestCase ;
46+ import org .elasticsearch .snapshots .SnapshotId ;
47+ import org .elasticsearch .snapshots .SnapshotsService ;
3848import org .elasticsearch .snapshots .mockstore .BlobStoreWrapper ;
49+ import org .elasticsearch .threadpool .ThreadPool ;
3950
51+ import java .io .IOException ;
52+ import java .io .InputStream ;
4053import java .util .ArrayList ;
4154import java .util .Collection ;
4255import java .util .Collections ;
4356import java .util .List ;
4457import java .util .Map ;
4558
59+ import static org .hamcrest .Matchers .greaterThan ;
60+ import static org .hamcrest .Matchers .lessThan ;
61+
4662@ SuppressForbidden (reason = "this test uses a HttpServer to emulate an S3 endpoint" )
4763public class S3BlobStoreRepositoryTests extends ESMockAPIBasedRepositoryIntegTestCase {
4864
65+ private static final TimeValue TEST_COOLDOWN_PERIOD = TimeValue .timeValueSeconds (5L );
66+
4967 @ Override
5068 protected String repositoryType () {
5169 return S3Repository .TYPE ;
@@ -82,6 +100,7 @@ protected Settings nodeSettings(int nodeOrdinal) {
82100 secureSettings .setString (S3ClientSettings .SECRET_KEY_SETTING .getConcreteSettingForNamespace ("test" ).getKey (), "secret" );
83101
84102 return Settings .builder ()
103+ .put (ThreadPool .ESTIMATED_TIME_INTERVAL_SETTING .getKey (), 0 ) // We have tests that verify an exact wait time
85104 .put (S3ClientSettings .ENDPOINT_SETTING .getConcreteSettingForNamespace ("test" ).getKey (), httpServerUrl ())
86105 // Disable chunked encoding as it simplifies a lot the request parsing on the httpServer side
87106 .put (S3ClientSettings .DISABLE_CHUNKED_ENCODING .getConcreteSettingForNamespace ("test" ).getKey (), true )
@@ -92,6 +111,41 @@ protected Settings nodeSettings(int nodeOrdinal) {
92111 .build ();
93112 }
94113
114+ public void testEnforcedCooldownPeriod () throws IOException {
115+ final String repoName = createRepository (randomName (), Settings .builder ().put (repositorySettings ())
116+ .put (S3Repository .COOLDOWN_PERIOD .getKey (), TEST_COOLDOWN_PERIOD ).build ());
117+
118+ final SnapshotId fakeOldSnapshot = client ().admin ().cluster ().prepareCreateSnapshot (repoName , "snapshot-old" )
119+ .setWaitForCompletion (true ).setIndices ().get ().getSnapshotInfo ().snapshotId ();
120+ final RepositoriesService repositoriesService = internalCluster ().getInstance (RepositoriesService .class );
121+ final BlobStoreRepository repository = (BlobStoreRepository ) repositoriesService .repository (repoName );
122+ final RepositoryData repositoryData =
123+ PlainActionFuture .get (f -> repository .threadPool ().generic ().execute (() -> repository .getRepositoryData (f )));
124+ final RepositoryData modifiedRepositoryData = repositoryData .withVersions (Collections .singletonMap (fakeOldSnapshot ,
125+ SnapshotsService .SHARD_GEN_IN_REPO_DATA_VERSION .minimumCompatibilityVersion ()));
126+ final BytesReference serialized =
127+ BytesReference .bytes (modifiedRepositoryData .snapshotsToXContent (XContentFactory .jsonBuilder (), false ));
128+ PlainActionFuture .get (f -> repository .threadPool ().generic ().execute (ActionRunnable .run (f , () -> {
129+ try (InputStream stream = serialized .streamInput ()) {
130+ repository .blobStore ().blobContainer (repository .basePath ()).writeBlobAtomic (
131+ BlobStoreRepository .INDEX_FILE_PREFIX + modifiedRepositoryData .getGenId (), stream , serialized .length (), true );
132+ }
133+ })));
134+
135+ final String newSnapshotName = "snapshot-new" ;
136+ final long beforeThrottledSnapshot = repository .threadPool ().relativeTimeInNanos ();
137+ client ().admin ().cluster ().prepareCreateSnapshot (repoName , newSnapshotName ).setWaitForCompletion (true ).setIndices ().get ();
138+ assertThat (repository .threadPool ().relativeTimeInNanos () - beforeThrottledSnapshot , greaterThan (TEST_COOLDOWN_PERIOD .getNanos ()));
139+
140+ final long beforeThrottledDelete = repository .threadPool ().relativeTimeInNanos ();
141+ client ().admin ().cluster ().prepareDeleteSnapshot (repoName , newSnapshotName ).get ();
142+ assertThat (repository .threadPool ().relativeTimeInNanos () - beforeThrottledDelete , greaterThan (TEST_COOLDOWN_PERIOD .getNanos ()));
143+
144+ final long beforeFastDelete = repository .threadPool ().relativeTimeInNanos ();
145+ client ().admin ().cluster ().prepareDeleteSnapshot (repoName , fakeOldSnapshot .getName ()).get ();
146+ assertThat (repository .threadPool ().relativeTimeInNanos () - beforeFastDelete , lessThan (TEST_COOLDOWN_PERIOD .getNanos ()));
147+ }
148+
95149 /**
96150 * S3RepositoryPlugin that allows to disable chunked encoding and to set a low threshold between single upload and multipart upload.
97151 */
0 commit comments