# Batteries

Source: https://gortexa.sheng.page/en/components/batteries/

Four optional infrastructure packages: `mq` for NATS/JetStream messaging, `cache` (in-memory by default, redis opt-in), `storage` for a PgBouncer-safe pgx pool, and `client` for OTel-instrumented outbound calls. Each is importable on its own; what you do not use is not wired in.

Fetch the module first: `go get github.com/yshengliao/gortexa@v0.28.3`. Every snippet below is compile-verified against v0.28.3.

## mq

NATS-backed pluggable pub/sub. `cfg.MQ.Driver` selects `nats` (core, at-most-once) or `jetstream` (durable, at-least-once, handlers must be idempotent). An empty `group_id` fans out; a non-empty one load-balances.

```go
import "github.com/yshengliao/gortexa/mq"

pub, sub, err := mq.New(cfg.MQ)
if err != nil {
	return err
}
defer pub.Close(context.Background()) // ctx is a shutdown budget: on expiry it returns early and the teardown finishes in the background
err = sub.Subscribe(ctx, "orders", func(ctx context.Context, m mq.Message) error {
	return handle(m.Value)
})
err = pub.Publish(ctx, "orders", mq.Message{Value: []byte("hello")})
```

`Message` carries `Key` / `Value` / `Headers`. Two header names are reserved on every driver and rejected at Publish with `InvalidArgument`: `gortexa-key`, and anything in the broker's case-insensitive `nats-*` namespace (`Nats-Msg-Id`, `Nats-Expected-*`, `Nats-Rollup`, …). You can also call `mq.NewNATS` / `mq.NewJetStream` to pick a driver directly.

## cache

Process-local in-memory by default; `cfg.Cache.Driver: redis` opts into a distributed backend.

```go
import (
	"errors"
	"github.com/yshengliao/gortexa/cache"
)

c, err := cache.New(cfg.Cache)
if err != nil {
	return err
}
defer c.Close()
if err := c.Set(ctx, "k", []byte("v"), time.Minute); err != nil {
	return err
}
b, err := c.Get(ctx, "k")
if errors.Is(err, cache.ErrCacheMiss) {
	// key absent
}
```

## storage

Builds a PgBouncer-safe pgx connection pool plus a query tracer that emits OTel DB spans. It returns a `*pgxpool.Pool`.

```go
import (
	"go.opentelemetry.io/otel"
	"github.com/yshengliao/gortexa/storage"
)

tracer := storage.NewDBTracer(otel.GetTracerProvider())
pool, err := storage.NewPool(ctx, cfg.DB, tracer) // pass nil for no spans
if err != nil {
	return err
}
defer pool.Close()
```

## client

Outbound client constructors with OTel instrumentation already wired in.

```go
import "github.com/yshengliao/gortexa/client"

conn, err := client.NewGRPCConn(client.GRPCClientConfig{Target: "svc:50051", Insecure: true})
if err != nil {
	return err
}
defer conn.Close()

hc := client.NewHTTPClient(client.HTTPClientConfig{Timeout: 5 * time.Second}) // zero defaults to 30s
```

`Insecure: true` is for local development only; use TLS in production.

## Related

- [Configuration](https://gortexa.sheng.page/en/configuration/): the config keys and semantics for cache, MQ and storage
- [Core](https://gortexa.sheng.page/en/components/core/): how these are assembled into the kernel
- [Full example](https://gortexa.sheng.page/en/examples/full-example/): one `main.go` wiring everything together
