/* 此api实现复制写入操作 步骤如下: 1.先写入本地 2. 如果需要复制则 将消息投递replicate到master 3. 如果2操作成功完成则返回 4. 如果2失败,则删除本地副本并投递删除消息到master 5. 返回4的结果 同理删除 */ func ReplicatedWrite(masterNode string, s *storage.Store, volumeId storage.VolumeId, needle *storage.Needle, r *http.Request) (size uint32, errorStatus string) { ret, err := s.Write(volumeId, needle) needToReplicate := !s.HasVolume(volumeId) if err != nil { errorStatus = "Failed to write to local disk (" + err.Error() + ")" } else if ret > 0 { //写入成功 needToReplicate = needToReplicate || s.GetVolume(volumeId).NeedToReplicate() } else { errorStatus = "Failed to write to local disk" } //??? --lewgun if !needToReplicate && ret > 0 { needToReplicate = s.GetVolume(volumeId).NeedToReplicate() } if needToReplicate { //send to other replica locations if r.FormValue("type") != "replicate" { //只有最原始的才会执行distributedOperation //向master复制 if !distributedOperation(masterNode, s, volumeId, func(location operation.Location) bool { _, err := operation.Upload( "http://"+location.Url+r.URL.Path+"?type=replicate&ts="+strconv.FormatUint(needle.LastModified, 10), string(needle.Name), bytes.NewReader(needle.Data), needle.IsGzipped(), string(needle.Mime)) return err == nil }) { ret = 0 errorStatus = "Failed to write to replicas for volume " + volumeId.String() }// if !distributedOperation(masterNode, } // if r.FormValue("type") != "replicate" { } //复制失败,删除本地副本 if errorStatus != "" { if _, err = s.Delete(volumeId, needle); err != nil { errorStatus += "\nCannot delete " + strconv.FormatUint(needle.Id, 10) + " from " + volumeId.String() + ": " + err.Error() } else { distributedOperation( masterNode, s, volumeId, func(location operation.Location) bool { return nil == util.Delete("http://"+location.Url+r.URL.Path+"?type=replicate") } ) // distributedOperation(...) } // if errorStatus != "" { } size = ret return }
func distributedOperation(masterNode string, store *storage.Store, volumeId storage.VolumeId, op func(location operation.Location) bool) bool { if lookupResult, lookupErr := operation.Lookup(masterNode, volumeId.String()); lookupErr == nil { length := 0 selfUrl := (store.Ip + ":" + strconv.Itoa(store.Port)) results := make(chan bool) for _, location := range lookupResult.Locations { //排除自己 if location.Url != selfUrl { length++ //异步复制 go func(location operation.Location, results chan bool) { results <- op(location) }(location, results) } } ret := true //等待操作结束 无论成功与否 for i := 0; i < length; i++ { ret = ret && <-results } return ret } else { glog.V(0).Infoln("Failed to lookup for", volumeId, lookupErr.Error()) } return false }
//http://192.168.1.1:8080/admin/assign_volume func AllocateVolume(dn *DataNode, vid storage.VolumeId, option *VolumeGrowOption) error { values := make(url.Values) values.Add("volume", vid.String()) values.Add("collection", option.Collection) values.Add("replication", option.ReplicaPlacement.String()) values.Add("ttl", option.Ttl.String()) jsonBlob, err := util.Post("http://"+dn.PublicUrl+"/admin/assign_volume", values) if err != nil { return err } var ret AllocateVolumeResult if err := json.Unmarshal(jsonBlob, &ret); err != nil { return err } if ret.Error != "" { return errors.New(ret.Error) } return nil }
func (vg *VolumeGrowth) grow(topo *Topology, vid storage.VolumeId, option *VolumeGrowOption, servers ...*DataNode) error { for _, server := range servers { if err := AllocateVolume(server, vid, option); err == nil { vi := storage.VolumeInfo{ Id: vid, Size: 0, Collection: option.Collection, ReplicaPlacement: option.ReplicaPlacement, Ttl: option.Ttl, Version: storage.CurrentVersion, } server.AddOrUpdateVolume(vi) topo.RegisterVolumeLayout(vi, server) glog.V(0).Infoln("Created Volume", vid, "on", server) } else { glog.V(0).Infoln("Failed to assign", vid, "to", servers, "error", err) return fmt.Errorf("Failed to assign %s: %s", vid.String(), err.Error()) } } return nil }