Added plan of tests

This commit is contained in:
Alexander Pivkin
2025-12-20 10:21:45 +03:00
parent a889e1c7dd
commit 640a6d2a46
171 changed files with 57454 additions and 0 deletions
+8
View File
@@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml
@@ -0,0 +1,12 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="RedisDemo 1: ping" type="GoApplicationRunConfiguration" factoryName="Go Application">
<module name="get-started-with-redis" />
<working_directory value="$PROJECT_DIR$" />
<parameters value="ping" />
<kind value="PACKAGE" />
<package value="github.com/JetBrains/go-code-samples/get-started-with-redis" />
<directory value="$PROJECT_DIR$" />
<filePath value="$PROJECT_DIR$" />
<method v="2" />
</configuration>
</component>
@@ -0,0 +1,12 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="RedisDemo 2: get/set" type="GoApplicationRunConfiguration" factoryName="Go Application">
<module name="get-started-with-redis" />
<working_directory value="$PROJECT_DIR$" />
<parameters value="getset" />
<kind value="PACKAGE" />
<package value="github.com/JetBrains/go-code-samples/get-started-with-redis" />
<directory value="$PROJECT_DIR$" />
<filePath value="$PROJECT_DIR$" />
<method v="2" />
</configuration>
</component>
@@ -0,0 +1,12 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="RedisDemo 3: expire" type="GoApplicationRunConfiguration" factoryName="Go Application">
<module name="get-started-with-redis" />
<working_directory value="$PROJECT_DIR$" />
<parameters value="expire" />
<kind value="PACKAGE" />
<package value="github.com/JetBrains/go-code-samples/get-started-with-redis" />
<directory value="$PROJECT_DIR$" />
<filePath value="$PROJECT_DIR$" />
<method v="2" />
</configuration>
</component>
@@ -0,0 +1,12 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="RedisDemo 4: pipeline" type="GoApplicationRunConfiguration" factoryName="Go Application">
<module name="get-started-with-redis" />
<working_directory value="$PROJECT_DIR$" />
<parameters value="pipeline" />
<kind value="PACKAGE" />
<package value="github.com/JetBrains/go-code-samples/get-started-with-redis" />
<directory value="$PROJECT_DIR$" />
<filePath value="$PROJECT_DIR$" />
<method v="2" />
</configuration>
</component>
@@ -0,0 +1,12 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="RedisDemo 5: transaction" type="GoApplicationRunConfiguration" factoryName="Go Application">
<module name="get-started-with-redis" />
<working_directory value="$PROJECT_DIR$" />
<parameters value="transaction" />
<kind value="PACKAGE" />
<package value="github.com/JetBrains/go-code-samples/get-started-with-redis" />
<directory value="$PROJECT_DIR$" />
<filePath value="$PROJECT_DIR$" />
<method v="2" />
</configuration>
</component>
@@ -0,0 +1,12 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="RedisDemo 6: pub/sub" type="GoApplicationRunConfiguration" factoryName="Go Application">
<module name="get-started-with-redis" />
<working_directory value="$PROJECT_DIR$" />
<parameters value="pubsub" />
<kind value="PACKAGE" />
<package value="github.com/JetBrains/go-code-samples/get-started-with-redis" />
<directory value="$PROJECT_DIR$" />
<filePath value="$PROJECT_DIR$" />
<method v="2" />
</configuration>
</component>
@@ -0,0 +1,12 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="RedisDemo: pipeline benchmark" type="GoTestRunConfiguration" factoryName="Go Test">
<module name="get-started-with-redis" />
<working_directory value="$PROJECT_DIR$" />
<kind value="DIRECTORY" />
<package value="github.com/JetBrains/go-code-samples/get-started-with-redis" />
<directory value="$PROJECT_DIR$" />
<filePath value="$PROJECT_DIR$" />
<framework value="gobench" />
<method v="2" />
</configuration>
</component>
@@ -0,0 +1,12 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="RedisDemo: reset data" type="GoApplicationRunConfiguration" factoryName="Go Application">
<module name="get-started-with-redis" />
<working_directory value="$PROJECT_DIR$" />
<parameters value="reset" />
<kind value="PACKAGE" />
<package value="github.com/JetBrains/go-code-samples/get-started-with-redis" />
<directory value="$PROJECT_DIR$" />
<filePath value="$PROJECT_DIR$" />
<method v="2" />
</configuration>
</component>
@@ -0,0 +1,5 @@
# Getting Started with Redis in Go
This tutorial demonstrates how to use Redis, the popular in-memory database that doubles as cache, pub/sub service, and streaming service, with Go and GoLand.
Find the tutorial [here]().
@@ -0,0 +1,35 @@
package main
import (
"context"
"fmt"
"github.com/redis/go-redis/v9"
"time"
)
func expiringKeys(client *redis.Client) error {
ctx := context.Background()
// Add a temporary player
err := client.HSet(ctx, "player:10", "name", "Crymyios", "score", 0, "team", "Knucklewimp", "challenges_completed", 0).Err()
if err != nil {
return fmt.Errorf("cannot set player:10: %w", err)
}
// Set an expiration time for player:10
if !client.Expire(ctx, "player:10", time.Second).Val() {
return fmt.Errorf("cannot set expiration time for player:10")
}
// Get player:10
for i := 0; i < 3; i++ {
val, err := client.HGet(ctx, "player:10", "name").Result()
if err != nil {
fmt.Printf("player:10 has expired: %v\n", err)
return nil
}
fmt.Printf("player:10's name: %s\n", val)
time.Sleep(500 * time.Millisecond)
}
return nil
}
@@ -0,0 +1,32 @@
package main
import (
"context"
"fmt"
"github.com/redis/go-redis/v9"
"golang.org/x/text/cases"
"golang.org/x/text/language"
)
// Task: The quest is a lowercase string. Change it to title case.
// Then print the challenges in the order they have to be completed.
func getAndSet(client *redis.Client) error {
ctx := context.Background()
quest, err := client.Get(ctx, "quest").Result()
if err != nil {
return fmt.Errorf("cannot get quest: %w", err)
}
quest = cases.Title(language.English).String(quest)
err = client.Set(ctx, "quest", quest, 0).Err()
if err != nil {
return fmt.Errorf("cannot update quest: %w", err)
}
fmt.Printf("Quest is now: %s\n", client.Get(ctx, "quest").Val())
return nil
}
@@ -0,0 +1,13 @@
module github.com/JetBrains/go-code-samples/get-started-with-redis
go 1.20
require (
github.com/redis/go-redis/v9 v9.0.5
golang.org/x/text v0.9.0
)
require (
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
)
@@ -0,0 +1,18 @@
github.com/bsm/ginkgo/v2 v2.7.0 h1:ItPMPH90RbmZJt5GtkcNvIRuGEdwlBItdNVoyzaNQao=
github.com/bsm/ginkgo/v2 v2.7.0/go.mod h1:AiKlXPm7ItEHNc/2+OkrNG4E0ITzojb9/xWzvQ9XZ9w=
github.com/bsm/gomega v1.26.0 h1:LhQm+AFcgV2M0WyKroMASzAzCAJVpAxQXv4SaI9a69Y=
github.com/bsm/gomega v1.26.0/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/redis/go-redis/v9 v9.0.5 h1:CuQcn5HIEeK7BgElubPP8CGtE0KakrnbBSTLjathl5o=
github.com/redis/go-redis/v9 v9.0.5/go.mod h1:WqMKv5vnQbRuZstUwxQI195wHy+t4PuXDOjzMvcuQHk=
golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
@@ -0,0 +1,103 @@
package main
import (
"fmt"
"log"
"os"
)
const (
dbconn = "localhost:6379"
db = 0
)
func usage() {
fmt.Println(`Usage: go run main.go <demo>
Where <demo> is one of:
- ping: run the ping command
- getset: get and set a string value
- expire: set an expiring key
- pipeline: run a batch of commands
- transaction: run a batch of commands that must all succeed
- pubsub: send a message to a channel and listen to the channel
- reset: restore the initial data set`)
}
func run() error {
// Create a new client from a connection string and a database number (0-15)
client := newClient(dbconn, 0)
if len(os.Args) < 2 {
usage()
return nil
}
switch os.Args[1] {
case "ping":
// Ping the redis server and fetch some database information.
fmt.Printf("\nPing: Test the connection\n")
ping(client)
case "getset":
// Get and set a string value.
fmt.Printf("\nGet/Set: Update the quest to title case\n")
err := getAndSet(client)
if err != nil {
return fmt.Errorf("getAndSet failed: %w", err)
}
case "expire":
// Set an expiring key and wait for it to expire
fmt.Printf("\nExpire: Add a player temporarily\n")
err := expiringKeys(client)
if err != nil {
return fmt.Errorf("expiringKeys failed: %w", err)
}
case "pipeline":
// Run a batch of commands.
fmt.Printf("\nPipeline: Update score and challenges_completed for team Snarkdumbthimble\n")
err := pipeline(client)
if err != nil {
return fmt.Errorf("pipeline failed: %w", err)
}
case "transaction":
// Run several commands that must all succeed.
// If any of them fails, the transaction will be canceled.
fmt.Printf("\nTransaction: Rearrange the teams\n")
err := transaction(client)
if err != nil {
return fmt.Errorf("transaction failed: %w", err)
}
case "pubsub":
// Send messages to a publish/receive channel.
// Listen to the channel and receive the messages.
fmt.Printf("\nPub/Sub: Publish challenges to subscribed teams\n")
err := pubsub(client)
if err != nil {
return fmt.Errorf("pubsub stopped: %w", err)
}
case "reset":
// Reset the database
fmt.Printf("\nReset the database\n")
err := resetdata(client)
if err != nil {
return fmt.Errorf("cannot reset database: %w", err)
}
default:
usage()
}
return nil
}
func main() {
err := run()
if err != nil {
log.Println(err)
os.Exit(1)
}
os.Exit(0)
}
@@ -0,0 +1,23 @@
package main
import (
"context"
"fmt"
"github.com/redis/go-redis/v9"
)
func ping(client *redis.Client) error {
// For the demo, we need only a background context
ctx := context.Background()
// Ping the redis server. It should respond with "PONG".
fmt.Println(client.Ping(ctx))
// Get the client info.
info, err := client.ClientInfo(ctx).Result()
if err != nil {
return fmt.Errorf("method ClientInfo failed: %w", err)
}
fmt.Printf("%#v\n", info)
return nil
}
@@ -0,0 +1,43 @@
package main
import (
"context"
"fmt"
"github.com/redis/go-redis/v9"
)
// Task: Update the score and the challenges_completed
// for team Snarkdumbthimble that has finished challenge #1.
func pipeline(client *redis.Client) error {
ctx := context.Background()
_, err := client.Pipelined(ctx, func(pipe redis.Pipeliner) error {
err := pipe.HSet(ctx, "player:7", "score", 15, "challenges_completed", 1).Err()
if err != nil {
return err
}
err = pipe.HSet(ctx, "player:8", "score", 18, "challenges_completed", 1).Err()
if err != nil {
return err
}
err = pipe.HSet(ctx, "player:9", "score", 12, "challenges_completed", 1).Err()
return err
})
if err != nil {
return fmt.Errorf("pipelined failed: %w", err)
}
fmt.Printf("Player 7's score: %s, challenges completed: %s\n",
client.HGet(ctx, "player:7", "score").Val(),
client.HGet(ctx, "player:7", "challenges_completed").Val())
fmt.Printf("Player 8's score: %s, challenges completed: %s\n",
client.HGet(ctx, "player:8", "score").Val(),
client.HGet(ctx, "player:8", "challenges_completed").Val())
fmt.Printf("Player 9's score: %s, challenges completed: %s\n",
client.HGet(ctx, "player:9", "score").Val(),
client.HGet(ctx, "player:9", "challenges_completed").Val())
return nil
}
@@ -0,0 +1,53 @@
package main
import (
"context"
"fmt"
"github.com/redis/go-redis/v9"
"testing"
)
func incrementScorePipe(client *redis.Client, player string) error {
ctx := context.Background()
client.Pipelined(ctx, func(pipe redis.Pipeliner) error {
for i := 0; i < 1000; i++ {
err := pipe.HIncrBy(ctx, player, "score", 1).Err()
if err != nil {
return fmt.Errorf("cannot increment score for player %s to %d: %w", player, i, err)
}
}
pipe.HSet(ctx, player, "score", 1)
return nil
})
return nil
}
func incrementScoreNoPipe(client *redis.Client, player string) error {
ctx := context.Background()
for i := 0; i < 1000; i++ {
err := client.HIncrBy(ctx, player, "score", 1).Err()
if err != nil {
return fmt.Errorf("cannot increment score for player %s to %d: %w", player, i, err)
}
}
client.HSet(ctx, player, "score", 1)
return nil
}
func BenchmarkPipeline(b *testing.B) {
client := newClient(dbconn, 0)
b.Run("PipelinedHIncrBy", func(b *testing.B) {
for i := 0; i < b.N; i++ {
incrementScorePipe(client, "player:1")
}
})
}
func BenchmarkNoPipeline(b *testing.B) {
client := newClient(dbconn, 0)
b.Run("HIncrBy", func(b *testing.B) {
for i := 0; i < b.N; i++ {
incrementScoreNoPipe(client, "player:2")
}
})
}
@@ -0,0 +1,164 @@
package main
import (
"context"
"fmt"
"github.com/redis/go-redis/v9"
"time"
)
// Task: Send each challenge to the "challenge" channel.
// Each team is subscribed to the "challenge" channel and enters the challenge upon receiving.
const (
// the name of the PubSub channel
pubsubChan = "challenge"
)
// Res is the result of reading the pubsub channel
type Res struct {
result string
err error
}
// Team manages a team's subscription
// Each team uses its own client to subscribe to the "challenge" channel
type Team struct {
name string
client *redis.Client
channel *redis.PubSub
}
// getTeams scans the database for "team:*" keys
// and returns a slice of Team structs with names filled from the keys
func getTeams(client *redis.Client) []Team {
ctx := context.Background()
teams := make([]Team, 3)
teamsets := make([]string, 0, 3)
keys := make([]string, 0, 3)
var cursor uint64
for {
// Scan returns a slice of matches. The count may or may not be reached
// in the first call to Scan, so the code needs to call Scan in a loop and
// append the found keys to the teamsets slice until the cursor "returns to 0".
var err error
keys, cursor, err = client.Scan(ctx, cursor, "team:*", 3).Result()
if err != nil {
break
}
teamsets = append(teamsets, keys...)
if cursor == 0 {
break
}
}
// Lazily assume that the scan has returned 3 team sets
for i := 0; i < 3; i++ {
teams[i].name = teamsets[i]
// each team uses its own client
teams[i].client = newClient(dbconn, 0)
}
return teams
}
// subscribe subscribes to the "challenge" channel
// and waits for the subscription to be completed
func (team *Team) subscribe() error {
ctx := context.Background()
// Subscribe to the "challenge" channel
pubSub := team.client.Subscribe(ctx, pubsubChan)
// The first Subscribe() call creates the channel.
// Until that point, any attempt to publish something fails.
reply, err := pubSub.Receive(ctx)
if err != nil {
return fmt.Errorf("subscribing to channel '%s' failed: %w", pubsubChan, err)
}
// Expected response type is "*Subscription". Otherwise, something failed.
switch reply.(type) {
case *redis.Subscription:
// Success!
case *redis.Message:
// The channel is already active and contains messages, hence also a success
case *redis.Pong:
// letL's call it a success
default:
return fmt.Errorf("subscribing to a channel failed: received a reply of type %T, expected: *redis.Subscription", reply)
}
team.channel = pubSub
fmt.Printf("%s subscribed to channel '%s'\n", team.name, pubsubChan)
return nil
}
// receive receives messages from the "challenge" channel.
// It starts a goroutine that reads from the pubsub channel until
// the channel is closed or the context is done.
func (team *Team) receive(ctx context.Context, resChan chan<- Res) {
ch := team.channel.Channel()
defer close(resChan)
for {
select {
case msg, ok := <-ch:
if !ok {
// The pubsub channel has been closed
return
}
resChan <- Res{fmt.Sprintf("%s received challenge '%s'", team.name, msg.Payload), nil}
case <-ctx.Done():
resChan <- Res{"", ctx.Err()}
return
}
}
}
// publish publishes the challenge to the "challenge" channel
func publish(client *redis.Client, challenge string) error {
ctx := context.Background()
fmt.Printf("publishing challenge '%s'\n", challenge)
return client.Publish(ctx, pubsubChan, challenge).Err()
}
// pubsub subscribes to the "challenge" channel, publishes the challenges,
// and receives the published messages.
func pubsub(client *redis.Client) (err error) {
ctx := context.Background()
// Step 1: subscribe each team
teams := getTeams(client)
for i := 0; i < 3; i++ {
err = teams[i].subscribe()
if err != nil {
return fmt.Errorf("subscribing failed: %w", err)
}
}
// Step 2: publish challenges
// Read the challenges from the sorted set "challenges" and publish them
for i := int64(0); i < 5; i++ {
challenge := client.ZRange(ctx, "challenges", i, i).Val()[0]
err = publish(client, challenge)
if err != nil {
return fmt.Errorf("cannot publish challenge %s: %w", challenge, err)
}
}
// Close the channel after one second, to terminate the receive loops.
time.AfterFunc(time.Second, func() {
teams[0].channel.Close()
fmt.Println(`PubSub channel "challenges" closed`)
})
// Step 3: receive published messages
rch := make(chan Res)
for i := 0; i < 3; i++ {
go teams[i].receive(ctx, rch)
}
for msg := range rch {
if msg.err != nil {
return fmt.Errorf("cannot receive challenge: %w", msg.err)
}
fmt.Println(msg.result)
}
return nil
}
@@ -0,0 +1,11 @@
package main
import "github.com/redis/go-redis/v9"
func newClient(conn string, db int) *redis.Client {
return redis.NewClient(&redis.Options{
Addr: conn,
DB: db,
Password: "",
})
}
@@ -0,0 +1,57 @@
package main
import (
"context"
"encoding/csv"
"fmt"
"github.com/redis/go-redis/v9"
"io"
"os"
)
const (
setupfile = "testdata/setup.redis"
)
// resetdata flushes all data from the current database (db 0)
// and runs the commands from file setup.redis to set up the database.
func resetdata(client *redis.Client) error {
ctx := context.Background()
// read file "setup.redis" line by line
setup, err := os.Open(setupfile)
if err != nil {
return fmt.Errorf("cannot open %s: %w", setupfile, err)
}
defer setup.Close()
client.FlushDB(ctx) // FlushDB never fails.
csv := csv.NewReader(setup)
csv.Comma = ' '
csv.FieldsPerRecord = -1 // Variable number of fields per line
for {
cmd, err := csv.Read()
if err == io.EOF {
return nil
}
if err != nil {
return fmt.Errorf("csv: cannot read a line from %s: %w", setupfile, err)
}
// cmd is a slice of strings, Do() expects a slice of 'any'.
// The memory layout of the two slice types is not the same,
// so we need to convert cmd to a slice of 'any'.
doCmd := make([]interface{}, len(cmd))
for i, v := range cmd {
doCmd[i] = v
}
err = client.Do(ctx, doCmd...).Err()
if err != nil {
return fmt.Errorf("resetdata: cannot execute '%v': %w", cmd, err)
}
}
return nil
}
@@ -0,0 +1,16 @@
FLUSHDB
SET quest "the chicken coop catastrophe"
SET description "Players must rescue a group of chickens that have been taken hostage by a group of evil goblins."
HSET "player:1" name Sykios score 0 team Dorkfoot challenges_completed 0
HSET "player:2" name Nidios score 0 team Dorkfoot challenges_completed 0
HSET "player:3" name Tiaitia score 0 team Dorkfoot challenges_completed 0
HSET "player:4" name Belaeos score 0 team Knucklewimp challenges_completed 0
HSET "player:5" name Polytia score 0 team Knucklewimp challenges_completed 0
HSET "player:6" name Moritia score 0 team Knucklewimp challenges_completed 0
HSET "player:7" name Daryos score 0 team Snarkdumbthimble challenges_completed 0
HSET "player:8" name Blalios score 0 team Snarkdumbthimble challenges_completed 0
HSET "player:9" name Ighteatia score 0 team Snarkdumbthimble challenges_completed 0
SADD "team:Dorkfoot" Sykios Nidios Tiaitia
SADD "team:Knucklewimp" Belaeos Polytia Moritia
SADD "team:Snarkdumbthimble" Daryos Blalios Ighteatia
ZADD "challenges" 1 "Enter the hidden dungeon" 2 "Find the chicken coop" 3 "Defeat the goblins" 4 "Rescue the chickens" 5 "Escape the dungeon"
@@ -0,0 +1,76 @@
package main
import (
"context"
"fmt"
"github.com/redis/go-redis/v9"
)
// Task:
// - Create a new team: Grumblebum
// - Move Sykios, Nidios, and Belaeos to the new team
// - Move Tiaitia to team Knucklewimp
// - Remove team Dorkfoot
func transaction(client *redis.Client) error {
ctx := context.Background()
_, err := client.TxPipelined(ctx, func(pipe redis.Pipeliner) error {
// Move Sykios to team Grumblebum
err := pipe.HSet(ctx, "player:1", "team", "Grumblebum").Err()
if err != nil {
return err
}
// Move Nidios to team Grumblebum
err = pipe.HSet(ctx, "player:2", "team", "Grumblebum").Err()
if err != nil {
return err
}
// Move Belaeos to team Grumblebum
err = pipe.HSet(ctx, "player:4", "team", "Grumblebum").Err()
if err != nil {
return err
}
// Move Tiaitia to team Knucklewimp
err = pipe.HSet(ctx, "player:3", "team", "Knucklewimp").Err()
if err != nil {
return err
}
// Team update: remove Belaeos from team Knucklewimp
err = pipe.SRem(ctx, "team:Knucklewimp", "Belaeos").Err()
if err != nil {
return err
}
// Team update: add Tiaitia to team Knucklewimp
err = pipe.SAdd(ctx, "team:Knucklewimp", "Tiaitia").Err()
if err != nil {
return err
}
// Add team Grumblebum
err = pipe.SAdd(ctx, "team:Grumblebum", "Sykios", "Nidios", "Belaeos").Err()
if err != nil {
return err
}
// Remove team Dorkfoot. A set is removed by removing all elements.
err = pipe.SRem(ctx, "team:Dorkfoot", "Sykios", "Nidios", "Tiaitia").Err()
if err != nil {
return err
}
return nil
})
if err != nil {
return fmt.Errorf("TxPipelined failed: %w", err)
}
fmt.Printf("Sykios's new team: %s\n", client.HGet(ctx, "player:1", "team").Val())
fmt.Printf("Belaeos's new team: %s\n", client.HGet(ctx, "player:4", "team").Val())
fmt.Printf("Tiaitia's new team: %s\n", client.HGet(ctx, "player:3", "team").Val())
fmt.Printf("Team Grumblebum: %s\n", client.SMembers(ctx, "team:Grumblebum").Val())
fmt.Printf("Team Knucklewimp: %s\n", client.SMembers(ctx, "team:Knucklewimp").Val())
return nil
}