chore: setup boilerplate for particle processor worker

This sets up an example of listening to firestore data and reacting. It
also sets up our first worker in the kubernetes cluster.
This commit is contained in:
talksik
2026-03-25 09:52:23 -07:00
parent 86bdc78852
commit d8a45b22d3
6 changed files with 126 additions and 0 deletions
+67
View File
@@ -0,0 +1,67 @@
package main
import (
"context"
"fmt"
"log"
"log/slog"
"github.com/flowy-live/llink/internal/utils"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"cloud.google.com/go/firestore"
)
func createClient(ctx context.Context) *firestore.Client {
projectId := utils.MustGetEnv("GCP_PROJECT")
client, err := firestore.NewClient(ctx, projectId)
if err != nil {
log.Fatalf("Failed to create client: %v", err)
}
return client
}
// The purpose of the particle processor worker is to listen for new particles
// across all streams and perform side effects such as
// - generate transcript if the particle is of type media
// - send mobile notifications if a client is offline
// - update the parent stream's `last_child_created_at`
func main() {
ctx := context.Background()
client := createClient(ctx)
defer client.Close()
it := client.CollectionGroup("children").Snapshots(ctx)
var initialLoad = true
for {
snap, err := it.Next()
if e := status.Code(err); e == codes.DeadlineExceeded || e == codes.Canceled {
panic(fmt.Errorf("error: %w", err))
}
if err != nil {
slog.Error("error in processing snapshot", "error", err)
}
if snap != nil {
if initialLoad {
slog.Info("initial load", "changeCount", len(snap.Changes))
} else {
for _, change := range snap.Changes {
switch change.Kind {
case firestore.DocumentAdded:
slog.Info("document added: ")
case firestore.DocumentModified:
slog.Info("document modified")
case firestore.DocumentRemoved:
slog.Info("document removed")
}
slog.Info("received document snapshot", "data", change.Doc.Data())
}
}
}
initialLoad = false
}
}