venerdì 28 agosto 2026

Pipeline 100% Reattive: Gestire flussi continui con Spring WebFlux, PostgreSQL, R2DBC e Docker

Quando si progetta un'API destinata all'elaborazione di stream continui di dati, l'approccio sincrono tradizionale basato su JDBC e Spring MVC mostra rapidamente i propri limiti architetturali, portando frequentemente alla saturazione dei thread a disposizione del server. L'adozione di Spring WebFlux in sinergia con R2DBC e PostgreSQL consente di superare tale ostacolo, implementando una pipeline end-to-end asincrona e non bloccante. Questo paradigma garantisce un flusso dati ottimizzato tra database e client, riducendo drasticamente l'impatto sulla memoria di sistema.

1. Configurazione dell'Infrastruttura Reattiva

L'implementazione del supporto reattivo richiede l'inclusione delle dipendenze relative a WebFlux, Spring Data R2DBC e il driver asincrono specifico per PostgreSQL all'interno del descrittore di progetto (Maven o Gradle).

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-webflux</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-r2dbc</artifactId>
    </dependency>
    <dependency>
        <groupId>org.postgresql</groupId>
        <artifactId>r2dbc-postgresql</artifactId>
    </dependency>
</dependencies>

Nel file di configurazione application.yml, è imperativo sostituire il prefisso JDBC standard per inizializzare il connection pool reattivo. Di seguito una configurazione production-ready che include la gestione ottimizzata del pool di connessioni e il tracciamento delle query:

spring:
  r2dbc:
    url: ${DB_URL:r2dbc:postgresql://localhost:5432/telemetry_db}
    username: ${DB_USER:postgres}
    password: ${DB_PASSWORD:postgres}
    pool:
      initial-size: 10
      max-size: 50
      max-idle-time: 30m
      validation-query: "SELECT 1"
logging:
  level:
    org.springframework.r2dbc: DEBUG # Espone i log delle query reattive in fase di sviluppo

2. Modellazione del Dominio e Livello di Persistenza

Il framework Spring Data R2DBC si discosta dall'astrazione sincrona tipica di JPA e non utilizza l'Entity Manager. Il mapping oggetto-relazionale avviene tramite annotazioni native e dirette. La seguente classe rappresenta l'entità SensorMetric, strutturata sfruttando Lombok per minimizzare il codice boilerplate:

import org.springframework.data.annotation.Id;
import org.springframework.data.relational.core.mapping.Table;
import org.springframework.data.relational.core.mapping.Column;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
import java.time.LocalDateTime;

@Data
@NoArgsConstructor
@AllArgsConstructor
@Table("sensor_metrics")
public class SensorMetric {
    @Id
    private Long id;

    @Column("sensor_id")
    private String sensorId;

    private Double reading;

    @Column("recorded_at")
    private LocalDateTime timestamp;
}

Il livello di persistenza è demandato a un'interfaccia che estende ReactiveCrudRepository, esponendo i tipi reattivi nativi di Project Reactor (Flux per collezioni e Mono per risultati singoli):

import org.springframework.data.repository.reactive.ReactiveCrudRepository;
import org.springframework.stereotype.Repository;
import reactor.core.publisher.Flux;

@Repository
public interface SensorMetricRepository extends ReactiveCrudRepository<SensorMetric, Long> {
    // Il framework genera dinamicamente una query asincrona non bloccante
    Flux<SensorMetric> findBySensorId(String sensorId);
}

3. Implementazione dello Streaming nel Controller

A livello di Controller, l'impiego di TEXT_EVENT_STREAM_VALUE permette di instaurare una comunicazione Server-Sent Events (SSE) verso il client, mentre APPLICATION_NDJSON_VALUE risulta ottimale per l'ingestione efficiente di flussi continui senza caricare l'intero payload in RAM.

import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

@RestController
@RequestMapping("/api/v1/metrics")
public class MetricStreamController {

    private final SensorMetricRepository repository;

    public MetricStreamController(SensorMetricRepository repository) {
        this.repository = repository;
    }

    @GetMapping(value = "/stream/{sensorId}", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<SensorMetric> streamMetricsOut(@PathVariable String sensorId) {
        return repository.findBySensorId(sensorId);
    }

    @PostMapping(value = "/ingest", consumes = MediaType.APPLICATION_NDJSON_VALUE)
    @ResponseStatus(HttpStatus.CREATED)
    public Mono<Void> ingestMetricsIn(@RequestBody Flux<SensorMetric> metricsStream) {
        return repository.saveAll(metricsStream).then();
    }
}

L'aspetto architetturale più rilevante di questa implementazione è il meccanismo di Backpressure. Qualora il client in lettura presenti latenze operative, il server Netty notifica Project Reactor affinché adegui il rate di emissione. Tale segnale viene propagato a cascata fino al driver R2DBC, che sospenderà temporaneamente le richieste di fetch al demone PostgreSQL, prevenendo attivamente l'esaurimento della memoria heap.

4. Orchestrazione Docker e Integrazione dei Dati Spaziali (PostGIS)

Nello sviluppo di moduli software complessi, quali sistemi per l'estrazione di supply plan o l'analisi di telemetria geolocalizzata in formato GeoJSON, l'architettura reattiva dimostra un'elevata scalabilità se integrata con l'estensione PostGIS. Il driver R2DBC per PostgreSQL supporta nativamente i tipi geometrici spaziali, consentendo di elaborare ed esportare grandi volumi di coordinate senza causare il blocco dei thread applicativi.

La configurazione docker-compose.yml seguente illustra come orchestrare agevolmente l'API insieme a un'istanza database dotata di estensione spaziale preconfigurata:

version: '3.8'
services:
  postgres-postgis:
    image: postgis/postgis:15-alpine
    environment:
      POSTGRES_DB: telemetry_db
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
    ports:
      - "5432:5432"

  webflux-api:
    build: .
    ports:
      - "8080:8080"
    environment:
      DB_URL: r2dbc:postgresql://postgres-postgis:5432/telemetry_db

5. Validazione End-to-End (E2E) in Pipeline CI/CD

Il collaudo di endpoint asincroni (SSE e NDJSON) evidenzia spesso i limiti strutturali dei client REST tradizionali. L'approccio ingegneristico più solido consiste nell'implementazione di una suite di test automatizzati tramite Karate DSL. Grazie alla sua sintassi dichiarativa, Karate permette di validare flussi di dati continui ed è facilmente integrabile in pipeline di Continuous Integration (es. Jenkins).

Feature: Validazione E2E dell'API WebFlux Stream

  Background:
    * url 'http://localhost:8080/api/v1/metrics'

  Scenario: Ingestione bulk in NDJSON e verifica stream in lettura SSE
    Given path 'ingest'
    And header Content-Type = 'application/x-ndjson'
    And request '{"sensorId": "S-100", "reading": 22.5}\n{"sensorId": "S-100", "reading": 23.1}'
    When method post
    Then status 201

    Given path 'stream/S-100'
    When method get
    Then status 200
    And match header Content-Type contains 'text/event-stream'

L'adozione sinergica di queste tecnologie definisce un ecosistema software resiliente, progettato per sostenere elevati carichi transazionali e picchi di traffico massicci, pur mantenendo un ridotto e controllato consumo delle risorse infrastrutturali.

lunedì 24 agosto 2026

Domain-Driven Design e Clean Code nell'Ecosistema Spring Boot con Java 25

Nello sviluppo enterprise contemporaneo, costruire applicazioni resilienti ed evolvibili richiede l'abbandono di modelli anemici e architetture centrate sul database. L'adozione congiunta di Domain-Driven Design (DDD) e Clean Code (applicati tramite l'Architettura Esagonale o Ports & Adapters) permette di isolare il core di business dai dettagli infrastrutturali.

Con il rilascio di Java 25 (LTS), il linguaggio offre strumenti formidabili per esprimere i costrutti del DDD in modo nativo: Records, Sealed Interfaces, Pattern Matching avanzato e costruttori flessibili permettono di modellare Value Objects, Entità ed Eventi di Dominio con un livello di rigore e pulizia mai visto prima.


1. Fondamenti: Cosa Significa DDD e Clean Code?

Cos'è il Clean Code?

Formalizzato da Robert C. Martin ("Uncle Bob"), il Clean Code è codice progettato per essere compreso immediatamente da altri sviluppatori, prima ancora che dalle macchine:

  • Single Responsibility Principle (SRP): Una classe o un metodo deve avere una sola ragione per cambiare.
  • Indipendenza dai Framework: Il business core non deve dipendere da Spring, Hibernate, Jackson o protocolli di rete.
  • Espressività e Nomi Intenzionali: Il codice deve essere auto-documentante e privo di effetti collaterali oscuri.
  • Testabilità Isolata: Il core di business deve essere verificabile istantaneamente con Unit Test puri su POJO.

Cos'è il Domain-Driven Design (DDD)?

Introdotto da Eric Evans, il DDD pone la complessità del business ("il Dominio") al centro della progettazione software:

  • Ubiquitous Language (Linguaggio Ubiquo): Vocabolario condiviso tra esperti di business e sviluppatori, riflesso in modo trasparente nelle classi e nei metodi.
  • Bounded Context (Contesti Delimitati): Confini espliciti che delimitano la consistenza semantica del modello.
  • Pattern Tattici:
    • Value Objects: Oggetti immutabili definiti solo dal proprio stato/valore, senza ID (perfetti come record in Java 25).
    • Entities: Oggetti definiti dalla loro identità persistente (es. OrderId).
    • Aggregate Root: Unità atomica di consistenza che incapsula entità interne e garantisce le regole e gli invarianti di business.
    • Domain Events: Fatti immutabili accaduti nel dominio, ideali da modellare con sealed interface e record.
    • Repository Port: Astrazione pura Java che simula una collezione in memoria per salvare/caricare l'aggregato.

2. Il Problema: Anemic Domain Model vs Rich Domain Model

Nelle applicazioni Spring tradizionali, è comune incontrare l'anti-pattern dell'Anemic Domain Model, dove le entità sono meri contenitori di dati (getter/setter) e la logica è sparpagliata in classi @Service gigantesche. Ecco come cambia il paradigma con il Rich Domain Model:

Caratteristica Anemic Model (Anti-Pattern) Rich Domain Model (DDD + Java 25)
Invarianti di Business Violati facilmente da setter indiscriminati. Protetti dentro l'Aggregate Root e Value Objects.
Posizione della Logica Dispersa in grossi @Service procedurali. Incapsulata in Entità, Value Objects e Metodi di Business.
Accoppiamento Framework @Entity, @Table, annotazioni Jackson nel core. POJO e Record puri, zero annotazioni esterne.
Velocità dei Test Test lenti con contesti Spring o mock complessi. Test unitari ultra-rapidi eseguiti in pochi millisecondi.

3. Struttura del Bounded Context (Architettura Esagonale)

L'Architettura Esagonale (Ports & Adapters) permette di mantenere il Dominio al centro, completamente isolato.

com.example.ecommerce.order
 ├── domain                     <-- CORE POJO/RECORD (Zero Spring/JPA)
 │    ├── model                 <-- Order (Aggregate), OrderId, Money, OrderLine
 │    ├── event                 <-- OrderDomainEvent (Sealed Interface)
 │    ├── exception             <-- OrderDomainException
 │    └── repository            <-- OrderRepository (Output Port)
 ├── application                <-- CASI D'USO
 │    ├── dto                   <-- CreateOrderCommand, OrderResponseDto
 │    └── service               <-- CreateOrderUseCaseService
 └── infrastructure             <-- DETTAGLI TECNICI E ADAPTERS
      ├── persistence           <-- OrderJpaEntity, SpringDataOrderRepository
      │    ├── adapter          <-- OrderRepositoryJpaAdapter 
      │    └── mapper           <-- OrderPersistenceMapper (MapStruct)
      └── rest                  <-- OrderRestController (Web Adapter)

4. Configurazione Maven: pom.xml per Java 25

Per supportare le ultime feature, configuriamo Spring Boot 3.5+, MapStruct e Testcontainers per Java 25:

<!-- dependencies essenziali nel pom.xml -->
<properties>
    <java.version>25</java.version>
    <org.mapstruct.version>1.6.3</org.mapstruct.version>
</properties>

<dependencies>
    <!-- Spring Boot Web, Data JPA, Validation -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- MapStruct per il mapping pulito -->
    <dependency>
        <groupId>org.mapstruct</groupId>
        <artifactId>mapstruct</artifactId>
        <version>${org.mapstruct.version}</version>
    </dependency>
</dependencies>

5. Implementazione del Dominio (Java 25)

5.1 Value Objects Immutabili (Records)

I record di Java sono la traduzione naturale dei Value Objects del DDD: garantiscono immutabilità, comparazione per valore e permettono validazioni compatte.

package com.example.ecommerce.order.domain.model;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Currency;

public record Money(BigDecimal amount, Currency currency) {

    public static final Currency EUR = Currency.getInstance("EUR");

    // Compact constructor per le invarianti
    public Money {
        if (amount == null || amount.compareTo(BigDecimal.ZERO) < 0) {
            throw new IllegalArgumentException("L'importo non può essere negativo");
        }
        if (currency == null) {
            throw new IllegalArgumentException("La valuta è obbligatoria");
        }
        amount = amount.setScale(2, RoundingMode.HALF_UP);
    }

    public static Money ofEuros(double value) {
        return new Money(BigDecimal.valueOf(value), EUR);
    }

    public static Money zero(Currency currency) {
        return new Money(BigDecimal.ZERO, currency);
    }

    public Money add(Money other) {
        if (!this.currency.equals(other.currency)) {
            throw new IllegalArgumentException("Valute incompatibili");
        }
        return new Money(this.amount.add(other.amount), this.currency);
    }

    public Money multiply(int quantity) {
        return new Money(this.amount.multiply(BigDecimal.valueOf(quantity)), this.currency);
    }
}

Gli identificatori fortemente tipizzati prevengono errori comuni (es. scambiare un OrderId con un CustomerId):

public record OrderId(java.util.UUID value) {
    public OrderId {
        if (value == null) throw new IllegalArgumentException("OrderId non può essere nullo");
    }
    public static OrderId generate() { return new OrderId(java.util.UUID.randomUUID()); }
}

5.2 Domain Events con Sealed Interfaces

Le Sealed Interfaces permettono di creare gerarchie chiuse, perfette per garantire un pattern matching esaustivo in fase di compilazione.

package com.example.ecommerce.order.domain.event;

import com.example.ecommerce.order.domain.model.OrderId;
import com.example.ecommerce.order.domain.model.Money;
import java.time.Instant;
import java.util.UUID;

public sealed interface OrderDomainEvent {
    OrderId orderId();
    Instant occurredOn();

    record OrderCreatedEvent(OrderId orderId, UUID customerId, Instant occurredOn) implements OrderDomainEvent {}
    record OrderPaidEvent(OrderId orderId, Money totalPaid, Instant occurredOn) implements OrderDomainEvent {}
    record OrderCancelledEvent(OrderId orderId, String reason, Instant occurredOn) implements OrderDomainEvent {}
}

5.3 L'Aggregate Root: Il Cuore del Dominio

L'Order non ha annotazioni JPA. Espone solo metodi di business espliciti (ubiquitous language) e non espone setter per alterare illegalmente il suo stato interno.

package com.example.ecommerce.order.domain.model;

import com.example.ecommerce.order.domain.event.OrderDomainEvent;
import com.example.ecommerce.order.domain.event.OrderDomainEvent.*;
import java.time.Instant;
import java.util.*;

public class Order {
    private final OrderId id;
    private final UUID customerId;
    private final List<OrderLine> lines;
    private OrderStatus status;
    private final List<OrderDomainEvent> domainEvents = new ArrayList<>();

    public Order(OrderId id, UUID customerId) {
        if (customerId == null) throw new IllegalArgumentException("CustomerId obbligatorio");
        this.id = Objects.requireNonNull(id, "OrderId obbligatorio");
        this.customerId = customerId;
        this.lines = new ArrayList<>();
        this.status = OrderStatus.CREATED;

        recordEvent(new OrderCreatedEvent(this.id, this.customerId, Instant.now()));
    }

    public void addProduct(UUID productId, Money unitPrice, int quantity) {
        if (this.status != OrderStatus.CREATED) {
            throw new IllegalStateException("Impossibile modificare un ordine nello stato " + this.status);
        }
        this.lines.add(new OrderLine(productId, unitPrice, quantity));
    }

    public Money calculateTotal() {
        if (lines.isEmpty()) return Money.zero(Money.EUR);
        return lines.stream()
                .map(OrderLine::calculateSubtotal)
                .reduce(Money.zero(Money.EUR), Money::add);
    }

    public void markAsPaid() {
        if (this.status != OrderStatus.CREATED) {
            throw new IllegalStateException("Solo gli ordini CREATED possono essere pagati");
        }
        if (this.lines.isEmpty()) {
            throw new IllegalStateException("Impossibile saldare un ordine privo di articoli");
        }
        this.status = OrderStatus.PAID;
        recordEvent(new OrderPaidEvent(this.id, calculateTotal(), Instant.now()));
    }

    private void recordEvent(OrderDomainEvent event) {
        this.domainEvents.add(event);
    }

    public List<OrderDomainEvent> pullDomainEvents() {
        var events = List.copyOf(domainEvents);
        domainEvents.clear();
        return events;
    }

    // Solo Getter (stato in sola lettura, collezioni non modificabili)
    public OrderId getId() { return id; }
    public OrderStatus getStatus() { return status; }
    public List<OrderLine> getLines() { return Collections.unmodifiableList(lines); }
}

6. L'Application Layer: Use Cases e Pattern Matching

Il servizio applicativo orchestra il recupero, la mutazione e il salvataggio. Usiamo l'operatore switch potenziato di Java 25 per dispacciare gli eventi in modo type-safe ed esaustivo.

package com.example.ecommerce.order.application.service;

import com.example.ecommerce.order.domain.event.OrderDomainEvent;
import com.example.ecommerce.order.domain.event.OrderDomainEvent.*;
import com.example.ecommerce.order.domain.model.*;
import com.example.ecommerce.order.domain.repository.OrderRepository;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@Transactional
public class CreateOrderUseCaseService {

    private final OrderRepository orderRepository;
    private final ApplicationEventPublisher eventPublisher;

    public CreateOrderUseCaseService(OrderRepository orderRepository, ApplicationEventPublisher eventPublisher) {
        this.orderRepository = orderRepository;
        this.eventPublisher = eventPublisher;
    }

    public OrderResponseDto handle(CreateOrderCommand command) {
        Order order = new Order(OrderId.generate(), command.customerId());

        command.items().forEach(item -> 
            order.addProduct(item.productId(), Money.ofEuros(item.price()), item.quantity())
        );

        Order savedOrder = orderRepository.save(order);

        // Dispatching eventi con Pattern Matching
        savedOrder.pullDomainEvents().forEach(this::dispatchDomainEvent);

        return new OrderResponseDto(savedOrder.getId().value(), savedOrder.getStatus().name());
    }

    private void dispatchDomainEvent(OrderDomainEvent event) {
        // Switch esaustivo su sealed interface (Java 25)
        switch (event) {
            case OrderCreatedEvent created -> eventPublisher.publishEvent(created);
            case OrderPaidEvent paid       -> eventPublisher.publishEvent(paid);
            case OrderCancelledEvent canc  -> eventPublisher.publishEvent(canc);
        }
    }
}

7. L'Infrastructure Layer: L'Adapter JPA

L'infrastruttura implementa il repository (Port) del Dominio, mappando il POJO Order in un'entità JPA (OrderJpaEntity) tramite MapStruct.

@Component
public class OrderRepositoryJpaAdapter implements OrderRepository {

    private final SpringDataOrderRepository springDataRepository;
    private final OrderPersistenceMapper mapper; // Interfaccia MapStruct

    public OrderRepositoryJpaAdapter(SpringDataOrderRepository springRepo, OrderPersistenceMapper mapper) {
        this.springDataRepository = springRepo;
        this.mapper = mapper;
    }

    @Override
    public Order save(Order order) {
        // Dominio -> JPA
        var entity = mapper.toJpaEntity(order);
        var saved = springDataRepository.save(entity);
        // JPA -> Dominio
        return mapper.toDomainEntity(saved);
    }
}

8. Il Superpotere: Testabilità Estrema

Grazie all'isolamento architetturale, i test di business vengono eseguiti senza mock complessi e senza avviare il contesto Spring. Sono test rapidissimi, eseguiti in millisecondi.

class OrderTest {
    @Test
    void shouldCalculateTotalAndMarkAsPaid() {
        // Arrange
        Order order = new Order(OrderId.generate(), UUID.randomUUID());
        order.addProduct(UUID.randomUUID(), Money.ofEuros(49.90), 2);
        
        // Act
        order.markAsPaid();
        
        // Assert
        assertEquals(Money.ofEuros(99.80), order.calculateTotal());
        assertEquals(OrderStatus.PAID, order.getStatus());
        
        var events = order.pullDomainEvents();
        assertTrue(events.stream().anyMatch(e -> e instanceof OrderDomainEvent.OrderPaidEvent));
    }
}

Conclusione

L'integrazione di Java 25, Domain-Driven Design e Clean Code in un'Architettura Esagonale risolve i limiti storici dello sviluppo enterprise monolitico:

  1. Isolamento Totale: Il dominio non conosce i database o il web. Potresti passare da REST a gRPC, o da PostgreSQL a MongoDB, e le classi core non cambierebbero di una virgola.
  2. Sintassi Moderna: Records e Sealed Interfaces rimuovono tonnellate di boilerplate, rendendo il codice elegante e strettamente tipizzato.
  3. Qualità Assicurata: Le regole di business sono centralizzate nell'Aggregate Root e facilmente testabili, azzerando le probabilità di corruzione dello stato interno.

Iniziare un progetto con questo grado di disaccoppiamento richiede un piccolo investimento iniziale in mapping (tramite MapStruct), ma il ritorno in termini di leggibilità, flessibilità e stabilità è incalcolabile.

martedì 27 aprile 2021

Spring - JPA - Monitor Log Slow Query

 

Per eseguire un monitoraggio delle tempistiche di esecuzione delle query è possibile creare un file di log che contenga i tempi in modo da verificare l'eventuale aggiunta di indici.

Definire le tempistiche di monitoraggio di esecuzione della query nel file di properties:

spring.jpa.properties.hibernate.session.events.log.LOG_QUERIES_SLOWER_THAN_MS=50



Aggiungere il logger nel file logback-spring.xml

<!-- query time debug -->

<appender name="query" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>queryLog.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>queryLog.log.%d{yyyy-MM-dd}.%i.gz</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>50MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<maxHistory>3</maxHistory>
</rollingPolicy>
<encoder>
<pattern>%m%n</pattern>
</encoder>
</appender>


<logger name="org.hibernate.SQL_SLOW" level="INFO">
<appender-ref ref="query"/>
</logger>

il file generato contiene:

SlowQuery: 297 milliseconds. SQL: 'HikariProxyPreparedStatement@984805610 wrapping com.mysql.cj.jdbc.ClientPreparedStatement: select a.id as col_0_0_ from abc a where a.id > 10'


venerdì 26 febbraio 2021

HAProxy - Halog


HaProxy possiede un suo log analyzer che si chiama halog

halog -srv -ut -H < haproxy.log | column -t
halog -u -H -q < haproxy.log | column -t | more
halog -u -H -q -hs 404 < haproxy.log | column -t | more

viene estratta questa lista dove vengono evidenziate gli errori le chiamate etc per ogni occorrenza.


domenica 13 dicembre 2020

InfluxDB

 Qualche esperimento veloce con un db time series come Influx DB. Questi db sono specifici per utilizzare dati temporali ed è fornito di alcuni strumenti molto comodi di visualizzazione come Chronograf

Per prima cosa ho installato su una vmware una debian 8, successivamente ho installato influxDB e Chronograf dalla repo seguendo le info sul sito.

Como dataset ho utilizzato il db del meteo sono circa 3M di righe. Importando con un semplice programmino in java.

A questo punto ho iniziato a fare qualche esperimento con Chronograf ottenendo delle visualizzazioni molto esplicative con pochi click.





lunedì 22 luglio 2019

SqlServer - conflict between "Latin1_General_CI_AS" and "SQL_Latin1_General_CP1_CI_AS"



Capita spesso di avere problemi di collate sui db SqlServer quando si utilizza Hibernate. Soprattuto quando cambia la codifica del db o la versione dello stesso:


Caused by: com.microsoft.sqlserver.jdbc.SQLServerException: Cannot resolve the collation conflict between "Latin1_General_CI_AS" and "SQL_Latin1_General_CP1_CI_AS" in the equal to operation.

SELECT SERVERPROPERTY('EditionId') AS EditionId
GO
use master
GO
ALTER DATABASE [dbaname] SET SINGLE_USER WITH ROLLBACK IMMEDIATE
GO
GO
use [dbaname];
GO
USE [master]
GO
ALTER DATABASE [dbaname] COLLATE Latin1_General_CI_AS
GO
ALTER DATABASE [dbaname] SET MULTI_USER
GO

un query che genera uno script di alter potrebbe essere di aiuto per eseguire la modifica sul singolo campo [via]:


SELECT 'ALTER TABLE [' + SYSOBJECTS.Name + '] ALTER COLUMN [' + SYSCOLUMNS.Name + '] ' +
SYSTYPES.name + 
    CASE systypes.NAME
    WHEN 'text' THEN ' '
    ELSE
    '(' + RTRIM(CASE SYSCOLUMNS.length
    WHEN -1 THEN 'MAX'
    ELSE CONVERT(CHAR,SYSCOLUMNS.length)
    END) + ') ' 
    END

    + ' ' + ' COLLATE Latin1_General_CI_AS ' + CASE ISNULLABLE WHEN 0 THEN 'NOT NULL' ELSE 'NULL' END
    FROM SYSCOLUMNS , SYSOBJECTS , SYSTYPES
    WHERE SYSCOLUMNS.ID = SYSOBJECTS.ID
    AND SYSOBJECTS.TYPE = 'U'
    AND SYSTYPES.Xtype = SYSCOLUMNS.xtype
    AND SYSCOLUMNS.COLLATION IS NOT NULL
    AND NOT ( sysobjects.NAME LIKE 'sys%' )
    AND NOT ( SYSTYPES.name LIKE 'sys%' )
    GO

giovedì 13 giugno 2019

SqlServer - Verificare lo stato degli indici

Query di comodità per verificare lo stato di deframmentazione degli indici.

L'ultima colonna contiene il comando di rebuild dell'indice.