Application performance tuning, JVM optimization, database query tuning, Vaadin UI performance
This skill provides performance optimization guidance for the CIA platform across JVM tuning, PostgreSQL query optimization, Vaadin UI rendering, and Spring Framework efficiency. It ensures the political intelligence platform delivers responsive analysis to users.
Apply this skill when:
Do NOT use for:
| Metric | Target | Measurement |
|---|---|---|
| Page load time | < 3 seconds | Lighthouse, browser DevTools |
| API response time | < 500ms (p95) | CloudWatch metrics |
| Database query time | < 100ms (p95) | PostgreSQL pg_stat_statements |
| JVM heap usage | < 80% of max | JMX / CloudWatch |
| Vaadin push latency | < 200ms | Client-side measurement |
| Startup time | < 30 seconds | Application logs |
# Production JVM flags for CIA platform (Java 21+)
JAVA_OPTS="-server \
-Xms2g -Xmx4g \
-XX:+UseG1GC \
-XX:MaxGCPauseMillis=200 \
-XX:+UseStringDeduplication \
-XX:+OptimizeStringConcat \
-XX:MetaspaceSize=256m \
-XX:MaxMetaspaceSize=512m \
-XX:+HeapDumpOnOutOfMemoryError \
-XX:HeapDumpPath=/var/log/cia/heapdump.hprof \
-Djava.security.egd=file:/dev/./urandom"
// ✅ EFFICIENT: Stream processing for large datasets
@Service
public class VotingDataProcessor {
@Transactional(readOnly = true)
public void processVotingRecords(String sessionId) {
try (Stream<VoteData> votes = voteRepository.streamBySession(sessionId)) {
votes.map(this::analyzeVote)
.filter(Objects::nonNull)
.forEach(resultRepository::save);
}
// Stream auto-closes, no memory accumulation
}
}
// ❌ INEFFICIENT: Loading all records into memory
public void processVotingRecords(String sessionId) {
List<VoteData> allVotes = voteRepository.findBySession(sessionId);
// May load millions of records into heap
}
-- High-impact indexes for common CIA queries
-- Politician lookup by party and status
CREATE INDEX idx_person_party_status
ON person_data (party, status) WHERE status = 'ACTIVE';
-- Voting record time-series queries
CREATE INDEX idx_vote_date_committee
ON vote_data (vote_date DESC, committee_id);
-- Document search optimization
CREATE INDEX idx_document_search
ON document_content USING gin(to_tsvector('swedish', content));
-- Materialized view refresh tracking
CREATE INDEX idx_mv_refresh_status
ON materialized_view_log (view_name, last_refresh DESC);
// ❌ N+1 Query Problem
@Entity
public class Committee {
@OneToMany(fetch = FetchType.LAZY)
private List<CommitteeMember> members;
}
// Accessing committee.getMembers() in a loop = N+1 queries
// ✅ FIX: Use JOIN FETCH or EntityGraph
@Query("SELECT c FROM Committee c JOIN FETCH c.members WHERE c.id = :id")
Committee findWithMembers(@Param("id") Long id);
// ✅ FIX: Use @EntityGraph
@EntityGraph(attributePaths = {"members", "members.person"})
Committee findById(Long id);
-- Concurrent refresh to avoid locks during read
REFRESH MATERIALIZED VIEW CONCURRENTLY view_riksdagen_politician_summary;
-- Schedule refresh based on data freshness requirements
-- High priority: refresh every 15 minutes during business hours
-- Low priority: refresh daily during off-peak
// ✅ EFFICIENT: Lazy loading with DataProvider
Grid<PoliticianSummary> grid = new Grid<>(PoliticianSummary.class);
grid.setDataProvider(
DataProvider.fromCallbacks(
query -> politicianService.fetch(query.getOffset(), query.getLimit()),
query -> politicianService.count()
)
);
// ❌ INEFFICIENT: Loading all items upfront
grid.setItems(politicianService.findAll()); // Loads everything
// ✅ Use virtual scrolling for long lists
grid.setPageSize(50);
grid.setMultiSort(true);
// ✅ Minimize push updates
@Push(transport = Transport.WEBSOCKET_XHR)
public class MainView extends AppLayout {
// Only push critical real-time updates
}
// ✅ Defer non-critical UI updates
UI.getCurrent().access(() -> {
notificationComponent.update(newData);
});
@CssImport instead of inline styles for reusevaadin.productionMode=true)@ClientCallable methods@Service
public class PoliticianService {
// Cache frequently accessed, rarely changing data
@Cacheable(value = "politicians", key = "#personId",
unless = "#result == null")
public PoliticianSummary getPolitician(String personId) {
return repository.findSummaryById(personId);
}
// Evict cache when data is updated
@CacheEvict(value = "politicians", key = "#personId")
public void updatePolitician(String personId, PoliticianUpdate update) {
repository.update(personId, update);
}
}
// ✅ Read-only transactions for queries (no dirty checking)
@Transactional(readOnly = true)
public List<VotingSummary> getVotingSummaries(String sessionId) {
return votingRepository.findSummariesBySession(sessionId);
}
// ✅ Batch operations for bulk inserts
@Transactional
public void importVotingData(List<VoteData> votes) {
int batchSize = 50;
for (int i = 0; i < votes.size(); i++) {
entityManager.persist(votes.get(i));
if (i % batchSize == 0) {
entityManager.flush();
entityManager.clear(); // Release memory
}
}
}
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@State(Scope.Benchmark)
public class PoliticianLookupBenchmark {
@Benchmark
public void benchmarkPoliticianSearch() {
politicianService.searchByName("test");
}
}
pg_stat_statements for query analysis