Giter Club home page Giter Club logo

azure-event-hubs-go's Introduction

Please note, a newer package is available: azeventhubs as of [2023-05-09]. We strongly encourage you to upgrade. See the Migration Guide for more details.

Microsoft Azure Event Hubs Client for Golang

Go Report Card godoc Build Status Coverage Status

Azure Event Hubs is a highly scalable publish-subscribe service that can ingest millions of events per second and stream them into multiple applications. This lets you process and analyze the massive amounts of data produced by your connected devices and applications. Once Event Hubs has collected the data, you can retrieve, transform and store it by using any real-time analytics provider or with batching/storage adapters.

Refer to the online documentation to learn more about Event Hubs in general.

This library is a pure Golang implementation of Azure Event Hubs over AMQP.

Install with Go modules

If you want to use stable versions of the library, please use Go modules.

NOTE: versions prior to 3.0.0 depend on pack.ag/amqp which is no longer maintained. Any new code should not use versions prior to 3.0.0.

Using go get targeting version 3.x.x

go get -u github.com/Azure/azure-event-hubs-go/v3

Using go get targeting version 2.x.x

go get -u github.com/Azure/azure-event-hubs-go/v2

Using go get targeting version 1.x.x

go get -u github.com/Azure/azure-event-hubs-go

Using Event Hubs

In this section we'll cover some basics of the library to help you get started.

This library has two main dependencies, vcabbage/amqp and Azure AMQP Common. The former provides the AMQP protocol implementation and the latter provides some common authentication, persistence and request-response message flows.

Quick start

Let's send and receive "hello, world!" to all the partitions in an Event Hub.

package main

import (
	"context"
	"fmt"
	"os"
	"os/signal"
	"time"
	
	"github.com/Azure/azure-event-hubs-go/v3"
)

func main() {
	connStr := "Endpoint=sb://namespace.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=superSecret1234=;EntityPath=hubName"
	hub, err := eventhub.NewHubFromConnectionString(connStr)

	if err != nil {
		fmt.Println(err)
		return
	}

	ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
	defer cancel()

	// send a single message into a random partition
	err = hub.Send(ctx, eventhub.NewEventFromString("hello, world!"))
	if err != nil {
		fmt.Println(err)
		return
	}

	handler := func(c context.Context, event *eventhub.Event) error {
		fmt.Println(string(event.Data))
		return nil
	}

	// listen to each partition of the Event Hub
	runtimeInfo, err := hub.GetRuntimeInformation(ctx)
	if err != nil {
		fmt.Println(err)
		return
	}
	
	for _, partitionID := range runtimeInfo.PartitionIDs { 
		// Start receiving messages 
		// 
		// Receive blocks while attempting to connect to hub, then runs until listenerHandle.Close() is called 
		// <- listenerHandle.Done() signals listener has stopped
		// listenerHandle.Err() provides the last error the receiver encountered 
		listenerHandle, err := hub.Receive(ctx, partitionID, handler)
		if err != nil {
			fmt.Println(err)
			return
		}
    }

	// Wait for a signal to quit:
	signalChan := make(chan os.Signal, 1)
	signal.Notify(signalChan, os.Interrupt, os.Kill)
	<-signalChan

	err = hub.Close(context.Background())
	if err != nil {
		fmt.Println(err)
	}
}

Environment Variables

In the above example, the Hub instance was created using environment variables. Here is a list of environment variables used in this project.

Event Hub env vars

  • EVENTHUB_NAMESPACE the namespace of the Event Hub instance
  • EVENTHUB_NAME the name of the Event Hub instance

SAS TokenProvider environment variables:

There are two sets of environment variables which can produce a SAS TokenProvider

  1. Expected Environment Variables:

    • EVENTHUB_KEY_NAME the name of the Event Hub key
    • EVENTHUB_KEY_VALUE the secret for the Event Hub key named in EVENTHUB_KEY_NAME
  2. Expected Environment Variable:

    • EVENTHUB_CONNECTION_STRING connection string from the Azure portal like: Endpoint=sb://foo.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=fluffypuppy;EntityPath=hubName

AAD TokenProvider environment variables:

  1. Client Credentials: attempt to authenticate with a Service Principal via
    • AZURE_TENANT_ID the Azure Tenant ID
    • AZURE_CLIENT_ID the Azure Application ID
    • AZURE_CLIENT_SECRET a key / secret for the corresponding application
  2. Client Certificate: attempt to authenticate with a Service Principal via
    • AZURE_TENANT_ID the Azure Tenant ID
    • AZURE_CLIENT_ID the Azure Application ID
    • AZURE_CERTIFICATE_PATH the path to the certificate file
    • AZURE_CERTIFICATE_PASSWORD the password for the certificate

The Azure Environment used can be specified using the name of the Azure Environment set in "AZURE_ENVIRONMENT" var.

Authentication

Event Hubs offers a couple different paths for authentication, shared access signatures (SAS) and Azure Active Directory (AAD) JWT authentication. Both token types are available for use and are exposed through the TokenProvider interface.

// TokenProvider abstracts the fetching of authentication tokens
TokenProvider interface {
    GetToken(uri string) (*Token, error)
}

SAS token provider

The SAS token provider uses the namespace of the Event Hub, the name of the "Shared access policy" key and the value of the key to produce a token.

You can create new Shared access policies through the Azure portal as shown below. SAS policies in the Azure portal

You can create a SAS token provider in a couple different ways. You can build one with a key name and key value like this.

provider := sas.TokenProviderWithKey("myKeyName", "myKeyValue")

Or, you can create a token provider from environment variables like this.

// TokenProviderWithEnvironmentVars creates a new SAS TokenProvider from environment variables
//
// There are two sets of environment variables which can produce a SAS TokenProvider
//
// 1) Expected Environment Variables:
//   - "EVENTHUB_KEY_NAME" the name of the Event Hub key
//   - "EVENTHUB_KEY_VALUE" the secret for the Event Hub key named in "EVENTHUB_KEY_NAME"
//
// 2) Expected Environment Variable:
//   - "EVENTHUB_CONNECTION_STRING" connection string from the Azure portal

provider, err := sas.NewTokenProvider(sas.TokenProviderWithEnvironmentVars())

AAD JWT token provider

The AAD JWT token provider uses Azure Active Directory to authenticate the service and acquire a token (JWT) which is used to authenticate with Event Hubs. The authenticated identity must have Contributor role based authorization for the Event Hub instance. This article provides more information about this preview feature.

The easiest way to create a JWT token provider is via environment variables.

// 1. Client Credentials: attempt to authenticate with a Service Principal via "AZURE_TENANT_ID", "AZURE_CLIENT_ID" and
//    "AZURE_CLIENT_SECRET"
//
// 2. Client Certificate: attempt to authenticate with a Service Principal via "AZURE_TENANT_ID", "AZURE_CLIENT_ID",
//    "AZURE_CERTIFICATE_PATH" and "AZURE_CERTIFICATE_PASSWORD"
//
// 3. Managed Service Identity (MSI): attempt to authenticate via MSI
//
//
// The Azure Environment used can be specified using the name of the Azure Environment set in "AZURE_ENVIRONMENT" var.
provider, err := aad.NewJWTProvider(aad.JWTProviderWithEnvironmentVars())

You can also provide your own adal.ServicePrincipalToken.

config := &aad.TokenProviderConfiguration{
    ResourceURI: azure.PublicCloud.ResourceManagerEndpoint,
    Env:         &azure.PublicCloud,
}

spToken, err := config.NewServicePrincipalToken()
if err != nil {
    // handle err
}
provider, err := aad.NewJWTProvider(aad.JWTProviderWithAADToken(aadToken))

Send And Receive

The basics of messaging are sending and receiving messages. Here are the different ways you can do that.

Sending to a particular partition

By default, a Hub will send messages any of the load balanced partitions. Sometimes you want to send to only a particular partition. You can do this in two ways.

  1. You can supply a partition key on an event
    event := eventhub.NewEventFromString("foo")
    event.PartitionKey = "bazz"
    hub.Send(ctx, event) // send event to the partition ID to which partition key hashes
  2. You can build a hub instance that will only send to one partition.
    partitionID := "0"
    hub, err := eventhub.NewHubFromEnvironment(eventhub.HubWithPartitionedSender(partitionID))

Sending batches of events

Sending a batch of messages is more efficient than sending a single message. SendBatch takes an *EventBatchIterator that will automatically create batches from a slice of *Event.

import (
    eventhub "github.com/Azure/azure-event-hubs-go/v3"
)
...
var events []*eventhub.Event
events = append(events, eventhub.NewEventFromString("one"))
events = append(events, eventhub.NewEventFromString("two"))
events = append(events, eventhub.NewEventFromString("three"))

err := client.SendBatch(ctx, eventhub.NewEventBatchIterator(events...))

Controlling retries for sends

By default, a Hub will retry sending messages forever if the errors that occur are retryable (for instance, network timeouts. You can control the number of retries using the HubWithSenderMaxRetryCount option when constructing your Hub client. For instance, to limit the number of retries to 5:

// NOTE: you can use any 'NewHub*' method.
eventhub.NewHubFromConnectionString("<connection string>", eventhub.HubWithSenderMaxRetryCount(5))

Receiving

When receiving messages from an Event Hub, you always need to specify the partition you'd like to receive from. Hub.Receive is a non-blocking call, which takes a message handler func and options. Since Event Hub is just a long log of messages, you also have to tell it where to start from. By default, a receiver will start from the beginning of the log, but there are options to help you specify your starting offset.

The Receive func returns a handle to the running receiver and an error. If error is returned, the receiver was unable to start. If error is nil, the receiver is running and can be stopped by calling Close on the Hub or the handle returned.

  • Receive messages from a partition from the beginning of the log
    handle, err := hub.Receive(ctx, partitionID, func(ctx context.Context, event *eventhub.Event) error {
        // do stuff
    })
  • Receive from the latest message onward
    handle, err := hub.Receive(ctx, partitionID, handler, eventhub.ReceiveWithLatestOffset())
  • Receive from a specified offset
    handle, err := hub.Receive(ctx, partitionID, handler, eventhub.ReceiveWithStartingOffset(offset))

At some point, a receiver process is going to stop. You will likely want it to start back up at the spot that it stopped processing messages. This is where message offsets can be used to start from where you have left off.

The Hub struct can be customized to use an persist.CheckpointPersister. By default, a Hub uses an in-memory CheckpointPersister, but accepts anything that implements the persist.CheckpointPersister interface.

// CheckpointPersister provides persistence for the received offset for a given namespace, hub name, consumer group, partition Id and
// offset so that if a receiver where to be interrupted, it could resume after the last consumed event.
CheckpointPersister interface {
    Write(namespace, name, consumerGroup, partitionID string, checkpoint Checkpoint) error
    Read(namespace, name, consumerGroup, partitionID string) (Checkpoint, error)
}

For example, you could use the persist.FilePersister to save your checkpoints to a directory.

persister, err := persist.NewFilePersister(directoryPath)
if err != nil {
	// handle err
}
hub, err := eventhub.NewHubFromEnvironment(eventhub.HubWithOffsetPersistence(persister))

Event Processor Host

The key to scale for Event Hubs is the idea of partitioned consumers. In contrast to the competing consumers pattern, the partitioned consumer pattern enables high scale by removing the contention bottleneck and facilitating end to end parallelism.

The Event Processor Host (EPH) is an intelligent consumer agent that simplifies the management of checkpointing, leasing, and parallel event readers. EPH is intended to be run across multiple processes and machines while load balancing message consumers. A message consumer in EPH will take a lease on a partition, begin processing messages and periodically write a check point to a persistent store. If at any time a new EPH process is added or lost, the remaining processors will balance the existing leases amongst the set of EPH processes.

The default implementation of partition leasing and check pointing is based on Azure Storage. Below is an example using EPH to start listening to all of the partitions of an Event Hub and print the messages received.

Receiving Events

package main

import (
	"context"
	"fmt"
	"os"
	"os/signal"
	"time"
	
	"github.com/Azure/azure-amqp-common-go/v4/conn"
	"github.com/Azure/azure-amqp-common-go/v4/sas"
	"github.com/Azure/azure-event-hubs-go/v3"
	"github.com/Azure/azure-event-hubs-go/v3/eph"
	"github.com/Azure/azure-event-hubs-go/v3/storage"
	"github.com/Azure/azure-storage-blob-go/azblob"
	"github.com/Azure/go-autorest/autorest/azure"
)

func main() {
	// Azure Storage account information
    storageAccountName := "mystorageaccount"
    storageAccountKey := "Zm9vCg=="
    // Azure Storage container to store leases and checkpoints
    storageContainerName := "ephcontainer"
    
    // Azure Event Hub connection string
    eventHubConnStr := "Endpoint=sb://namespace.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=superSecret1234=;EntityPath=hubName"
    parsed, err := conn.ParsedConnectionFromStr(eventHubConnStr)
    if err != nil {
        // handle error
    }
    
    // create a new Azure Storage Leaser / Checkpointer
    cred, err := azblob.NewSharedKeyCredential(storageAccountName, storageAccountKey)
    if err != nil {
	fmt.Println(err)
	return
    }

    leaserCheckpointer, err := storage.NewStorageLeaserCheckpointer(cred, storageAccountName, storageContainerName, azure.PublicCloud)
    if err != nil {
	fmt.Println(err)
	return
    }
    
    // SAS token provider for Azure Event Hubs
    provider, err := sas.NewTokenProvider(sas.TokenProviderWithKey(parsed.KeyName, parsed.Key))
    if err != nil {
	fmt.Println(err)
	return
    }
    
    ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
    defer cancel()
    // create a new EPH processor
    processor, err := eph.New(ctx, parsed.Namespace, parsed.HubName, provider, leaserCheckpointer, leaserCheckpointer)
    if err != nil {
	fmt.Println(err)
	return
    }
    
    // register a message handler -- many can be registered
    handlerID, err := processor.RegisterHandler(ctx, 
	func(c context.Context, e *eventhub.Event) error {
		fmt.Println(string(e.Data))
    		return nil
    })
    if err != nil {
	fmt.Println(err)
	return
    }
    
    fmt.Printf("handler id: %q is running\n", handlerID)

    // unregister a handler to stop that handler from receiving events
    // processor.UnregisterHandler(ctx, handleID)
    
    // start handling messages from all of the partitions balancing across multiple consumers
    err = processor.StartNonBlocking(ctx)
    if err != nil {
        fmt.Println(err)
        return
    }
    
    // Wait for a signal to quit:
    signalChan := make(chan os.Signal, 1)
    signal.Notify(signalChan, os.Interrupt, os.Kill)
    <-signalChan
    
    err = processor.Close(context.Background())
    if err != nil {
        fmt.Println(err)
        return
    }
}

Examples

Contributing

This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us the rights to use your contribution. For details, visit https://cla.microsoft.com.

When you submit a pull request, a CLA-bot will automatically determine whether you need to provide a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions provided by the bot. You will only need to do this once across all repos using our CLA.

This project has adopted the Microsoft Open Source Code of Conduct. For more information see the Code of Conduct FAQ or contact [email protected] with any additional questions or comments.

See CONTRIBUTING.md.

Running Tests

To setup the integration test environment, ensure the following pre-requisites are in place

  • install WSL (if on Windows)
  • install golang
  • add paths to .profile
    • export PATH=$PATH:/usr/local/go/bin:$HOME/go/bin
    • export GOPATH=$HOME/go
  • install go dev dependencies
    • run go get github.com/fzipp/gocyclo
    • run go get -u golang.org/x/lint/golint
  • run the following bash commands
    • sudo apt install jq
  • install gcc
    • on Ubuntu:
      • sudo apt update
      • sudo apt install build-essential
  • download terraform and add to the path
  • install Azure CLI
  • run az login

To run all tests run make test

To cleanup dev tools in go.mod and go.sum prior to check-in run make tidy or go mode tidy

License

MIT, see LICENSE.

azure-event-hubs-go's People

Contributors

arne-cl avatar askingcat avatar bryanklewis avatar catalinaperalta avatar cperaltah avatar dependabot[bot] avatar devigned avatar e96wic avatar elsesiy avatar estensen avatar gavinfish avatar hades32 avatar jhendrixmsft avatar jonasrichard avatar jonnylangefeld avatar jseely avatar krpranay avatar mhennings avatar microsoft-github-policy-service[bot] avatar microsoftopensource avatar msftgits avatar nicolasgab avatar princjef avatar r290 avatar richard-derivco avatar richardpark-msft avatar rwngallego avatar sylvain2703 avatar tiny-dancer avatar vcabbage avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

azure-event-hubs-go's Issues

Sending a message with a timeout on the context prevents continuous sending

Expected Behavior

The timeout on the context should be used for the send request only.

Actual Behavior

The timeout is reused for future calls. As a side effect future sends fail.

Cause / Solution

// Send sends an event to the Event Hub
func (h *Hub) Send(ctx context.Context, event *Event, opts ...SendOption) error {
	span, ctx := h.startSpanFromContext(ctx, "eh.Hub.Send")
	defer span.Finish()

	sender, err := h.getSender(**ctx**)
	if err != nil {
		return err
	}

	return sender.Send(ctx, event, opts...)
}

Instead of ctx context.Background() should be passed down to getSender(). In addition to keep the tracing intact context variables should be copied.

Environment

  • OS: linux/amd64
  • Go version: go1.10.3
  • Version of Library: v0.4.0 / fbff7e5

Event processor host: status description was not found on rpc message

Expected Behavior

No error when creating a new event hub processor

Actual Behavior

Error when creating eventhub processor : status description was not found on rpc message

Environment

  • OS: Docker image built in golang:alpine
  • Go version: 1.11.1
  • Version of Library: not sure, just used go get

I was using the code from the example for the eventhub processor (https://github.com/Azure/azure-event-hubs-go#event-processor-host) and it was working fine for a while (about a few weeks) but now when running processor, err := eph.New(ctx, parsed.Namespace, parsed.HubName, provider, leaserCheckpointer, leaserCheckpointer) I get the error message "status description was not found on rpc message"
Any idea what might cause this issue?

Interface that describes Hub

Having an interface that represents a Hub would allow for the developers to mock the Hub when testing.

Is there any other way to mock it?

Question: What ports must be open from on-prem?

Currently I am deploying a consumer service in our on-prem production env that uses this library and consumes messages from an EventHub.

However, I am unsure what ports and endpoints needs to requested for being whitelisted for the networking purposes. When I start the service in a prod env, I get an error here:

runtimeInfo, err := hub.GetRuntimeInformation(ctx)
	if err != nil {
		return err
	}

ERROR: 2018/10/16 15:19:21 dial tcp 104.42.97.95:5671: connect: connection timed out

Can I assume 104.42.97.95 is a static IP and request for connectivity for that IP with my network team or is there some more to it? Like would the IPs change dynamically? This works fine in my dev env.

I am a newbee with Azure/Event Hubs in general. Please excuse my ignorance. I am looking for a document that speaks about all these.

Add Receiver Options to EPH

Expected Behavior

Spinning up a Receiver allows me to specify a lot more opts than it's currently available using the EPH.

e.g. I'd be really interested in using the recently added ReceiveFromTimestamp option.

Actual Behavior

I can't pass any temporal options/offset settings to the EPH. At least, I couldn't find a way to do it.

Environment

  • OS: Mac OS X 10.14.1
  • Go version: go1.11.2 darwin/amd64
  • Version of Library: v1.1.0

SendBatch

Hi,

I'm planning to use SendBatch to send multiple events to an Event Hub. I assume that this have a maximum events per batch?

I've haven't been unable to find any information on this neither in the SDK itself or in the service documentation.

Can I put as many events I'd like into a single event batch?
And is this documented somewhere?

Thanks,
Kasper

Event ordering

Hi,

How does this package provide any kind of guarantee as to event ordering when each event is handled in its own independent go-routine? They would race against each other at the whim of the go scheduler?

Why not just provide a channel with the events as interface and leave it to the user whether they should be consumed sequentially or with independent go-routines?

TCP connections stay open when sending

Expected Behavior

I have a piece of code that sends data by calling NewHubFromConnectionString(), SendBatch() and finally Close() on the Hub. The functions is called multiple times in a loop. After leaving the code running for a while I can see many connections left open to the Event Hub endpoint, and they keep piling up.

Actual Behavior

I was assuming that when calling Hub.Close() the connection to EH would be closed.

Should Hub.Close() also call Close() on the sender? e.g. in https://github.com/Azure/azure-event-hubs-go/blob/master/hub.go#L521

Environment

  • OS: Linux or Mac
  • Go version: go version go1.11.2 darwin/amd64
  • Version of Library: 1.0.1

Timeout while sending batch

We have a microservice running that sends one message to an eventhub every second. We're seeing timeouts (context.deadlineExceeded) in this setup multiple times per day with a timeoutCtx of 5 seconds. Sometimes it helps to create a new hub object, at other times sending the subsequent message also fails with the same timeout and error. We have another microservice written in Java that has a similar logic where we don't see this behaviour.

Until now we saw these errors:

  • amqp: connection closed
  • amqp: link closed

Are there any options we can try out, how should we deal with reconnecting?

Environment

  • OS: Linux
  • Go version: 1.11
  • Version of Library: 1.1.3

Add web sockets support

Expected Behavior

Should be able to connect to Event Hubs via web sockets rather than over the standard AMQP ports to better support common open ports.

Actual Behavior

The library only supports common AMQP ports.

/cc #65

Improve the testing experience when using this library

Expected Behavior

It should be easy to mock dependencies on this library.

Actual Behavior

It is difficult to mock behaviors in this library due to a mix of public / private struct fields and limited use of interfaces in the public interface.

The problem is that this is likely to be a breaking change to the interface and thus would probably need to land in v2.0. This is issue is to track this and to ensure in the next major version testing / mocking is handled better.

#77 spurred this discussion and contains an example of problem.

Environment

  • Version of Library: v1.0

Goroutine Leak in azure-event-hubs-go/storage.(*LeaserCheckpointer).persistDirtyPartitions

Expected Behavior

Event consumption and processing with consistent amount of goroutines increasing and decreasing as necessary to accomplish function.

Actual Behavior

Continual increase in goroutines until the process runs out of resources and becomes un-responsive while using the Event Processor Host and the included LeaseCheckpointer while events are being consumed on a consistent basis.

Analysis

From my analysis it appears there is goroutine leak in the storage package(azure-eventhubs-go/storage.go:493). The function persistDirtyPartitions creates a channel and sends to a channel that has receipt in the next for loop, this set of actions is leaking a send almost every lease and persist interval as events are flowing through.

I've reproduced the issue using the example code from this repo's readme and adding a goroutine count loop and an endpoint to dump a stack. The example receiver code is below. Run the receiver, and send events through an Event Hub with Multiple Partitions for a few minutes and then look at the stack, you can see that there are many leaked chan send go routines on the persistDirtyPartitions function.

Potential Fix

I've changed the storage.go code to something simpler and it seems to resolve the issue, i may not understand why the more complex channel version was there, so take it as a reference.

func (sl *LeaserCheckpointer) persistDirtyPartitions(ctx context.Context) error {
	sl.leasesMu.Lock()
	defer sl.leasesMu.Unlock()

	span, ctx := startConsumerSpanFromContext(ctx, "storage.LeaserCheckpointer.persistDirtyPartitions")
	defer span.End()

	// resCh := make(chan dirtyResult)
	// for partitionID := range sl.dirtyPartitions {
	// 	go func(id string) {
	// 		err := sl.persistLease(ctx, id)
	// 		resCh <- dirtyResult{
	// 			Err:         err,
	// 			PartitionID: id,
	// 		}
	// 	}(partitionID)
	// }

	// var lastErr error
	// for i := 0; i < len(sl.dirtyPartitions); i++ {
	// 	select {
	// 	case <-ctx.Done():
	// 		return ctx.Err()
	// 	case res := <-resCh:
	// 		if res.Err != nil {
	// 			lastErr = res.Err
	// 		}
	// 		delete(sl.dirtyPartitions, res.PartitionID)
	// 	}
	// }
	// return lastErr

	ptd := make(map[string]string)
	var lastErr error
	for partitionID := range sl.dirtyPartitions {
		go func(id string) {
			err := sl.persistLease(ctx, id)
			if err != nil {
				lastErr = err
			}
			ptd[id] = id
		}(partitionID)
	}

	for pID := range ptd {
		delete(sl.dirtyPartitions, pID)
	}

	return lastErr
}

Environment

  • OS: macOS Mojave 10.14.2
  • Go version: 1.11.2
  • Version of Library: v1.1.2

Example Receive Code

package main

import (
	"context"
	"fmt"
	"net/http"
	"os"
	"os/signal"
	"runtime"
	"runtime/debug"
	"runtime/pprof"
	"time"

	"github.com/Azure/azure-amqp-common-go/conn"
	"github.com/Azure/azure-amqp-common-go/sas"
	eventhub "github.com/Azure/azure-event-hubs-go"
	"github.com/Azure/azure-event-hubs-go/eph"
	"github.com/Azure/azure-event-hubs-go/storage"
	"github.com/Azure/azure-storage-blob-go/azblob"
	"github.com/Azure/go-autorest/autorest/azure"
	log "github.com/sirupsen/logrus"
)

func main() {

	mux := http.NewServeMux()
	mux.HandleFunc("/_stack", getStackTraceHandler)

	httpServerClosedChan := make(chan struct{})
	go func() {
		log.Infof(
			"Listening for HTTP requests at: %v\n",
			"http://localhost:8080",
		)
		httpErr := http.ListenAndServe(":8080", mux)
		if httpErr != nil && httpErr != http.ErrServerClosed {
			log.Errorf("HTTP Server error: %v\n", httpErr)
		}
		close(httpServerClosedChan)
	}()

	// Azure Storage account information
	storageAccountName := ""
	storageAccountKey := ""
	// Azure Storage container to store leases and checkpoints
	storageContainerName := ""

	// Azure Event Hub connection string
	eventHubConnStr := ""
	parsed, err := conn.ParsedConnectionFromStr(eventHubConnStr)
	if err != nil {
		// handle error
	}

	// create a new Azure Storage Leaser / Checkpointer
	cred, err := azblob.NewSharedKeyCredential(storageAccountName, storageAccountKey)
	leaserCheckpointer, err := storage.NewStorageLeaserCheckpointer(cred, storageAccountName, storageContainerName, azure.PublicCloud)
	if err != nil {
		// handle error
	}

	// SAS token provider for Azure Event Hubs
	provider, err := sas.NewTokenProvider(sas.TokenProviderWithKey(parsed.KeyName, parsed.Key))
	if err != nil {
		// handle error
	}

	ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
	defer cancel()
	// create a new EPH processor
	processor, err := eph.New(ctx, parsed.Namespace, parsed.HubName, provider, leaserCheckpointer, leaserCheckpointer)
	if err != nil {
		// handle error
	}

	// register a message handler -- many can be registered
	handlerId, err := processor.RegisterHandler(ctx,
		func(c context.Context, event *eventhub.Event) error {
			fmt.Println(string(event.Data))
			return nil
		})
	if err != nil {
		// handle error
	}

	go func() {
		for {
			select {
			default:
				numGr := runtime.NumGoroutine()
				log.Infof("goroutine count: %v\n", numGr)
				<-time.After(5 * time.Second)
			}
		}
	}()

	// start handling messages from all of the partitions balancing across multiple consumers
	processor.StartNonBlocking(ctx)

	log.Infof("Receiving events from eventhub: %v %v handlerId: %v\n", parsed.Namespace, parsed.HubName, handlerId)

	// Wait for a signal to quit:
	signalChan := make(chan os.Signal, 1)
	signal.Notify(signalChan, os.Interrupt, os.Kill)
	<-signalChan

	processor.UnregisterHandler(ctx, handlerId)

	err = processor.Close(context.Background())
	if err != nil {
		// handle error
	}
}

func getStackTraceHandler(w http.ResponseWriter, r *http.Request) {
	stack := debug.Stack()
	w.Write(stack)
	pprof.Lookup("goroutine").WriteTo(w, 2)
}

Setting checkpoint SequenceNumbers in events?

I have a piece of code that needs to read from an Event Hub from fixed duration back in time up until right now and quit listening/return.

This is straightforward to write with hub.GetParitionInformation to provide the latest sequence number and event.GetCheckpoint on each event received to match the event sequence number with to see if its done reading.

The problem then arise when I want to write unit tests of this code. I can easily mock Hub but I'm not able to create events with the sequence numbers set?

Am I missing something or should I go about solving the problem differently? Any pointers as to how to write this would be appreciated.

go get fails

Expected Behavior

$ go get -u github.com/Azure/azure-event-hubs-go/...

should run without failing.

Actual Behavior

It fails with this error message:

# github.com/Azure/azure-amqp-common-go/aad
../../src/github.com/Azure/azure-amqp-common-go/aad/jwt.go:203:44: cannot use token.ExpiresOn (type json.Number) as type string in argument to strconv.ParseInt
../../src/github.com/Azure/azure-amqp-common-go/aad/jwt.go:216:69: cannot use token.ExpiresOn (type json.Number) as type string in argument to auth.NewToken

Environment

  • OS: Debian GNU/Linux
  • Go version: go1.11rc2 linux/amd64
  • Version of Library: ed70919

Context Timeouts to stop receiver.

Expected Behavior

hub.Receiver Stops receiving messages after context timeout.

Code:

ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	for _, partitionID := range partitions {
		hub.Receive(ctx, partitionID, handler, eventhub.ReceiveWithLatestOffset())
	}
cancel()
fmt.Println("listening...")

The above sample code is creating a receiver per partition and then the context (ctx) is cancelled (worst case context gets done after 10 sec). I am expecting the receiver to stop at that point. Please correct if my understanding is wrong. Would be good if that can be documented.

I do realize that to stop the receiver I can call hub.Close(ctx).

Actual Behavior

hub.Receiver continues to receive messages after the context is cancelled.

Environment

  • OS: Linux
  • Go version: go version go1.10.3 darwin/amd64
  • Version of Library: master branch

Timeouts

Hi,

How should we go about implementing a timeout on Receive?

The example in the README implies one would be able to set it via the context on Receive but this is not the case. I'm not sure I understand why Receive takes a context at all.

Pointer dereference error when using connection string

Expected Behavior

A connection to be established with Eventhub using environment variables:
EVENTHUB_CONNECTION_STRING,
EVENTHUB_NAMESPACE
EVENTHUB_NAME

Actual Behavior

The program fails when trying to find an aadProvider and never reaches the code to find an sasProvider. Stack trace below:

panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x10 pc=0x121eafa]

goroutine 1 [running]:
eventhub/vendor/github.com/Azure/go-autorest/autorest/adal.retry(0x13022c0, 0xc420088f60, 0xc420124100, 0x0, 0x0, 0x12da6ec)
/Users/conordarcy/Documents/go/src/eventhub/vendor/github.com/Azure/go-autorest/autorest/adal/token.go:718 +0x18a
eventhub/vendor/github.com/Azure/go-autorest/autorest/adal.(*ServicePrincipalToken).refreshInternal(0xc420122000, 0x1304e40, 0xc420016088, 0x12d3089, 0x1c, 0x0, 0x0)
/Users/conordarcy/Documents/go/src/eventhub/vendor/github.com/Azure/go-autorest/autorest/adal/token.go:653 +0x27f
eventhub/vendor/github.com/Azure/go-autorest/autorest/adal.(*ServicePrincipalToken).RefreshWithContext(0xc420122000, 0x1304e40, 0xc420016088, 0x0, 0x0)
/Users/conordarcy/Documents/go/src/eventhub/vendor/github.com/Azure/go-autorest/autorest/adal/token.go:581 +0xa5
eventhub/vendor/github.com/Azure/go-autorest/autorest/adal.(*ServicePrincipalToken).Refresh(0xc420122000, 0x35, 0x12d3089)
/Users/conordarcy/Documents/go/src/eventhub/vendor/github.com/Azure/go-autorest/autorest/adal/token.go:573 +0x43
eventhub/vendor/github.com/Azure/azure-amqp-common-go/aad.(*TokenProviderConfiguration).NewServicePrincipalToken(0xc42011c000, 0x0, 0x0, 0xc420014140)
/Users/conordarcy/Documents/go/src/eventhub/vendor/github.com/Azure/azure-amqp-common-go/aad/jwt.go:194 +0x5fd
eventhub/vendor/github.com/Azure/azure-amqp-common-go/aad.NewJWTProvider(0xc420057e80, 0x1, 0x1, 0xc420062040, 0xc420057ec0, 0xc420057e40)
/Users/conordarcy/Documents/go/src/eventhub/vendor/github.com/Azure/azure-amqp-common-go/aad/jwt.go:137 +0x126
eventhub/vendor/github.com/Azure/azure-event-hubs-go.NewHubWithNamespaceNameAndEnvironment(0xc42001c103, 0x18, 0xc42001414e, 0x4, 0x0, 0x0, 0x0, 0xc420010e00, 0xc420057f18, 0x105b3ad)
/Users/conordarcy/Documents/go/src/eventhub/vendor/github.com/Azure/azure-event-hubs-go/hub.go:139 +0x6f
eventhub/vendor/github.com/Azure/azure-event-hubs-go.NewHubFromEnvironment(0x0, 0x0, 0x0, 0x12329d7, 0xc420057f78, 0xc42007e058)
/Users/conordarcy/Documents/go/src/eventhub/vendor/github.com/Azure/azure-event-hubs-go/hub.go:207 +0x156
main.main()
/Users/conordarcy/Documents/go/src/eventhub/main.go:14 +0x32
exit status 2

Environment

  • OS: MacOS
  • Go version: go version go1.10.2 darwin/amd64
  • Version of Library: 0.3.0

Variables that are set:
EVENTHUB_NAMESPACE
EVENTHUB_NAME
EVENTHUB_CONNECTION_STRING

Hello World sample consumer not receiving messages

Expected Behavior

Consumer will receive messages

Edit: Producer does not send event

Actual Behavior

consumer only displays "I am listening..."
My environment variables were put inside a single .env file. Env vars included subscriptionID, clientID, clientSecret, tenant id, eventhubnamespace, name as well as connection string just to be sure.
I use the "godotenv" library to automatically read the env file on import
the exe files for consumer and producer are in a single directory along with the .env file. I'm sure the env variables work because the producer works. Also, both producer and consumer use the same constant values

Environment

  • OS: Windows 10
  • Go version: go1.10.2 windows/amd64
  • Version of Library: latest

Code not compiling

Expected Behavior

Should be able to build and run

Actual Behavior

Getting the following error

# github.com/Azure/azure-amqp-common-go/aad
../../../workspace/go/pkg/mod/github.com/!azure/[email protected]/aad/jwt.go:203:44: cannot use token.ExpiresOn (type json.Number) as type string in argument to strconv.ParseInt
../../../workspace/go/pkg/mod/github.com/!azure/[email protected]/aad/jwt.go:216:69: cannot use token.ExpiresOn (type json.Number) as type string in argument to auth.NewToken

Environment

  • OS: ProductName: Mac OS X ProductVersion: 10.13.6 BuildVersion: 17G65
  • Go version: go1.11 darwin/amd64
  • Version of Library: v1.0.1

Go env

GOARCH="amd64"
GOBIN=""
GOCACHE="/Users/sara/Library/Caches/go-build"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOOS="darwin"
GOPATH="/Users/sara/workspace/go"
GOPROXY=""
GORACE=""
GOROOT="/usr/local/Cellar/go/1.11/libexec"
GOTMPDIR=""
GOTOOLDIR="/usr/local/Cellar/go/1.11/libexec/pkg/tool/darwin_amd64"
GCCGO="gccgo"
CC="clang"
CXX="clang++"
CGO_ENABLED="1"
GOMOD=""
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/mg/1d7kb3ss0jl5ndy8_3f5q08c0000gn/T/go-build066835474=/tmp/go-build -gno-record-gcc-switches -fno-common"

Connection reset by peer on attempt to GetRuntimeInformation

Expected Behavior

A connection should be established with the following environment variables set.
EVENTHUB_CONNECTION_STRING,
EVENTHUB_NAMESPACE
EVENTHUB_NAME

Actual Behavior

When attempting to invoke hub.GetRuntimeInformation it returns the following error - read tcp 10.118.100.85:50582->104.42.97.95:5671: read: connection reset by peer

Current failing code -

        hub, err := eh.NewHubFromEnvironment()
	if err != nil {
		fmt.Println("Failed to create event hub-", err)
		return err
	}
	ctx, cancel := context.WithTimeout(context.Background(), 3600*time.Second)
	defer cancel()

	handler := func(c context.Context, event *eh.Event) error {
		fmt.Println(*event)
		return nil
	}
	runtimeInfo, err := hub.GetRuntimeInformation(ctx) //FAILS HERE
	if err != nil {
		fmt.Println("Failed to get runtime information-", err) 
		return err
	}

One interesting piece of information found while debugging is that it looks like the token provider in the namespace is not being set correctly (see attached debug image). That said, this could be a complete red herring.

Environment

  • OS: mac
  • Go version: 1.10.2 darwin/amd64
  • Version of Library: v 0.3.1
    screen shot 2018-06-18 at 4 48 35 pm

High Throughput Round Robin

Verify greater than 6 million messages per minute can be sent reliably to a 100 throughput unit Event Hub instance with 128 partitions.

GetRuntimeInformation not working

We have started seeing an issue that seems to be unrelated to any changes done in our end.

GetRuntimeInformation has begun returning the error status description was not found on rpc message which is causing a crash as it use the runtime information to discover partitions.

Any advice on how to fix this would be much appreciated.

Environment

  • Go version: 1.11.4
  • Version of Library:
[[constraint]]
  name = "github.com/Azure/azure-amqp-common-go"
  version = "1.1.3"

[[constraint]]
  name = "github.com/Azure/azure-event-hubs-go"
  version = "1.1.1"

Add SAS token tests

The tests only use JWT tokens. There should be tests for SAS tokens as well.

Using event-hub go library to connect to an iot hub

Expected Behavior

Not sure if this is possible. @devigned mentioned on another repository, that it might be possible to connect to an IoT Hub using this library. If this is indeed possible, I would expect to be able to create a connection to the hub by providing a connection string in the format:
HostName=hubname.azure-devices.net;DeviceId=deviceID;SharedAccessKey=...

Actual Behavior

When I attempt to connect I get a message that says "key "Endpoint" must not be empty"

Am I trying to do something that wasn't designed for?

Environment

  • OS: MacOS v10.13.6
  • Go version: go version go1.11.2 darwin/amd64
  • Version of Library: V1.1.2

event.PartitionKey is nil

Expected Behavior

For the partition key to be included in the event

Actual Behavior

The partition key is null, is there something else i need to do to get it filled in?

Environment

  • OS: win 10
  • Go version: 1.9
  • Version of Library: latest from go get

Mock calls to AMQP in tests

Expected Behavior

Tests will execute without needing a connection to an AMQP broker. This will allow quicker validation for pull requests.

Actual Behavior

Tests require authentication and connection to Event Hubs.

Event hubs eventually stop writing

Expected Behavior

We can write using a single event hub instance forever happily.

Actual Behavior

After some time, the client begins erroring. It goes along for a while, a week this time, and then starts experiencing failures.

Once a failure happens on an instance of a service, all requests fail until that service is restarted, with the following error returned from hub.Send:

Dec 06 21:45:52 interactive5.dal09.mixer.com tetrisd2[4539]: time="2018-12-06T21:45:52Z" level=warning msg="<reacted error>: read tcp <internal ip>:33282->104.208.144.8:5671: read: connection reset by peer"
Dec 06 21:46:37 interactive5.dal09.mixer.com tetrisd2[4539]: time="2018-12-06T21:46:37Z" level=warning msg="<reacted error>: read tcp <internal ip>:33282->104.208.144.8:5671: read: connection reset by peer"
Dec 06 21:46:42 interactive5.dal09.mixer.com tetrisd2[4539]: time="2018-12-06T21:46:42Z" level=warning msg="<reacted error>: read tcp <internal ip>:33282->104.208.144.8:5671: read: connection reset by peer"

This tends to happen on one server at a time, and often (but not always) start on the other servers. In this case, within two hours of the first server dropping, four other servers also started experiencing the issue. We see this happening on two different services, on different hardware writing to different event hubs.

In both services the event hub is instantiated with:

tokenProvider, err := sas.NewTokenProvider(tokenProviderFromConnectionString(config.EventHub.ConnectionString))
if err != nil {
    panic(errors.Wrap(err, "app/services: could not create Event Hub token provider"))
}
hub, err := eventhub.NewHub(config.EventHub.Namespace, config.EventHub.Name, tokenProvider)
if err != nil {
    panic(errors.Wrap(err, "app/services: could not create Event Hub instance"))
}

// tokenProviderFromConnectionString creates a TokenProviderOption with the
// given event hub connection string.
func tokenProviderFromConnectionString(connectionString string) sas.TokenProviderOption {
	return func(provider *sas.TokenProvider) error {
		parsed, err := conn.ParsedConnectionFromStr(connectionString)
		if err != nil {
			return err
		}
		return sas.TokenProviderWithKey(parsed.KeyName, parsed.Key)(provider)
	}
}

After which we simply use the hub.Send method with an unspecified partition.

Environment

Expose "x-opt-enqueued-time" as part of event properties

Environment

  • OS: Windows10
  • Go version: 1.12.1
  • Version of Library: latest

Expose "x-opt-enqueued-time" as part of event properties. This will be useful on the eventprocessorhost receiver client to measure any lags or delays in processing

Handle com.microsoft:server-busy error with retry and backoff

Expected Behavior

When server responds with an error of com.microsoft:server-busy, the send operation should retry after a period of time.

Actual Behavior

The error is returned from send and the consumer of the library needs to deal with it.

Compilation error due to latest release of Azue Storage Blob Go

Expected Behavior

I can clone and compile this project successfully.

Actual Behavior

Since the latest release of Azure Storage Blob (0.3.0) the older versions of the SDK have been removed but there are still references to it in this project i.e. credentials.go leading to compilation errors.

Environment

  • OS: Mac OS X 10.13.6
  • Go version: go1.11.1 darwin/amd64
  • Version of Library: Latest (1.0.0) but all are affected

Missing configuration param for checkpointing frequency

Expected Behavior

As a user, I want to be able to set the frequency of checkpoints being written to the Azure Blob storage. I'm using an EPH for managing the partitions at scale.

Actual Behavior

The handleMessage method emits a checkpoint update on every single message. I'm in a high volume environment (> 100 messages/day) which noticeably slows down the possible throughput.

Environment

  • OS: Mac OS X 10.14.1
  • Go version: go1.11.2 darwin/amd64
  • Version of Library: v1.0.1

Ampq package dependency is set to master, leading to brittle releases

Expected Behavior

The title explains the root issue, the consequence is that this commit : vcabbage/amqp@390d7ea added an error parameter to msg.Reject()

When building the project, or a project using the lib :

vendor\github.com\Azure\azure-event-hubs-go\receiver.go:207:13: not enough arguments in call to msg.Reject
        have ()
        want (*amqp.Error)

Resolution

Fix the version of the dependencies to avoid breaking changes sneaking in

Environment

  • OS: any
  • Go version: any
  • Version of Library: latest

Expired credentials

Hi,

We're having issues with sending events to event hub. This particular sender sends events once a day. First time it runs, it works fine. 24 hour later we consistently get the error: link detached, reason: *Error{Condition: amqp:link:detach-forced, Description: Unauthorized access. 'Send' claim(s) are required to perform this operation. Resource: 'x.servicebus.windows.net/manifests'., Info: map[]}.

Any idea why this is happening and whether we're missing something or its a bug in the SDK?

We setup event hubs as follows:

// Setup SAS token based provider for EventHub client
tpo := sas.TokenProviderWithKey(c.Azure.EventHubs.KeyName, c.Azure.EventHubs.KeyValue)
tp, err := sas.NewTokenProvider(tpo)
if err != nil {
	log.Error(errors.Wrapf(err, "Unable to create SAS token provider for EventHub"))
	os.Exit(1)
}

// setup the EventHub client using the SAS token provider
eventHubClient, err := eventhub.NewHub(c.Azure.EventHubs.Namespace, c.Azure.EventHubs.ManifestsEventHub, tp)
if err != nil {
	log.Error(errors.Wrapf(err, "Unable to create EventHub client"))
	os.Exit(1)
}

Environment

  • OS: Docker scratch
  • Go version: 1.11.1
  • Version of Library: v0.4.0 (fbff7e5d87fa647a80ea5059107c7309ac391b6c)

Consumer Groups

Hi,

This question is likely somewhere in between SDK and the Azure Event Hubs service domain. Feel free to redirect me if there is a better place to ask it.

I'm a bit confused about the concept of "consumer groups" in Event Hubs. As I understand it, the key reason to having consumer groups in Kafka is to:

  1. Let the service handle persisting checkpoints (enabling things like having the application crash, restart a new one and have it resume where the last one left it off without having to deal with storing the checkpoint in safe and durable place)
  2. Abstract away the actual partitions (ie. having one consumer capable of consuming from multiple partitions simultaneously - or having two consumers consume a single partition without duplicates)

Going through the SDK, neither seems to be supported in Event Hubs? I have to specify both a partition ID as well as an offset when starting a consumer.

If that's the case, whey do Event Hubs have consumer groups at all?
Other than the somewhat arbitrary(?) limitation of only 5 concurrent readers within a consumer group and 20 consumer groups.

Best regards,
Kasper

Inconsistent function call

Expected Behavior

got get -u github.com/Azure/azure-event-hubs-go/... results in no error.

Actual Behavior

Seeing an error in a function call (inconsistent number of arguments)

mbp-0:$ go get -u github.com/Azure/azure-event-hubs-go/...
# github.com/Azure/azure-event-hubs-go/internal/test
../../Azure/azure-event-hubs-go/internal/test/suite.go:177:34: not enough arguments in call to client.ListByNamespace
	have (context.Context, string, string)
	want (context.Context, string, string, *int32, *int32)
mbp-0:$

Environment

  • OS: Darwin (MAC OS)
  • Go version: go version go1.10.3 darwin/amd64
  • Version of Library:

File persister being overwritten by receiver options

Configuration

  • Eventhub configured with offset persistence using persist.FilePersister
  • Receiver(s) configured with ReceiveFromTimestamp
  • Static configuration (the configuration does not change between runs)

Expected Behavior

The offset saved in the FilePersister directory of the hub optionally supersedes the offset given as receiver option.

Actual Behavior

On receiver creation the offset in the FilePersister directory is immediately overwritten with the one from the receiver option (e.g. https://github.com/Azure/azure-event-hubs-go/blob/master/receiver.go#L104). As a consequence events are received multiple times if the application is restarted.

I can understand if this is by design and cannot be changed, in that case I'll create a workaround for my specific implementation. Ideally a setting could be created to which offset has preference, either specific for the file persister or for all persisters.

Environment

  • OS: Fedora 29
  • Go version: go version go1.11.5 linux/amd64
  • Version of Library: head (commit b14f5f2)

not enough arguments in call to client.ListByNamespace

Expected Behavior

I expect the go get to finish successfully

Actual Behavior

C:\Users\Musterion>go get -u github.com/Azure/azure-event-hubs-go/...

github.com/Azure/azure-event-hubs-go/internal/test

Development\go\src\github.com\Azure\azure-event-hubs-go\internal\test\suite.go:177:34: not enough arguments in call to client.ListByNamespace
have (context.Context, string, string)
want (context.Context, string, string, *int32, *int32)

Environment

  • OS: Windows 10
  • Go version: go1.10.2 windows/amd64
  • Version of Library: latest

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.