snapshot of https://github.com/ThreeDotsLabs/watermill.git (master, 2026-09-15; blobs > 131072B omitted)
Some checks failed
master / ci (push) Failing after 0s
Some checks failed
master / ci (push) Failing after 0s
This commit is contained in:
@@ -0,0 +1,148 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
stdSQL "database/sql"
|
||||
"encoding/json"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"github.com/ThreeDotsLabs/watermill/message"
|
||||
driver "github.com/go-sql-driver/mysql"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/ThreeDotsLabs/watermill"
|
||||
"github.com/ThreeDotsLabs/watermill-sql/v4/pkg/sql"
|
||||
)
|
||||
|
||||
const topic = "counter"
|
||||
|
||||
func main() {
|
||||
db := createDB()
|
||||
logger := watermill.NewStdLogger(false, false)
|
||||
|
||||
go runWatermillRouter(db, logger)
|
||||
|
||||
sigs := make(chan os.Signal, 1)
|
||||
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
|
||||
|
||||
// no graceful shutdown, to increase chance of problems :-)
|
||||
<-sigs
|
||||
}
|
||||
|
||||
type messagePayload struct {
|
||||
CounterUUID string `json:"counter_uuid"`
|
||||
}
|
||||
|
||||
func runWatermillRouter(db *stdSQL.DB, logger watermill.LoggerAdapter) {
|
||||
subscriber, err := sql.NewSubscriber(
|
||||
sql.BeginnerFromStdSQL(db),
|
||||
sql.SubscriberConfig{
|
||||
SchemaAdapter: sql.DefaultMySQLSchema{},
|
||||
OffsetsAdapter: sql.DefaultMySQLOffsetsAdapter{},
|
||||
InitializeSchema: true,
|
||||
},
|
||||
logger,
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
router, err := message.NewRouter(message.RouterConfig{}, logger)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
router.AddConsumerHandler(
|
||||
"counter",
|
||||
topic,
|
||||
subscriber,
|
||||
processMessage,
|
||||
)
|
||||
|
||||
if err := router.Run(context.Background()); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func processMessage(msg *message.Message) error {
|
||||
tx, ok := sql.TxFromContext(msg.Context())
|
||||
if !ok {
|
||||
return errors.New("tx not found in message context")
|
||||
}
|
||||
|
||||
payload := messagePayload{}
|
||||
err := json.Unmarshal(msg.Payload, &payload)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "unable to unmarshal payload")
|
||||
}
|
||||
|
||||
// let's do it more fragile, let's get the value from DB instead of simple increment
|
||||
counterValue, err := dbCounterValue(msg.Context(), tx, payload.CounterUUID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
counterValue += 1
|
||||
|
||||
if err := updateDbCounter(msg.Context(), tx, payload.CounterUUID, counterValue); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func updateDbCounter(ctx context.Context, tx sql.Tx, counterUUD string, counterValue int) error {
|
||||
_, err := tx.ExecContext(
|
||||
ctx,
|
||||
"INSERT INTO counter (id, value) VALUES (?, ?) ON DUPLICATE KEY UPDATE value = ?",
|
||||
counterUUD,
|
||||
counterValue,
|
||||
counterValue,
|
||||
)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "can't update counter value")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func dbCounterValue(ctx context.Context, tx sql.Tx, counterUUID string) (int, error) {
|
||||
var counterValue int
|
||||
rows, err := tx.QueryContext(ctx, "SELECT value from counter WHERE id = ?", counterUUID)
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "can't get counter value")
|
||||
}
|
||||
|
||||
if !rows.Next() {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
err = rows.Scan(&counterValue)
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "can't get counter value")
|
||||
}
|
||||
|
||||
return counterValue, nil
|
||||
}
|
||||
|
||||
func createDB() *stdSQL.DB {
|
||||
conf := driver.NewConfig()
|
||||
conf.Net = "tcp"
|
||||
conf.User = "root"
|
||||
conf.Addr = "mysql"
|
||||
conf.DBName = "example"
|
||||
|
||||
db, err := stdSQL.Open("mysql", conf.FormatDSN())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = db.Ping()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return db
|
||||
}
|
||||
Reference in New Issue
Block a user