Codebase list golang-github-garyburd-redigo / b4e1b2a
Imported Upstream version 0.0~git20150901.0.d8dbe4d Tianon Gravi 8 years ago
11 changed file(s) with 638 addition(s) and 110 deletion(s). Raw diff Collapse all Expand all
0
1 Apache License
2 Version 2.0, January 2004
3 http://www.apache.org/licenses/
4
5 TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
7 1. Definitions.
8
9 "License" shall mean the terms and conditions for use, reproduction,
10 and distribution as defined by Sections 1 through 9 of this document.
11
12 "Licensor" shall mean the copyright owner or entity authorized by
13 the copyright owner that is granting the License.
14
15 "Legal Entity" shall mean the union of the acting entity and all
16 other entities that control, are controlled by, or are under common
17 control with that entity. For the purposes of this definition,
18 "control" means (i) the power, direct or indirect, to cause the
19 direction or management of such entity, whether by contract or
20 otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 outstanding shares, or (iii) beneficial ownership of such entity.
22
23 "You" (or "Your") shall mean an individual or Legal Entity
24 exercising permissions granted by this License.
25
26 "Source" form shall mean the preferred form for making modifications,
27 including but not limited to software source code, documentation
28 source, and configuration files.
29
30 "Object" form shall mean any form resulting from mechanical
31 transformation or translation of a Source form, including but
32 not limited to compiled object code, generated documentation,
33 and conversions to other media types.
34
35 "Work" shall mean the work of authorship, whether in Source or
36 Object form, made available under the License, as indicated by a
37 copyright notice that is included in or attached to the work
38 (an example is provided in the Appendix below).
39
40 "Derivative Works" shall mean any work, whether in Source or Object
41 form, that is based on (or derived from) the Work and for which the
42 editorial revisions, annotations, elaborations, or other modifications
43 represent, as a whole, an original work of authorship. For the purposes
44 of this License, Derivative Works shall not include works that remain
45 separable from, or merely link (or bind by name) to the interfaces of,
46 the Work and Derivative Works thereof.
47
48 "Contribution" shall mean any work of authorship, including
49 the original version of the Work and any modifications or additions
50 to that Work or Derivative Works thereof, that is intentionally
51 submitted to Licensor for inclusion in the Work by the copyright owner
52 or by an individual or Legal Entity authorized to submit on behalf of
53 the copyright owner. For the purposes of this definition, "submitted"
54 means any form of electronic, verbal, or written communication sent
55 to the Licensor or its representatives, including but not limited to
56 communication on electronic mailing lists, source code control systems,
57 and issue tracking systems that are managed by, or on behalf of, the
58 Licensor for the purpose of discussing and improving the Work, but
59 excluding communication that is conspicuously marked or otherwise
60 designated in writing by the copyright owner as "Not a Contribution."
61
62 "Contributor" shall mean Licensor and any individual or Legal Entity
63 on behalf of whom a Contribution has been received by Licensor and
64 subsequently incorporated within the Work.
65
66 2. Grant of Copyright License. Subject to the terms and conditions of
67 this License, each Contributor hereby grants to You a perpetual,
68 worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 copyright license to reproduce, prepare Derivative Works of,
70 publicly display, publicly perform, sublicense, and distribute the
71 Work and such Derivative Works in Source or Object form.
72
73 3. Grant of Patent License. Subject to the terms and conditions of
74 this License, each Contributor hereby grants to You a perpetual,
75 worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 (except as stated in this section) patent license to make, have made,
77 use, offer to sell, sell, import, and otherwise transfer the Work,
78 where such license applies only to those patent claims licensable
79 by such Contributor that are necessarily infringed by their
80 Contribution(s) alone or by combination of their Contribution(s)
81 with the Work to which such Contribution(s) was submitted. If You
82 institute patent litigation against any entity (including a
83 cross-claim or counterclaim in a lawsuit) alleging that the Work
84 or a Contribution incorporated within the Work constitutes direct
85 or contributory patent infringement, then any patent licenses
86 granted to You under this License for that Work shall terminate
87 as of the date such litigation is filed.
88
89 4. Redistribution. You may reproduce and distribute copies of the
90 Work or Derivative Works thereof in any medium, with or without
91 modifications, and in Source or Object form, provided that You
92 meet the following conditions:
93
94 (a) You must give any other recipients of the Work or
95 Derivative Works a copy of this License; and
96
97 (b) You must cause any modified files to carry prominent notices
98 stating that You changed the files; and
99
100 (c) You must retain, in the Source form of any Derivative Works
101 that You distribute, all copyright, patent, trademark, and
102 attribution notices from the Source form of the Work,
103 excluding those notices that do not pertain to any part of
104 the Derivative Works; and
105
106 (d) If the Work includes a "NOTICE" text file as part of its
107 distribution, then any Derivative Works that You distribute must
108 include a readable copy of the attribution notices contained
109 within such NOTICE file, excluding those notices that do not
110 pertain to any part of the Derivative Works, in at least one
111 of the following places: within a NOTICE text file distributed
112 as part of the Derivative Works; within the Source form or
113 documentation, if provided along with the Derivative Works; or,
114 within a display generated by the Derivative Works, if and
115 wherever such third-party notices normally appear. The contents
116 of the NOTICE file are for informational purposes only and
117 do not modify the License. You may add Your own attribution
118 notices within Derivative Works that You distribute, alongside
119 or as an addendum to the NOTICE text from the Work, provided
120 that such additional attribution notices cannot be construed
121 as modifying the License.
122
123 You may add Your own copyright statement to Your modifications and
124 may provide additional or different license terms and conditions
125 for use, reproduction, or distribution of Your modifications, or
126 for any such Derivative Works as a whole, provided Your use,
127 reproduction, and distribution of the Work otherwise complies with
128 the conditions stated in this License.
129
130 5. Submission of Contributions. Unless You explicitly state otherwise,
131 any Contribution intentionally submitted for inclusion in the Work
132 by You to the Licensor shall be under the terms and conditions of
133 this License, without any additional terms or conditions.
134 Notwithstanding the above, nothing herein shall supersede or modify
135 the terms of any separate license agreement you may have executed
136 with Licensor regarding such Contributions.
137
138 6. Trademarks. This License does not grant permission to use the trade
139 names, trademarks, service marks, or product names of the Licensor,
140 except as required for reasonable and customary use in describing the
141 origin of the Work and reproducing the content of the NOTICE file.
142
143 7. Disclaimer of Warranty. Unless required by applicable law or
144 agreed to in writing, Licensor provides the Work (and each
145 Contributor provides its Contributions) on an "AS IS" BASIS,
146 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 implied, including, without limitation, any warranties or conditions
148 of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 PARTICULAR PURPOSE. You are solely responsible for determining the
150 appropriateness of using or redistributing the Work and assume any
151 risks associated with Your exercise of permissions under this License.
152
153 8. Limitation of Liability. In no event and under no legal theory,
154 whether in tort (including negligence), contract, or otherwise,
155 unless required by applicable law (such as deliberate and grossly
156 negligent acts) or agreed to in writing, shall any Contributor be
157 liable to You for damages, including any direct, indirect, special,
158 incidental, or consequential damages of any character arising as a
159 result of this License or out of the use or inability to use the
160 Work (including but not limited to damages for loss of goodwill,
161 work stoppage, computer failure or malfunction, or any and all
162 other commercial damages or losses), even if such Contributor
163 has been advised of the possibility of such damages.
164
165 9. Accepting Warranty or Additional Liability. While redistributing
166 the Work or Derivative Works thereof, You may choose to offer,
167 and charge a fee for, acceptance of support, warranty, indemnity,
168 or other liability obligations and/or rights consistent with this
169 License. However, in accepting such obligations, You may act only
170 on Your own behalf and on Your sole responsibility, not on behalf
171 of any other Contributor, and only if You agree to indemnify,
172 defend, and hold each Contributor harmless for any liability
173 incurred by, or claims asserted against, such Contributor by reason
174 of your accepting any such warranty or additional liability.
3939 "MONITOR": {Set: MonitorState},
4040 }
4141
42 func init() {
43 for n, ci := range commandInfos {
44 commandInfos[strings.ToLower(n)] = ci
45 }
46 }
47
4248 func LookupCommandInfo(commandName string) CommandInfo {
49 if ci, ok := commandInfos[commandName]; ok {
50 return ci
51 }
4352 return commandInfos[strings.ToUpper(commandName)]
4453 }
0 package internal
1
2 import "testing"
3
4 func TestLookupCommandInfo(t *testing.T) {
5 for _, n := range []string{"watch", "WATCH", "wAtch"} {
6 if LookupCommandInfo(n) == (CommandInfo{}) {
7 t.Errorf("LookupCommandInfo(%q) = CommandInfo{}, expected non-zero value", n)
8 }
9 }
10 }
11
12 func benchmarkLookupCommandInfo(b *testing.B, names ...string) {
13 for i := 0; i < b.N; i++ {
14 for _, c := range names {
15 LookupCommandInfo(c)
16 }
17 }
18 }
19
20 func BenchmarkLookupCommandInfoCorrectCase(b *testing.B) {
21 benchmarkLookupCommandInfo(b, "watch", "WATCH", "monitor", "MONITOR")
22 }
23
24 func BenchmarkLookupCommandInfoMixedCase(b *testing.B) {
25 benchmarkLookupCommandInfo(b, "wAtch", "WeTCH", "monItor", "MONiTOR")
26 }
4848
4949 _, err = c.Do("SELECT", "9")
5050 if err != nil {
51 c.Close()
5152 return nil, err
5253 }
5354
5455 n, err := redis.Int(c.Do("DBSIZE"))
5556 if err != nil {
57 c.Close()
5658 return nil, err
5759 }
5860
5961 if n != 0 {
62 c.Close()
6063 return nil, errors.New("database #9 is not empty, test can not continue")
6164 }
6265
2020 "fmt"
2121 "io"
2222 "net"
23 "net/url"
24 "regexp"
2325 "strconv"
2426 "sync"
2527 "time"
5052 numScratch [40]byte
5153 }
5254
53 // Dial connects to the Redis server at the given network and address.
54 func Dial(network, address string) (Conn, error) {
55 dialer := xDialer{}
56 return dialer.Dial(network, address)
57 }
58
5955 // DialTimeout acts like Dial but takes timeouts for establishing the
6056 // connection to the server, writing a command and reading a reply.
57 //
58 // DialTimeout is deprecated.
6159 func DialTimeout(network, address string, connectTimeout, readTimeout, writeTimeout time.Duration) (Conn, error) {
62 netDialer := net.Dialer{Timeout: connectTimeout}
63 dialer := xDialer{
64 NetDial: netDialer.Dial,
65 ReadTimeout: readTimeout,
66 WriteTimeout: writeTimeout,
67 }
68 return dialer.Dial(network, address)
69 }
70
71 // A Dialer specifies options for connecting to a Redis server.
72 type xDialer struct {
73 // NetDial specifies the dial function for creating TCP connections. If
74 // NetDial is nil, then net.Dial is used.
75 NetDial func(network, addr string) (net.Conn, error)
76
77 // ReadTimeout specifies the timeout for reading a single command
78 // reply. If ReadTimeout is zero, then no timeout is used.
79 ReadTimeout time.Duration
80
81 // WriteTimeout specifies the timeout for writing a single command. If
82 // WriteTimeout is zero, then no timeout is used.
83 WriteTimeout time.Duration
84 }
85
86 // Dial connects to the Redis server at address on the named network.
87 func (d *xDialer) Dial(network, address string) (Conn, error) {
88 dial := d.NetDial
89 if dial == nil {
90 dial = net.Dial
91 }
92 netConn, err := dial(network, address)
60 return Dial(network, address,
61 DialConnectTimeout(connectTimeout),
62 DialReadTimeout(readTimeout),
63 DialWriteTimeout(writeTimeout))
64 }
65
66 // DialOption specifies an option for dialing a Redis server.
67 type DialOption struct {
68 f func(*dialOptions)
69 }
70
71 type dialOptions struct {
72 readTimeout time.Duration
73 writeTimeout time.Duration
74 dial func(network, addr string) (net.Conn, error)
75 db int
76 password string
77 }
78
79 // DialReadTimeout specifies the timeout for reading a single command reply.
80 func DialReadTimeout(d time.Duration) DialOption {
81 return DialOption{func(do *dialOptions) {
82 do.readTimeout = d
83 }}
84 }
85
86 // DialWriteTimeout specifies the timeout for writing a single command.
87 func DialWriteTimeout(d time.Duration) DialOption {
88 return DialOption{func(do *dialOptions) {
89 do.writeTimeout = d
90 }}
91 }
92
93 // DialConnectTimeout specifies the timeout for connecting to the Redis server.
94 func DialConnectTimeout(d time.Duration) DialOption {
95 return DialOption{func(do *dialOptions) {
96 dialer := net.Dialer{Timeout: d}
97 do.dial = dialer.Dial
98 }}
99 }
100
101 // DialNetDial specifies a custom dial function for creating TCP
102 // connections. If this option is left out, then net.Dial is
103 // used. DialNetDial overrides DialConnectTimeout.
104 func DialNetDial(dial func(network, addr string) (net.Conn, error)) DialOption {
105 return DialOption{func(do *dialOptions) {
106 do.dial = dial
107 }}
108 }
109
110 // DialDatabase specifies the database to select when dialing a connection.
111 func DialDatabase(db int) DialOption {
112 return DialOption{func(do *dialOptions) {
113 do.db = db
114 }}
115 }
116
117 // DialPassword specifies the password to use when connecting to
118 // the Redis server.
119 func DialPassword(password string) DialOption {
120 return DialOption{func(do *dialOptions) {
121 do.password = password
122 }}
123 }
124
125 // Dial connects to the Redis server at the given network and
126 // address using the specified options.
127 func Dial(network, address string, options ...DialOption) (Conn, error) {
128 do := dialOptions{
129 dial: net.Dial,
130 }
131 for _, option := range options {
132 option.f(&do)
133 }
134
135 netConn, err := do.dial(network, address)
93136 if err != nil {
94137 return nil, err
95138 }
96 return &conn{
139 c := &conn{
97140 conn: netConn,
98141 bw: bufio.NewWriter(netConn),
99142 br: bufio.NewReader(netConn),
100 readTimeout: d.ReadTimeout,
101 writeTimeout: d.WriteTimeout,
102 }, nil
143 readTimeout: do.readTimeout,
144 writeTimeout: do.writeTimeout,
145 }
146
147 if do.password != "" {
148 if _, err := c.Do("AUTH", do.password); err != nil {
149 netConn.Close()
150 return nil, err
151 }
152 }
153
154 if do.db != 0 {
155 if _, err := c.Do("SELECT", do.db); err != nil {
156 netConn.Close()
157 return nil, err
158 }
159 }
160
161 return c, nil
162 }
163
164 var pathDBRegexp = regexp.MustCompile(`/(\d)\z`)
165
166 // DialURL connects to a Redis server at the given URL using the Redis
167 // URI scheme. URLs should follow the draft IANA specification for the
168 // scheme (https://www.iana.org/assignments/uri-schemes/prov/redis).
169 func DialURL(rawurl string, options ...DialOption) (Conn, error) {
170 u, err := url.Parse(rawurl)
171 if err != nil {
172 return nil, err
173 }
174
175 if u.Scheme != "redis" {
176 return nil, fmt.Errorf("invalid redis URL scheme: %s", u.Scheme)
177 }
178
179 // As per the IANA draft spec, the host defaults to localhost and
180 // the port defaults to 6379.
181 host, port, err := net.SplitHostPort(u.Host)
182 if err != nil {
183 // assume port is missing
184 host = u.Host
185 port = "6379"
186 }
187 if host == "" {
188 host = "localhost"
189 }
190 address := net.JoinHostPort(host, port)
191
192 if u.User != nil {
193 password, isSet := u.User.Password()
194 if isSet {
195 options = append(options, DialPassword(password))
196 }
197 }
198
199 match := pathDBRegexp.FindStringSubmatch(u.Path)
200 if len(match) == 2 {
201 db, err := strconv.Atoi(match[1])
202 if err != nil {
203 return nil, fmt.Errorf("invalid database: %s", u.Path[1:])
204 }
205 if db != 0 {
206 options = append(options, DialDatabase(db))
207 }
208 } else if u.Path != "" {
209 return nil, fmt.Errorf("invalid database: %s", u.Path[1:])
210 }
211
212 return Dial("tcp", address, options...)
103213 }
104214
105215 // NewConn returns a new Redigo connection for the given net connection.
416526 }
417527
418528 if cmd != "" {
419 c.writeCommand(cmd, args)
529 if err := c.writeCommand(cmd, args); err != nil {
530 return nil, c.fatal(err)
531 }
420532 }
421533
422534 if err := c.bw.Flush(); err != nil {
1818 "bytes"
1919 "math"
2020 "net"
21 "os"
2122 "reflect"
2223 "strings"
2324 "testing"
421422 }
422423 }
423424
425 var dialErrors = []struct {
426 rawurl string
427 expectedError string
428 }{
429 {
430 "localhost",
431 "invalid redis URL scheme",
432 },
433 // The error message for invalid hosts is diffferent in different
434 // versions of Go, so just check that there is an error message.
435 {
436 "redis://weird url",
437 "",
438 },
439 {
440 "redis://foo:bar:baz",
441 "",
442 },
443 {
444 "http://www.google.com",
445 "invalid redis URL scheme: http",
446 },
447 {
448 "redis://x:abc123@localhost",
449 "no password is set",
450 },
451 {
452 "redis://localhost:6379/abc123",
453 "invalid database: abc123",
454 },
455 }
456
457 func TestDialURL(t *testing.T) {
458 for _, d := range dialErrors {
459 _, err := redis.DialURL(d.rawurl)
460 if err == nil || !strings.Contains(err.Error(), d.expectedError) {
461 t.Errorf("DialURL did not return expected error (expected %v to contain %s)", err, d.expectedError)
462 }
463 }
464
465 checkPort := func(network, address string) (net.Conn, error) {
466 if address != "localhost:6379" {
467 t.Errorf("DialURL did not set port to 6379 by default (got %v)", address)
468 }
469 return net.Dial(network, address)
470 }
471 c, err := redis.DialURL("redis://localhost", redis.DialNetDial(checkPort))
472 if err != nil {
473 t.Error("dial error:", err)
474 }
475 c.Close()
476
477 checkHost := func(network, address string) (net.Conn, error) {
478 if address != "localhost:6379" {
479 t.Errorf("DialURL did not set host to localhost by default (got %v)", address)
480 }
481 return net.Dial(network, address)
482 }
483 c, err = redis.DialURL("redis://:6379", redis.DialNetDial(checkHost))
484 if err != nil {
485 t.Error("dial error:", err)
486 }
487 c.Close()
488
489 // Check that the database is set correctly
490 c1, err := redis.DialURL("redis://:6379/8")
491 defer c1.Close()
492 if err != nil {
493 t.Error("Dial error:", err)
494 }
495 dbSize, _ := redis.Int(c1.Do("DBSIZE"))
496 if dbSize > 0 {
497 t.Fatal("DB 8 has existing keys; aborting test to avoid overwriting data")
498 }
499 c1.Do("SET", "var", "val")
500
501 c2, err := redis.Dial("tcp", ":6379")
502 defer c2.Close()
503 if err != nil {
504 t.Error("dial error:", err)
505 }
506 _, err = c2.Do("SELECT", "8")
507 if err != nil {
508 t.Error(err)
509 }
510 got, err := redis.String(c2.Do("GET", "var"))
511 if err != nil {
512 t.Error(err)
513 }
514 if got != "val" {
515 t.Error("DialURL did not correctly set the db.")
516 }
517 _, err = c2.Do("DEL", "var")
518 }
519
424520 // Connect to local instance of Redis running on the default port.
425521 func ExampleDial(x int) {
426522 c, err := redis.Dial("tcp", ":6379")
427523 if err != nil {
428524 // handle error
525 }
526 defer c.Close()
527 }
528
529 // Connect to remote instance of Redis using a URL.
530 func ExampleDialURL() {
531 c, err := redis.DialURL(os.Getenv("REDIS_URL"))
532 if err != nil {
533 // handle connection error
429534 }
430535 defer c.Close()
431536 }
3131 redis.Conn
3232 }
3333
34 func (c *poolTestConn) Close() error { c.d.open -= 1; return nil }
35 func (c *poolTestConn) Err() error { return c.err }
36
37 func (c *poolTestConn) Do(commandName string, args ...interface{}) (reply interface{}, err error) {
34 func (c *poolTestConn) Close() error {
35 c.d.mu.Lock()
36 c.d.open -= 1
37 c.d.mu.Unlock()
38 return c.Conn.Close()
39 }
40
41 func (c *poolTestConn) Err() error { return c.err }
42
43 func (c *poolTestConn) Do(commandName string, args ...interface{}) (interface{}, error) {
3844 if commandName == "ERR" {
3945 c.err = args[0].(error)
4046 commandName = "PING"
5157 }
5258
5359 type poolDialer struct {
60 mu sync.Mutex
5461 t *testing.T
5562 dialed int
5663 open int
5966 }
6067
6168 func (d *poolDialer) dial() (redis.Conn, error) {
69 d.mu.Lock()
6270 d.dialed += 1
63 if d.dialErr != nil {
71 dialErr := d.dialErr
72 d.mu.Unlock()
73 if dialErr != nil {
6474 return nil, d.dialErr
6575 }
6676 c, err := redistest.Dial()
6777 if err != nil {
6878 return nil, err
6979 }
80 d.mu.Lock()
7081 d.open += 1
82 d.mu.Unlock()
7183 return &poolTestConn{d: d, Conn: c}, nil
7284 }
7385
7486 func (d *poolDialer) check(message string, p *redis.Pool, dialed, open int) {
87 d.mu.Lock()
7588 if d.dialed != dialed {
7689 d.t.Errorf("%s: dialed=%d, want %d", message, d.dialed, dialed)
7790 }
8194 if active := p.ActiveCount(); active != open {
8295 d.t.Errorf("%s: active=%d, want %d", message, active, open)
8396 }
97 d.mu.Unlock()
8498 }
8599
86100 func TestPoolReuse(t *testing.T) {
110124 MaxIdle: 2,
111125 Dial: d.dial,
112126 }
127 defer p.Close()
128
113129 for i := 0; i < 10; i++ {
114130 c1 := p.Get()
115131 c1.Do("PING")
132148 MaxIdle: 2,
133149 Dial: d.dial,
134150 }
151 defer p.Close()
135152
136153 c := p.Get()
137154 c.Do("ERR", io.EOF)
153170 MaxIdle: 2,
154171 Dial: d.dial,
155172 }
173 defer p.Close()
156174
157175 c1 := p.Get()
158176 c1.Do("PING")
194212 IdleTimeout: 300 * time.Second,
195213 Dial: d.dial,
196214 }
215 defer p.Close()
197216
198217 now := time.Now()
199218 redis.SetNowFunc(func() time.Time { return now })
212231 c.Close()
213232
214233 d.check("2", p, 2, 1)
215
216 p.Close()
217234 }
218235
219236 func TestPoolConcurrenSendReceive(t *testing.T) {
220237 p := &redis.Pool{
221238 Dial: redistest.Dial,
222239 }
240 defer p.Close()
241
223242 c := p.Get()
224243 done := make(chan error, 1)
225244 go func() {
237256 t.Fatalf("Do() returned error %v", err)
238257 }
239258 c.Close()
240 p.Close()
241259 }
242260
243261 func TestPoolBorrowCheck(t *testing.T) {
247265 Dial: d.dial,
248266 TestOnBorrow: func(redis.Conn, time.Time) error { return redis.Error("BLAH") },
249267 }
268 defer p.Close()
250269
251270 for i := 0; i < 10; i++ {
252271 c := p.Get()
254273 c.Close()
255274 }
256275 d.check("1", p, 10, 1)
257 p.Close()
258276 }
259277
260278 func TestPoolMaxActive(t *testing.T) {
264282 MaxActive: 2,
265283 Dial: d.dial,
266284 }
285 defer p.Close()
286
267287 c1 := p.Get()
268288 c1.Do("PING")
269289 c2 := p.Get()
288308 c3.Close()
289309
290310 d.check("4", p, 2, 2)
291 p.Close()
292311 }
293312
294313 func TestPoolMonitorCleanup(t *testing.T) {
298317 MaxActive: 2,
299318 Dial: d.dial,
300319 }
320 defer p.Close()
321
301322 c := p.Get()
302323 c.Send("MONITOR")
303324 c.Close()
304325
305326 d.check("", p, 1, 0)
306 p.Close()
307327 }
308328
309329 func TestPoolPubSubCleanup(t *testing.T) {
313333 MaxActive: 2,
314334 Dial: d.dial,
315335 }
336 defer p.Close()
316337
317338 c := p.Get()
318339 c.Send("SUBSCRIBE", "x")
333354 t.Errorf("got commands %v, want %v", d.commands, want)
334355 }
335356 d.commands = nil
336
337 p.Close()
338357 }
339358
340359 func TestPoolTransactionCleanup(t *testing.T) {
344363 MaxActive: 2,
345364 Dial: d.dial,
346365 }
366 defer p.Close()
347367
348368 c := p.Get()
349369 c.Do("WATCH", "key")
405425 t.Errorf("got commands %v, want %v", d.commands, want)
406426 }
407427 d.commands = nil
408
409 p.Close()
410428 }
411429
412430 func startGoroutines(p *redis.Pool, cmd string, args ...interface{}) chan error {
435453 Wait: true,
436454 }
437455 defer p.Close()
456
438457 c := p.Get()
439458 errs := startGoroutines(p, "PING")
440459 d.check("before close", p, 1, 1)
461480 Dial: d.dial,
462481 Wait: true,
463482 }
483 defer p.Close()
484
464485 c := p.Get()
465486 if _, err := c.Do("PING"); err != nil {
466487 t.Fatal(err)
496517 Wait: true,
497518 }
498519 defer p.Close()
520
499521 c := p.Get()
500522 errs := startGoroutines(p, "ERR", testErr)
501523 d.check("before close", p, 1, 1)
524546 Wait: true,
525547 }
526548 defer p.Close()
549
527550 c := p.Get()
528551 errs := startGoroutines(p, "ERR", testErr)
529552 d.check("before close", p, 1, 1)
564587 // test ensures that iteration will work correctly if multiple threads are
565588 // iterating simultaneously.
566589 func TestLocking_TestOnBorrowFails_PoolDoesntCrash(t *testing.T) {
567 count := 100
590 const count = 100
568591
569592 // First we'll Create a pool where the pilfering of idle connections fails.
570593 d := poolDialer{t: t}
579602 defer p.Close()
580603
581604 // Fill the pool with idle connections.
582 b1 := sync.WaitGroup{}
583 b1.Add(count)
584 b2 := sync.WaitGroup{}
585 b2.Add(count)
586 for i := 0; i < count; i++ {
587 go func() {
588 c := p.Get()
589 if c.Err() != nil {
590 t.Errorf("pool get failed: %v", c.Err())
591 }
592 b1.Done()
593 b1.Wait()
594 c.Close()
595 b2.Done()
596 }()
597 }
598 b2.Wait()
599 if d.dialed != count {
600 t.Errorf("Expected %d dials, got %d", count, d.dialed)
605 conns := make([]redis.Conn, count)
606 for i := range conns {
607 conns[i] = p.Get()
608 }
609 for i := range conns {
610 conns[i].Close()
601611 }
602612
603613 // Spawn a bunch of goroutines to thrash the pool.
604 b2.Add(count)
614 var wg sync.WaitGroup
615 wg.Add(count)
605616 for i := 0; i < count; i++ {
606617 go func() {
607618 c := p.Get()
609620 t.Errorf("pool get failed: %v", c.Err())
610621 }
611622 c.Close()
612 b2.Done()
623 wg.Done()
613624 }()
614625 }
615 b2.Wait()
626 wg.Wait()
616627 if d.dialed != count*2 {
617628 t.Errorf("Expected %d dials, got %d", count*2, d.dialed)
618629 }
1313
1414 package redis
1515
16 import (
17 "errors"
18 )
16 import "errors"
1917
2018 // Subscription represents a subscribe or unsubscribe notification.
2119 type Subscription struct {
5149
5250 // The message data.
5351 Data []byte
52 }
53
54 // Pong represents a pubsub pong notification.
55 type Pong struct {
56 Data string
5457 }
5558
5659 // PubSubConn wraps a Conn with convenience methods for subscribers.
8992 return c.Conn.Flush()
9093 }
9194
92 // Receive returns a pushed message as a Subscription, Message, PMessage or
93 // error. The return value is intended to be used directly in a type switch as
94 // illustrated in the PubSubConn example.
95 // Ping sends a PING to the server with the specified data.
96 func (c PubSubConn) Ping(data string) error {
97 c.Conn.Send("PING", data)
98 return c.Conn.Flush()
99 }
100
101 // Receive returns a pushed message as a Subscription, Message, PMessage, Pong
102 // or error. The return value is intended to be used directly in a type switch
103 // as illustrated in the PubSubConn example.
95104 func (c PubSubConn) Receive() interface{} {
96105 reply, err := Values(c.Conn.Receive())
97106 if err != nil {
123132 return err
124133 }
125134 return s
135 case "pong":
136 var p Pong
137 if _, err := Scan(reply, &p.Data); err != nil {
138 return err
139 }
140 return p
126141 }
127142 return errors.New("redigo: unknown pubsub notification")
128143 }
139139
140140 pc.Do("PUBLISH", "c1", "hello")
141141 expectPushed(t, c, "PUBLISH c1 hello", redis.Message{Channel: "c1", Data: []byte("hello")})
142
143 c.Ping("hello")
144 expectPushed(t, c, `Ping("hello")`, redis.Pong{"hello"})
145
146 c.Conn.Send("PING")
147 c.Conn.Flush()
148 expectPushed(t, c, `Send("PING")`, redis.Pong{})
142149 }
274274 // err is not equal to nil, then Ints returns nil, err.
275275 func Ints(reply interface{}, err error) ([]int, error) {
276276 var ints []int
277 if reply == nil {
278 return ints, ErrNil
279 }
280277 values, err := Values(reply, err)
281278 if err != nil {
282279 return ints, err
309306 }
310307 return m, nil
311308 }
309
310 // IntMap is a helper that converts an array of strings (alternating key, value)
311 // into a map[string]int. The HGETALL commands return replies in this format.
312 // Requires an even number of values in result.
313 func IntMap(result interface{}, err error) (map[string]int, error) {
314 values, err := Values(result, err)
315 if err != nil {
316 return nil, err
317 }
318 if len(values)%2 != 0 {
319 return nil, errors.New("redigo: IntMap expects even number of values result")
320 }
321 m := make(map[string]int, len(values)/2)
322 for i := 0; i < len(values); i += 2 {
323 key, ok := values[i].([]byte)
324 if !ok {
325 return nil, errors.New("redigo: ScanMap key not a bulk string value")
326 }
327 value, err := Int(values[i+1], nil)
328 if err != nil {
329 return nil, err
330 }
331 m[string(key)] = value
332 }
333 return m, nil
334 }
335
336 // Int64Map is a helper that converts an array of strings (alternating key, value)
337 // into a map[string]int64. The HGETALL commands return replies in this format.
338 // Requires an even number of values in result.
339 func Int64Map(result interface{}, err error) (map[string]int64, error) {
340 values, err := Values(result, err)
341 if err != nil {
342 return nil, err
343 }
344 if len(values)%2 != 0 {
345 return nil, errors.New("redigo: Int64Map expects even number of values result")
346 }
347 m := make(map[string]int64, len(values)/2)
348 for i := 0; i < len(values); i += 2 {
349 key, ok := values[i].([]byte)
350 if !ok {
351 return nil, errors.New("redigo: ScanMap key not a bulk string value")
352 }
353 value, err := Int64(values[i+1], nil)
354 if err != nil {
355 return nil, err
356 }
357 m[string(key)] = value
358 }
359 return m, nil
360 }
3131 }
3232
3333 func cannotConvert(d reflect.Value, s interface{}) error {
34 return fmt.Errorf("redigo: Scan cannot convert from %s to %s",
35 reflect.TypeOf(s), d.Type())
36 }
37
38 func convertAssignBytes(d reflect.Value, s []byte) (err error) {
34 var sname string
35 switch s.(type) {
36 case string:
37 sname = "Redis simple string"
38 case Error:
39 sname = "Redis error"
40 case int64:
41 sname = "Redis integer"
42 case []byte:
43 sname = "Redis bulk string"
44 case []interface{}:
45 sname = "Redis array"
46 default:
47 sname = reflect.TypeOf(s).String()
48 }
49 return fmt.Errorf("cannot convert from %s to %s", sname, d.Type())
50 }
51
52 func convertAssignBulkString(d reflect.Value, s []byte) (err error) {
3953 switch d.Type().Kind() {
4054 case reflect.Float32, reflect.Float64:
4155 var x float64
97111 func convertAssignValue(d reflect.Value, s interface{}) (err error) {
98112 switch s := s.(type) {
99113 case []byte:
100 err = convertAssignBytes(d, s)
114 err = convertAssignBulkString(d, s)
101115 case int64:
102116 err = convertAssignInt(d, s)
103117 default:
106120 return err
107121 }
108122
109 func convertAssignValues(d reflect.Value, s []interface{}) error {
123 func convertAssignArray(d reflect.Value, s []interface{}) error {
110124 if d.Type().Kind() != reflect.Slice {
111125 return cannotConvert(d, s)
112126 }
143157 if d := reflect.ValueOf(d); d.Type().Kind() != reflect.Ptr {
144158 err = cannotConvert(d, s)
145159 } else {
146 err = convertAssignBytes(d.Elem(), s)
160 err = convertAssignBulkString(d.Elem(), s)
147161 }
148162 }
149163 case int64:
180194 if d := reflect.ValueOf(d); d.Type().Kind() != reflect.Ptr {
181195 err = cannotConvert(d, s)
182196 } else {
183 err = convertAssignValues(d.Elem(), s)
197 err = convertAssignArray(d.Elem(), s)
184198 }
185199 }
186200 case Error:
205219 // following the copied values.
206220 func Scan(src []interface{}, dest ...interface{}) ([]interface{}, error) {
207221 if len(src) < len(dest) {
208 return nil, errors.New("redigo: Scan array short")
222 return nil, errors.New("redigo.Scan: array short")
209223 }
210224 var err error
211225 for i, d := range dest {
212226 err = convertAssign(d, src[i])
213227 if err != nil {
228 err = fmt.Errorf("redigo.Scan: cannot assign to dest %d: %v", i, err)
214229 break
215230 }
216231 }
260275 //case "omitempty":
261276 // fs.omitempty = true
262277 default:
263 panic(errors.New("redigo: unknown field flag " + s + " for type " + t.Name()))
278 panic(fmt.Errorf("redigo: unknown field tag %s for type %s", s, t.Name()))
264279 }
265280 }
266281 }
320335 return ss
321336 }
322337
323 var errScanStructValue = errors.New("redigo: ScanStruct value must be non-nil pointer to a struct")
338 var errScanStructValue = errors.New("redigo.ScanStruct: value must be non-nil pointer to a struct")
324339
325340 // ScanStruct scans alternating names and values from src to a struct. The
326341 // HGETALL and CONFIG GET commands return replies in this format.
349364 ss := structSpecForType(d.Type())
350365
351366 if len(src)%2 != 0 {
352 return errors.New("redigo: ScanStruct expects even number of values in values")
367 return errors.New("redigo.ScanStruct: number of values not a multiple of 2")
353368 }
354369
355370 for i := 0; i < len(src); i += 2 {
359374 }
360375 name, ok := src[i].([]byte)
361376 if !ok {
362 return errors.New("redigo: ScanStruct key not a bulk string value")
377 return fmt.Errorf("redigo.ScanStruct: key %d not a bulk string value", i)
363378 }
364379 fs := ss.fieldSpec(name)
365380 if fs == nil {
366381 continue
367382 }
368383 if err := convertAssignValue(d.FieldByIndex(fs.index), s); err != nil {
369 return err
384 return fmt.Errorf("redigo.ScanStruct: cannot assign field %s: %v", fs.name, err)
370385 }
371386 }
372387 return nil
373388 }
374389
375390 var (
376 errScanSliceValue = errors.New("redigo: ScanSlice dest must be non-nil pointer to a struct")
391 errScanSliceValue = errors.New("redigo.ScanSlice: dest must be non-nil pointer to a struct")
377392 )
378393
379394 // ScanSlice scans src to the slice pointed to by dest. The elements the dest
406421 continue
407422 }
408423 if err := convertAssignValue(d.Index(i), s); err != nil {
409 return err
424 return fmt.Errorf("redigo.ScanSlice: cannot assign element %d: %v", i, err)
410425 }
411426 }
412427 return nil
419434 for i, name := range fieldNames {
420435 fss[i] = ss.m[name]
421436 if fss[i] == nil {
422 return errors.New("redigo: ScanSlice bad field name " + name)
437 return fmt.Errorf("redigo.ScanSlice: ScanSlice bad field name %s", name)
423438 }
424439 }
425440 }
426441
427442 if len(fss) == 0 {
428 return errors.New("redigo: ScanSlice no struct fields")
443 return errors.New("redigo.ScanSlice: no struct fields")
429444 }
430445
431446 n := len(src) / len(fss)
432447 if n*len(fss) != len(src) {
433 return errors.New("redigo: ScanSlice length not a multiple of struct field count")
448 return errors.New("redigo.ScanSlice: length not a multiple of struct field count")
434449 }
435450
436451 ensureLen(d, n)
448463 continue
449464 }
450465 if err := convertAssignValue(d.FieldByIndex(fs.index), s); err != nil {
451 return err
466 return fmt.Errorf("redigo.ScanSlice: cannot assign element %d to field %s: %v", i*len(fss)+j, fs.name, err)
452467 }
453468 }
454469 }