Repository: koordinator-sh/koordinator

Issue:
[question] skip Pending Pod when set  cpuburst  for pod in qpsmanager
<!-- Please only use this template for submitting question -->

**What happened:**

Recently, we deployed the coordinator into the test environment. When I dig into qps-manager, I find this piece of code difficult to understand.
https://github.com/koordinator-sh/koordinator/blob/8b2c429df9f9a22f48330c5fcf385df345966108/pkg/koordlet/qosmanager/plugins/cpuburst/cpu_burst.go#L233-L239

I think the Pending Pod should be skipped. In this situation, the Pod isn't scheduled to any Node. Is that correct?



**What you expected to happen:**

**Environment:**

<!-- Please add environment information, if you know what is necessary, just provide some -->

- Koordinator version: - v0.6.2
- Kubernetes version (use kubectl version): v1.22.5
- docker/containerd version: containerd 1.5.0
- OS (e.g: cat /etc/os-release): Ubuntu 20.04.4 LTS
- Kernel (e.g. uname -a): Linux 5.10.112-11.al8.x86_64 #1 SMP Tue May 24 16:05:50 CST 2022 x86_64 x86_64 x86_64 GNU/Linux

**Anything else we need to know:**


Hints from issue comments:
@saintube: @bobsongplus 
`phase: Pending` does not mean the pod has not been scheduled. Pods scheduled while the regular containers have not been started can also be regarded as Pending, a common case being a pod that simply runs its initContainers.
BTW, the above `podMetas` are retrieved from the Kubelet's `/pods` service, which only shows the scheduled pods on that node.@googs1025: Try to optimize this part.  https://github.com/koordinator-sh/koordinator/pull/2550


I saw that there are several plugins in qosmanager that have similar operations. 

Solution patch:
diff --git a/cmd/koord-descheduler/app/server.go b/cmd/koord-descheduler/app/server.go
index 03cd62ea..38a87cd8 100644
--- a/cmd/koord-descheduler/app/server.go
+++ b/cmd/koord-descheduler/app/server.go
@@ -23,6 +23,7 @@ import (
 	"net/http"
 	"os"
 	goruntime "runtime"
+	"time"
 
 	"github.com/spf13/cobra"
 	corev1 "k8s.io/api/core/v1"
@@ -176,13 +177,24 @@ func Run(ctx context.Context, cc *deschedulerappconfig.CompletedConfig, desched
 	}
 
 	// Start up the healthz server.
+	gracefulShutdownSecureServer := func() {}
 	if cc.SecureServing != nil {
 		handler := buildHandlerChain(newHealthzAndMetricsHandler(&cc.ComponentConfig, checks...))
-		// TODO: handle stoppedCh and listenerStoppedCh returned by c.SecureServing.Serve
-		if _, _, err := cc.SecureServing.Serve(handler, 0, ctx.Done()); err != nil {
+		internalStopCh := make(chan struct{})
+		shutdownTimeout := 5 * time.Second
+		stoppedCh, listenerStoppedCh, err := cc.SecureServing.Serve(handler, shutdownTimeout, internalStopCh)
+		if err != nil {
 			// fail early for secure handlers, removing the old error loop from above
+			close(internalStopCh)
 			return fmt.Errorf("failed to start secure server: %v", err)
 		}
+		gracefulShutdownSecureServer = func() {
+			close(internalStopCh)
+			<-listenerStoppedCh
+			klog.Info("[graceful-termination] secure server has stopped listening")
+			<-stoppedCh
+			klog.Info("[graceful-termination] secure server is exiting")
+		}
 	}
 
 	cc.Manager.Add(desched)
@@ -194,6 +206,7 @@ func Run(ctx context.Context, cc *deschedulerappconfig.CompletedConfig, desched
 				StartDescheduler(ctx, cc)
 			},
 			OnStoppedLeading: func() {
+				gracefulShutdownSecureServer()
 				select {
 				case <-ctx.Done():
 					// We were asked to terminate. Exit 0.
@@ -217,6 +230,7 @@ func Run(ctx context.Context, cc *deschedulerappconfig.CompletedConfig, desched
 	}
 
 	StartDescheduler(ctx, cc)
+	gracefulShutdownSecureServer()
 	return fmt.Errorf("finished without leader elect")
 }
 
diff --git a/cmd/koord-scheduler/app/server.go b/cmd/koord-scheduler/app/server.go
index 3d77a659..53679fc6 100644
--- a/cmd/koord-scheduler/app/server.go
+++ b/cmd/koord-scheduler/app/server.go
@@ -23,6 +23,7 @@ import (
 	"net/http"
 	"os"
 	goruntime "runtime"
+	"time"
 
 	"github.com/spf13/cobra"
 
@@ -201,6 +202,7 @@ func Run(ctx context.Context, cc *schedulerserverconfig.CompletedConfig, sched *
 	}
 
 	// Start up the healthz server.
+	gracefulShutdownSecureServer := func() {}
 	if cc.InsecureServing != nil {
 		handler := buildHandlerChain(newHealthzAndMetricsHandler(&cc.ComponentConfig, cc.InformerFactory, cc.ServicesEngine, sched, isLeader, checks...), nil, nil)
 		if err := cc.InsecureServing.Serve(handler, 0, ctx.Done()); err != nil {
@@ -209,11 +211,21 @@ func Run(ctx context.Context, cc *schedulerserverconfig.CompletedConfig, sched *
 	}
 	if cc.SecureServing != nil {
 		handler := buildHandlerChain(newHealthzAndMetricsHandler(&cc.ComponentConfig, cc.InformerFactory, cc.ServicesEngine, sched, isLeader, checks...), cc.Authentication.Authenticator, cc.Authorization.Authorizer)
-		// TODO: handle stoppedCh and listenerStoppedCh returned by c.SecureServing.Serve
-		if _, _, err := cc.SecureServing.Serve(handler, 0, ctx.Done()); err != nil {
+		internalStopCh := make(chan struct{})
+		shutdownTimeout := 5 * time.Second
+		stoppedCh, listenerStoppedCh, err := cc.SecureServing.Serve(handler, shutdownTimeout, internalStopCh)
+		if err != nil {
 			// fail early for secure handlers, removing the old error loop from above
+			close(internalStopCh)
 			return fmt.Errorf("failed to start secure server: %v", err)
 		}
+		gracefulShutdownSecureServer = func() {
+			close(internalStopCh)
+			<-listenerStoppedCh
+			klog.Info("[graceful-termination] secure server has stopped listening")
+			<-stoppedCh
+			klog.Info("[graceful-termination] secure server is exiting")
+		}
 	}
 
 	startInformersAndWaitForSync := func(ctx context.Context) {
@@ -257,6 +269,7 @@ func Run(ctx context.Context, cc *schedulerserverconfig.CompletedConfig, sched *
 				sched.Run(ctx)
 			},
 			OnStoppedLeading: func() {
+				gracefulShutdownSecureServer()
 				select {
 				case <-ctx.Done():
 					// We were asked to terminate. Exit 0.
@@ -284,6 +297,7 @@ func Run(ctx context.Context, cc *schedulerserverconfig.CompletedConfig, sched *
 	close(waitingForLeader)
 	go extenderFactory.Run()
 	sched.Run(ctx)
+	gracefulShutdownSecureServer()
 	return fmt.Errorf("finished without leader elect")
 }
 
diff --git a/pkg/features/features.go b/pkg/features/features.go
index 682c6ff1..ad054afd 100644
--- a/pkg/features/features.go
+++ b/pkg/features/features.go
@@ -96,9 +96,6 @@ const (
 
 	// ValidatePodDeviceResource enables validate pod device resource
 	ValidatePodDeviceResource featuregate.Feature = "ValidatePodDeviceResource"
-
-	// DevicePluginAdaption enables adaption for third party device plugins within device share scheduling.
-	DevicePluginAdaption featuregate.Feature = "DevicePluginAdaption"
 )
 
 var defaultFeatureGates = map[featuregate.Feature]featuregate.FeatureSpec{
@@ -122,7 +119,6 @@ var defaultFeatureGates = map[featuregate.Feature]featuregate.FeatureSpec{
 	EnableSyncGPUSharedResource:            {Default: false, PreRelease: featuregate.Alpha},
 	ColocationProfileController:            {Default: false, PreRelease: featuregate.Alpha},
 	ValidatePodDeviceResource:              {Default: false, PreRelease: featuregate.Alpha},
-	DevicePluginAdaption:                   {Default: false, PreRelease: featuregate.Alpha},
 	EnablePodEnhancedValidator:             {Default: false, PreRelease: featuregate.Alpha},
 }
 
diff --git a/pkg/features/scheduler_features.go b/pkg/features/scheduler_features.go
index 4bd67035..62d44e6a 100644
--- a/pkg/features/scheduler_features.go
+++ b/pkg/features/scheduler_features.go
@@ -70,6 +70,12 @@ const (
 	// OmitNodeLabelsForReservation is used to omit node labels while matching reservation affinity.
 	OmitNodeLabelsForReservation featuregate.Feature = "OmitNodeLabelsForReservation"
 
+	// owner: @zqzten
+	// alpha: v1.7
+	//
+	// DevicePluginAdaption enables adaption for third party device plugins within device share scheduling.
+	DevicePluginAdaption featuregate.Feature = "DevicePluginAdaption"
+
 	CSIStorageCapacity featuregate.Feature = "CSIStorageCapacity"
 
 	GenericEphemeralVolume featuregate.Feature = "GenericEphemeralVolume"
@@ -92,6 +98,7 @@ var defaultSchedulerFeatureGates = map[featuregate.Feature]featuregate.FeatureSp
 	SupportParentQuotaSubmitPod:               {Default: false, PreRelease: featuregate.Alpha},
 	LazyReservationRestore:                    {Default: false, PreRelease: featuregate.Alpha},
 	OmitNodeLabelsForReservation:              {Default: false, PreRelease: featuregate.Alpha},
+	DevicePluginAdaption:                      {Default: false, PreRelease: featuregate.Alpha},
 	CSIStorageCapacity:                        {Default: true, PreRelease: featuregate.GA}, // remove in 1.26
 	GenericEphemeralVolume:                    {Default: true, PreRelease: featuregate.GA},
 	PodDisruptionBudget:                       {Default: true, PreRelease: featuregate.GA},
diff --git a/pkg/koordlet/qosmanager/plugins/cgreconcile/cgroup_reconcile.go b/pkg/koordlet/qosmanager/plugins/cgreconcile/cgroup_reconcile.go
index adece737..af11b699 100644
--- a/pkg/koordlet/qosmanager/plugins/cgreconcile/cgroup_reconcile.go
+++ b/pkg/koordlet/qosmanager/plugins/cgreconcile/cgroup_reconcile.go
@@ -150,7 +150,7 @@ func (m *cgroupResourcesReconcile) calculateResources(nodeCfg *slov1alpha1.Resou
 	for _, podMeta := range podMetas {
 		pod := podMeta.Pod
 		// ignore non-running pods
-		if pod.Status.Phase != corev1.PodRunning && pod.Status.Phase != corev1.PodPending {
+		if util.IsPodInactive(pod) {
 			klog.V(5).Infof("skip calculate cgroup summary for non-running pod %s", util.GetPodKey(pod))
 			continue
 		}
diff --git a/pkg/koordlet/qosmanager/plugins/cpuburst/cpu_burst.go b/pkg/koordlet/qosmanager/plugins/cpuburst/cpu_burst.go
index 36d37af0..b1d09f19 100644
--- a/pkg/koordlet/qosmanager/plugins/cpuburst/cpu_burst.go
+++ b/pkg/koordlet/qosmanager/plugins/cpuburst/cpu_burst.go
@@ -230,11 +230,8 @@ func (b *cpuBurst) start() {
 			// ignore non-burstable pod, e.g. LSR, BE pods
 			continue
 		}
-		if podMeta.Pod.Status.Phase != corev1.PodPending && podMeta.Pod.Status.Phase != corev1.PodRunning {
-			// ignore pods that status.phase is not pending or running,
-			// because the other pods(include succeed,failed and unknown) do not have any containers running
-			// and therefore do not have a cgroup file,
-			// so there is no need to deal with it
+		if util.IsPodInactive(podMeta.Pod) {
+			// ignore pods that status.phase is not pending or running
 			continue
 		}
 
diff --git a/pkg/koordlet/qosmanager/plugins/resctrl/resctrl_reconcile.go b/pkg/koordlet/qosmanager/plugins/resctrl/resctrl_reconcile.go
index 7b41cbb2..14ae7fce 100644
--- a/pkg/koordlet/qosmanager/plugins/resctrl/resctrl_reconcile.go
+++ b/pkg/koordlet/qosmanager/plugins/resctrl/resctrl_reconcile.go
@@ -465,7 +465,7 @@ func (r *resctrlReconcile) reconcileResctrlGroups(qosStrategy *slov1alpha1.Resou
 		}
 
 		// only Running and Pending pods are considered
-		if pod.Status.Phase != corev1.PodRunning && pod.Status.Phase != corev1.PodPending {
+		if util.IsPodInactive(pod) {
 			continue
 		}
 
diff --git a/pkg/scheduler/metrics/metrics.go b/pkg/scheduler/metrics/metrics.go
index 57058a3d..b940412e 100644
--- a/pkg/scheduler/metrics/metrics.go
+++ b/pkg/scheduler/metrics/metrics.go
@@ -48,6 +48,18 @@ var (
 			Name:      "reservation_status_phase",
 			Help:      `The current number of reservations in each status phase (e.g. Pending, Available, Succeeded, Failed)`,
 		}, []string{"name", "phase"}))
+	ReservationResource = utilmetrics.NewGCGaugeVec(
+		"reservation_resource",
+		prometheus.NewGaugeVec(
+			prometheus.GaugeOpts{
+				Subsystem: schedulermetrics.SchedulerSubsystem,
+				Name:      "reservation_resource",
+				Help:      "Resource metrics for a reservation, including allocatable, allocated, and utilization with unit.",
+			},
+			[]string{"type", "name", "resource", "unit"},
+		),
+	)
+
 	ElasticQuotaProcessLatency = metrics.NewHistogramVec(
 		&metrics.HistogramOpts{
 			Subsystem: schedulermetrics.SchedulerSubsystem,
@@ -101,12 +113,29 @@ var (
 
 	gcMetricsList = []prometheus.Collector{
 		ReservationStatusPhase.GetGaugeVec(),
+		ReservationResource.GetGaugeVec(),
 	}
 )
 
 const (
-	reservationNameKey  = "name"
-	reservationPhaseKey = "phase"
+	reservationNameKey         = "name"
+	reservationPhaseKey        = "phase"
+	reservationResourceKey     = "resource"
+	reservationResourceTypeKey = "type"
+	reservationResourceUnitKey = "unit"
+)
+
+const (
+	UnitCore  = "core"
+	UnitGiB   = "Gi"
+	UnitRaw   = "raw"
+	UnitRatio = "ratio"
+)
+
+const (
+	TypeAllocatable = "allocatable"
+	TypeAllocated   = "allocated"
+	TypeUtilization = "utilization"
 )
 
 // RecordReservationPhase records the phase of a reservation as a metric.
@@ -119,6 +148,17 @@ func RecordReservationPhase(name string, phase string, value float64) {
 	ReservationStatusPhase.WithSet(labels, value)
 }
 
+// RecordReservationResourceByTypeWithUnit records the resource record of a reservation as a metric.
+func RecordReservationResourceByTypeWithUnit(name, resource, typ, unit string, value float64) {
+	labels := prometheus.Labels{
+		reservationResourceTypeKey: typ,
+		reservationNameKey:         name,
+		reservationResourceKey:     resource,
+		reservationResourceUnitKey: unit,
+	}
+	ReservationResource.WithSet(labels, value)
+}
+
 var registerMetrics sync.Once
 
 // Register all metrics.
diff --git a/pkg/scheduler/plugins/deviceshare/plugin.go b/pkg/scheduler/plugins/deviceshare/plugin.go
index 381d2910..afd243f0 100644
--- a/pkg/scheduler/plugins/deviceshare/plugin.go
+++ b/pkg/scheduler/plugins/deviceshare/plugin.go
@@ -39,7 +39,6 @@ import (
 	"github.com/koordinator-sh/koordinator/pkg/scheduler/frameworkext"
 	"github.com/koordinator-sh/koordinator/pkg/scheduler/frameworkext/schedulingphase"
 	"github.com/koordinator-sh/koordinator/pkg/scheduler/frameworkext/topologymanager"
-	utilfeature "github.com/koordinator-sh/koordinator/pkg/util/feature"
 	reservationutil "github.com/koordinator-sh/koordinator/pkg/util/reservation"
 )
 
@@ -618,7 +617,7 @@ func (p *Plugin) preBindObject(ctx context.Context, cycleState *framework.CycleS
 		return framework.NewStatus(framework.Error, err.Error())
 	}
 
-	if utilfeature.DefaultMutableFeatureGate.Enabled(features.DevicePluginAdaption) {
+	if k8sfeature.DefaultMutableFeatureGate.Enabled(features.DevicePluginAdaption) {
 		if err := p.adaptForDevicePlugin(object, state.allocationResult, nodeName); err != nil {
 			return framework.AsStatus(err)
 		}
@@ -704,6 +703,7 @@ func New(obj runtime.Object, handle framework.Handle) (framework.Plugin, error)
 	deviceCache := newNodeDeviceCache()
 	registerDeviceEventHandler(deviceCache, extendedHandle.KoordinatorSharedInformerFactory())
 	registerPodEventHandler(deviceCache, handle.SharedInformerFactory(), extendedHandle.KoordinatorSharedInformerFactory())
+	extendedHandle.RegisterForgetPodHandler(deviceCache.deletePod)
 	go deviceCache.gcNodeDevice(context.TODO(), handle.SharedInformerFactory(), defaultGCPeriod)
 
 	gpuSharedResourceTemplatesCache := newGPUSharedResourceTemplatesCache()
diff --git a/pkg/scheduler/plugins/elasticquota/controller.go b/pkg/scheduler/plugins/elasticquota/controller.go
index 855c6626..31b8e8cf 100644
--- a/pkg/scheduler/plugins/elasticquota/controller.go
+++ b/pkg/scheduler/plugins/elasticquota/controller.go
@@ -270,6 +270,24 @@ func (ctrl *Controller) syncElasticQuotaStatusMetricsWorker() {
 }
 
 func syncElasticQuotaMetrics(eq *v1alpha1.ElasticQuota, summary *core.QuotaInfoSummary) {
+	resourceField, quotaLabels := getResourceFieldAndQuotaLabels(eq, summary)
+	for k, v := range resourceField {
+		decorateResource(eq, v)
+		k = strings.TrimPrefix(k, extension.QuotaKoordinatorPrefix+"/")
+		RecordElasticQuotaMetric(ElasticQuotaStatusMetric, v, k, quotaLabels)
+	}
+}
+
+func deleteElasticQuotaMetrics(eq *v1alpha1.ElasticQuota, summary *core.QuotaInfoSummary) {
+	resourceField, quotaLabels := getResourceFieldAndQuotaLabels(eq, summary)
+	for k, v := range resourceField {
+		decorateResource(eq, v)
+		k = strings.TrimPrefix(k, extension.QuotaKoordinatorPrefix+"/")
+		DeleteElasticQuotaMetric(ElasticQuotaStatusMetric, v, k, quotaLabels)
+	}
+}
+
+func getResourceFieldAndQuotaLabels(eq *v1alpha1.ElasticQuota, summary *core.QuotaInfoSummary) (map[string]v1.ResourceList, map[string]string) {
 	quotaLabels := map[string]string{
 		"name":      summary.Name,
 		"tree":      summary.Tree,
@@ -294,20 +312,9 @@ func syncElasticQuotaMetrics(eq *v1alpha1.ElasticQuota, summary *core.QuotaInfoS
 			m[extension.AnnotationUnschedulableResource] = unschedulable
 		}
 	}
-
-	for k, v := range m {
-		decorateResource(eq, v)
-
-		k = strings.TrimPrefix(k, extension.QuotaKoordinatorPrefix+"/")
-		RecordElasticQuotaMetric(ElasticQuotaStatusMetric, v, k, quotaLabels)
-	}
-	decorateResource(eq, summary.Used)
-	RecordElasticQuotaMetric(ElasticQuotaStatusMetric, summary.Used, "used", quotaLabels)
-
-	decorateResource(eq, summary.Min)
-	decorateResource(eq, summary.Max)
-	decorateResource(eq, summary.SharedWeight)
-	RecordElasticQuotaMetric(ElasticQuotaSpecMetric, summary.Min, "min", quotaLabels)
-	RecordElasticQuotaMetric(ElasticQuotaSpecMetric, summary.Max, "max", quotaLabels)
-	RecordElasticQuotaMetric(ElasticQuotaSpecMetric, summary.SharedWeight, "sharedWeight", quotaLabels)
+	m["used"] = summary.Used
+	m["min"] = summary.Min
+	m["max"] = summary.Max
+	m["sharedWeight"] = summary.SharedWeight
+	return m, quotaLabels
 }
diff --git a/pkg/scheduler/plugins/elasticquota/metrics.go b/pkg/scheduler/plugins/elasticquota/metrics.go
index 70ad6ddc..eeb28e6e 100644
--- a/pkg/scheduler/plugins/elasticquota/metrics.go
+++ b/pkg/scheduler/plugins/elasticquota/metrics.go
@@ -75,6 +75,17 @@ func RecordElasticQuotaMetric(gaugeVec *metrics.GaugeVec, resources corev1.Resou
 func recordResourceMetric(gaugeVec *metrics.GaugeVec, value int64, resourceName, field string, labels map[string]string) {
 	labels["resource"] = resourceName
 	labels["field"] = field
-
 	gaugeVec.With(labels).Set(float64(value))
 }
+
+func DeleteElasticQuotaMetric(gaugeVec *metrics.GaugeVec, resources corev1.ResourceList, field string, labels map[string]string) {
+	for resourceName := range resources {
+		deleteResourceMetric(gaugeVec, string(resourceName), field, labels)
+	}
+}
+
+func deleteResourceMetric(gaugeVec *metrics.GaugeVec, resourceName, field string, labels map[string]string) {
+	labels["resource"] = resourceName
+	labels["field"] = field
+	gaugeVec.Delete(labels)
+}
diff --git a/pkg/scheduler/plugins/elasticquota/node_handler.go b/pkg/scheduler/plugins/elasticquota/node_handler.go
index d5ed4c15..166b5df9 100644
--- a/pkg/scheduler/plugins/elasticquota/node_handler.go
+++ b/pkg/scheduler/plugins/elasticquota/node_handler.go
@@ -18,6 +18,7 @@ package elasticquota
 
 import (
 	corev1 "k8s.io/api/core/v1"
+	"k8s.io/client-go/tools/cache"
 	"k8s.io/klog/v2"
 )
 
@@ -51,9 +52,14 @@ func (g *Plugin) OnNodeUpdate(oldObj, newObj interface{}) {
 }
 
 func (g *Plugin) OnNodeDelete(obj interface{}) {
-	node, ok := obj.(*corev1.Node)
-	if !ok {
-		klog.Errorf("node is nil")
+	var node *corev1.Node
+	switch t := obj.(type) {
+	case *corev1.Node:
+		node = t
+	case cache.DeletedFinalStateUnknown:
+		node, _ = t.Obj.(*corev1.Node)
+	}
+	if node == nil {
 		return
 	}
 
diff --git a/pkg/scheduler/plugins/elasticquota/quota_handler.go b/pkg/scheduler/plugins/elasticquota/quota_handler.go
index 3fc14d00..5f8bec62 100644
--- a/pkg/scheduler/plugins/elasticquota/quota_handler.go
+++ b/pkg/scheduler/plugins/elasticquota/quota_handler.go
@@ -23,6 +23,7 @@ import (
 	corev1 "k8s.io/api/core/v1"
 	quotav1 "k8s.io/apiserver/pkg/quota/v1"
 	k8sfeature "k8s.io/apiserver/pkg/util/feature"
+	"k8s.io/client-go/tools/cache"
 	"k8s.io/klog/v2"
 
 	"github.com/koordinator-sh/koordinator/apis/extension"
@@ -99,12 +100,21 @@ func (g *Plugin) OnQuotaUpdate(oldObj, newObj interface{}) {
 
 // OnQuotaDelete if a quotaGroup is deleted, the pods should migrate to defaultQuotaGroup.
 func (g *Plugin) OnQuotaDelete(obj interface{}) {
-	quota := obj.(*schedulerv1alpha1.ElasticQuota)
+	var quota *schedulerv1alpha1.ElasticQuota
+	switch t := obj.(type) {
+	case *schedulerv1alpha1.ElasticQuota:
+		quota = t
+	case cache.DeletedFinalStateUnknown:
+		quota, _ = t.Obj.(*schedulerv1alpha1.ElasticQuota)
+	}
 	if quota == nil {
 		klog.Errorf("quota is nil")
 		return
 	}
-
+	summary, _ := g.GetQuotaSummary(quota.Name, false)
+	if summary != nil {
+		deleteElasticQuotaMetrics(quota, summary)
+	}
 	klog.V(5).Infof("OnQuotaDeleteFunc delete quota: %v", quota.Name)
 	g.deleteQuotaToTreeMap(quota.Name)
 	mgr := g.GetGroupQuotaManagerForTree(quota.Labels[extension.LabelQuotaTreeID])
diff --git a/pkg/scheduler/plugins/loadaware/load_aware.go b/pkg/scheduler/plugins/loadaware/load_aware.go
index fe1dce7c..ed471e32 100644
--- a/pkg/scheduler/plugins/loadaware/load_aware.go
+++ b/pkg/scheduler/plugins/loadaware/load_aware.go
@@ -28,7 +28,6 @@ import (
 	"k8s.io/apimachinery/pkg/types"
 	"k8s.io/apimachinery/pkg/util/sets"
 	"k8s.io/klog/v2"
-	podutil "k8s.io/kubernetes/pkg/api/v1/pod"
 	"k8s.io/kubernetes/pkg/scheduler/framework"
 
 	"github.com/koordinator-sh/koordinator/apis/extension"
@@ -93,9 +92,12 @@ func New(args runtime.Object, handle framework.Handle) (framework.Plugin, error)
 	if err != nil {
 		return nil, err
 	}
-	assignCache := newPodAssignCache(estimator)
+	assignCache := newPodAssignCache(estimator, pluginArgs)
 	podInformer := frameworkExtender.SharedInformerFactory().Core().V1().Pods()
 	frameworkexthelper.ForceSyncFromInformer(context.TODO().Done(), frameworkExtender.SharedInformerFactory(), podInformer.Informer(), assignCache)
+	frameworkExtender.RegisterForgetPodHandler(func(pod *corev1.Pod) {
+		assignCache.unAssign(pod.Spec.NodeName, pod)
+	})
 	nodeMetricLister := frameworkExtender.KoordinatorSharedInformerFactory().Slo().V1alpha1().NodeMetrics().Lister()
 
 	return &Plugin{
@@ -337,7 +339,7 @@ func (p *Plugin) estimatedAssignedPodUsed(nodeName string, nodeMetric *slov1alph
 			stillInTheReportInterval(assignInfo.timestamp, nodeMetricUpdateTime, nodeMetricReportInterval) ||
 			(scoreWithAggregation(p.args.Aggregated) &&
 				getTargetAggregatedUsage(nodeMetric, &p.args.Aggregated.ScoreAggregatedDuration, p.args.Aggregated.ScoreAggregationType) == nil) ||
-			p.shouldEstimatePodByConfig(assignInfo, now) {
+			(!assignInfo.estimatedDeadline.IsZero() && assignInfo.estimatedDeadline.After(now)) {
 			estimated := assignInfo.estimated
 			if estimated == nil {
 				continue
@@ -357,33 +359,6 @@ func (p *Plugin) estimatedAssignedPodUsed(nodeName string, nodeMetric *slov1alph
 	return estimatedUsed, estimatedPods
 }
 
-func (p *Plugin) shouldEstimatePodByConfig(info *podAssignInfo, now time.Time) bool {
-	var afterPodScheduled, afterInitialized int64 = -1, -1
-	if p.args.AllowCustomizeEstimation {
-		afterPodScheduled = extension.GetCustomEstimatedSecondsAfterPodScheduled(info.pod)
-		afterInitialized = extension.GetCustomEstimatedSecondsAfterInitialized(info.pod)
-	}
-	if s := p.args.EstimatedSecondsAfterPodScheduled; s != nil && afterPodScheduled < 0 {
-		afterPodScheduled = *s
-	}
-	if s := p.args.EstimatedSecondsAfterInitialized; s != nil && afterInitialized < 0 {
-		afterInitialized = *s
-	}
-	if afterInitialized > 0 {
-		if _, c := podutil.GetPodCondition(&info.pod.Status, corev1.PodInitialized); c != nil && c.Status == corev1.ConditionTrue {
-			// if EstimatedSecondsAfterPodScheduled is set and pod is initialized, ignore EstimatedSecondsAfterPodScheduled
-			// EstimatedSecondsAfterPodScheduled might be set to a long duration to wait for time consuming init containers in pod.
-			if t := c.LastTransitionTime; !t.IsZero() {
-				return t.Add(time.Duration(afterInitialized) * time.Second).After(now)
-			}
-		}
-	}
-	if afterPodScheduled > 0 && info.timestamp.Add(time.Duration(afterPodScheduled)*time.Second).After(now) {
-		return true
-	}
-	return false
-}
-
 func loadAwareSchedulingScorer(resToWeightMap, used map[corev1.ResourceName]int64, allocatable corev1.ResourceList) int64 {
 	var nodeScore, weightSum int64
 	for resourceName, weight := range resToWeightMap {
diff --git a/pkg/scheduler/plugins/loadaware/pod_assign_cache.go b/pkg/scheduler/plugins/loadaware/pod_assign_cache.go
index 70ff50fc..e7397d79 100644
--- a/pkg/scheduler/plugins/loadaware/pod_assign_cache.go
+++ b/pkg/scheduler/plugins/loadaware/pod_assign_cache.go
@@ -26,6 +26,8 @@ import (
 	"k8s.io/client-go/tools/cache"
 	podutil "k8s.io/kubernetes/pkg/api/v1/pod"
 
+	"github.com/koordinator-sh/koordinator/apis/extension"
+	"github.com/koordinator-sh/koordinator/pkg/scheduler/apis/config"
 	"github.com/koordinator-sh/koordinator/pkg/scheduler/plugins/loadaware/estimator"
 	"github.com/koordinator-sh/koordinator/pkg/util"
 )
@@ -41,18 +43,21 @@ type podAssignCache struct {
 	// podAssignInfo is indexed using the Pod's types.UID
 	podInfoItems map[string]map[types.UID]*podAssignInfo
 	estimator    estimator.Estimator
+	args         *config.LoadAwareSchedulingArgs
 }
 
 type podAssignInfo struct {
-	timestamp time.Time
-	pod       *corev1.Pod
-	estimated map[corev1.ResourceName]int64
+	timestamp         time.Time
+	pod               *corev1.Pod
+	estimated         map[corev1.ResourceName]int64
+	estimatedDeadline time.Time
 }
 
-func newPodAssignCache(estimator estimator.Estimator) *podAssignCache {
+func newPodAssignCache(estimator estimator.Estimator, args *config.LoadAwareSchedulingArgs) *podAssignCache {
 	return &podAssignCache{
 		podInfoItems: map[string]map[types.UID]*podAssignInfo{},
 		estimator:    estimator,
+		args:         args,
 	}
 }
 
@@ -94,11 +99,15 @@ func (p *podAssignCache) assign(nodeName string, pod *corev1.Pod) {
 	if err != nil || len(estimated) == 0 {
 		estimated = nil
 	}
-	// try to use time from PodScheduled condition first
 	var timestamp time.Time
-	if _, c := podutil.GetPodCondition(&pod.Status, corev1.PodScheduled); c != nil && c.Status == corev1.ConditionTrue {
+	// try to use time from PodScheduled condition first
+	if _, c := podutil.GetPodCondition(&pod.Status, corev1.PodScheduled); c != nil && c.Status == corev1.ConditionTrue && !c.LastTransitionTime.IsZero() {
 		timestamp = c.LastTransitionTime.Time
+	} else {
+		// if PodScheduled condition not found, fallback to use assign timestamp from scheduler internal, which cannot be zero.
+		timestamp = timeNowFn()
 	}
+	estimatedDeadline := p.shouldEstimatePodDeadline(pod, timestamp)
 	p.lock.Lock()
 	defer p.lock.Unlock()
 	m := p.podInfoItems[nodeName]
@@ -107,20 +116,46 @@ func (p *podAssignCache) assign(nodeName string, pod *corev1.Pod) {
 		p.podInfoItems[nodeName] = m
 	}
 
-	if _, ok := m[pod.UID]; ok {
-		m[pod.UID].pod = pod
-		m[pod.UID].estimated = estimated
+	if info, ok := m[pod.UID]; ok {
+		info.timestamp = timestamp
+		info.pod = pod
+		info.estimated = estimated
+		info.estimatedDeadline = estimatedDeadline
 	} else {
-		// if PodScheduled condition not found, fallback to use assign timestamp from scheduler internal, which cannot be zero.
-		if timestamp.IsZero() {
-			timestamp = timeNowFn()
-		}
 		m[pod.UID] = &podAssignInfo{
-			timestamp: timestamp,
-			pod:       pod,
-			estimated: estimated,
+			timestamp:         timestamp,
+			pod:               pod,
+			estimated:         estimated,
+			estimatedDeadline: estimatedDeadline,
+		}
+	}
+}
+
+func (p *podAssignCache) shouldEstimatePodDeadline(pod *corev1.Pod, timestamp time.Time) time.Time {
+	var afterPodScheduled, afterInitialized int64 = -1, -1
+	if p.args.AllowCustomizeEstimation {
+		afterPodScheduled = extension.GetCustomEstimatedSecondsAfterPodScheduled(pod)
+		afterInitialized = extension.GetCustomEstimatedSecondsAfterInitialized(pod)
+	}
+	if s := p.args.EstimatedSecondsAfterPodScheduled; s != nil && afterPodScheduled < 0 {
+		afterPodScheduled = *s
+	}
+	if s := p.args.EstimatedSecondsAfterInitialized; s != nil && afterInitialized < 0 {
+		afterInitialized = *s
+	}
+	if afterInitialized > 0 {
+		if _, c := podutil.GetPodCondition(&pod.Status, corev1.PodInitialized); c != nil && c.Status == corev1.ConditionTrue {
+			// if EstimatedSecondsAfterPodScheduled is set and pod is initialized, ignore EstimatedSecondsAfterPodScheduled
+			// EstimatedSecondsAfterPodScheduled might be set to a long duration to wait for time consuming init containers in pod.
+			if t := c.LastTransitionTime; !t.IsZero() {
+				return t.Add(time.Duration(afterInitialized) * time.Second)
+			}
 		}
 	}
+	if afterPodScheduled > 0 && !timestamp.IsZero() {
+		return timestamp.Add(time.Duration(afterPodScheduled) * time.Second)
+	}
+	return time.Time{}
 }
 
 func (p *podAssignCache) unAssign(nodeName string, pod *corev1.Pod) {
@@ -148,18 +183,15 @@ func (p *podAssignCache) OnUpdate(oldObj, newObj interface{}) {
 	if !ok || pod == nil {
 		return
 	}
-	oldPodInfo := p.getPodAssignInfo(pod.Spec.NodeName, pod)
-	if oldPodInfo == nil { // pod was not cached
-		if pod.Spec.NodeName != "" && !util.IsPodTerminated(pod) { // pod is assigned and not terminated
-			p.assign(pod.Spec.NodeName, pod)
-		}
-	} else {
-		if util.IsPodTerminated(pod) { // pod become terminated
-			p.unAssign(pod.Spec.NodeName, pod)
-		}
-		if !reflect.DeepEqual(pod.Spec, oldPodInfo.pod.Spec) { // pod spec changed
-			p.assign(pod.Spec.NodeName, pod)
-		}
+	switch oldPodInfo := p.getPodAssignInfo(pod.Spec.NodeName, pod); {
+	case oldPodInfo == nil: // pod was not cached
+		p.assign(pod.Spec.NodeName, pod)
+	case util.IsPodTerminated(pod): // pod has nodeName & pod become terminated
+		p.unAssign(pod.Spec.NodeName, pod)
+	case !reflect.DeepEqual(&pod.Spec, &oldPodInfo.pod.Spec) ||
+		!reflect.DeepEqual(pod.Status.Conditions, oldPodInfo.pod.Status.Conditions):
+		// pod spec or pod conditions changed, renew cached pod
+		p.assign(pod.Spec.NodeName, pod)
 	}
 }
 
diff --git a/pkg/scheduler/plugins/nodenumaresource/pod_eventhandler.go b/pkg/scheduler/plugins/nodenumaresource/pod_eventhandler.go
index 75f816d8..848e86d4 100644
--- a/pkg/scheduler/plugins/nodenumaresource/pod_eventhandler.go
+++ b/pkg/scheduler/plugins/nodenumaresource/pod_eventhandler.go
@@ -43,6 +43,7 @@ func registerPodEventHandler(handle framework.Handle, resourceManager ResourceMa
 	frameworkexthelper.ForceSyncFromInformer(context.TODO().Done(), handle.SharedInformerFactory(), podInformer, eventHandler)
 	extendedHandle, ok := handle.(frameworkext.ExtendedHandle)
 	if ok {
+		extendedHandle.RegisterForgetPodHandler(eventHandler.deletePod)
 		reservationInformer := extendedHandle.KoordinatorSharedInformerFactory().Scheduling().V1alpha1().Reservations()
 		reservationEventHandler := reservationutil.NewReservationToPodEventHandler(eventHandler, reservationutil.IsObjValidActiveReservation)
 		frameworkexthelper.ForceSyncFromInformer(context.TODO().Done(), extendedHandle.KoordinatorSharedInformerFactory(), reservationInformer.Informer(), reservationEventHandler)
diff --git a/pkg/scheduler/plugins/reservation/controller/controller.go b/pkg/scheduler/plugins/reservation/controller/controller.go
index fd92d91a..c0f2c3a6 100644
--- a/pkg/scheduler/plugins/reservation/controller/controller.go
+++ b/pkg/scheduler/plugins/reservation/controller/controller.go
@@ -255,6 +255,7 @@ func (c *Controller) syncStatus(reservation *schedulingv1alpha1.Reservation) err
 	if apiext.IsReservationAllocateOnce(reservation) {
 		reservationutil.SetReservationSucceeded(reservation)
 	}
+	RecordReservationResource(reservation) // must be called after actualAllocated
 
 	return c.updateReservationStatus(reservation)
 }
@@ -337,3 +338,54 @@ func RecordReservationPhases(reservation *schedulingv1alpha1.Reservation) {
 		metrics.RecordReservationPhase(reservation.Name, phase.name, boolFloat64(isCurrentPhase))
 	}
 }
+
+func RecordReservationResource(reservation *schedulingv1alpha1.Reservation) {
+	if reservation.Status.Allocatable == nil {
+		return
+	}
+	const (
+		MilliCorePerCore = 1000
+		BytesPerGiB      = 1024 * 1024 * 1024
+	)
+
+	resources := quotav1.ResourceNames(reservation.Status.Allocatable)
+	for _, resourceName := range resources {
+		allocatable := reservation.Status.Allocatable[resourceName]
+		allocated := reservation.Status.Allocated[resourceName]
+
+		var allocatableVal, allocatedVal float64
+		var unit string
+
+		switch resourceName {
+		case corev1.ResourceCPU:
+			// mCPU -> Core
+			allocatableVal = float64(allocatable.MilliValue()) / MilliCorePerCore
+			allocatedVal = float64(allocated.MilliValue()) / MilliCorePerCore
+			unit = metrics.UnitCore
+		case corev1.ResourceMemory:
+			// bytes -> GiB
+			allocatableVal = float64(allocatable.Value()) / BytesPerGiB
+			allocatedVal = float64(allocated.Value()) / BytesPerGiB
+			unit = metrics.UnitGiB
+		default:
+			allocatableVal = float64(allocatable.Value())
+			allocatedVal = float64(allocated.Value())
+			unit = metrics.UnitRaw
+		}
+
+		// allocatable
+		metrics.RecordReservationResourceByTypeWithUnit(
+			reservation.Name, string(resourceName), metrics.TypeAllocatable, unit, allocatableVal)
+
+		// allocated
+		metrics.RecordReservationResourceByTypeWithUnit(
+			reservation.Name, string(resourceName), metrics.TypeAllocated, unit, allocatedVal)
+
+		// utilization
+		if allocatableVal > 0 {
+			utilization := allocatedVal / allocatableVal
+			metrics.RecordReservationResourceByTypeWithUnit(
+				reservation.Name, string(resourceName), metrics.TypeUtilization, metrics.UnitRatio, utilization)
+		}
+	}
+}
diff --git a/pkg/scheduler/plugins/reservation/plugin.go b/pkg/scheduler/plugins/reservation/plugin.go
index 5ecd2258..f15f28a1 100644
--- a/pkg/scheduler/plugins/reservation/plugin.go
+++ b/pkg/scheduler/plugins/reservation/plugin.go
@@ -115,7 +115,7 @@ func New(args runtime.Object, handle framework.Handle) (framework.Plugin, error)
 	cache := newReservationCache(reservationLister)
 	nm := newNominator(podLister, reservationLister)
 	registerReservationEventHandler(cache, koordSharedInformerFactory, nm)
-	registerPodEventHandler(cache, nm, sharedInformerFactory)
+	registerPodEventHandler(extendedHandle, cache, nm, sharedInformerFactory)
 
 	// TODO(joseph): Considering the amount of changed code,
 	// temporarily use global variable to store ReservationCache instance,
diff --git a/pkg/scheduler/plugins/reservation/pod_eventhandler.go b/pkg/scheduler/plugins/reservation/pod_eventhandler.go
index 9debcb67..2a075613 100644
--- a/pkg/scheduler/plugins/reservation/pod_eventhandler.go
+++ b/pkg/scheduler/plugins/reservation/pod_eventhandler.go
@@ -26,6 +26,7 @@ import (
 	"k8s.io/klog/v2"
 
 	apiext "github.com/koordinator-sh/koordinator/apis/extension"
+	"github.com/koordinator-sh/koordinator/pkg/scheduler/frameworkext"
 	frameworkexthelper "github.com/koordinator-sh/koordinator/pkg/scheduler/frameworkext/helper"
 	"github.com/koordinator-sh/koordinator/pkg/util"
 )
@@ -35,11 +36,12 @@ type podEventHandler struct {
 	nominator *nominator
 }
 
-func registerPodEventHandler(cache *reservationCache, nominator *nominator, factory informers.SharedInformerFactory) {
+func registerPodEventHandler(handle frameworkext.ExtendedHandle, cache *reservationCache, nominator *nominator, factory informers.SharedInformerFactory) {
 	eventHandler := &podEventHandler{
 		cache:     cache,
 		nominator: nominator,
 	}
+	handle.RegisterForgetPodHandler(eventHandler.deletePod)
 	informer := factory.Core().V1().Pods().Informer()
 	frameworkexthelper.ForceSyncFromInformer(context.TODO().Done(), factory, informer, eventHandler)
 }
diff --git a/pkg/util/pod.go b/pkg/util/pod.go
index 8617c083..543d6ed7 100644
--- a/pkg/util/pod.go
+++ b/pkg/util/pod.go
@@ -73,6 +73,21 @@ func IsPodTerminated(pod *corev1.Pod) bool {
 	return pod.Status.Phase == corev1.PodSucceeded || pod.Status.Phase == corev1.PodFailed
 }
 
+// IsPodInactive returns true if the pod is not in Pending or Running phase.
+// Pods in other states (Succeeded, Failed, Unknown) typically do not have any running containers
+// and therefore do not have an associated cgroup directory.
+//
+// Note: A PodPhase of Pending does NOT mean the pod has not been scheduled.
+// It may have already been assigned to this node, but its regular containers
+// have not started yet (e.g., still running initContainers).
+//
+// Also, podMetas are retrieved from Kubelet's /pods endpoint, which only returns
+// Pods that have been successfully scheduled to this node.
+func IsPodInactive(pod *corev1.Pod) bool {
+	return pod == nil ||
+		(pod.Status.Phase != corev1.PodPending && pod.Status.Phase != corev1.PodRunning)
+}
+
 func GetCPUSetFromPod(podAnnotations map[string]string) (string, error) {
 	if podAnnotations == nil {
 		return "", nil



Test patch:
diff --git a/pkg/scheduler/plugins/deviceshare/plugin_test.go b/pkg/scheduler/plugins/deviceshare/plugin_test.go
index 7209189b..a0b77ca7 100644
--- a/pkg/scheduler/plugins/deviceshare/plugin_test.go
+++ b/pkg/scheduler/plugins/deviceshare/plugin_test.go
@@ -4725,7 +4725,7 @@ func Test_Plugin_PreBind(t *testing.T) {
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			defer utilfeature.SetFeatureGateDuringTest(t, utilfeature.DefaultMutableFeatureGate, koordfeatures.DevicePluginAdaption, tt.devicePluginAdaptionFeatureEnabled)()
+			defer utilfeature.SetFeatureGateDuringTest(t, k8sfeature.DefaultMutableFeatureGate, koordfeatures.DevicePluginAdaption, tt.devicePluginAdaptionFeatureEnabled)()
 
 			suit := newPluginTestSuit(t, nil)
 			_, err := suit.ClientSet().CoreV1().Pods(testPod.Namespace).Create(context.TODO(), testPod, metav1.CreateOptions{})
diff --git a/pkg/scheduler/plugins/elasticquota/controller_test.go b/pkg/scheduler/plugins/elasticquota/controller_test.go
index d90540e3..abe76df1 100644
--- a/pkg/scheduler/plugins/elasticquota/controller_test.go
+++ b/pkg/scheduler/plugins/elasticquota/controller_test.go
@@ -365,7 +365,53 @@ func Test_updateElasticQuotaStatusIfChanged(t *testing.T) {
 	}
 }
 
+func Test_deleteElasticQuotaMetrics(t *testing.T) {
+	eq, summary := getEqAndSummary()
+	syncElasticQuotaMetrics(eq, summary)
+	dtoMetrics := getMetrics(t)
+	assert.Equal(t, 22, len(dtoMetrics))
+	deleteElasticQuotaMetrics(eq, summary)
+	dtoMetricsD := getMetrics(t)
+	assert.Equal(t, 0, len(dtoMetricsD))
+}
+
 func Test_syncElasticQuotaMetrics(t *testing.T) {
+	eq, summary := getEqAndSummary()
+	syncElasticQuotaMetrics(eq, summary)
+	dtoMetrics := getMetrics(t)
+	expect := []string{
+		`{"label":[{"name":"field","value":"allocated"},{"name":"is_parent","value":"true"},{"name":"name","value":"test-eq"},{"name":"parent","value":"root"},{"name":"resource","value":"cpu"},{"name":"tree","value":"tree-1"}],"gauge":{"value":7000}}`,
+		`{"label":[{"name":"field","value":"allocated"},{"name":"is_parent","value":"true"},{"name":"name","value":"test-eq"},{"name":"parent","value":"root"},{"name":"resource","value":"memory"},{"name":"tree","value":"tree-1"}],"gauge":{"value":50}}`,
+		`{"label":[{"name":"field","value":"child-request"},{"name":"is_parent","value":"true"},{"name":"name","value":"test-eq"},{"name":"parent","value":"root"},{"name":"resource","value":"cpu"},{"name":"tree","value":"tree-1"}],"gauge":{"value":6000}}`,
+		`{"label":[{"name":"field","value":"child-request"},{"name":"is_parent","value":"true"},{"name":"name","value":"test-eq"},{"name":"parent","value":"root"},{"name":"resource","value":"memory"},{"name":"tree","value":"tree-1"}],"gauge":{"value":50}}`,
+		`{"label":[{"name":"field","value":"guaranteed"},{"name":"is_parent","value":"true"},{"name":"name","value":"test-eq"},{"name":"parent","value":"root"},{"name":"resource","value":"cpu"},{"name":"tree","value":"tree-1"}],"gauge":{"value":8000}}`,
+		`{"label":[{"name":"field","value":"guaranteed"},{"name":"is_parent","value":"true"},{"name":"name","value":"test-eq"},{"name":"parent","value":"root"},{"name":"resource","value":"memory"},{"name":"tree","value":"tree-1"}],"gauge":{"value":50}}`,
+		`{"label":[{"name":"field","value":"max"},{"name":"is_parent","value":"true"},{"name":"name","value":"test-eq"},{"name":"parent","value":"root"},{"name":"resource","value":"cpu"},{"name":"tree","value":"tree-1"}],"gauge":{"value":4000}}`,
+		`{"label":[{"name":"field","value":"max"},{"name":"is_parent","value":"true"},{"name":"name","value":"test-eq"},{"name":"parent","value":"root"},{"name":"resource","value":"memory"},{"name":"tree","value":"tree-1"}],"gauge":{"value":200}}`,
+		`{"label":[{"name":"field","value":"min"},{"name":"is_parent","value":"true"},{"name":"name","value":"test-eq"},{"name":"parent","value":"root"},{"name":"resource","value":"cpu"},{"name":"tree","value":"tree-1"}],"gauge":{"value":2000}}`,
+		`{"label":[{"name":"field","value":"min"},{"name":"is_parent","value":"true"},{"name":"name","value":"test-eq"},{"name":"parent","value":"root"},{"name":"resource","value":"memory"},{"name":"tree","value":"tree-1"}],"gauge":{"value":100}}`,
+		`{"label":[{"name":"field","value":"non-preemptible-request"},{"name":"is_parent","value":"true"},{"name":"name","value":"test-eq"},{"name":"parent","value":"root"},{"name":"resource","value":"cpu"},{"name":"tree","value":"tree-1"}],"gauge":{"value":3000}}`,
+		`{"label":[{"name":"field","value":"non-preemptible-request"},{"name":"is_parent","value":"true"},{"name":"name","value":"test-eq"},{"name":"parent","value":"root"},{"name":"resource","value":"memory"},{"name":"tree","value":"tree-1"}],"gauge":{"value":50}}`,
+		`{"label":[{"name":"field","value":"non-preemptible-used"},{"name":"is_parent","value":"true"},{"name":"name","value":"test-eq"},{"name":"parent","value":"root"},{"name":"resource","value":"cpu"},{"name":"tree","value":"tree-1"}],"gauge":{"value":2000}}`,
+		`{"label":[{"name":"field","value":"non-preemptible-used"},{"name":"is_parent","value":"true"},{"name":"name","value":"test-eq"},{"name":"parent","value":"root"},{"name":"resource","value":"memory"},{"name":"tree","value":"tree-1"}],"gauge":{"value":50}}`,
+		`{"label":[{"name":"field","value":"request"},{"name":"is_parent","value":"true"},{"name":"name","value":"test-eq"},{"name":"parent","value":"root"},{"name":"resource","value":"cpu"},{"name":"tree","value":"tree-1"}],"gauge":{"value":4000}}`,
+		`{"label":[{"name":"field","value":"request"},{"name":"is_parent","value":"true"},{"name":"name","value":"test-eq"},{"name":"parent","value":"root"},{"name":"resource","value":"memory"},{"name":"tree","value":"tree-1"}],"gauge":{"value":50}}`,
+		`{"label":[{"name":"field","value":"runtime"},{"name":"is_parent","value":"true"},{"name":"name","value":"test-eq"},{"name":"parent","value":"root"},{"name":"resource","value":"cpu"},{"name":"tree","value":"tree-1"}],"gauge":{"value":5000}}`,
+		`{"label":[{"name":"field","value":"runtime"},{"name":"is_parent","value":"true"},{"name":"name","value":"test-eq"},{"name":"parent","value":"root"},{"name":"resource","value":"memory"},{"name":"tree","value":"tree-1"}],"gauge":{"value":50}}`,
+		`{"label":[{"name":"field","value":"unschedulable-resource"},{"name":"is_parent","value":"true"},{"name":"name","value":"test-eq"},{"name":"parent","value":"root"},{"name":"resource","value":"cpu"},{"name":"tree","value":"tree-1"}],"gauge":{"value":4000}}`,
+		`{"label":[{"name":"field","value":"unschedulable-resource"},{"name":"is_parent","value":"true"},{"name":"name","value":"test-eq"},{"name":"parent","value":"root"},{"name":"resource","value":"memory"},{"name":"tree","value":"tree-1"}],"gauge":{"value":8}}`,
+		`{"label":[{"name":"field","value":"used"},{"name":"is_parent","value":"true"},{"name":"name","value":"test-eq"},{"name":"parent","value":"root"},{"name":"resource","value":"cpu"},{"name":"tree","value":"tree-1"}],"gauge":{"value":1000}}`,
+		`{"label":[{"name":"field","value":"used"},{"name":"is_parent","value":"true"},{"name":"name","value":"test-eq"},{"name":"parent","value":"root"},{"name":"resource","value":"memory"},{"name":"tree","value":"tree-1"}],"gauge":{"value":50}}`,
+	}
+	assert.Equal(t, len(expect), len(dtoMetrics))
+	for i := range dtoMetrics {
+		v := dtoMetrics[i]
+		jsonStrBytes, _ := json.Marshal(v)
+		assert.Equal(t, expect[i], string(jsonStrBytes))
+	}
+}
+
+func getEqAndSummary() (*v1alpha1.ElasticQuota, *core.QuotaInfoSummary) {
 	eq := &v1alpha1.ElasticQuota{
 		ObjectMeta: metav1.ObjectMeta{
 			Labels: map[string]string{
@@ -396,7 +442,10 @@ func Test_syncElasticQuotaMetrics(t *testing.T) {
 		Allocated:             MakeResourceList().CPU(7).Mem(50).Obj(),
 		Guaranteed:            MakeResourceList().CPU(8).Mem(50).Obj(),
 	}
-	syncElasticQuotaMetrics(eq, summary)
+	return eq, summary
+}
+
+func getMetrics(t *testing.T) []*dto.Metric {
 	metricsCh := make(chan prometheus.Metric, 10)
 	go func() {
 		ElasticQuotaSpecMetric.Collect(metricsCh)
@@ -424,36 +473,7 @@ loopChan:
 	sort.Slice(dtoMetrics, func(i, j int) bool {
 		return dtoMetrics[i].String() < dtoMetrics[j].String()
 	})
-	expect := []string{
-		`{"label":[{"name":"field","value":"allocated"},{"name":"is_parent","value":"true"},{"name":"name","value":"test-eq"},{"name":"parent","value":"root"},{"name":"resource","value":"cpu"},{"name":"tree","value":"tree-1"}],"gauge":{"value":7000}}`,
-		`{"label":[{"name":"field","value":"allocated"},{"name":"is_parent","value":"true"},{"name":"name","value":"test-eq"},{"name":"parent","value":"root"},{"name":"resource","value":"memory"},{"name":"tree","value":"tree-1"}],"gauge":{"value":50}}`,
-		`{"label":[{"name":"field","value":"child-request"},{"name":"is_parent","value":"true"},{"name":"name","value":"test-eq"},{"name":"parent","value":"root"},{"name":"resource","value":"cpu"},{"name":"tree","value":"tree-1"}],"gauge":{"value":6000}}`,
-		`{"label":[{"name":"field","value":"child-request"},{"name":"is_parent","value":"true"},{"name":"name","value":"test-eq"},{"name":"parent","value":"root"},{"name":"resource","value":"memory"},{"name":"tree","value":"tree-1"}],"gauge":{"value":50}}`,
-		`{"label":[{"name":"field","value":"guaranteed"},{"name":"is_parent","value":"true"},{"name":"name","value":"test-eq"},{"name":"parent","value":"root"},{"name":"resource","value":"cpu"},{"name":"tree","value":"tree-1"}],"gauge":{"value":8000}}`,
-		`{"label":[{"name":"field","value":"guaranteed"},{"name":"is_parent","value":"true"},{"name":"name","value":"test-eq"},{"name":"parent","value":"root"},{"name":"resource","value":"memory"},{"name":"tree","value":"tree-1"}],"gauge":{"value":50}}`,
-		`{"label":[{"name":"field","value":"max"},{"name":"is_parent","value":"true"},{"name":"name","value":"test-eq"},{"name":"parent","value":"root"},{"name":"resource","value":"cpu"},{"name":"tree","value":"tree-1"}],"gauge":{"value":4000}}`,
-		`{"label":[{"name":"field","value":"max"},{"name":"is_parent","value":"true"},{"name":"name","value":"test-eq"},{"name":"parent","value":"root"},{"name":"resource","value":"memory"},{"name":"tree","value":"tree-1"}],"gauge":{"value":200}}`,
-		`{"label":[{"name":"field","value":"min"},{"name":"is_parent","value":"true"},{"name":"name","value":"test-eq"},{"name":"parent","value":"root"},{"name":"resource","value":"cpu"},{"name":"tree","value":"tree-1"}],"gauge":{"value":2000}}`,
-		`{"label":[{"name":"field","value":"min"},{"name":"is_parent","value":"true"},{"name":"name","value":"test-eq"},{"name":"parent","value":"root"},{"name":"resource","value":"memory"},{"name":"tree","value":"tree-1"}],"gauge":{"value":100}}`,
-		`{"label":[{"name":"field","value":"non-preemptible-request"},{"name":"is_parent","value":"true"},{"name":"name","value":"test-eq"},{"name":"parent","value":"root"},{"name":"resource","value":"cpu"},{"name":"tree","value":"tree-1"}],"gauge":{"value":3000}}`,
-		`{"label":[{"name":"field","value":"non-preemptible-request"},{"name":"is_parent","value":"true"},{"name":"name","value":"test-eq"},{"name":"parent","value":"root"},{"name":"resource","value":"memory"},{"name":"tree","value":"tree-1"}],"gauge":{"value":50}}`,
-		`{"label":[{"name":"field","value":"non-preemptible-used"},{"name":"is_parent","value":"true"},{"name":"name","value":"test-eq"},{"name":"parent","value":"root"},{"name":"resource","value":"cpu"},{"name":"tree","value":"tree-1"}],"gauge":{"value":2000}}`,
-		`{"label":[{"name":"field","value":"non-preemptible-used"},{"name":"is_parent","value":"true"},{"name":"name","value":"test-eq"},{"name":"parent","value":"root"},{"name":"resource","value":"memory"},{"name":"tree","value":"tree-1"}],"gauge":{"value":50}}`,
-		`{"label":[{"name":"field","value":"request"},{"name":"is_parent","value":"true"},{"name":"name","value":"test-eq"},{"name":"parent","value":"root"},{"name":"resource","value":"cpu"},{"name":"tree","value":"tree-1"}],"gauge":{"value":4000}}`,
-		`{"label":[{"name":"field","value":"request"},{"name":"is_parent","value":"true"},{"name":"name","value":"test-eq"},{"name":"parent","value":"root"},{"name":"resource","value":"memory"},{"name":"tree","value":"tree-1"}],"gauge":{"value":50}}`,
-		`{"label":[{"name":"field","value":"runtime"},{"name":"is_parent","value":"true"},{"name":"name","value":"test-eq"},{"name":"parent","value":"root"},{"name":"resource","value":"cpu"},{"name":"tree","value":"tree-1"}],"gauge":{"value":5000}}`,
-		`{"label":[{"name":"field","value":"runtime"},{"name":"is_parent","value":"true"},{"name":"name","value":"test-eq"},{"name":"parent","value":"root"},{"name":"resource","value":"memory"},{"name":"tree","value":"tree-1"}],"gauge":{"value":50}}`,
-		`{"label":[{"name":"field","value":"unschedulable-resource"},{"name":"is_parent","value":"true"},{"name":"name","value":"test-eq"},{"name":"parent","value":"root"},{"name":"resource","value":"cpu"},{"name":"tree","value":"tree-1"}],"gauge":{"value":4000}}`,
-		`{"label":[{"name":"field","value":"unschedulable-resource"},{"name":"is_parent","value":"true"},{"name":"name","value":"test-eq"},{"name":"parent","value":"root"},{"name":"resource","value":"memory"},{"name":"tree","value":"tree-1"}],"gauge":{"value":8}}`,
-		`{"label":[{"name":"field","value":"used"},{"name":"is_parent","value":"true"},{"name":"name","value":"test-eq"},{"name":"parent","value":"root"},{"name":"resource","value":"cpu"},{"name":"tree","value":"tree-1"}],"gauge":{"value":1000}}`,
-		`{"label":[{"name":"field","value":"used"},{"name":"is_parent","value":"true"},{"name":"name","value":"test-eq"},{"name":"parent","value":"root"},{"name":"resource","value":"memory"},{"name":"tree","value":"tree-1"}],"gauge":{"value":50}}`,
-	}
-	assert.Equal(t, len(expect), len(dtoMetrics))
-	for i := range dtoMetrics {
-		v := dtoMetrics[i]
-		jsonStrBytes, _ := json.Marshal(v)
-		assert.Equal(t, expect[i], string(jsonStrBytes))
-	}
+	return dtoMetrics
 }
 
 type eqWrapper struct{ *v1alpha1.ElasticQuota }
diff --git a/pkg/scheduler/plugins/loadaware/load_aware_test.go b/pkg/scheduler/plugins/loadaware/load_aware_test.go
index 8ca4945e..993626fa 100644
--- a/pkg/scheduler/plugins/loadaware/load_aware_test.go
+++ b/pkg/scheduler/plugins/loadaware/load_aware_test.go
@@ -37,7 +37,6 @@ import (
 	frameworkruntime "k8s.io/kubernetes/pkg/scheduler/framework/runtime"
 	schedulertesting "k8s.io/kubernetes/pkg/scheduler/testing"
 	"k8s.io/utils/pointer"
-	"k8s.io/utils/ptr"
 
 	"github.com/koordinator-sh/koordinator/apis/extension"
 	slov1alpha1 "github.com/koordinator-sh/koordinator/apis/slo/v1alpha1"
@@ -425,132 +424,6 @@ func TestEnableScheduleWhenNodeMetricsExpired(t *testing.T) {
 	}
 }
 
-func TestShouldEstimatePodByConfig(t *testing.T) {
-	now := time.Now()
-	tests := []struct {
-		name                              string
-		estimatedSecondsAfterPodScheduled *int64
-		estimatedSecondsAfterInitialized  *int64
-		allowCustomizeEstimation          bool
-		info                              *podAssignInfo
-		want                              bool
-	}{
-		{
-			name: "disabled",
-			info: &podAssignInfo{},
-		},
-		{
-			name:                              "enabled for pod scheduled",
-			estimatedSecondsAfterPodScheduled: ptr.To((int64(180))),
-			info: &podAssignInfo{
-				pod: schedulertesting.MakePod().Namespace("default").Name("pod").Conditions([]corev1.PodCondition{
-					{Type: corev1.PodScheduled, Status: corev1.ConditionTrue, LastTransitionTime: metav1.NewTime(now.Add(-time.Minute))},
-				}).Obj(),
-				timestamp: now.Add(-time.Minute),
-			},
-			want: true,
-		},
-		{
-			name:                              "disabled pod scheduled when pod initialized",
-			estimatedSecondsAfterPodScheduled: ptr.To((int64(180))),
-			estimatedSecondsAfterInitialized:  ptr.To((int64(10))),
-			info: &podAssignInfo{
-				pod: schedulertesting.MakePod().Namespace("default").Name("pod").Conditions([]corev1.PodCondition{
-					{Type: corev1.PodScheduled, Status: corev1.ConditionTrue, LastTransitionTime: metav1.NewTime(now.Add(-time.Minute))},
-					{Type: corev1.PodInitialized, Status: corev1.ConditionTrue, LastTransitionTime: metav1.NewTime(now.Add(-30 * time.Second))},
-				}).Obj(),
-				timestamp: now.Add(-time.Minute),
-			},
-		},
-		{
-			name:                             "enabled for pod initialized",
-			estimatedSecondsAfterInitialized: ptr.To((int64(180))),
-			info: &podAssignInfo{
-				pod: schedulertesting.MakePod().Namespace("default").Name("pod").Conditions([]corev1.PodCondition{
-					{Type: corev1.PodInitialized, Status: corev1.ConditionTrue, LastTransitionTime: metav1.NewTime(now.Add(-time.Minute))},
-				}).Obj(),
-			},
-			want: true,
-		},
-		{
-			name:                             "disabled for pod initialized when condition is not satisfied",
-			estimatedSecondsAfterInitialized: ptr.To((int64(180))),
-			info: &podAssignInfo{
-				pod: schedulertesting.MakePod().Namespace("default").Name("pod").Conditions([]corev1.PodCondition{
-					{Type: corev1.PodInitialized, Status: corev1.ConditionFalse, LastTransitionTime: metav1.NewTime(now.Add(-time.Minute))},
-				}).Obj(),
-			},
-		},
-		{
-			name:                     "after pod scheduled from metadata",
-			allowCustomizeEstimation: true,
-			info: &podAssignInfo{
-				pod: schedulertesting.MakePod().Namespace("default").
-					Annotation(extension.AnnotationCustomEstimatedSecondsAfterPodScheduled, "180").
-					Name("pod").Conditions([]corev1.PodCondition{
-					{Type: corev1.PodScheduled, Status: corev1.ConditionTrue, LastTransitionTime: metav1.NewTime(now.Add(-time.Minute))},
-				}).Obj(),
-				timestamp: now.Add(-time.Minute),
-			},
-			want: true,
-		},
-		{
-			name:                     "after initialized from metadata",
-			allowCustomizeEstimation: true,
-			info: &podAssignInfo{
-				pod: schedulertesting.MakePod().Namespace("default").
-					Annotation(extension.AnnotationCustomEstimatedSecondsAfterInitialized, "180").
-					Name("pod").Conditions([]corev1.PodCondition{
-					{Type: corev1.PodInitialized, Status: corev1.ConditionTrue, LastTransitionTime: metav1.NewTime(now.Add(-time.Minute))},
-				}).Obj(),
-			},
-			want: true,
-		},
-	}
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			var v1beta3args v1beta3.LoadAwareSchedulingArgs
-			v1beta3.SetDefaults_LoadAwareSchedulingArgs(&v1beta3args)
-			v1beta3args.EstimatedSecondsAfterPodScheduled = tt.estimatedSecondsAfterPodScheduled
-			v1beta3args.EstimatedSecondsAfterInitialized = tt.estimatedSecondsAfterInitialized
-			v1beta3args.AllowCustomizeEstimation = tt.allowCustomizeEstimation
-			var loadAwareSchedulingArgs config.LoadAwareSchedulingArgs
-			err := v1beta3.Convert_v1beta3_LoadAwareSchedulingArgs_To_config_LoadAwareSchedulingArgs(&v1beta3args, &loadAwareSchedulingArgs, nil)
-			assert.NoError(t, err)
-
-			koordClientSet := koordfake.NewSimpleClientset()
-			koordSharedInformerFactory := koordinatorinformers.NewSharedInformerFactory(koordClientSet, 0)
-			extenderFactory, _ := frameworkext.NewFrameworkExtenderFactory(
-				frameworkext.WithKoordinatorClientSet(koordClientSet),
-				frameworkext.WithKoordinatorSharedInformerFactory(koordSharedInformerFactory),
-			)
-			proxyNew := frameworkext.PluginFactoryProxy(extenderFactory, New)
-
-			cs := kubefake.NewSimpleClientset()
-			informerFactory := informers.NewSharedInformerFactory(cs, 0)
-			snapshot := newTestSharedLister(nil, nil)
-			registeredPlugins := []schedulertesting.RegisterPluginFunc{
-				schedulertesting.RegisterBindPlugin(defaultbinder.Name, defaultbinder.New),
-				schedulertesting.RegisterQueueSortPlugin(queuesort.Name, queuesort.New),
-			}
-			fh, err := schedulertesting.NewFramework(context.TODO(), registeredPlugins, "koord-scheduler",
-				frameworkruntime.WithClientSet(cs),
-				frameworkruntime.WithInformerFactory(informerFactory),
-				frameworkruntime.WithSnapshotSharedLister(snapshot),
-			)
-			assert.Nil(t, err)
-
-			p, err := proxyNew(&loadAwareSchedulingArgs, fh)
-			assert.NotNil(t, p)
-			assert.Nil(t, err)
-			koordSharedInformerFactory.Start(context.TODO().Done())
-			koordSharedInformerFactory.WaitForCacheSync(context.TODO().Done())
-			actual := p.(*Plugin).shouldEstimatePodByConfig(tt.info, now)
-			assert.Equal(t, tt.want, actual)
-		})
-	}
-}
-
 func TestFilterUsage(t *testing.T) {
 	tests := []struct {
 		name                      string
diff --git a/pkg/scheduler/plugins/loadaware/pod_assign_cache_test.go b/pkg/scheduler/plugins/loadaware/pod_assign_cache_test.go
index 8617c776..e0ad2187 100644
--- a/pkg/scheduler/plugins/loadaware/pod_assign_cache_test.go
+++ b/pkg/scheduler/plugins/loadaware/pod_assign_cache_test.go
@@ -24,7 +24,10 @@ import (
 	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/types"
+	schedulertesting "k8s.io/kubernetes/pkg/scheduler/testing"
+	"k8s.io/utils/ptr"
 
+	"github.com/koordinator-sh/koordinator/apis/extension"
 	"github.com/koordinator-sh/koordinator/pkg/scheduler/apis/config"
 	"github.com/koordinator-sh/koordinator/pkg/scheduler/plugins/loadaware/estimator"
 )
@@ -103,7 +106,7 @@ func TestPodAssignCache_OnAdd(t *testing.T) {
 			}()
 			timeNowFn = fakeTimeNowFn
 			e, _ := estimator.NewDefaultEstimator(&config.LoadAwareSchedulingArgs{}, nil)
-			assignCache := newPodAssignCache(e)
+			assignCache := newPodAssignCache(e, &config.LoadAwareSchedulingArgs{})
 			assignCache.OnAdd(tt.pod, true)
 			assert.Equal(t, tt.wantCache, assignCache.podInfoItems)
 		})
@@ -112,10 +115,10 @@ func TestPodAssignCache_OnAdd(t *testing.T) {
 
 func TestPodAssignCache_OnUpdate(t *testing.T) {
 	tests := []struct {
-		name        string
-		pod         *corev1.Pod
-		assignCache *podAssignCache
-		wantCache   map[string]map[types.UID]*podAssignInfo
+		name         string
+		pod          *corev1.Pod
+		podInfoItems map[string]map[types.UID]*podAssignInfo
+		wantCache    map[string]map[types.UID]*podAssignInfo
 	}{
 		{
 			name:      "update pending pod",
@@ -137,25 +140,23 @@ func TestPodAssignCache_OnUpdate(t *testing.T) {
 					Phase: corev1.PodFailed,
 				},
 			},
-			assignCache: &podAssignCache{
-				podInfoItems: map[string]map[types.UID]*podAssignInfo{
-					"test-node": {
-						"123456789": &podAssignInfo{
-							pod: &corev1.Pod{
-								ObjectMeta: metav1.ObjectMeta{
-									UID:       "123456789",
-									Namespace: "default",
-									Name:      "test",
-								},
-								Spec: corev1.PodSpec{
-									NodeName: "test-node",
-								},
-								Status: corev1.PodStatus{
-									Phase: corev1.PodRunning,
-								},
+			podInfoItems: map[string]map[types.UID]*podAssignInfo{
+				"test-node": {
+					"123456789": &podAssignInfo{
+						pod: &corev1.Pod{
+							ObjectMeta: metav1.ObjectMeta{
+								UID:       "123456789",
+								Namespace: "default",
+								Name:      "test",
+							},
+							Spec: corev1.PodSpec{
+								NodeName: "test-node",
+							},
+							Status: corev1.PodStatus{
+								Phase: corev1.PodRunning,
 							},
-							timestamp: fakeTimeNowFn(),
 						},
+						timestamp: fakeTimeNowFn(),
 					},
 				},
 			},
@@ -198,7 +199,7 @@ func TestPodAssignCache_OnUpdate(t *testing.T) {
 			},
 		},
 		{
-			name: "update scheduled running pod, timestamp won't be updated",
+			name: "update pod metadata only, cache won't be updated",
 			pod: &corev1.Pod{
 				ObjectMeta: metav1.ObjectMeta{
 					UID:       "123456789",
@@ -212,25 +213,86 @@ func TestPodAssignCache_OnUpdate(t *testing.T) {
 					Phase: corev1.PodRunning,
 				},
 			},
-			assignCache: &podAssignCache{
-				podInfoItems: map[string]map[types.UID]*podAssignInfo{
-					"test-node": {
-						"123456789": &podAssignInfo{
-							pod: &corev1.Pod{
-								ObjectMeta: metav1.ObjectMeta{
-									UID:       "123456789",
-									Namespace: "default",
-									Name:      "test",
-								},
-								Spec: corev1.PodSpec{
-									NodeName: "test-node",
-								},
-								Status: corev1.PodStatus{
-									Phase: corev1.PodRunning,
+			podInfoItems: map[string]map[types.UID]*podAssignInfo{
+				"test-node": {
+					"123456789": &podAssignInfo{
+						pod: &corev1.Pod{
+							ObjectMeta: metav1.ObjectMeta{
+								UID:       "123456789",
+								Namespace: "default",
+								Name:      "test",
+							},
+							Spec: corev1.PodSpec{
+								NodeName: "test-node",
+							},
+							Status: corev1.PodStatus{
+								Phase: corev1.PodRunning,
+							},
+						},
+						timestamp: fakeTimeNowFn().Add(1000),
+					},
+				},
+			},
+			wantCache: map[string]map[types.UID]*podAssignInfo{
+				"test-node": {
+					"123456789": &podAssignInfo{
+						pod: &corev1.Pod{
+							ObjectMeta: metav1.ObjectMeta{
+								UID:       "123456789",
+								Namespace: "default",
+								Name:      "test",
+							},
+							Spec: corev1.PodSpec{
+								NodeName: "test-node",
+							},
+							Status: corev1.PodStatus{
+								Phase: corev1.PodRunning,
+							},
+						},
+						timestamp: fakeTimeNowFn().Add(1000),
+					},
+				},
+			},
+		},
+		{
+			name: "update pod conditions, cache will be updated",
+			pod: &corev1.Pod{
+				ObjectMeta: metav1.ObjectMeta{
+					UID:       "123456789",
+					Namespace: "default",
+					Name:      "test",
+				},
+				Spec: corev1.PodSpec{
+					NodeName: "test-node",
+				},
+				Status: corev1.PodStatus{
+					Phase: corev1.PodRunning,
+					Conditions: []corev1.PodCondition{
+						{Type: corev1.PodScheduled, Status: corev1.ConditionTrue, LastTransitionTime: metav1.NewTime(fakeTimeNowFn().Add(1000))},
+						{Type: corev1.PodInitialized, Status: corev1.ConditionTrue, LastTransitionTime: metav1.NewTime(fakeTimeNowFn().Add(3000))},
+					},
+				},
+			},
+			podInfoItems: map[string]map[types.UID]*podAssignInfo{
+				"test-node": {
+					"123456789": &podAssignInfo{
+						pod: &corev1.Pod{
+							ObjectMeta: metav1.ObjectMeta{
+								UID:       "123456789",
+								Namespace: "default",
+								Name:      "test",
+							},
+							Spec: corev1.PodSpec{
+								NodeName: "test-node",
+							},
+							Status: corev1.PodStatus{
+								Phase: corev1.PodRunning,
+								Conditions: []corev1.PodCondition{
+									{Type: corev1.PodScheduled, Status: corev1.ConditionTrue, LastTransitionTime: metav1.NewTime(fakeTimeNowFn().Add(1000))},
 								},
 							},
-							timestamp: fakeTimeNowFn().Add(1000),
 						},
+						timestamp: fakeTimeNowFn().Add(1000),
 					},
 				},
 			},
@@ -248,6 +310,10 @@ func TestPodAssignCache_OnUpdate(t *testing.T) {
 							},
 							Status: corev1.PodStatus{
 								Phase: corev1.PodRunning,
+								Conditions: []corev1.PodCondition{
+									{Type: corev1.PodScheduled, Status: corev1.ConditionTrue, LastTransitionTime: metav1.NewTime(fakeTimeNowFn().Add(1000))},
+									{Type: corev1.PodInitialized, Status: corev1.ConditionTrue, LastTransitionTime: metav1.NewTime(fakeTimeNowFn().Add(3000))},
+								},
 							},
 						},
 						timestamp: fakeTimeNowFn().Add(1000),
@@ -263,10 +329,10 @@ func TestPodAssignCache_OnUpdate(t *testing.T) {
 				timeNowFn = preTimeNowFn
 			}()
 			timeNowFn = fakeTimeNowFn
-			assignCache := tt.assignCache
-			if assignCache == nil {
-				e, _ := estimator.NewDefaultEstimator(&config.LoadAwareSchedulingArgs{}, nil)
-				assignCache = newPodAssignCache(e)
+			e, _ := estimator.NewDefaultEstimator(&config.LoadAwareSchedulingArgs{}, nil)
+			assignCache := newPodAssignCache(e, &config.LoadAwareSchedulingArgs{})
+			if len(tt.podInfoItems) > 0 {
+				assignCache.podInfoItems = tt.podInfoItems
 			}
 			assignCache.OnUpdate(nil, tt.pod)
 			assert.Equal(t, tt.wantCache, assignCache.podInfoItems)
@@ -288,25 +354,25 @@ func TestPodAssignCache_OnDelete(t *testing.T) {
 			Phase: corev1.PodFailed,
 		},
 	}
-	assignCache := &podAssignCache{
-		podInfoItems: map[string]map[types.UID]*podAssignInfo{
-			"test-node": {
-				"123456789": &podAssignInfo{
-					pod: &corev1.Pod{
-						ObjectMeta: metav1.ObjectMeta{
-							UID:       "123456789",
-							Namespace: "default",
-							Name:      "test",
-						},
-						Spec: corev1.PodSpec{
-							NodeName: "test-node",
-						},
-						Status: corev1.PodStatus{
-							Phase: corev1.PodRunning,
-						},
+	e, _ := estimator.NewDefaultEstimator(&config.LoadAwareSchedulingArgs{}, nil)
+	assignCache := newPodAssignCache(e, &config.LoadAwareSchedulingArgs{})
+	assignCache.podInfoItems = map[string]map[types.UID]*podAssignInfo{
+		"test-node": {
+			"123456789": &podAssignInfo{
+				pod: &corev1.Pod{
+					ObjectMeta: metav1.ObjectMeta{
+						UID:       "123456789",
+						Namespace: "default",
+						Name:      "test",
+					},
+					Spec: corev1.PodSpec{
+						NodeName: "test-node",
+					},
+					Status: corev1.PodStatus{
+						Phase: corev1.PodRunning,
 					},
-					timestamp: fakeTimeNowFn(),
 				},
+				timestamp: fakeTimeNowFn(),
 			},
 		},
 	}
@@ -314,3 +380,90 @@ func TestPodAssignCache_OnDelete(t *testing.T) {
 	wantCache := map[string]map[types.UID]*podAssignInfo{}
 	assert.Equal(t, wantCache, assignCache.podInfoItems)
 }
+
+func TestShouldEstimatePodDeadline(t *testing.T) {
+	now := time.Now()
+	tests := []struct {
+		name                              string
+		estimatedSecondsAfterPodScheduled *int64
+		estimatedSecondsAfterInitialized  *int64
+		allowCustomizeEstimation          bool
+		pod                               *corev1.Pod
+		expected                          time.Time
+	}{
+		{
+			name: "disabled",
+		},
+		{
+			name:                              "enabled for pod scheduled",
+			estimatedSecondsAfterPodScheduled: ptr.To((int64(180))),
+			pod: schedulertesting.MakePod().Namespace("default").Name("pod").Conditions([]corev1.PodCondition{
+				{Type: corev1.PodScheduled, Status: corev1.ConditionTrue, LastTransitionTime: metav1.NewTime(now.Add(-time.Minute))},
+			}).Obj(),
+			expected: now.Add(2 * time.Minute),
+		},
+		{
+			name:                              "disabled pod scheduled when pod initialized",
+			estimatedSecondsAfterPodScheduled: ptr.To((int64(180))),
+			estimatedSecondsAfterInitialized:  ptr.To((int64(10))),
+			pod: schedulertesting.MakePod().Namespace("default").Name("pod").Conditions([]corev1.PodCondition{
+				{Type: corev1.PodScheduled, Status: corev1.ConditionTrue, LastTransitionTime: metav1.NewTime(now.Add(-time.Minute))},
+				{Type: corev1.PodInitialized, Status: corev1.ConditionTrue, LastTransitionTime: metav1.NewTime(now.Add(-30 * time.Second))},
+			}).Obj(),
+			expected: now.Add(-20 * time.Second),
+		},
+		{
+			name:                             "enabled for pod initialized",
+			estimatedSecondsAfterInitialized: ptr.To((int64(180))),
+			pod: schedulertesting.MakePod().Namespace("default").Name("pod").Conditions([]corev1.PodCondition{
+				{Type: corev1.PodInitialized, Status: corev1.ConditionTrue, LastTransitionTime: metav1.NewTime(now.Add(-time.Minute))},
+			}).Obj(),
+			expected: now.Add(2 * time.Minute),
+		},
+		{
+			name:                             "disabled for pod initialized when condition is not satisfied",
+			estimatedSecondsAfterInitialized: ptr.To((int64(180))),
+			pod: schedulertesting.MakePod().Namespace("default").Name("pod").Conditions([]corev1.PodCondition{
+				{Type: corev1.PodInitialized, Status: corev1.ConditionFalse, LastTransitionTime: metav1.NewTime(now.Add(-time.Minute))},
+			}).Obj(),
+		},
+		{
+			name:                     "after pod scheduled from metadata",
+			allowCustomizeEstimation: true,
+			pod: schedulertesting.MakePod().Namespace("default").
+				Annotation(extension.AnnotationCustomEstimatedSecondsAfterPodScheduled, "180").
+				Name("pod").Conditions([]corev1.PodCondition{
+				{Type: corev1.PodScheduled, Status: corev1.ConditionTrue, LastTransitionTime: metav1.NewTime(now.Add(-time.Minute))},
+			}).Obj(),
+			expected: now.Add(2 * time.Minute),
+		},
+		{
+			name:                     "after initialized from metadata",
+			allowCustomizeEstimation: true,
+			pod: schedulertesting.MakePod().Namespace("default").
+				Annotation(extension.AnnotationCustomEstimatedSecondsAfterInitialized, "180").
+				Name("pod").Conditions([]corev1.PodCondition{
+				{Type: corev1.PodInitialized, Status: corev1.ConditionTrue, LastTransitionTime: metav1.NewTime(now.Add(-time.Minute))},
+			}).Obj(),
+			expected: now.Add(2 * time.Minute),
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			preTimeNowFn := timeNowFn
+			defer func() {
+				timeNowFn = preTimeNowFn
+			}()
+			timeNowFn = fakeTimeNowFn
+			args := &config.LoadAwareSchedulingArgs{
+				EstimatedSecondsAfterPodScheduled: tt.estimatedSecondsAfterPodScheduled,
+				EstimatedSecondsAfterInitialized:  tt.estimatedSecondsAfterInitialized,
+				AllowCustomizeEstimation:          tt.allowCustomizeEstimation,
+			}
+			e, _ := estimator.NewDefaultEstimator(args, nil)
+			assignCache := newPodAssignCache(e, args)
+			actual := assignCache.shouldEstimatePodDeadline(tt.pod, now.Add(-time.Minute))
+			assert.Equal(t, tt.expected, actual)
+		})
+	}
+}
diff --git a/pkg/scheduler/plugins/loadaware/service_test.go b/pkg/scheduler/plugins/loadaware/service_test.go
index bb4b9940..cb22bda5 100644
--- a/pkg/scheduler/plugins/loadaware/service_test.go
+++ b/pkg/scheduler/plugins/loadaware/service_test.go
@@ -43,7 +43,7 @@ func TestQueryNode(t *testing.T) {
 		timeNowFn = fakeTimeNowFn
 		e, _ := estimator.NewDefaultEstimator(&config.LoadAwareSchedulingArgs{}, nil)
 		pl := &Plugin{
-			podAssignCache: newPodAssignCache(e),
+			podAssignCache: newPodAssignCache(e, &config.LoadAwareSchedulingArgs{}),
 		}
 		testNodeName := "test-node"
 		pendingPod := &corev1.Pod{
diff --git a/pkg/scheduler/plugins/reservation/controller/controller_test.go b/pkg/scheduler/plugins/reservation/controller/controller_test.go
index 0c01acee..8d833c13 100644
--- a/pkg/scheduler/plugins/reservation/controller/controller_test.go
+++ b/pkg/scheduler/plugins/reservation/controller/controller_test.go
@@ -224,7 +224,10 @@ func TestSyncStatus(t *testing.T) {
 	// register metrics
 	metricsRegistry := basemetrics.NewKubeRegistry()
 	metrics.ReservationStatusPhase.GetGaugeVec().Reset()
-	metricsRegistry.Registerer().MustRegister(metrics.ReservationStatusPhase.GetGaugeVec())
+	metrics.ReservationResource.GetGaugeVec().Reset()
+	metricsRegistry.Registerer().MustRegister(
+		metrics.ReservationStatusPhase.GetGaugeVec(),
+		metrics.ReservationResource.GetGaugeVec())
 
 	reservation := &schedulingv1alpha1.Reservation{
 		ObjectMeta: metav1.ObjectMeta{
@@ -348,4 +351,24 @@ func TestSyncStatus(t *testing.T) {
 	if err := testutil.GatherAndCompare(metricsRegistry, strings.NewReader(expectedSuccessedMetrics), "scheduler_reservation_status_phase"); err != nil {
 		t.Error(err)
 	}
+
+	expectedUtilizationMetrics := fmt.Sprintf(`
+# HELP scheduler_reservation_resource Resource metrics for a reservation, including allocatable, allocated, and utilization with unit.
+# TYPE scheduler_reservation_resource gauge
+scheduler_reservation_resource{name="%s",resource="cpu",type="allocatable",unit="core"} 10
+scheduler_reservation_resource{name="%s",resource="cpu",type="allocated",unit="core"} 8
+scheduler_reservation_resource{name="%s",resource="cpu",type="utilization",unit="ratio"} 0.8
+scheduler_reservation_resource{name="%s",resource="memory",type="allocatable",unit="Gi"} 10
+scheduler_reservation_resource{name="%s",resource="memory",type="allocated",unit="Gi"} 8
+scheduler_reservation_resource{name="%s",resource="memory",type="utilization",unit="ratio"} 0.8
+`,
+		"normalReservation", "normalReservation",
+		"normalReservation", "normalReservation",
+		"normalReservation", "normalReservation",
+	)
+
+	if err := testutil.GatherAndCompare(metricsRegistry, strings.NewReader(expectedUtilizationMetrics),
+		"scheduler_reservation_resource"); err != nil {
+		t.Error(err)
+	}
 }
diff --git a/pkg/util/pod_test.go b/pkg/util/pod_test.go
index db940ab7..f73951d6 100644
--- a/pkg/util/pod_test.go
+++ b/pkg/util/pod_test.go
@@ -21,6 +21,9 @@ import (
 
 	"github.com/stretchr/testify/assert"
 
+	corev1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+
 	apiext "github.com/koordinator-sh/koordinator/apis/extension"
 )
 
@@ -59,3 +62,64 @@ func Test_GetCPUSetFromPod(t *testing.T) {
 		})
 	}
 }
+
+func TestIsPodInactive(t *testing.T) {
+	tests := []struct {
+		name     string
+		pod      *corev1.Pod
+		expected bool
+	}{
+		{
+			name:     "Pod is nil",
+			pod:      nil,
+			expected: true,
+		},
+		{
+			name: "Pod is Pending",
+			pod: &corev1.Pod{
+				ObjectMeta: metav1.ObjectMeta{Name: "pod-pending"},
+				Status:     corev1.PodStatus{Phase: corev1.PodPending},
+			},
+			expected: false,
+		},
+		{
+			name: "Pod is Running",
+			pod: &corev1.Pod{
+				ObjectMeta: metav1.ObjectMeta{Name: "pod-running"},
+				Status:     corev1.PodStatus{Phase: corev1.PodRunning},
+			},
+			expected: false,
+		},
+		{
+			name: "Pod is Succeeded",
+			pod: &corev1.Pod{
+				ObjectMeta: metav1.ObjectMeta{Name: "pod-succeeded"},
+				Status:     corev1.PodStatus{Phase: corev1.PodSucceeded},
+			},
+			expected: true,
+		},
+		{
+			name: "Pod is Failed",
+			pod: &corev1.Pod{
+				ObjectMeta: metav1.ObjectMeta{Name: "pod-failed"},
+				Status:     corev1.PodStatus{Phase: corev1.PodFailed},
+			},
+			expected: true,
+		},
+		{
+			name: "Pod is Unknown",
+			pod: &corev1.Pod{
+				ObjectMeta: metav1.ObjectMeta{Name: "pod-unknown"},
+				Status:     corev1.PodStatus{Phase: "Unknown"},
+			},
+			expected: true,
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			result := IsPodInactive(tt.pod)
+			assert.Equal(t, tt.expected, result)
+		})
+	}
+}