or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

events.mdindex.mdruntime.mdscheduling.mdscopes.mdserver.mdshutdown.md

shutdown.mddocs/

0

# Graceful Shutdown

1

2

Coordinated shutdown capabilities for application components enabling clean resource cleanup and graceful termination of long-running operations. Provides interfaces and utilities for implementing graceful shutdown patterns across the application.

3

4

## Capabilities

5

6

### GracefulShutdownCapable Interface

7

8

Core interface for components that support graceful shutdown with resource cleanup and active task reporting.

9

10

```java { .api }

11

/**

12

* Interface for beans that support graceful shutdown

13

* Enables coordinated shutdown with resource cleanup

14

*/

15

public interface GracefulShutdownCapable {

16

17

/**

18

* Perform graceful shutdown of this component

19

* Should complete ongoing operations and release resources

20

* @return CompletionStage that completes when shutdown is finished

21

*/

22

CompletionStage<?> shutdownGracefully();

23

24

/**

25

* Report the number of currently active tasks/operations

26

* Used for monitoring shutdown progress

27

* @return Optional number of active tasks, empty if not trackable

28

*/

29

OptionalLong reportActiveTasks();

30

31

/**

32

* Combine multiple completion stages into a single stage

33

* Utility method for coordinating multiple async operations

34

* @param stages Stream of completion stages to combine

35

* @return CompletionStage that completes when all input stages complete

36

*/

37

@NonNull

38

static CompletionStage<?> allOf(@NonNull Stream<CompletionStage<?>> stages);

39

40

/**

41

* Shutdown all provided GracefulShutdownCapable instances

42

* Coordinates shutdown across multiple components with exception handling

43

* @param stages Stream of components to shutdown

44

* @return CompletionStage that completes when all components are shutdown

45

*/

46

@NonNull

47

static CompletionStage<?> shutdownAll(@NonNull Stream<? extends GracefulShutdownCapable> stages);

48

49

/**

50

* Combine active task counts from multiple GracefulShutdownCapable components

51

* Aggregates active task reports across multiple components

52

* @param delegates Iterable of components to query for active tasks

53

* @return Combined active task count, or empty if no tasks are trackable

54

*/

55

@NonNull

56

static OptionalLong combineActiveTasks(@NonNull Iterable<? extends GracefulShutdownCapable> delegates);

57

}

58

```

59

60

**Usage Examples:**

61

62

```java

63

import io.micronaut.runtime.graceful.GracefulShutdownCapable;

64

import java.util.concurrent.CompletionStage;

65

import java.util.concurrent.CompletableFuture;

66

67

// Database connection pool with graceful shutdown

68

@Singleton

69

public class DatabaseConnectionPool implements GracefulShutdownCapable {

70

71

private final HikariDataSource dataSource;

72

private final AtomicLong activeConnections = new AtomicLong(0);

73

74

public DatabaseConnectionPool(HikariDataSource dataSource) {

75

this.dataSource = dataSource;

76

}

77

78

@Override

79

public CompletionStage<?> shutdownGracefully() {

80

logger.info("Starting graceful shutdown of database connection pool");

81

82

return CompletableFuture.runAsync(() -> {

83

try {

84

// Wait for active connections to complete

85

long maxWaitSeconds = 30;

86

long waitStart = System.currentTimeMillis();

87

88

while (activeConnections.get() > 0 &&

89

(System.currentTimeMillis() - waitStart) < maxWaitSeconds * 1000) {

90

Thread.sleep(100);

91

}

92

93

// Close the data source

94

dataSource.close();

95

logger.info("Database connection pool shutdown completed");

96

97

} catch (InterruptedException e) {

98

Thread.currentThread().interrupt();

99

logger.warn("Database shutdown interrupted", e);

100

}

101

});

102

}

103

104

@Override

105

public OptionalLong reportActiveTasks() {

106

return OptionalLong.of(activeConnections.get());

107

}

108

109

public Connection getConnection() throws SQLException {

110

activeConnections.incrementAndGet();

111

try {

112

return new ConnectionWrapper(dataSource.getConnection()) {

113

@Override

114

public void close() throws SQLException {

115

super.close();

116

activeConnections.decrementAndGet();

117

}

118

};

119

} catch (SQLException e) {

120

activeConnections.decrementAndGet();

121

throw e;

122

}

123

}

124

}

125

126

// Message processing service with graceful shutdown

127

@Singleton

128

public class MessageProcessor implements GracefulShutdownCapable {

129

130

private final ExecutorService processingExecutor;

131

private final AtomicLong activeMessages = new AtomicLong(0);

132

private volatile boolean shutdownRequested = false;

133

134

public MessageProcessor() {

135

this.processingExecutor = Executors.newFixedThreadPool(10);

136

}

137

138

@Override

139

public CompletionStage<?> shutdownGracefully() {

140

logger.info("Starting graceful shutdown of message processor");

141

shutdownRequested = true;

142

143

return CompletableFuture.runAsync(() -> {

144

// Stop accepting new messages

145

processingExecutor.shutdown();

146

147

try {

148

// Wait for active messages to complete (max 60 seconds)

149

boolean terminated = processingExecutor.awaitTermination(60, TimeUnit.SECONDS);

150

151

if (!terminated) {

152

logger.warn("Message processor shutdown timed out, forcing shutdown");

153

processingExecutor.shutdownNow();

154

}

155

156

logger.info("Message processor shutdown completed. Active messages: {}",

157

activeMessages.get());

158

159

} catch (InterruptedException e) {

160

Thread.currentThread().interrupt();

161

processingExecutor.shutdownNow();

162

}

163

});

164

}

165

166

@Override

167

public OptionalLong reportActiveTasks() {

168

return OptionalLong.of(activeMessages.get());

169

}

170

171

public CompletableFuture<Void> processMessage(Message message) {

172

if (shutdownRequested) {

173

return CompletableFuture.failedFuture(

174

new IllegalStateException("Shutdown in progress"));

175

}

176

177

activeMessages.incrementAndGet();

178

179

return CompletableFuture.runAsync(() -> {

180

try {

181

// Process the message

182

handleMessage(message);

183

} finally {

184

activeMessages.decrementAndGet();

185

}

186

}, processingExecutor);

187

}

188

}

189

```

190

191

### GracefulShutdownManager

192

193

Central manager for coordinating graceful shutdown across all registered components.

194

195

```java { .api }

196

/**

197

* Singleton service that manages graceful shutdown across all components

198

* Automatically discovers and coordinates GracefulShutdownCapable beans

199

*/

200

@Singleton

201

@Requires(classes = GracefulShutdownCapable.class)

202

@Experimental

203

public final class GracefulShutdownManager {

204

205

/**

206

* Initiate graceful shutdown of all GracefulShutdownCapable beans

207

* Automatically discovers all beans implementing GracefulShutdownCapable

208

* @return CompletionStage that completes when all components are shutdown

209

*/

210

@NonNull

211

public CompletionStage<?> shutdownGracefully();

212

213

/**

214

* Report combined active tasks from all GracefulShutdownCapable beans

215

* Aggregates active task counts from all discovered components

216

* @return Optional total count of active tasks across all components

217

*/

218

@NonNull

219

public OptionalLong reportActiveTasks();

220

}

221

```

222

223

**Usage Examples:**

224

225

```java

226

import io.micronaut.runtime.graceful.GracefulShutdownManager;

227

228

@Singleton

229

public class ApplicationShutdownCoordinator {

230

231

private final GracefulShutdownManager shutdownManager;

232

233

public ApplicationShutdownCoordinator(GracefulShutdownManager shutdownManager) {

234

this.shutdownManager = shutdownManager;

235

}

236

237

@EventListener

238

public void onShutdownEvent(ShutdownEvent event) {

239

logger.info("Application shutdown initiated");

240

241

// Get current active task count

242

OptionalLong activeTasks = shutdownManager.reportActiveTasks();

243

if (activeTasks.isPresent()) {

244

logger.info("Active tasks at shutdown: {}", activeTasks.getAsLong());

245

}

246

247

// Initiate graceful shutdown

248

CompletionStage<?> shutdownStage = shutdownManager.shutdownGracefully();

249

250

// Wait for completion (with timeout)

251

try {

252

shutdownStage.toCompletableFuture().get(90, TimeUnit.SECONDS);

253

logger.info("βœ… Graceful shutdown completed successfully");

254

} catch (TimeoutException e) {

255

logger.warn("⚠️ Graceful shutdown timed out after 90 seconds");

256

} catch (Exception e) {

257

logger.error("❌ Error during graceful shutdown", e);

258

}

259

}

260

261

// Manual shutdown trigger (e.g., for admin endpoint)

262

public CompletableFuture<ShutdownReport> initiateShutdown() {

263

return CompletableFuture.supplyAsync(() -> {

264

long startTime = System.currentTimeMillis();

265

OptionalLong initialActiveTasks = shutdownManager.reportActiveTasks();

266

267

try {

268

shutdownManager.shutdownGracefully()

269

.toCompletableFuture()

270

.get(60, TimeUnit.SECONDS);

271

272

long duration = System.currentTimeMillis() - startTime;

273

return new ShutdownReport(true, duration, initialActiveTasks);

274

275

} catch (Exception e) {

276

long duration = System.currentTimeMillis() - startTime;

277

return new ShutdownReport(false, duration, initialActiveTasks, e);

278

}

279

});

280

}

281

}

282

283

// Shutdown report data class

284

public class ShutdownReport {

285

private final boolean successful;

286

private final long durationMs;

287

private final OptionalLong initialActiveTasks;

288

private final Exception error;

289

290

// Constructors and getters...

291

}

292

```

293

294

### GracefulShutdownListener Interface

295

296

Listener interface for responding to graceful shutdown events.

297

298

```java { .api }

299

/**

300

* Listener interface for graceful shutdown events

301

* Allows components to respond to shutdown phases

302

*/

303

public interface GracefulShutdownListener {

304

305

/**

306

* Called when graceful shutdown begins

307

* Opportunity to prepare for shutdown

308

*/

309

default void onShutdownStart() {

310

// Default implementation does nothing

311

}

312

313

/**

314

* Called during shutdown progress

315

* @param remainingTasks Number of tasks still active

316

*/

317

default void onShutdownProgress(long remainingTasks) {

318

// Default implementation does nothing

319

}

320

321

/**

322

* Called when graceful shutdown completes successfully

323

*/

324

default void onShutdownComplete() {

325

// Default implementation does nothing

326

}

327

328

/**

329

* Called if graceful shutdown fails or times out

330

* @param cause The exception that caused the failure, if any

331

*/

332

default void onShutdownFailed(Throwable cause) {

333

// Default implementation does nothing

334

}

335

}

336

```

337

338

**Usage Examples:**

339

340

```java

341

import io.micronaut.runtime.graceful.GracefulShutdownListener;

342

343

@Singleton

344

public class ShutdownProgressLogger implements GracefulShutdownListener {

345

346

private long shutdownStartTime;

347

348

@Override

349

public void onShutdownStart() {

350

shutdownStartTime = System.currentTimeMillis();

351

logger.info("πŸ”„ Graceful shutdown initiated");

352

}

353

354

@Override

355

public void onShutdownProgress(long remainingTasks) {

356

long elapsed = System.currentTimeMillis() - shutdownStartTime;

357

logger.info("⏳ Shutdown progress: {} tasks remaining after {}ms",

358

remainingTasks, elapsed);

359

}

360

361

@Override

362

public void onShutdownComplete() {

363

long totalTime = System.currentTimeMillis() - shutdownStartTime;

364

logger.info("βœ… Graceful shutdown completed in {}ms", totalTime);

365

}

366

367

@Override

368

public void onShutdownFailed(Throwable cause) {

369

long totalTime = System.currentTimeMillis() - shutdownStartTime;

370

logger.error("❌ Graceful shutdown failed after {}ms", totalTime, cause);

371

}

372

}

373

374

// Metrics collection during shutdown

375

@Singleton

376

public class ShutdownMetricsCollector implements GracefulShutdownListener {

377

378

private final MeterRegistry meterRegistry;

379

private Timer.Sample shutdownTimer;

380

381

public ShutdownMetricsCollector(MeterRegistry meterRegistry) {

382

this.meterRegistry = meterRegistry;

383

}

384

385

@Override

386

public void onShutdownStart() {

387

shutdownTimer = Timer.start(meterRegistry);

388

meterRegistry.counter("shutdown.started").increment();

389

}

390

391

@Override

392

public void onShutdownProgress(long remainingTasks) {

393

meterRegistry.gauge("shutdown.remaining.tasks", remainingTasks);

394

}

395

396

@Override

397

public void onShutdownComplete() {

398

if (shutdownTimer != null) {

399

shutdownTimer.stop(Timer.builder("shutdown.duration")

400

.description("Time taken for graceful shutdown")

401

.register(meterRegistry));

402

}

403

meterRegistry.counter("shutdown.completed").increment();

404

}

405

406

@Override

407

public void onShutdownFailed(Throwable cause) {

408

if (shutdownTimer != null) {

409

shutdownTimer.stop(Timer.builder("shutdown.duration.failed")

410

.description("Time taken for failed shutdown")

411

.register(meterRegistry));

412

}

413

meterRegistry.counter("shutdown.failed").increment();

414

}

415

}

416

```

417

418

### GracefulShutdownConfiguration

419

420

Configuration options for customizing graceful shutdown behavior.

421

422

```java { .api }

423

/**

424

* Configuration for graceful shutdown behavior

425

* Allows customization of shutdown timeouts and policies

426

*/

427

@ConfigurationProperties("micronaut.application.graceful-shutdown")

428

public class GracefulShutdownConfiguration {

429

430

/**

431

* Enable or disable graceful shutdown

432

* @return true if graceful shutdown is enabled

433

*/

434

public boolean isEnabled();

435

436

/**

437

* Maximum time to wait for graceful shutdown to complete

438

* @return Shutdown timeout duration

439

*/

440

public Duration getTimeout();

441

442

/**

443

* Time to wait between shutdown progress checks

444

* @return Progress check interval

445

*/

446

public Duration getProgressCheckInterval();

447

448

/**

449

* Whether to force shutdown if graceful shutdown times out

450

* @return true to force shutdown on timeout

451

*/

452

public boolean isForceShutdownOnTimeout();

453

454

/**

455

* Log level for shutdown progress messages

456

* @return Log level for shutdown logging

457

*/

458

public LogLevel getLogLevel();

459

}

460

```

461

462

**Configuration Examples:**

463

464

```yaml

465

# application.yml

466

micronaut:

467

application:

468

graceful-shutdown:

469

enabled: true

470

timeout: 60s

471

progress-check-interval: 5s

472

force-shutdown-on-timeout: true

473

log-level: INFO

474

```

475

476

```java

477

// Using configuration in custom shutdown logic

478

@Singleton

479

public class ConfigurableShutdownManager {

480

481

private final GracefulShutdownConfiguration config;

482

private final GracefulShutdownManager shutdownManager;

483

484

public ConfigurableShutdownManager(GracefulShutdownConfiguration config,

485

GracefulShutdownManager shutdownManager) {

486

this.config = config;

487

this.shutdownManager = shutdownManager;

488

}

489

490

public CompletableFuture<Void> shutdown() {

491

if (!config.isEnabled()) {

492

logger.info("Graceful shutdown disabled, performing immediate shutdown");

493

return CompletableFuture.completedFuture(null);

494

}

495

496

return CompletableFuture.runAsync(() -> {

497

try {

498

Duration timeout = config.getTimeout();

499

logger.log(config.getLogLevel(), "Starting graceful shutdown with {}s timeout",

500

timeout.getSeconds());

501

502

CompletableFuture<?> shutdownFuture = shutdownManager

503

.shutdownGracefully()

504

.toCompletableFuture();

505

506

// Wait with configured timeout

507

shutdownFuture.get(timeout.toMillis(), TimeUnit.MILLISECONDS);

508

509

logger.log(config.getLogLevel(), "Graceful shutdown completed successfully");

510

511

} catch (TimeoutException e) {

512

if (config.isForceShutdownOnTimeout()) {

513

logger.warn("Graceful shutdown timed out, forcing shutdown");

514

// Force shutdown logic here

515

} else {

516

logger.error("Graceful shutdown timed out and force shutdown disabled");

517

throw new RuntimeException("Shutdown timeout", e);

518

}

519

} catch (Exception e) {

520

logger.error("Error during graceful shutdown", e);

521

throw new RuntimeException("Shutdown error", e);

522

}

523

});

524

}

525

}

526

```

527

528

## Advanced Shutdown Patterns

529

530

### Resource Pool Shutdown

531

532

Example of graceful shutdown for resource pools and connection managers.

533

534

```java { .api }

535

@Singleton

536

public class ConnectionManager implements GracefulShutdownCapable {

537

538

private final Map<String, ConnectionPool> connectionPools = new ConcurrentHashMap<>();

539

private volatile boolean shutdownInitiated = false;

540

541

@Override

542

public CompletionStage<?> shutdownGracefully() {

543

shutdownInitiated = true;

544

logger.info("Shutting down {} connection pools", connectionPools.size());

545

546

List<CompletableFuture<Void>> shutdownFutures = connectionPools.values()

547

.stream()

548

.map(pool -> CompletableFuture.runAsync(() -> {

549

try {

550

pool.close();

551

} catch (Exception e) {

552

logger.error("Error closing connection pool", e);

553

}

554

}))

555

.collect(Collectors.toList());

556

557

return CompletableFuture.allOf(shutdownFutures.toArray(new CompletableFuture[0]));

558

}

559

560

@Override

561

public OptionalLong reportActiveTasks() {

562

return OptionalLong.of(

563

connectionPools.values().stream()

564

.mapToLong(ConnectionPool::getActiveConnectionCount)

565

.sum()

566

);

567

}

568

}

569

```

570

571

### Background Task Shutdown

572

573

Example of graceful shutdown for background task processors.

574

575

```java { .api }

576

@Singleton

577

public class BackgroundTaskProcessor implements GracefulShutdownCapable {

578

579

private final ScheduledExecutorService scheduler;

580

private final Map<String, CompletableFuture<Void>> activeTasks = new ConcurrentHashMap<>();

581

582

public BackgroundTaskProcessor() {

583

this.scheduler = Executors.newScheduledThreadPool(5);

584

}

585

586

@Override

587

public CompletionStage<?> shutdownGracefully() {

588

logger.info("Shutting down background task processor");

589

590

// Stop accepting new tasks

591

scheduler.shutdown();

592

593

return CompletableFuture.runAsync(() -> {

594

try {

595

// Wait for scheduled tasks to complete

596

boolean terminated = scheduler.awaitTermination(30, TimeUnit.SECONDS);

597

598

if (!terminated) {

599

logger.warn("Scheduled tasks did not complete within timeout");

600

scheduler.shutdownNow();

601

}

602

603

// Wait for active background tasks

604

CompletableFuture<Void> allTasks = CompletableFuture.allOf(

605

activeTasks.values().toArray(new CompletableFuture[0])

606

);

607

608

allTasks.get(60, TimeUnit.SECONDS);

609

logger.info("All background tasks completed");

610

611

} catch (Exception e) {

612

logger.error("Error during background task shutdown", e);

613

// Cancel remaining tasks

614

activeTasks.values().forEach(task -> task.cancel(true));

615

}

616

});

617

}

618

619

@Override

620

public OptionalLong reportActiveTasks() {

621

return OptionalLong.of(activeTasks.size());

622

}

623

}

624

```