gob: 接口只在编码时注册,而没有在解码时注册。

huangapple go评论73阅读模式
英文:

gob: interface is only registered on Encode but not on Decode

问题

我正在开发一个使用Datastore的App Engine应用程序。我试图对一个接口进行gob编码,并将其存储到Datastore中。但是当我尝试从Datastore加载时,出现以下错误:

gob: 未为接口注册名称:"main27155.strand"

奇怪的是,在调用save()方法之后,load()方法开始正常工作。它不再返回错误,并且从Datastore中加载的所有内容都按预期加载。但是当我重新启动实例时,load()方法再次停止工作。

我提到的load()save()方法是指datastore.PropertyLoadSaver接口定义的方法。

从外观上看,这似乎是一个与gob注册类型/接口有关的问题,但是我在load()save()方法中都有完全相同的gob.Register()调用。

我甚至尝试将gob.Register()调用从load()save()方法中移除,并添加到init()函数中。观察到完全相同的行为。

如何在冷启动时加载我的Datastore数据?

英文:

I'm working on an appengine app using the datastore. I'm attempting to gob
encode an interface and store it into the datastore. But when I try to load from
the datastore, I get the error:

gob: name not registered for interface: "main27155.strand"

The peculiar thing is that the load() method starts working after having
called the save() method. It no longer returns an error, and everything saved
in the datastore is loaded as expected. But when I restart the intance, the
load() method stops working again.

The load and save methods I mention refer to the methods defined by the
datastore.PropertyLoadSaver interface

From the looks of it, it seems like a problem with registering the
type/interfaces with gob, but I have exactly the same gob.Register() calls in
both the load() and save() methods.

I even tried removing the gob.Register() calls from both load and save methods
and adding it to init(). The exact same behavior was observed.

How can I load my datastore on a cold start?

type bio struct {¬                          
	Id       string¬                        
	Hp       int¬                           
	godie    chan bool //should be buffered¬
	dam      chan int¬                      
	Genetics dna¬                           
}¬                                          

type dna interface {
	decode() mRNA
	Get(int) trait
	Set(int, trait)
	Duplicate() dna
	Len() int
}
type trait interface {
	mutate() trait
}

// implements dna{}
type strand []trait

// implements trait{}
type tdecoration string
type color struct {
	None bool // If true, colors are not shown in theme
	Bg   bool // If true, color is a background color
	R    int  // 0-255
	G    int
	B    int
}

.

func start(w http.ResponseWriter, r *http.Request) error {
	c := appengine.NewContext(r)

	var bs []bio
	if _, err := datastore.NewQuery("bio").GetAll(c, &bs); err != nil {
		log.Println("bs is len: ", len(bs))
		return err
	}

	...
	return nil
}

func stop(w http.ResponseWriter, r *http.Request) error {
	c := appengine.NewContext(r)
	log.Println("Saving top 20 colors")
	var k []*datastore.Key
	var bs []*bio
	stat := getStats()
	for i, b := range stat.Leaderboard {
		k = append(k, datastore.NewKey(c, "bio", b.Id, 0, nil))
		bv := b
		bs = append(bs, &bv)
		// At most 20 bios survive across reboots
		if i > 178 {
			break
		}
	}

	// Assemble slice of keys for deletion
	dk, err := datastore.NewQuery("bio").KeysOnly().GetAll(c, nil)
	if err != nil {
		return errors.New(fmt.Sprintf("Query error: %s", err.Error()))
	}

	fn := func(c appengine.Context) error {
		// Delete all old entries
		err := datastore.DeleteMulti(c, dk)
		if err != nil {
			return errors.New(fmt.Sprintf("Delete error: %s", err.Error()))
		}

		// save the elite in the datastore
		_, err = datastore.PutMulti(c, k, bs)
		if err != nil {
			return err
		}
		return nil
	}

	return datastore.RunInTransaction(c, fn, &datastore.TransactionOptions{XG: true})
}

// satisfy datastore PropertyLoadSaver interface ===============================

func (b *bio) Load(c <-chan datastore.Property) error {
	gob.Register(&color{})
	gob.Register(new(tdecoration))
	var str strand
	gob.Register(str)

	tmp := struct {
		Id     string
		Hp     int
		Gengob []byte
	}{}
	if err := datastore.LoadStruct(&tmp, c); err != nil {
		return err
	}

	b.Id = tmp.Id
	b.Hp = tmp.Hp

	return gob.NewDecoder(strings.NewReader(string(tmp.Gengob))).Decode(&(b.Genetics))
}
func (b *bio) Save(c chan<- datastore.Property) error {
	defer close(c)
	gob.Register(&color{})
	gob.Register(new(tdecoration))
	var str strand
	gob.Register(str)

	var buf bytes.Buffer
	gen := b.Genetics
	if err := gob.NewEncoder(&buf).Encode(&gen); err != nil {
		log.Println(err)
		return err
	}
	dp := []datastore.Property{
		{Name: "Id", Value: b.Id},
		{Name: "Hp", Value: int64(b.Hp)},
		{Name: "Gengob", Value: buf.Bytes(), NoIndex: true},
	}
	for _, p := range dp {
		c <- p
	}
	return nil
}

Additional info: This behavior was not present before I stuffed the datastore
calls in stop() into datastore.RunInTransaction()

答案1

得分: 1

在init()函数中使用RegisterName()注册所有类型。从存储中删除所有现有数据,然后你就可以开始了。

每次构建应用程序时,App Engine都会为主包生成一个混淆的名称。由Register()生成的名称包括这个混淆的包名。使用混淆名称编码的任何gob只能在相同构建的应用程序中读取。如果通过修改代码导致应用程序重新构建,那么应用程序将无法解码先前存储的gob。

英文:

Register all types an in init() functions using RegisterName(). Delete all existing data from the store and you should be good to go.

App Engine generates a mangled name for the main package every time the application is built. The name generated by Register() includes this mangled package name. Any gobs encoded with the mangled name will only be readable using the same build of the app. If you cause the application to be rebuilt by modifying the code, then the app will not be able to decode gobs stored previously.

huangapple
  • 本文由 发表于 2015年9月6日 04:49:24
  • 转载请务必保留本文链接:https://go.coder-hub.com/32417541.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定