@@ -312,6 +312,11 @@ do_analyze_rel(Relation onerel, VacuumParams *params,
312312 Oid save_userid ;
313313 int save_sec_context ;
314314 int save_nestlevel ;
315+ int64 AnalyzePageHit = VacuumPageHit ;
316+ int64 AnalyzePageMiss = VacuumPageMiss ;
317+ int64 AnalyzePageDirty = VacuumPageDirty ;
318+ PgStat_Counter startreadtime = 0 ;
319+ PgStat_Counter startwritetime = 0 ;
315320
316321 if (inh )
317322 ereport (elevel ,
@@ -346,8 +351,14 @@ do_analyze_rel(Relation onerel, VacuumParams *params,
346351 /* measure elapsed time iff autovacuum logging requires it */
347352 if (IsAutoVacuumWorkerProcess () && params -> log_min_duration >= 0 )
348353 {
354+ if (track_io_timing )
355+ {
356+ startreadtime = pgStatBlockReadTime ;
357+ startwritetime = pgStatBlockWriteTime ;
358+ }
359+
349360 pg_rusage_init (& ru0 );
350- if (params -> log_min_duration > 0 )
361+ if (params -> log_min_duration >= 0 )
351362 starttime = GetCurrentTimestamp ();
352363 }
353364
@@ -692,15 +703,90 @@ do_analyze_rel(Relation onerel, VacuumParams *params,
692703 /* Log the action if appropriate */
693704 if (IsAutoVacuumWorkerProcess () && params -> log_min_duration >= 0 )
694705 {
706+ TimestampTz endtime = GetCurrentTimestamp ();
707+
695708 if (params -> log_min_duration == 0 ||
696- TimestampDifferenceExceeds (starttime , GetCurrentTimestamp () ,
709+ TimestampDifferenceExceeds (starttime , endtime ,
697710 params -> log_min_duration ))
711+ {
712+ long delay_in_ms ;
713+ double read_rate = 0 ;
714+ double write_rate = 0 ;
715+ StringInfoData buf ;
716+
717+ /*
718+ * Calculate the difference in the Page Hit/Miss/Dirty that
719+ * happened as part of the analyze by subtracting out the
720+ * pre-analyze values which we saved above.
721+ */
722+ AnalyzePageHit = VacuumPageHit - AnalyzePageHit ;
723+ AnalyzePageMiss = VacuumPageMiss - AnalyzePageMiss ;
724+ AnalyzePageDirty = VacuumPageDirty - AnalyzePageDirty ;
725+
726+ /*
727+ * We do not expect an analyze to take > 25 days and it simplifies
728+ * things a bit to use TimestampDifferenceMilliseconds.
729+ */
730+ delay_in_ms = TimestampDifferenceMilliseconds (starttime , endtime );
731+
732+ /*
733+ * Note that we are reporting these read/write rates in the same
734+ * manner as VACUUM does, which means that while the 'average read
735+ * rate' here actually corresponds to page misses and resulting
736+ * reads which are also picked up by track_io_timing, if enabled,
737+ * the 'average write rate' is actually talking about the rate of
738+ * pages being dirtied, not being written out, so it's typical to
739+ * have a non-zero 'avg write rate' while I/O Timings only reports
740+ * reads.
741+ *
742+ * It's not clear that an ANALYZE will ever result in
743+ * FlushBuffer() being called, but we track and support reporting
744+ * on I/O write time in case that changes as it's practically free
745+ * to do so anyway.
746+ */
747+
748+ if (delay_in_ms > 0 )
749+ {
750+ read_rate = (double ) BLCKSZ * AnalyzePageMiss / (1024 * 1024 ) /
751+ (delay_in_ms / 1000.0 );
752+ write_rate = (double ) BLCKSZ * AnalyzePageDirty / (1024 * 1024 ) /
753+ (delay_in_ms / 1000.0 );
754+ }
755+
756+ /*
757+ * We split this up so we don't emit empty I/O timing values when
758+ * track_io_timing isn't enabled.
759+ */
760+
761+ initStringInfo (& buf );
762+ appendStringInfo (& buf , _ ("automatic analyze of table \"%s.%s.%s\"\n" ),
763+ get_database_name (MyDatabaseId ),
764+ get_namespace_name (RelationGetNamespace (onerel )),
765+ RelationGetRelationName (onerel ));
766+ appendStringInfo (& buf , _ ("buffer usage: %lld hits, %lld misses, %lld dirtied\n" ),
767+ (long long ) AnalyzePageHit ,
768+ (long long ) AnalyzePageMiss ,
769+ (long long ) AnalyzePageDirty );
770+ appendStringInfo (& buf , _ ("avg read rate: %.3f MB/s, avg write rate: %.3f MB/s\n" ),
771+ read_rate , write_rate );
772+ if (track_io_timing )
773+ {
774+ appendStringInfoString (& buf , _ ("I/O Timings:" ));
775+ if (pgStatBlockReadTime - startreadtime > 0 )
776+ appendStringInfo (& buf , _ (" read=%.3f" ),
777+ (double ) (pgStatBlockReadTime - startreadtime ) / 1000 );
778+ if (pgStatBlockWriteTime - startwritetime > 0 )
779+ appendStringInfo (& buf , _ (" write=%.3f" ),
780+ (double ) (pgStatBlockWriteTime - startwritetime ) / 1000 );
781+ appendStringInfoChar (& buf , '\n' );
782+ }
783+ appendStringInfo (& buf , _ ("system usage: %s" ), pg_rusage_show (& ru0 ));
784+
698785 ereport (LOG ,
699- (errmsg ("automatic analyze of table \"%s.%s.%s\" system usage: %s" ,
700- get_database_name (MyDatabaseId ),
701- get_namespace_name (RelationGetNamespace (onerel )),
702- RelationGetRelationName (onerel ),
703- pg_rusage_show (& ru0 ))));
786+ (errmsg_internal ("%s" , buf .data )));
787+
788+ pfree (buf .data );
789+ }
704790 }
705791
706792 /* Roll back any GUC changes executed by index functions */
0 commit comments