Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix metadata, add helper generator #612

Closed

Conversation

Bolodya1997
Copy link

@Bolodya1997 Bolodya1997 commented Dec 3, 2020

Issues

  1. metadata server doesn't pass error from the next.Server() on request
  2. metadata provides raw sync.Map with no types

Solution

  1. Fix, add tests
  2. Create metadata helper generator:

gen.go:

package connect

//go:generate bash -c "genny -in=$(go list -f '{{.Dir}} github.com/networkservicemesh/sdk/pkg/tools/metadatahelper)/meta_data.template.go -out=client_meta_data.gen.go -pkg=$GOPACKAGE gen 'prefix=client valueType=networkservice.NetworkServiceClient'"

client_meta_data.gen.go:

// This file was automatically generated by genny.
// Any changes will be lost if this file is regenerated.
// see https://github.com/cheekybits/genny

package connect

import (
	"context"
	"sync"

	"github.com/networkservicemesh/api/pkg/api/networkservice"
	"github.com/networkservicemesh/sdk/pkg/networkservice/utils/metadata"
)

type _ClientKeyType struct{}

type clientMetadataHelper struct {
	m *sync.Map
}

func clientMetadata(ctx context.Context, isClient bool) *clientMetadataHelper {
	return &clientMetadataHelper{
		m: metadata.Map(ctx, isClient),
	}
}

func (h *clientMetadataHelper) Store(value networkservice.NetworkServiceClient) {
	h.m.Store(_ClientKeyType{}, value)
}

func (h *clientMetadataHelper) LoadOrStore(value networkservice.NetworkServiceClient) (networkservice.NetworkServiceClient, bool) {
	raw, ok := h.m.LoadOrStore(_ClientKeyType{}, value)
	return raw.(networkservice.NetworkServiceClient), ok
}

func (h *clientMetadataHelper) Load() (networkservice.NetworkServiceClient, bool) {
	if raw, ok := h.m.Load(_ClientKeyType{}); ok {
		return raw.(networkservice.NetworkServiceClient), true
	}
	return func() (v networkservice.NetworkServiceClient) { return }(), false
}

func (h *clientMetadataHelper) LoadAndDelete() (networkservice.NetworkServiceClient, bool) {
	if raw, ok := h.m.LoadAndDelete(_ClientKeyType{}); ok {
		return raw.(networkservice.NetworkServiceClient), true
	}
	return func() (v networkservice.NetworkServiceClient) { return }(), false
}

func (h *clientMetadataHelper) Delete() {
	h.m.Delete(_ClientKeyType{})
}

Signed-off-by: Vladimir Popov <vladimir.popov@xored.com>
Signed-off-by: Vladimir Popov <vladimir.popov@xored.com>
@Bolodya1997 Bolodya1997 force-pushed the fix-metadata branch 2 times, most recently from 0f92803 to 380faf5 Compare December 3, 2020 12:40
Signed-off-by: Vladimir Popov <vladimir.popov@xored.com>
@@ -37,12 +37,16 @@ func NewClient() networkservice.NetworkServiceClient {
}

func (m *metaDataClient) Request(ctx context.Context, request *networkservice.NetworkServiceRequest, opts ...grpc.CallOption) (*networkservice.Connection, error) {
ctx = store(ctx, request.GetConnection().GetId(), &m.Map)
connID := request.GetConnection().GetId()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Although... it should always be true that the conn..GetId() below is the same as the request.GetConnection().GetId() ... its good defensive coding what you've done here, but an issue here would have been a sign of a bug in updatetoken ...

```go
package connect

//go:generate genny -in=../../utils/metadata/helper/meta_data.template.go -out=client_meta_data.gen.go -pkg=connect gen "prefix=client valueType=networkservice.NetworkServiceClient"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a way to reference the meta_data_template.go file that doesn't involve file system? I ask, because this is all fine for using metadata inside the sdk repo... but would not work at all from the sdk-vpp repo...

Copy link
Author

@Bolodya1997 Bolodya1997 Dec 4, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have added scripts/genny.sh, so now we can get template by package + file name:

//go:generate bash ../../../scripts/genny.sh -in=github.com/networkservicemesh/sdk/pkg/tools/metadatahelper/meta_data.template.go -out=int_meta_data.gen.go gen "prefix=Int valueType=int"

such script can be copied to other repositories.

UPD: we can use go list -f '{{.Dir}}' package-name to find imported package in file system. And it is actually imported, because we are dependent on module containing such package.

//go:generate bash -c "genny -in=$(go list -f '{{.Dir}} github.com/networkservicemesh/sdk/pkg/tools/metadatahelper)/meta_data.template.go -out=client_meta_data.gen.go -pkg=$GOPACKAGE gen 'prefix=client valueType=networkservice.NetworkServiceClient'"

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clever solution :)

@Bolodya1997 Bolodya1997 marked this pull request as draft December 4, 2020 04:48
@Bolodya1997 Bolodya1997 marked this pull request as ready for review December 4, 2020 09:39
Signed-off-by: Vladimir Popov <vladimir.popov@xored.com>
Signed-off-by: Vladimir Popov <vladimir.popov@xored.com>
Copy link
Member

@denis-tingaikin denis-tingaikin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I very like the idea to use genny :)

But I feel we still able to not use it because actually, we can solve this via exist sync map generator.

Example

import (
	"sync"
	"testing"
	"github.com/networkservicemesh/api/pkg/api/registry"
	"github.com/networkservicemesh/sdk/pkg/registry/memory"
	"github.com/stretchr/testify/require"
)
func TestCastSyncMapToTypedMap(t *testing.T) {
	var metaDataMap sync.Map // == metadata.Map(...)
	metaDataMap.Store("key", &registry.NetworkServiceEndpoint{})
	memoryMap := (*memory.NetworkServiceEndpointSyncMap)(&metaDataMap)
	val, _ := memoryMap.Load("key")
	require.NotNil(t, val)
}

Note: by registry.NetworkServiceEndpoint can be used any other generated syncmap :)

Signed-off-by: Vladimir Popov <vladimir.popov@xored.com>
@edwarnicke
Copy link
Member

I very like the idea to use genny :)

But I feel we still able to not use it because actually, we can solve this via exist sync map generator.

Example

import (
	"sync"
	"testing"
	"github.com/networkservicemesh/api/pkg/api/registry"
	"github.com/networkservicemesh/sdk/pkg/registry/memory"
	"github.com/stretchr/testify/require"
)
func TestCastSyncMapToTypedMap(t *testing.T) {
	var metaDataMap sync.Map // == metadata.Map(...)
	metaDataMap.Store("key", &registry.NetworkServiceEndpoint{})
	memoryMap := (*memory.NetworkServiceEndpointSyncMap)(&metaDataMap)
	val, _ := memoryMap.Load("key")
	require.NotNil(t, val)
}

Note: by registry.NetworkServiceEndpoint can be used any other generated syncmap :)

I like the idea of using a simpler approach via syncmap... but don't quite follow your proposal above...

@denis-tingaikin
Copy link
Member

denis-tingaikin commented Dec 4, 2020

@edwarnicke I guess we can try to use generated sync maps to cast map from metadata to typed sync.Map (look at code suggestion #612 (review))
In this case we do not need add genny.

Vladimir Popov added 2 commits December 7, 2020 11:16
Signed-off-by: Vladimir Popov <vladimir.popov@xored.com>
Signed-off-by: Vladimir Popov <vladimir.popov@xored.com>
@Bolodya1997 Bolodya1997 marked this pull request as draft December 7, 2020 07:43
@Bolodya1997
Copy link
Author

@edwarnicke
We have discussed this internally with @denis-tingajkin and @haiodo and probably such helper is too much for the cases we want to cover with it:

  • as long as per-connection metadata is being cleaned up with on connection close, we don't actually need LoadAndDelete and Delete methods;
  • for the Store and LoadOrStore methods we most probably need only one of them, but not both.

Here is the thing we need to use in chain element:

type keyType struct{}

func storeConnInfo(ctx context.Context, connInfo *connectionInfo) {
	metadata.Map(ctx, false).Store(keyType{}, connInfo)
}

func loadConnInfo(ctx context.Context) (*connectionInfo, bool) {
	if raw, ok := metadata.Map(ctx, false).Load(keyType{}); ok {
		return raw.(*connectionInfo), true
	}
	return nil, false
}

and we don't want to create a template for it, because it is too small and making a template would affect our ability to give good names for such Store/Load methods.

So I am closing this PR and opening another one to fix issues, add testing and add metadata chain elements into the client, endpoint chains #621.

@Bolodya1997 Bolodya1997 closed this Dec 7, 2020
@Bolodya1997 Bolodya1997 deleted the fix-metadata branch January 20, 2021 06:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants