Files
swift/go/objectserver/repconn.go
Michael Barton 35ec05b8ae go: replication prioritizer
This adds two new commands:

  hummingbird moveparts [old ring.gz] [new ring.gz]

After a rebalance, moveparts will diff the two rings and instruct the
object replicators (via HTTP call) to execute jobs that bulk move
any re-assigned partitions.

  hummingbird restoredevice [ip] [device-name]

restoredevice will instruct peers of the selected device to sync over
their copies of any partitions that should be on that drive.  It can
be used to speed up restoring a drive after it's been swapped.

They should both run until all of the jobs have been taken up by the
replicator, so a large job might take a while.

This means the replicator has a semi-real HTTP server now, and not
just a debug port.  It binds to the replication object server's port
+ 500, which is totally arbitrary and open to bikeshedding.

Change-Id: I4bbb2719657eb84c5191e053a1654e986511bb95
2016-03-01 21:38:18 +00:00

162 lines
3.5 KiB
Go

// Copyright (c) 2015 Rackspace
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package objectserver
import (
"bufio"
"encoding/binary"
"encoding/json"
"fmt"
"io"
"net"
"net/http"
"net/http/httputil"
"time"
)
var RepUnmountedError = fmt.Errorf("Device unmounted")
var repDialer = (&net.Dialer{Timeout: 5 * time.Second, KeepAlive: 30 * time.Second}).Dial
const repITimeout = time.Minute * 10
const repOTimeout = time.Minute
type BeginReplicationRequest struct {
Device string
Partition string
NeedHashes bool
}
type BeginReplicationResponse struct {
Hashes map[string]string
}
type SyncFileRequest struct {
Path string
Xattrs string
Size int64
Done bool
}
type SyncFileResponse struct {
Exists bool
NewerExists bool
GoAhead bool
Msg string
}
type FileUploadResponse struct {
Success bool
Msg string
}
type RepConn struct {
rw *bufio.ReadWriter
c net.Conn
Disconnected bool
}
func (r *RepConn) SendMessage(v interface{}) error {
r.c.SetDeadline(time.Now().Add(repOTimeout))
jsoned, err := json.Marshal(v)
if err != nil {
r.Close()
return err
}
if err := binary.Write(r.rw, binary.BigEndian, uint32(len(jsoned))); err != nil {
r.Close()
return err
}
if _, err := r.rw.Write(jsoned); err != nil {
r.Close()
return err
}
if err := r.rw.Flush(); err != nil {
r.Close()
return err
}
return nil
}
func (r *RepConn) RecvMessage(v interface{}) (err error) {
r.c.SetDeadline(time.Now().Add(repITimeout))
var length uint32
if err = binary.Read(r.rw, binary.BigEndian, &length); err != nil {
r.Close()
return
}
data := make([]byte, length)
if _, err = io.ReadFull(r.rw, data); err != nil {
r.Close()
return
}
if err = json.Unmarshal(data, v); err != nil {
r.Close()
return
}
return
}
func (r *RepConn) Write(data []byte) (l int, err error) {
r.c.SetDeadline(time.Now().Add(repOTimeout))
if l, err = r.rw.Write(data); err != nil {
r.Close()
}
return
}
func (r *RepConn) Flush() (err error) {
r.c.SetDeadline(time.Now().Add(repOTimeout))
if err = r.rw.Flush(); err != nil {
r.Close()
}
return
}
func (r *RepConn) Read(data []byte) (l int, err error) {
r.c.SetDeadline(time.Now().Add(repITimeout))
if l, err = io.ReadFull(r.rw, data); err != nil {
r.Close()
}
return
}
func (r *RepConn) Close() {
r.Disconnected = true
r.c.Close()
}
func NewRepConn(ip string, port int, device string, partition string) (*RepConn, error) {
url := fmt.Sprintf("http://%s:%d/%s/%s", ip, port, device, partition)
req, err := http.NewRequest("REPCONN", url, nil)
if err != nil {
return nil, err
}
conn, err := repDialer("tcp", req.URL.Host)
if err != nil {
return nil, err
}
hc := httputil.NewClientConn(conn, nil)
resp, err := hc.Do(req)
if err != nil {
return nil, err
}
if resp.StatusCode/100 != 2 {
return nil, RepUnmountedError
}
newc, _ := hc.Hijack()
return &RepConn{rw: bufio.NewReadWriter(bufio.NewReader(newc), bufio.NewWriter(newc)), c: newc}, nil
}