Skip to content

Commit

Permalink
refactor(xconf): deep copt
Browse files Browse the repository at this point in the history
  • Loading branch information
hui.wang committed Jan 25, 2022
1 parent e47d024 commit 307bdf2
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 7 deletions.
3 changes: 1 addition & 2 deletions tests/all_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -383,9 +383,8 @@ func TestEnvBind(t *testing.T) {
So(cc.HttpAddress, ShouldEqual, host+":"+port)
So(cc.SubTest.Servers["s1"].Timeouts["read"], ShouldEqual, time.Duration(5)*time.Second)
So(x.UpdateWithFieldPathValues("http_address", "${XCONF_HOST_PORT}"), ShouldNotBeNil) // 解析会报错,XCONF_HOST_PORT不存在且为提供默认数值,Option指定ErrEnvBindNotExistWithoutDefault为true
// todo update逻辑应该先保护本地绑定逻辑,当更新会引起绑定逻辑失败时,应该提前在update时返回错误,不能将错误扩散到绑定阶段
_, err = x.Latest()
So(err, ShouldNotBeNil)
So(err, ShouldBeNil)
})
}

Expand Down
8 changes: 3 additions & 5 deletions xconf_remote.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,6 @@ func (x *XConf) notifyChanged() error {
}

func (x *XConf) onContentChanged(name string, confPath string, content []byte) {
x.dynamicUpdate.Lock()
defer x.dynamicUpdate.Unlock()

x.cc.LogDebug(fmt.Sprintf("got update:%s", confPath))
defer func() {
if reason := recover(); reason == nil {
Expand All @@ -83,6 +80,7 @@ func (x *XConf) onContentChanged(name string, confPath string, content []byte) {
err := unmarshal(content, data)

xutil.PanicErrWithWrap(err, "unmarshal_error(%v) ", err)
xutil.PanicErr(x.mergeToDest(confPath, data))
xutil.PanicErr(x.notifyChanged())
xutil.PanicErr(x.commonUpdateAndNotify(func() error {
return x.mergeToDest(confPath, data)
}))
}
5 changes: 5 additions & 0 deletions xconf_update.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,12 @@ func (x *XConf) commonUpdateAndNotify(f func() error) (err error) {
if !x.hasParsed {
return errors.New("should parsed first")
}
// 更新前保护本地数据,如果更新期间出现错误,则将数据回滚
dataLatestCachedBackup := xutil.DeepCopy(x.dataLatestCached)
defer func() {
if err != nil {
x.dataLatestCached = dataLatestCachedBackup.(map[string]interface{})
}
if reason := recover(); reason != nil {
err = fmt.Errorf("%v", reason)
}
Expand Down
114 changes: 114 additions & 0 deletions xutil/deepcopy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package xutil

import (
"reflect"
"time"
)

// DeepCopyInterface for delegating copy process to type
type DeepCopyInterface interface {
DeepCopy() interface{}
}

// DeepCopy creates a deep copy of whatever is passed to it and returns the copy
// in an interface{}. The returned value will need to be asserted to the
// correct type.
func DeepCopy(src interface{}) interface{} {
if src == nil {
return nil
}

// Make the interface a reflect.Value
original := reflect.ValueOf(src)

// Make a copy of the same type as the original.
cpy := reflect.New(original.Type()).Elem()

// Recursively copy the original.
copyRecursive(original, cpy)

// Return the copy as an interface.
return cpy.Interface()
}

// copyRecursive does the actual copying of the interface. It currently has
// limited support for what it can handle. Add as needed.
func copyRecursive(original, cpy reflect.Value) {
// check for implement deepcopy.Interface
if original.CanInterface() {
if copier, ok := original.Interface().(DeepCopyInterface); ok {
cpy.Set(reflect.ValueOf(copier.DeepCopy()))
return
}
}

// handle according to original's Kind
switch original.Kind() {
case reflect.Ptr:
// Get the actual value being pointed to.
originalValue := original.Elem()

// if it isn't valid, return.
if !originalValue.IsValid() {
return
}
cpy.Set(reflect.New(originalValue.Type()))
copyRecursive(originalValue, cpy.Elem())

case reflect.Interface:
// If this is a nil, don't do anything
if original.IsNil() {
return
}
// Get the value for the interface, not the pointer.
originalValue := original.Elem()

// Get the value by calling Elem().
copyValue := reflect.New(originalValue.Type()).Elem()
copyRecursive(originalValue, copyValue)
cpy.Set(copyValue)

case reflect.Struct:
t, ok := original.Interface().(time.Time)
if ok {
cpy.Set(reflect.ValueOf(t))
return
}
// Go through each field of the struct and copy it.
for i := 0; i < original.NumField(); i++ {
// The Type's StructField for a given field is checked to see if StructField.PkgPath
// is set to determine if the field is exported or not because CanSet() returns false
// for settable fields. I'm not sure why. -mohae
if original.Type().Field(i).PkgPath != "" {
continue
}
copyRecursive(original.Field(i), cpy.Field(i))
}

case reflect.Slice:
if original.IsNil() {
return
}
// Make a new slice and copy each element.
cpy.Set(reflect.MakeSlice(original.Type(), original.Len(), original.Cap()))
for i := 0; i < original.Len(); i++ {
copyRecursive(original.Index(i), cpy.Index(i))
}

case reflect.Map:
if original.IsNil() {
return
}
cpy.Set(reflect.MakeMap(original.Type()))
for _, key := range original.MapKeys() {
originalValue := original.MapIndex(key)
copyValue := reflect.New(originalValue.Type()).Elem()
copyRecursive(originalValue, copyValue)
copyKey := DeepCopy(key.Interface())
cpy.SetMapIndex(reflect.ValueOf(copyKey), copyValue)
}

default:
cpy.Set(original)
}
}

0 comments on commit 307bdf2

Please sign in to comment.