• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

HDT3213 / godis / 15238419529

25 May 2025 01:32PM UTC coverage: 72.019% (-3.7%) from 75.704%
15238419529

push

github

HDT3213
update github actions go version

1 of 1 new or added line in 1 file covered. (100.0%)

1149 existing lines in 29 files now uncovered.

8473 of 11765 relevant lines covered (72.02%)

0.8 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

74.07
/database/transaction.go
1
package database
2

3
import (
4
        "github.com/hdt3213/godis/interface/redis"
5
        "github.com/hdt3213/godis/redis/protocol"
6
        "strings"
7
)
8

9
// Watch set watching keys
10
func Watch(db *DB, conn redis.Connection, args [][]byte) redis.Reply {
1✔
11
        watching := conn.GetWatching()
1✔
12
        for _, bkey := range args {
2✔
13
                key := string(bkey)
1✔
14
                watching[key] = db.GetVersion(key)
1✔
15
        }
1✔
16
        return protocol.MakeOkReply()
1✔
17
}
18

19
func execGetVersion(db *DB, args [][]byte) redis.Reply {
×
20
        key := string(args[0])
×
21
        ver := db.GetVersion(key)
×
22
        return protocol.MakeIntReply(int64(ver))
×
23
}
×
24

25
func init() {
1✔
26
        registerCommand("GetVer", execGetVersion, readAllKeys, nil, 2, flagReadOnly)
1✔
27
}
1✔
28

29
// invoker should lock watching keys
30
func isWatchingChanged(db *DB, watching map[string]uint32) bool {
1✔
31
        for key, ver := range watching {
2✔
32
                currentVersion := db.GetVersion(key)
1✔
33
                if ver != currentVersion {
2✔
34
                        return true
1✔
35
                }
1✔
36
        }
37
        return false
1✔
38
}
39

40
// StartMulti starts multi-command-transaction
41
func StartMulti(conn redis.Connection) redis.Reply {
1✔
42
        if conn.InMultiState() {
1✔
43
                return protocol.MakeErrReply("ERR MULTI calls can not be nested")
×
44
        }
×
45
        conn.SetMultiState(true)
1✔
46
        return protocol.MakeOkReply()
1✔
47
}
48

49
// EnqueueCmd puts command line into `multi` pending queue
50
func EnqueueCmd(conn redis.Connection, cmdLine [][]byte) redis.Reply {
1✔
51
        cmdName := strings.ToLower(string(cmdLine[0]))
1✔
52
        cmd, ok := cmdTable[cmdName]
1✔
53
        if !ok {
1✔
54
                err := protocol.MakeErrReply("ERR unknown command '" + cmdName + "'")
×
55
                conn.AddTxError(err)
×
56
                return err
×
57
        }
×
58
        if cmd.prepare == nil {
1✔
59
                err := protocol.MakeErrReply("ERR command '" + cmdName + "' cannot be used in MULTI")
×
60
                conn.AddTxError(err)
×
61
                return err
×
62
        }
×
63
        if !validateArity(cmd.arity, cmdLine) {
2✔
64
                err := protocol.MakeArgNumErrReply(cmdName)
1✔
65
                conn.AddTxError(err)
1✔
66
                return err
1✔
67
        }
1✔
68
        conn.EnqueueCmd(cmdLine)
1✔
69
        return protocol.MakeQueuedReply()
1✔
70
}
71

72
func execMulti(db *DB, conn redis.Connection) redis.Reply {
1✔
73
        if !conn.InMultiState() {
1✔
74
                return protocol.MakeErrReply("ERR EXEC without MULTI")
×
75
        }
×
76
        defer conn.SetMultiState(false)
1✔
77
        if len(conn.GetTxErrors()) > 0 {
2✔
78
                return protocol.MakeErrReply("EXECABORT Transaction discarded because of previous errors.")
1✔
79
        }
1✔
80
        cmdLines := conn.GetQueuedCmdLine()
1✔
81
        return db.ExecMulti(conn, conn.GetWatching(), cmdLines)
1✔
82
}
83

84
// ExecMulti executes multi commands transaction Atomically and Isolated
85
func (db *DB) ExecMulti(conn redis.Connection, watching map[string]uint32, cmdLines []CmdLine) redis.Reply {
1✔
86
        // prepare
1✔
87
        writeKeys := make([]string, 0) // may contains duplicate
1✔
88
        readKeys := make([]string, 0)
1✔
89
        for _, cmdLine := range cmdLines {
2✔
90
                cmdName := strings.ToLower(string(cmdLine[0]))
1✔
91
                cmd := cmdTable[cmdName]
1✔
92
                prepare := cmd.prepare
1✔
93
                write, read := prepare(cmdLine[1:])
1✔
94
                writeKeys = append(writeKeys, write...)
1✔
95
                readKeys = append(readKeys, read...)
1✔
96
        }
1✔
97
        // set watch
98
        watchingKeys := make([]string, 0, len(watching))
1✔
99
        for key := range watching {
2✔
100
                watchingKeys = append(watchingKeys, key)
1✔
101
        }
1✔
102
        readKeys = append(readKeys, watchingKeys...)
1✔
103
        db.RWLocks(writeKeys, readKeys)
1✔
104
        defer db.RWUnLocks(writeKeys, readKeys)
1✔
105

1✔
106
        if isWatchingChanged(db, watching) { // watching keys changed, abort
2✔
107
                return protocol.MakeEmptyMultiBulkReply()
1✔
108
        }
1✔
109
        // execute
110
        results := make([]redis.Reply, 0, len(cmdLines))
1✔
111
        aborted := false
1✔
112
        undoCmdLines := make([][]CmdLine, 0, len(cmdLines))
1✔
113
        for _, cmdLine := range cmdLines {
2✔
114
                undoCmdLines = append(undoCmdLines, db.GetUndoLogs(cmdLine))
1✔
115
                result := db.execWithLock(cmdLine)
1✔
116
                if protocol.IsErrorReply(result) {
2✔
117
                        aborted = true
1✔
118
                        // don't rollback failed commands
1✔
119
                        undoCmdLines = undoCmdLines[:len(undoCmdLines)-1]
1✔
120
                        break
1✔
121
                }
122
                results = append(results, result)
1✔
123
        }
124
        if !aborted { //success
2✔
125
                db.addVersion(writeKeys...)
1✔
126
                return protocol.MakeMultiRawReply(results)
1✔
127
        }
1✔
128
        // undo if aborted
129
        size := len(undoCmdLines)
1✔
130
        for i := size - 1; i >= 0; i-- {
2✔
131
                curCmdLines := undoCmdLines[i]
1✔
132
                if len(curCmdLines) == 0 {
1✔
133
                        continue
×
134
                }
135
                for _, cmdLine := range curCmdLines {
2✔
136
                        db.execWithLock(cmdLine)
1✔
137
                }
1✔
138
        }
139
        return protocol.MakeErrReply("EXECABORT Transaction discarded because of previous errors.")
1✔
140
}
141

142
// DiscardMulti drops MULTI pending commands
143
func DiscardMulti(conn redis.Connection) redis.Reply {
1✔
144
        if !conn.InMultiState() {
1✔
145
                return protocol.MakeErrReply("ERR DISCARD without MULTI")
×
146
        }
×
147
        conn.ClearQueuedCmds()
1✔
148
        conn.SetMultiState(false)
1✔
149
        return protocol.MakeOkReply()
1✔
150
}
151

152
// GetUndoLogs return rollback commands
153
func (db *DB) GetUndoLogs(cmdLine [][]byte) []CmdLine {
1✔
154
        cmdName := strings.ToLower(string(cmdLine[0]))
1✔
155
        cmd, ok := cmdTable[cmdName]
1✔
156
        if !ok {
1✔
157
                return nil
×
158
        }
×
159
        undo := cmd.undo
1✔
160
        if undo == nil {
1✔
161
                return nil
×
162
        }
×
163
        return undo(db, cmdLine[1:])
1✔
164
}
165

166
// GetRelatedKeys analysis related keys
167
// returns related write keys and read keys
168
func GetRelatedKeys(cmdLine [][]byte) ([]string, []string) {
×
169
        cmdName := strings.ToLower(string(cmdLine[0]))
×
170
        cmd, ok := cmdTable[cmdName]
×
171
        if !ok {
×
172
                return nil, nil
×
173
        }
×
174
        prepare := cmd.prepare
×
175
        if prepare == nil {
×
176
                return nil, nil
×
177
        }
×
UNCOV
178
        return prepare(cmdLine[1:])
×
179
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc