Datenbank Calls Asyn gemacht. Problem: OOP Style für Context Passthrough benutzt. Dazu sterben meine Goroutinen nicht bei STRG+C. Scheiße!

This commit is contained in:
Marco Kittel 2025-07-19 16:44:37 +02:00
parent fee5dd1474
commit 9732096f53
4 changed files with 73 additions and 28 deletions

View File

@ -1,12 +1,15 @@
package main
import (
"context"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"os"
"os/signal"
"syscall"
"github.com/google/uuid"
"gittea.marcokittel.de/elio/eliotools/datawriter/internal/api"
@ -46,9 +49,17 @@ const (
func main() {
connectionString := os.Getenv("CONNECTIONSTRING")
nps := database.NewProductService(connectionString)
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer cancel()
// Brauche Context für Asyncrone Calls. Darum nutze ich DI. Ist ein Golang Antipattern :-(
// Muss noch mehr Lernen um idiomatisch zu werden
nps := database.NewProductService(ctx, connectionString)
//Hier prüfe ich nach alten Registrierungen und gebe Sie frei.
nps.Autorelease()
if len(connectionString) == 0 {
fmt.Println("Connectionstring fehlt!. Bsp.: <user>:<passwort>@tcp(127.0.0.1:3306)/elio?parseTime=true")
return
@ -184,4 +195,5 @@ func main() {
log.Printf("Easy Peasy: Die Party startet auf Port %s\n", port)
log.Printf("Probiers mal damit: %s\n", curlhelp)
log.Fatal(http.ListenAndServe(port, nil))
}

View File

@ -18,6 +18,7 @@ import (
type DatabaseWriter struct {
log logger.Logger
db *sql.DB
ctx context.Context
}
type DatabaseReader struct {
@ -26,8 +27,9 @@ type DatabaseReader struct {
type UUID string
func NewDatabaseReader(connectionString string) *DatabaseReader {
return &DatabaseReader{*NewDatabaseWriter(connectionString)}
// Das ist ein Golang Design Flaw... Context so zu übergeben, bitte nicht hauen
func NewDatabaseReader(ctx context.Context, connectionString string) *DatabaseReader {
return &DatabaseReader{*NewDatabaseWriter(ctx, connectionString)}
}
func (d *DatabaseWriter) connectDB(connectionString string) (*sql.DB, error) {
@ -102,8 +104,9 @@ func (d *DatabaseWriter) createReservationTableIfNotExist() error {
return err
}
func NewDatabaseWriter(connectionString string) *DatabaseWriter {
db := DatabaseWriter{log: logger.NewMarcoLogger()}
// Brauche einen Context für Asyncrone Datenbank-Abfragen. Design Flaw by me :-()
func NewDatabaseWriter(ctx context.Context, connectionString string) *DatabaseWriter {
db := DatabaseWriter{log: logger.NewMarcoLogger(), ctx: ctx}
sql, err := db.connectDB(connectionString)
if err != nil {
fmt.Printf("Datenbank nicht gefunden. %s", err)
@ -139,7 +142,9 @@ func (d *DatabaseWriter) ReleaseReservierungenAfterOneDay() error {
}
func (d *DatabaseWriter) UpdateOrInsertWarehouseProduct(warehouse string, productID string, amount int) error {
_, err := d.db.Exec(`
ctx, cancel := context.WithTimeout(d.ctx, 100*time.Millisecond)
defer cancel()
_, err := d.db.ExecContext(ctx, `
INSERT INTO warehouseproducts (warehouse, productid, amount)
VALUES (?, ?, ?)
ON DUPLICATE KEY UPDATE amount = VALUES(amount)`,
@ -152,7 +157,9 @@ func (d *DatabaseWriter) UpdateOrInsertWarehouseProduct(warehouse string, produc
}
func (d *DatabaseWriter) UpdateOrInsertDelivery(fromcountry string, tocountry string, state string, delivery int) error {
_, err := d.db.Exec(`
ctx, cancel := context.WithTimeout(d.ctx, 100*time.Millisecond)
defer cancel()
_, err := d.db.ExecContext(ctx, `
INSERT INTO deliverytimes (fromcountry, tocountry, state, delivery)
VALUES (?, ?, ?, ?)
ON DUPLICATE KEY UPDATE
@ -177,6 +184,8 @@ type ProductDelivery struct {
}
func (d *DatabaseReader) GetProductByProductIdDeliveryCountryAndState(prod_id, delivery_country, delivery_country_state string) ([]ProductDelivery, error) {
ctx, cancel := context.WithTimeout(d.ctx, 100*time.Millisecond)
defer cancel()
stmt := `
SELECT id, warehouse, (amount - reserviert) amount, delivery, deliveryId FROM(
SELECT whp.id, warehouse, whp.amount, sum(coalesce(r.amount,0) )reserviert, d.delivery, d.id deliveryId
@ -195,7 +204,7 @@ func (d *DatabaseReader) GetProductByProductIdDeliveryCountryAndState(prod_id, d
having amount > 0
`
rows, err := d.db.Query(stmt, prod_id, delivery_country, delivery_country_state)
rows, err := d.db.QueryContext(ctx, stmt, prod_id, delivery_country, delivery_country_state)
if err != nil {
return nil, err
}
@ -212,10 +221,12 @@ func (d *DatabaseReader) GetProductByProductIdDeliveryCountryAndState(prod_id, d
}
func (d *DatabaseReader) GetReservationStateById(productId UUID) (string, error) {
ctx, cancel := context.WithTimeout(d.ctx, 100*time.Millisecond)
defer cancel()
stmt := `
SELECT status FROM reservations WHERE id = ?
`
row, err := d.db.Query(stmt, productId)
row, err := d.db.QueryContext(ctx, stmt, productId)
if err != nil {
return "", err
}
@ -230,10 +241,12 @@ func (d *DatabaseReader) GetReservationStateById(productId UUID) (string, error)
}
func (d *DatabaseReader) GetReservationItemsByGroupId(groupId UUID) (string, error) {
ctx, cancel := context.WithTimeout(d.ctx, 100*time.Millisecond)
defer cancel()
stmt := `
SELECT status FROM reservations WHERE id = ?
`
row, err := d.db.Query(stmt, groupId)
row, err := d.db.QueryContext(ctx, stmt, groupId)
if err != nil {
return "", err
}
@ -248,7 +261,9 @@ func (d *DatabaseReader) GetReservationItemsByGroupId(groupId UUID) (string, err
}
func (d *DatabaseWriter) updateReservationState(Id UUID, status string) error {
_, err := d.db.Exec("UPDATE reservations SET Status=? WHERE id=?", status, Id)
ctx, cancel := context.WithTimeout(d.ctx, 100*time.Millisecond)
defer cancel()
_, err := d.db.ExecContext(ctx, "UPDATE reservations SET Status=? WHERE id=?", status, Id)
if err != nil {
return err
}
@ -256,7 +271,9 @@ func (d *DatabaseWriter) updateReservationState(Id UUID, status string) error {
}
func (d *DatabaseWriter) updateReservationStateByGroupId(groupId UUID, status string) error {
_, err := d.db.Exec("UPDATE reservations SET Status=? WHERE reservationGroupId=?", status, groupId)
ctx, cancel := context.WithTimeout(d.ctx, 100*time.Millisecond)
defer cancel()
_, err := d.db.ExecContext(ctx, "UPDATE reservations SET Status=? WHERE reservationGroupId=?", status, groupId)
if err != nil {
return err
}

View File

@ -1,6 +1,7 @@
package database
import (
"context"
"encoding/json"
"fmt"
"log"
@ -82,7 +83,8 @@ func Setup(dbr *DatabaseReader) error {
}
func TestDatabaseConnection(t *testing.T) {
var dbr = NewDatabaseReader(connectionString)
ctx := context.Background()
var dbr = NewDatabaseReader(ctx, connectionString)
err := dbr.db.Ping()
if err != nil {
t.Errorf("Datenbankverbindung fehlgeschlagen! %s", err)
@ -90,7 +92,8 @@ func TestDatabaseConnection(t *testing.T) {
}
func TestSelectStatementNoDatasetsAvailable(t *testing.T) {
var dbr = NewDatabaseReader(connectionString)
ctx := context.Background()
var dbr = NewDatabaseReader(ctx, connectionString)
err := Setup(dbr)
if err != nil {
t.Errorf("Setup failed! %s", err)
@ -102,7 +105,8 @@ func TestSelectStatementNoDatasetsAvailable(t *testing.T) {
}
func TestSelectStatement(t *testing.T) {
var dbr = NewDatabaseReader(connectionString)
ctx := context.Background()
var dbr = NewDatabaseReader(ctx, connectionString)
data, err := dbr.GetProductByProductIdDeliveryCountryAndState("A6053", "EU", "")
if err != nil {
t.Errorf("Es sollten Datensätze auffinbdar sein! %s", err)
@ -121,7 +125,8 @@ func TestSelectStatement(t *testing.T) {
}
func TestInsertStatement(t *testing.T) {
var dbr = NewDatabaseReader(connectionString)
ctx := context.Background()
var dbr = NewDatabaseReader(ctx, connectionString)
data, err := dbr.GetProductByProductIdDeliveryCountryAndState("A6053", "EU", "")
if err != nil {
t.Errorf("Es sollten Datensätze auffinbdar sein! %s", err)
@ -140,7 +145,8 @@ func TestInsertStatement(t *testing.T) {
}
func TestAbortReservationStatement(t *testing.T) {
var dbr = NewDatabaseReader(connectionString)
ctx := context.Background()
var dbr = NewDatabaseReader(ctx, connectionString)
data, err := dbr.GetProductByProductIdDeliveryCountryAndState("A6053", "EU", "")
if err != nil {
t.Errorf("Es sollten Datensätze auffinbdar sein! %s", err)
@ -166,7 +172,8 @@ func TestAbortReservationStatement(t *testing.T) {
}
func TestConfirmReservationStatement(t *testing.T) {
var dbr = NewDatabaseReader(connectionString)
ctx := context.Background()
var dbr = NewDatabaseReader(ctx, connectionString)
data, err := dbr.GetProductByProductIdDeliveryCountryAndState("A6053", "EU", "")
if err != nil {
t.Errorf("Es sollten Datensätze auffinbdar sein! %s", err)
@ -192,7 +199,8 @@ func TestConfirmReservationStatement(t *testing.T) {
}
func TestReleasedReservationStatement(t *testing.T) {
var dbr = NewDatabaseReader(connectionString)
ctx := context.Background()
var dbr = NewDatabaseReader(ctx, connectionString)
data, err := dbr.GetProductByProductIdDeliveryCountryAndState("A6053", "EU", "")
if err != nil {
t.Errorf("Es sollten Datensätze auffinbdar sein! %s", err)
@ -225,7 +233,8 @@ func TestFetchReservationData(t *testing.T) {
t.Errorf("Dieser Fehler beim Konvertieren der JSON Payload sollte nicht passieren! \n%s", err)
return
}
ps := NewProductService(connectionString)
ctx := context.Background()
ps := NewProductService(ctx, connectionString)
// groupId := uuid.New().String()
op, err := ps.FetchData(&payload)
log.Println(op)
@ -240,7 +249,8 @@ func TestFetchDataReservation(t *testing.T) {
t.Errorf("Dieser Fehler beim Konvertieren der JSON Payload sollte nicht passieren! \n%s", err)
return
}
ps := NewProductService(connectionString)
ctx := context.Background()
ps := NewProductService(ctx, connectionString)
groupId := uuid.New().String()
op, err := ps.FetchReservationData(&payload, UUID(groupId))
if err != nil {
@ -261,7 +271,8 @@ func TestFetchDataReservationVarition2(t *testing.T) {
t.Errorf("Dieser Fehler beim Konvertieren der JSON Payload sollte nicht passieren! \n%s", err)
return
}
ps := NewProductService(connectionString)
ctx := context.Background()
ps := NewProductService(ctx, connectionString)
op, err := ps.FetchData(&payload)
if err != nil {
t.Errorf("Das Datafetchen und umwandeln in eine Map muss sauber funktionieren! %s", err)
@ -281,7 +292,8 @@ func TestFetchDataReservationVarition3(t *testing.T) {
t.Errorf("Dieser Fehler beim Konvertieren der JSON Payload sollte nicht passieren! \n%s", err)
return
}
ps := NewProductService(connectionString)
ctx := context.Background()
ps := NewProductService(ctx, connectionString)
op, err := ps.FetchData(&payload)
if err != nil {
t.Errorf("Das Datafetchen und umwandeln in eine Map muss sauber funktionieren! %s", err)
@ -301,7 +313,8 @@ func TestOutgoingJsonString(t *testing.T) {
t.Errorf("Dieser Fehler beim Konvertieren der JSON Payload sollte nicht passieren! \n%s", err)
return
}
ps := NewProductService(connectionString)
ctx := context.Background()
ps := NewProductService(ctx, connectionString)
op, err := ps.FetchData(&payload)
if err != nil {
t.Errorf("Das Datafetchen und umwandeln in eine Map muss sauber funktionieren! %s", err)
@ -320,7 +333,8 @@ func TestOutgoingJsonString(t *testing.T) {
func TestOutgoingReservationInDatabase(t *testing.T) {
//Löscht die Tabellen und erzeugt die Testdaten neu.
//Das Verbessert den Überblick
var dbr = NewDatabaseReader(connectionString)
ctx := context.Background()
var dbr = NewDatabaseReader(ctx, connectionString)
err := Setup(dbr)
if err != nil {
@ -335,7 +349,7 @@ func TestOutgoingReservationInDatabase(t *testing.T) {
return
}
groupId := uuid.New().String()
ps := NewProductService(connectionString)
ps := NewProductService(ctx, connectionString)
op, err := ps.FetchReservationData(&payload, UUID(groupId))
if err != nil {
t.Errorf("Das Datafetchen und umwandeln in eine Map muss sauber funktionieren! %s", err)

View File

@ -4,6 +4,8 @@ import (
"fmt"
"log"
"time"
"golang.org/x/net/context"
)
type ProductService struct {
@ -49,8 +51,8 @@ type Context struct {
State string `json:"state"`
}
func NewProductService(connectionString string) *ProductService {
dbr := NewDatabaseReader(connectionString)
func NewProductService(ctx context.Context, connectionString string) *ProductService {
dbr := NewDatabaseReader(ctx, connectionString)
p := ProductService{dbr: dbr}
return &p
}