github.com/lianghucheng/zrddz@v0.0.0-20200923083010-c71f680932e2/src/gopkg.in/mgo.v2/server.go (about) 1 // mgo - MongoDB driver for Go 2 // 3 // Copyright (c) 2010-2012 - Gustavo Niemeyer <gustavo@niemeyer.net> 4 // 5 // All rights reserved. 6 // 7 // Redistribution and use in source and binary forms, with or without 8 // modification, are permitted provided that the following conditions are met: 9 // 10 // 1. Redistributions of source code must retain the above copyright notice, this 11 // list of conditions and the following disclaimer. 12 // 2. Redistributions in binary form must reproduce the above copyright notice, 13 // this list of conditions and the following disclaimer in the documentation 14 // and/or other materials provided with the distribution. 15 // 16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 // ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 27 package mgo 28 29 import ( 30 "errors" 31 "net" 32 "sort" 33 "sync" 34 "time" 35 36 "gopkg.in/mgo.v2/bson" 37 ) 38 39 // --------------------------------------------------------------------------- 40 // Mongo server encapsulation. 41 42 type mongoServer struct { 43 sync.RWMutex 44 Addr string 45 ResolvedAddr string 46 tcpaddr *net.TCPAddr 47 unusedSockets []*mongoSocket 48 liveSockets []*mongoSocket 49 closed bool 50 abended bool 51 sync chan bool 52 dial dialer 53 pingValue time.Duration 54 pingIndex int 55 pingCount uint32 56 pingWindow [6]time.Duration 57 info *mongoServerInfo 58 } 59 60 type dialer struct { 61 old func(addr net.Addr) (net.Conn, error) 62 new func(addr *ServerAddr) (net.Conn, error) 63 } 64 65 func (dial dialer) isSet() bool { 66 return dial.old != nil || dial.new != nil 67 } 68 69 type mongoServerInfo struct { 70 Master bool 71 Mongos bool 72 Tags bson.D 73 MaxWireVersion int 74 SetName string 75 } 76 77 var defaultServerInfo mongoServerInfo 78 79 func newServer(addr string, tcpaddr *net.TCPAddr, sync chan bool, dial dialer) *mongoServer { 80 server := &mongoServer{ 81 Addr: addr, 82 ResolvedAddr: tcpaddr.String(), 83 tcpaddr: tcpaddr, 84 sync: sync, 85 dial: dial, 86 info: &defaultServerInfo, 87 pingValue: time.Hour, // Push it back before an actual ping. 88 } 89 go server.pinger(true) 90 return server 91 } 92 93 var errPoolLimit = errors.New("per-server connection limit reached") 94 var errServerClosed = errors.New("server was closed") 95 96 // AcquireSocket returns a socket for communicating with the server. 97 // This will attempt to reuse an old connection, if one is available. Otherwise, 98 // it will establish a new one. The returned socket is owned by the call site, 99 // and will return to the cache when the socket has its Release method called 100 // the same number of times as AcquireSocket + Acquire were called for it. 101 // If the poolLimit argument is greater than zero and the number of sockets in 102 // use in this server is greater than the provided limit, errPoolLimit is 103 // returned. 104 func (server *mongoServer) AcquireSocket(poolLimit int, timeout time.Duration) (socket *mongoSocket, abended bool, err error) { 105 for { 106 server.Lock() 107 abended = server.abended 108 if server.closed { 109 server.Unlock() 110 return nil, abended, errServerClosed 111 } 112 n := len(server.unusedSockets) 113 if poolLimit > 0 && len(server.liveSockets)-n >= poolLimit { 114 server.Unlock() 115 return nil, false, errPoolLimit 116 } 117 if n > 0 { 118 socket = server.unusedSockets[n-1] 119 server.unusedSockets[n-1] = nil // Help GC. 120 server.unusedSockets = server.unusedSockets[:n-1] 121 info := server.info 122 server.Unlock() 123 err = socket.InitialAcquire(info, timeout) 124 if err != nil { 125 continue 126 } 127 } else { 128 server.Unlock() 129 socket, err = server.Connect(timeout) 130 if err == nil { 131 server.Lock() 132 // We've waited for the Connect, see if we got 133 // closed in the meantime 134 if server.closed { 135 server.Unlock() 136 socket.Release() 137 socket.Close() 138 return nil, abended, errServerClosed 139 } 140 server.liveSockets = append(server.liveSockets, socket) 141 server.Unlock() 142 } 143 } 144 return 145 } 146 panic("unreachable") 147 } 148 149 // Connect establishes a new connection to the server. This should 150 // generally be done through server.AcquireSocket(). 151 func (server *mongoServer) Connect(timeout time.Duration) (*mongoSocket, error) { 152 server.RLock() 153 master := server.info.Master 154 dial := server.dial 155 server.RUnlock() 156 157 logf("Establishing new connection to %s (timeout=%s)...", server.Addr, timeout) 158 var conn net.Conn 159 var err error 160 switch { 161 case !dial.isSet(): 162 // Cannot do this because it lacks timeout support. :-( 163 //conn, err = net.DialTCP("tcp", nil, server.tcpaddr) 164 conn, err = net.DialTimeout("tcp", server.ResolvedAddr, timeout) 165 if tcpconn, ok := conn.(*net.TCPConn); ok { 166 tcpconn.SetKeepAlive(true) 167 } else if err == nil { 168 panic("internal error: obtained TCP connection is not a *net.TCPConn!?") 169 } 170 case dial.old != nil: 171 conn, err = dial.old(server.tcpaddr) 172 case dial.new != nil: 173 conn, err = dial.new(&ServerAddr{server.Addr, server.tcpaddr}) 174 default: 175 panic("dialer is set, but both dial.old and dial.new are nil") 176 } 177 if err != nil { 178 logf("Connection to %s failed: %v", server.Addr, err.Error()) 179 return nil, err 180 } 181 logf("Connection to %s established.", server.Addr) 182 183 stats.conn(+1, master) 184 return newSocket(server, conn, timeout), nil 185 } 186 187 // Close forces closing all sockets that are alive, whether 188 // they're currently in use or not. 189 func (server *mongoServer) Close() { 190 server.Lock() 191 server.closed = true 192 liveSockets := server.liveSockets 193 unusedSockets := server.unusedSockets 194 server.liveSockets = nil 195 server.unusedSockets = nil 196 server.Unlock() 197 logf("Connections to %s closing (%d live sockets).", server.Addr, len(liveSockets)) 198 for i, s := range liveSockets { 199 s.Close() 200 liveSockets[i] = nil 201 } 202 for i := range unusedSockets { 203 unusedSockets[i] = nil 204 } 205 } 206 207 // RecycleSocket puts socket back into the unused cache. 208 func (server *mongoServer) RecycleSocket(socket *mongoSocket) { 209 server.Lock() 210 if !server.closed { 211 server.unusedSockets = append(server.unusedSockets, socket) 212 } 213 server.Unlock() 214 } 215 216 func removeSocket(sockets []*mongoSocket, socket *mongoSocket) []*mongoSocket { 217 for i, s := range sockets { 218 if s == socket { 219 copy(sockets[i:], sockets[i+1:]) 220 n := len(sockets) - 1 221 sockets[n] = nil 222 sockets = sockets[:n] 223 break 224 } 225 } 226 return sockets 227 } 228 229 // AbendSocket notifies the server that the given socket has terminated 230 // abnormally, and thus should be discarded rather than cached. 231 func (server *mongoServer) AbendSocket(socket *mongoSocket) { 232 server.Lock() 233 server.abended = true 234 if server.closed { 235 server.Unlock() 236 return 237 } 238 server.liveSockets = removeSocket(server.liveSockets, socket) 239 server.unusedSockets = removeSocket(server.unusedSockets, socket) 240 server.Unlock() 241 // Maybe just a timeout, but suggest a cluster sync up just in case. 242 select { 243 case server.sync <- true: 244 default: 245 } 246 } 247 248 func (server *mongoServer) SetInfo(info *mongoServerInfo) { 249 server.Lock() 250 server.info = info 251 server.Unlock() 252 } 253 254 func (server *mongoServer) Info() *mongoServerInfo { 255 server.Lock() 256 info := server.info 257 server.Unlock() 258 return info 259 } 260 261 func (server *mongoServer) hasTags(serverTags []bson.D) bool { 262 NextTagSet: 263 for _, tags := range serverTags { 264 NextReqTag: 265 for _, req := range tags { 266 for _, has := range server.info.Tags { 267 if req.Name == has.Name { 268 if req.Value == has.Value { 269 continue NextReqTag 270 } 271 continue NextTagSet 272 } 273 } 274 continue NextTagSet 275 } 276 return true 277 } 278 return false 279 } 280 281 var pingDelay = 15 * time.Second 282 283 func (server *mongoServer) pinger(loop bool) { 284 var delay time.Duration 285 if raceDetector { 286 // This variable is only ever touched by tests. 287 globalMutex.Lock() 288 delay = pingDelay 289 globalMutex.Unlock() 290 } else { 291 delay = pingDelay 292 } 293 op := queryOp{ 294 collection: "admin.$cmd", 295 query: bson.D{{"ping", 1}}, 296 flags: flagSlaveOk, 297 limit: -1, 298 } 299 for { 300 if loop { 301 time.Sleep(delay) 302 } 303 op := op 304 socket, _, err := server.AcquireSocket(0, delay) 305 if err == nil { 306 start := time.Now() 307 _, _ = socket.SimpleQuery(&op) 308 delay := time.Now().Sub(start) 309 310 server.pingWindow[server.pingIndex] = delay 311 server.pingIndex = (server.pingIndex + 1) % len(server.pingWindow) 312 server.pingCount++ 313 var max time.Duration 314 for i := 0; i < len(server.pingWindow) && uint32(i) < server.pingCount; i++ { 315 if server.pingWindow[i] > max { 316 max = server.pingWindow[i] 317 } 318 } 319 socket.Release() 320 server.Lock() 321 if server.closed { 322 loop = false 323 } 324 server.pingValue = max 325 server.Unlock() 326 logf("Ping for %s is %d ms", server.Addr, max/time.Millisecond) 327 } else if err == errServerClosed { 328 return 329 } 330 if !loop { 331 return 332 } 333 } 334 } 335 336 type mongoServerSlice []*mongoServer 337 338 func (s mongoServerSlice) Len() int { 339 return len(s) 340 } 341 342 func (s mongoServerSlice) Less(i, j int) bool { 343 return s[i].ResolvedAddr < s[j].ResolvedAddr 344 } 345 346 func (s mongoServerSlice) Swap(i, j int) { 347 s[i], s[j] = s[j], s[i] 348 } 349 350 func (s mongoServerSlice) Sort() { 351 sort.Sort(s) 352 } 353 354 func (s mongoServerSlice) Search(resolvedAddr string) (i int, ok bool) { 355 n := len(s) 356 i = sort.Search(n, func(i int) bool { 357 return s[i].ResolvedAddr >= resolvedAddr 358 }) 359 return i, i != n && s[i].ResolvedAddr == resolvedAddr 360 } 361 362 type mongoServers struct { 363 slice mongoServerSlice 364 } 365 366 func (servers *mongoServers) Search(resolvedAddr string) (server *mongoServer) { 367 if i, ok := servers.slice.Search(resolvedAddr); ok { 368 return servers.slice[i] 369 } 370 return nil 371 } 372 373 func (servers *mongoServers) Add(server *mongoServer) { 374 servers.slice = append(servers.slice, server) 375 servers.slice.Sort() 376 } 377 378 func (servers *mongoServers) Remove(other *mongoServer) (server *mongoServer) { 379 if i, found := servers.slice.Search(other.ResolvedAddr); found { 380 server = servers.slice[i] 381 copy(servers.slice[i:], servers.slice[i+1:]) 382 n := len(servers.slice) - 1 383 servers.slice[n] = nil // Help GC. 384 servers.slice = servers.slice[:n] 385 } 386 return 387 } 388 389 func (servers *mongoServers) Slice() []*mongoServer { 390 return ([]*mongoServer)(servers.slice) 391 } 392 393 func (servers *mongoServers) Get(i int) *mongoServer { 394 return servers.slice[i] 395 } 396 397 func (servers *mongoServers) Len() int { 398 return len(servers.slice) 399 } 400 401 func (servers *mongoServers) Empty() bool { 402 return len(servers.slice) == 0 403 } 404 405 func (servers *mongoServers) HasMongos() bool { 406 for _, s := range servers.slice { 407 if s.Info().Mongos { 408 return true 409 } 410 } 411 return false 412 } 413 414 // BestFit returns the best guess of what would be the most interesting 415 // server to perform operations on at this point in time. 416 func (servers *mongoServers) BestFit(mode Mode, serverTags []bson.D) *mongoServer { 417 var best *mongoServer 418 for _, next := range servers.slice { 419 if best == nil { 420 best = next 421 best.RLock() 422 if serverTags != nil && !next.info.Mongos && !best.hasTags(serverTags) { 423 best.RUnlock() 424 best = nil 425 } 426 continue 427 } 428 next.RLock() 429 swap := false 430 switch { 431 case serverTags != nil && !next.info.Mongos && !next.hasTags(serverTags): 432 // Must have requested tags. 433 case mode == Secondary && next.info.Master && !next.info.Mongos: 434 // Must be a secondary or mongos. 435 case next.info.Master != best.info.Master && mode != Nearest: 436 // Prefer slaves, unless the mode is PrimaryPreferred. 437 swap = (mode == PrimaryPreferred) != best.info.Master 438 case absDuration(next.pingValue-best.pingValue) > 15*time.Millisecond: 439 // Prefer nearest server. 440 swap = next.pingValue < best.pingValue 441 case len(next.liveSockets)-len(next.unusedSockets) < len(best.liveSockets)-len(best.unusedSockets): 442 // Prefer servers with less connections. 443 swap = true 444 } 445 if swap { 446 best.RUnlock() 447 best = next 448 } else { 449 next.RUnlock() 450 } 451 } 452 if best != nil { 453 best.RUnlock() 454 } 455 return best 456 } 457 458 func absDuration(d time.Duration) time.Duration { 459 if d < 0 { 460 return -d 461 } 462 return d 463 }