Skip to content

Commit

Permalink
[receiver/vcenter] Collection Time Performance Enhancement (#32991)
Browse files Browse the repository at this point in the history
**Description:** <Describe what has changed.>
There were already some improvements made as far as how networks calls
were made centered around Virtual Machines. This allowed collection
times to decrease from ~90s to ~27s in an environment with 1 Cluster, 2
Hosts, & 280 VMs.

Making similar changes for all resource types helped to further decrease
collection times. Now collection time has decreased from ~27s to <~3s
for the same environment.

Here's a general list of the changes made:
- Now makes all network calls (per datacenter) first and stores returned
data.
- Processes this data afterwards to convert to OTEL resources/metrics
(refactored to new file).
- Moves all metric recording to metrics.go to keep consistent.
- Moves all resource builder creation to resources.go to keep
consistent.
- Updates/fixes tests.

**Link to tracking Issue:** <Issue number if applicable>
#31837 Although this issue prescribes a solution to the problem
(goroutines) which ended up not being necessary

**Testing:** <Describe what testing was performed and which tests were
added.>
Unit Tests & Integration Tests Passing as well as Manual Testing in
Local Environments

**Documentation:** <Describe the documentation added.>
N/A
  • Loading branch information
StefanKurek authored May 13, 2024
1 parent 43aff69 commit d43a904
Show file tree
Hide file tree
Showing 32 changed files with 2,409 additions and 61,220 deletions.
27 changes: 27 additions & 0 deletions .chloggen/feat_vcenter-performance-tweaks.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Use this changelog template to create an entry for release notes.

# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
change_type: enhancement

# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver)
component: vcenterreceiver

# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
note: "Refactors how and when client makes calls in order to provide for faster collection times."

# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists.
issues: [31837]

# (Optional) One or more lines of additional information to render under the primary note.
# These lines will be padded with 2 spaces and then inserted directly into the document.
# Use pipe (|) for multiline entries.
subtext:

# If your change doesn't affect end users or the exported elements of any package,
# you should instead start your pull request title with [chore] or use the "Skip Changelog" label.
# Optional: The change log or logs in which this entry should be included.
# e.g. '[user]' or '[user, api]'
# Include 'user' if the change is relevant to end users.
# Include 'api' if there is a change to a library API.
# Default: '[user]'
change_logs: [user]
188 changes: 113 additions & 75 deletions receiver/vcenterreceiver/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ package vcenterreceiver // import "github.com/open-telemetry/opentelemetry-colle

import (
"context"
"errors"
"fmt"
"net/url"

Expand Down Expand Up @@ -85,66 +84,121 @@ func (vc *vcenterClient) Disconnect(ctx context.Context) error {
return nil
}

// Datacenters returns the datacenterComputeResources of the vSphere SDK
func (vc *vcenterClient) Datacenters(ctx context.Context) ([]*object.Datacenter, error) {
datacenters, err := vc.finder.DatacenterList(ctx, "*")
// Datacenters returns the Datacenters of the vSphere SDK
func (vc *vcenterClient) Datacenters(ctx context.Context) ([]mo.Datacenter, error) {
v, err := vc.vm.CreateContainerView(ctx, vc.vimDriver.ServiceContent.RootFolder, []string{"Datacenter"}, true)
if err != nil {
return []*object.Datacenter{}, fmt.Errorf("unable to get datacenter lists: %w", err)
return nil, fmt.Errorf("unable to retrieve Datacenters: %w", err)
}

var datacenters []mo.Datacenter
err = v.Retrieve(ctx, []string{"Datacenter"}, []string{
"name",
}, &datacenters)
if err != nil {
return nil, fmt.Errorf("unable to retrieve Datacenters: %w", err)
}

return datacenters, nil
}

// Datastores returns the Datastores of the vSphere SDK for a given datacenter
func (vc *vcenterClient) Datastores(ctx context.Context, datacenter *object.Datacenter) ([]*object.Datastore, error) {
vc.finder = vc.finder.SetDatacenter(datacenter)
datastores, err := vc.finder.DatastoreList(ctx, "*")
// Datastores returns the Datastores of the vSphere SDK
func (vc *vcenterClient) Datastores(ctx context.Context, containerMoRef vt.ManagedObjectReference) ([]mo.Datastore, error) {
v, err := vc.vm.CreateContainerView(ctx, containerMoRef, []string{"Datastore"}, true)
if err != nil {
return nil, fmt.Errorf("unable to retrieve Datastores: %w", err)
}

var datastores []mo.Datastore
err = v.Retrieve(ctx, []string{"Datastore"}, []string{
"name",
"summary.capacity",
"summary.freeSpace",
}, &datastores)
if err != nil {
return []*object.Datastore{}, fmt.Errorf("unable to get datastores: %w", err)
return nil, fmt.Errorf("unable to retrieve Datastores: %w", err)
}

return datastores, nil
}

// Computes returns the ComputeResources (and ClusterComputeResources) of the vSphere SDK for a given datacenter
func (vc *vcenterClient) Computes(ctx context.Context, datacenter *object.Datacenter) ([]*object.ComputeResource, error) {
vc.finder = vc.finder.SetDatacenter(datacenter)
computes, err := vc.finder.ComputeResourceList(ctx, "*")
// ComputeResources returns the ComputeResources (& ClusterComputeResources) of the vSphere SDK
func (vc *vcenterClient) ComputeResources(ctx context.Context, containerMoRef vt.ManagedObjectReference) ([]mo.ComputeResource, error) {
v, err := vc.vm.CreateContainerView(ctx, containerMoRef, []string{"ComputeResource"}, true)
if err != nil {
return nil, fmt.Errorf("unable to retrieve ComputeResources (& ClusterComputeResources): %w", err)
}

var computes []mo.ComputeResource
err = v.Retrieve(ctx, []string{"ComputeResource"}, []string{
"name",
"datastore",
"host",
"summary",
}, &computes)
if err != nil {
return []*object.ComputeResource{}, fmt.Errorf("unable to get compute lists: %w", err)
return nil, fmt.Errorf("unable to retrieve ComputeResources (& ClusterComputeResources): %w", err)
}

return computes, nil
}

// ResourcePools returns the ResourcePools in the vSphere SDK
func (vc *vcenterClient) ResourcePools(ctx context.Context) ([]*object.ResourcePool, error) {
rps, err := vc.finder.ResourcePoolList(ctx, "*")
// HostSystems returns the HostSystems of the vSphere SDK
func (vc *vcenterClient) HostSystems(ctx context.Context, containerMoRef vt.ManagedObjectReference) ([]mo.HostSystem, error) {
v, err := vc.vm.CreateContainerView(ctx, containerMoRef, []string{"HostSystem"}, true)
if err != nil {
return nil, fmt.Errorf("unable to retrieve resource pools: %w", err)
return nil, fmt.Errorf("unable to retrieve HostSystems: %w", err)
}
return rps, err

var hosts []mo.HostSystem
err = v.Retrieve(ctx, []string{"HostSystem"}, []string{
"name",
"summary.hardware.memorySize",
"summary.hardware.numCpuCores",
"summary.hardware.cpuMhz",
"summary.quickStats.overallMemoryUsage",
"summary.quickStats.overallCpuUsage",
"vm",
"parent",
}, &hosts)
if err != nil {
return nil, fmt.Errorf("unable to retrieve HostSystems: %w", err)
}

return hosts, nil
}

// VirtualApps returns the VirtualApps in the vSphere SDK
func (vc *vcenterClient) VirtualApps(ctx context.Context) ([]*object.VirtualApp, error) {
vApps, err := vc.finder.VirtualAppList(ctx, "*")
// ResourcePools returns the ResourcePools (&VirtualApps) of the vSphere SDK
func (vc *vcenterClient) ResourcePools(ctx context.Context, containerMoRef vt.ManagedObjectReference) ([]mo.ResourcePool, error) {
v, err := vc.vm.CreateContainerView(ctx, containerMoRef, []string{"ResourcePool"}, true)
if err != nil {
var notFoundErr *find.NotFoundError
if errors.As(err, &notFoundErr) {
return []*object.VirtualApp{}, nil
}
return nil, fmt.Errorf("unable to retrieve ResourcePools (&VirtualApps): %w", err)
}

return nil, fmt.Errorf("unable to retrieve vApps: %w", err)
var rps []mo.ResourcePool
err = v.Retrieve(ctx, []string{"ResourcePool"}, []string{
"summary",
"name",
"owner",
"vm",
}, &rps)
if err != nil {
return nil, fmt.Errorf("unable to retrieve ResourcePools (&VirtualApps): %w", err)
}
return vApps, err

return rps, nil
}

func (vc *vcenterClient) VMs(ctx context.Context) ([]mo.VirtualMachine, error) {
v, err := vc.vm.CreateContainerView(ctx, vc.vimDriver.ServiceContent.RootFolder, []string{"VirtualMachine"}, true)
// VMS returns the VirtualMachines of the vSphere SDK
func (vc *vcenterClient) VMs(ctx context.Context, containerMoRef vt.ManagedObjectReference) ([]mo.VirtualMachine, error) {
v, err := vc.vm.CreateContainerView(ctx, containerMoRef, []string{"VirtualMachine"}, true)
if err != nil {
return nil, fmt.Errorf("unable to retrieve VMs: %w", err)
}

var vms []mo.VirtualMachine
err = v.Retrieve(ctx, []string{"VirtualMachine"}, []string{
"name",
"config.hardware.numCPU",
"config.instanceUuid",
"config.template",
Expand All @@ -156,7 +210,6 @@ func (vc *vcenterClient) VMs(ctx context.Context) ([]mo.VirtualMachine, error) {
"summary.quickStats.ssdSwappedMemory",
"summary.quickStats.overallCpuUsage",
"summary.config.memorySizeMB",
"summary.config.name",
"summary.storage.committed",
"summary.storage.uncommitted",
"summary.runtime.host",
Expand All @@ -170,52 +223,42 @@ func (vc *vcenterClient) VMs(ctx context.Context) ([]mo.VirtualMachine, error) {
return vms, nil
}

type perfSampleResult struct {
counters map[string]*vt.PerfCounterInfo
results []performance.EntityMetric
}
// ResourcePoolInventoryListObjects returns the ResourcePools (with populated InventoryLists) of the vSphere SDK
func (vc *vcenterClient) ResourcePoolInventoryListObjects(ctx context.Context) ([]*object.ResourcePool, error) {
rps, err := vc.finder.ResourcePoolList(ctx, "*")
if err != nil {
return nil, fmt.Errorf("unable to retrieve ResourcePools with InventoryLists: %w", err)
}

type perfMetricsQueryResult struct {
counters map[string]*vt.PerfCounterInfo
resultsByMoRef map[string]*performance.EntityMetric
return rps, nil
}

func (vc *vcenterClient) performanceQuery(
ctx context.Context,
spec vt.PerfQuerySpec,
names []string,
objs []vt.ManagedObjectReference,
) (*perfSampleResult, error) {
if vc.pm == nil {
return &perfSampleResult{}, nil
}
vc.pm.Sort = true
sample, err := vc.pm.SampleByName(ctx, spec, names, objs)
if err != nil {
return nil, err
}
result, err := vc.pm.ToMetricSeries(ctx, sample)
if err != nil {
return nil, err
}
counterInfoByName, err := vc.pm.CounterInfoByName(ctx)
// VAppInventoryListObjects returns the vApps (with populated InventoryLists) of the vSphere SDK
func (vc *vcenterClient) VAppInventoryListObjects(ctx context.Context) ([]*object.VirtualApp, error) {
vApps, err := vc.finder.VirtualAppList(ctx, "*")
if err != nil {
return nil, err
return nil, fmt.Errorf("unable to retrieve vApps with InventoryLists: %w", err)
}
return &perfSampleResult{
counters: counterInfoByName,
results: result,
}, nil

return vApps, nil
}

// PerfMetricsQueryResult contains performance metric related data
type PerfMetricsQueryResult struct {
// Contains performance metrics keyed by MoRef string
resultsByRef map[string]*performance.EntityMetric
}

func (vc *vcenterClient) perfMetricsQuery(
// PerfMetricsQuery returns the requested performance metrics for the requested resources
// over a given sample interval and sample count
func (vc *vcenterClient) PerfMetricsQuery(
ctx context.Context,
spec vt.PerfQuerySpec,
names []string,
objs []vt.ManagedObjectReference,
) (*perfMetricsQueryResult, error) {
) (*PerfMetricsQueryResult, error) {
if vc.pm == nil {
return &perfMetricsQueryResult{}, nil
return &PerfMetricsQueryResult{}, nil
}
vc.pm.Sort = true
sample, err := vc.pm.SampleByName(ctx, spec, names, objs)
Expand All @@ -226,17 +269,12 @@ func (vc *vcenterClient) perfMetricsQuery(
if err != nil {
return nil, err
}
counterInfoByName, err := vc.pm.CounterInfoByName(ctx)
if err != nil {
return nil, err
}

resultsByMoRef := map[string]*performance.EntityMetric{}
resultsByRef := map[string]*performance.EntityMetric{}
for i := range result {
resultsByMoRef[result[i].Entity.Value] = &result[i]
resultsByRef[result[i].Entity.Value] = &result[i]
}
return &perfMetricsQueryResult{
counters: counterInfoByName,
resultsByMoRef: resultsByMoRef,
return &PerfMetricsQueryResult{
resultsByRef: resultsByRef,
}, nil
}
Loading

0 comments on commit d43a904

Please sign in to comment.