Explorar el Código

manage a Fields + Rows pool

Brad Fitzpatrick hace 12 años
padre
commit
db2a560a50
Se han modificado 5 ficheros con 95 adiciones y 9 borrados
  1. 39 0
      buffer.go
  2. 5 3
      connection.go
  3. 2 2
      packets.go
  4. 39 2
      rows.go
  5. 10 2
      statement.go

+ 39 - 0
buffer.go

@@ -121,3 +121,42 @@ func (b *buffer) takeCompleteBuffer() []byte {
 	}
 	}
 	return nil
 	return nil
 }
 }
+
+var fieldCache = make(chan []mysqlField, 16)
+
+func makeFields(n int) []mysqlField {
+	select {
+	case f := <-fieldCache:
+		if cap(f) >= n {
+			return f[:n]
+		}
+	default:
+	}
+	return make([]mysqlField, n)
+}
+
+func putFields(f []mysqlField) {
+	select {
+	case fieldCache <- f:
+	default:
+	}
+}
+
+var rowsCache = make(chan *mysqlRows, 16)
+
+func newMysqlRows() *mysqlRows {
+	select {
+	case r := <-rowsCache:
+		return r
+	default:
+		return new(mysqlRows)
+	}
+}
+
+func putMysqlRows(r *mysqlRows) {
+	*r = mysqlRows{} // zero it
+	select {
+	case rowsCache <- r:
+	default:
+	}
+}

+ 5 - 3
connection.go

@@ -199,13 +199,14 @@ func (mc *mysqlConn) Query(query string, args []driver.Value) (driver.Rows, erro
 			var resLen int
 			var resLen int
 			resLen, err = mc.readResultSetHeaderPacket()
 			resLen, err = mc.readResultSetHeaderPacket()
 			if err == nil {
 			if err == nil {
-				rows := &mysqlRows{mc, false, nil, false}
+				rows := newMysqlRows()
+				rows.mc = mc
 
 
 				if resLen > 0 {
 				if resLen > 0 {
 					// Columns
 					// Columns
 					rows.columns, err = mc.readColumns(resLen)
 					rows.columns, err = mc.readColumns(resLen)
 				}
 				}
-				return rows, err
+				return &mysqlRowsI{rows}, err
 			}
 			}
 		}
 		}
 		return nil, err
 		return nil, err
@@ -226,7 +227,8 @@ func (mc *mysqlConn) getSystemVar(name string) ([]byte, error) {
 	// Read Result
 	// Read Result
 	resLen, err := mc.readResultSetHeaderPacket()
 	resLen, err := mc.readResultSetHeaderPacket()
 	if err == nil {
 	if err == nil {
-		rows := &mysqlRows{mc, false, nil, false}
+		rows := newMysqlRows()
+		rows.mc = mc
 
 
 		if resLen > 0 {
 		if resLen > 0 {
 			// Columns
 			// Columns

+ 2 - 2
packets.go

@@ -537,7 +537,7 @@ func (mc *mysqlConn) handleOkPacket(data []byte) error {
 // Read Packets as Field Packets until EOF-Packet or an Error appears
 // Read Packets as Field Packets until EOF-Packet or an Error appears
 // http://dev.mysql.com/doc/internals/en/com-query-response.html#packet-Protocol::ColumnDefinition41
 // http://dev.mysql.com/doc/internals/en/com-query-response.html#packet-Protocol::ColumnDefinition41
 func (mc *mysqlConn) readColumns(count int) ([]mysqlField, error) {
 func (mc *mysqlConn) readColumns(count int) ([]mysqlField, error) {
-	columns := make([]mysqlField, count)
+	columns := makeFields(count)
 
 
 	for i := 0; ; i++ {
 	for i := 0; ; i++ {
 		data, err := mc.readPacket()
 		data, err := mc.readPacket()
@@ -585,7 +585,7 @@ func (mc *mysqlConn) readColumns(count int) ([]mysqlField, error) {
 		if err != nil {
 		if err != nil {
 			return nil, err
 			return nil, err
 		}
 		}
-		columns[i].name = string(name)
+		columns[i].name = string(name) // TODO(bradfitz): garbage. intern these.
 		pos += n
 		pos += n
 
 
 		// Original name [len coded string]
 		// Original name [len coded string]

+ 39 - 2
rows.go

@@ -19,14 +19,31 @@ type mysqlField struct {
 	flags     fieldFlag
 	flags     fieldFlag
 }
 }
 
 
+// mysqlRows is the driver-internal Rows struct that is never given to
+// the database/sql package. This struct is 40 bytes on 64-bit
+// machines and is recycled. Its size isn't very relevant, since we
+// recycle it.
+//
+// Allocate with newMysqlRows (from buffer.go) and return with
+// putMySQLRows.  See also: mysqlRowsI.
 type mysqlRows struct {
 type mysqlRows struct {
 	mc      *mysqlConn
 	mc      *mysqlConn
-	binary  bool
 	columns []mysqlField
 	columns []mysqlField
+	binary  bool // Note: packing small bool fields at the end
 	eof     bool
 	eof     bool
 }
 }
 
 
+// mysqlRowsI implements driver.Rows. Its wrapped *mysqlRows pointer
+// becomes nil and recycled on Close. This struct is kept small (8
+// bytes) to minimize garbage creation.
+type mysqlRowsI struct {
+	*mysqlRows
+}
+
 func (rows *mysqlRows) Columns() []string {
 func (rows *mysqlRows) Columns() []string {
+	if rows == nil {
+		return nil
+	}
 	columns := make([]string, len(rows.columns))
 	columns := make([]string, len(rows.columns))
 	for i := range columns {
 	for i := range columns {
 		columns[i] = rows.columns[i].name
 		columns[i] = rows.columns[i].name
@@ -34,7 +51,18 @@ func (rows *mysqlRows) Columns() []string {
 	return columns
 	return columns
 }
 }
 
 
-func (rows *mysqlRows) Close() (err error) {
+func (ri *mysqlRowsI) Close() error {
+	if ri.mysqlRows == nil {
+		return nil // make Close() idempotent
+	}
+
+	err := ri.mysqlRows.close()
+	putMysqlRows(ri.mysqlRows)
+	ri.mysqlRows = nil
+	return err
+}
+
+func (rows *mysqlRows) close() (err error) {
 	// Remove unread packets from stream
 	// Remove unread packets from stream
 	if !rows.eof {
 	if !rows.eof {
 		if rows.mc == nil || rows.mc.netConn == nil {
 		if rows.mc == nil || rows.mc.netConn == nil {
@@ -50,10 +78,19 @@ func (rows *mysqlRows) Close() (err error) {
 
 
 	rows.mc = nil
 	rows.mc = nil
 
 
+	if !rows.binary { // Binary rows use cached columns
+		putFields(rows.columns)
+	}
+	rows.columns = nil
+
 	return
 	return
 }
 }
 
 
 func (rows *mysqlRows) Next(dest []driver.Value) (err error) {
 func (rows *mysqlRows) Next(dest []driver.Value) (err error) {
+	if rows == nil {
+		return errInvalidConn
+	}
+
 	if rows.eof {
 	if rows.eof {
 		return io.EOF
 		return io.EOF
 	}
 	}

+ 10 - 2
statement.go

@@ -25,7 +25,13 @@ func (stmt *mysqlStmt) Close() error {
 	}
 	}
 
 
 	err := stmt.mc.writeCommandPacketUint32(comStmtClose, stmt.id)
 	err := stmt.mc.writeCommandPacketUint32(comStmtClose, stmt.id)
+
+	if stmt.columns != nil {
+		putFields(stmt.columns)
+		stmt.columns = nil
+	}
 	stmt.mc = nil
 	stmt.mc = nil
+
 	return err
 	return err
 }
 }
 
 
@@ -84,7 +90,9 @@ func (stmt *mysqlStmt) Query(args []driver.Value) (driver.Rows, error) {
 		return nil, err
 		return nil, err
 	}
 	}
 
 
-	rows := &mysqlRows{mc, true, nil, false}
+	rows := newMysqlRows()
+	rows.mc = stmt.mc
+	rows.binary = true
 
 
 	if resLen > 0 {
 	if resLen > 0 {
 		// Columns
 		// Columns
@@ -98,5 +106,5 @@ func (stmt *mysqlStmt) Query(args []driver.Value) (driver.Rows, error) {
 		}
 		}
 	}
 	}
 
 
-	return rows, err
+	return &mysqlRowsI{rows}, err
 }
 }