Tu'u i luga o le PostgreSQL log mai le AWS ao

Po'o sina fa'aoga tetrisology.
O mea fou uma ua galo tuai.
Epigraphs.
Tu'u i luga o le PostgreSQL log mai le AWS ao

Fausiaina o le faʻafitauli

E mana'omia lou la'uina mai lea taimi i lea taimi le faila faila o le PostgreSQL o lo'o iai nei mai le AWS cloud i lau 'au Linux fa'apitonu'u. E le o le taimi moni, ae, tatou fai atu, ma sina tuai.
Ole taimi ole la'uina o faila fa'aola faila ole 5 minute.
O le faila ogalaau i le AWS e sui i itula uma.

Mea faigaluega faʻaaoga

Ina ia sii mai le faila ogalaau i le talimalo, e faʻaaogaina se tusitusiga bash e taʻua ai le AWS API "aws rds download-db-log-file-vaega".

Tapulaa:

  • —db-instance-identifier: AWS instance name;
  • --log-file-name: igoa o le faila ogalaau o lo'o gaosia nei
  • --max-item: Ole aofa'iga o mea na toe fa'afo'i ile fa'atonuga.Le vaega tele o le faila na sii mai.
  • --starting-token: Faailoga amata

I lenei tulaga faapitoa, o le galuega o le utaina o ogalaau na tulai mai i le taimi o le galuega i luga mata'ituina le faatinoga ole fesili a PostgreSQL.

Ma e faigofie - o se galuega manaia mo aʻoaʻoga ma eseesega i taimi faigaluega.
Ou te manatu ua uma ona foia le faafitauli ona o le olaga i aso faisoo. Ae o se Google vave e leʻi fautuaina mai ni fofo, ma e leʻi tele soʻu manaʻoga e suʻesuʻe atili. Po o le a lava le itu, o se toleniga lelei.

Fa'aliga o le galuega

O le faila ogalaau mulimuli e aofia ai le tele o laina e fesuisuiai umi. I le faʻataʻitaʻiga, o le faila ogalaau e mafai ona faʻatusalia se mea e pei o lenei:
Tu'u i luga o le PostgreSQL log mai le AWS ao

Pe ua uma ona faamanatu atu ia te oe se mea? O le a le mea e fai ai Tetris? Ma o le mea lenei e fa'atatau i ai.
Afai tatou te mafaufau i filifiliga talafeagai e tulaʻi mai pe a faʻapipiʻi le isi faila i le ata (mo le faigofie, i lenei tulaga, ia tutusa le umi o laina), tatou te maua. vaega masani Tetris:

1) O le faila o lo'o la'uina i lalo lona atoaga ma fa'amautu. O le vaega tele e sili atu nai lo le faila mulimuli:
Tu'u i luga o le PostgreSQL log mai le AWS ao

2) Fa'aauau le faila. E laʻititi le lapoʻa nai lo le faila mulimuli:
Tu'u i luga o le PostgreSQL log mai le AWS ao

3) O le faila o se faʻaauau o le faila muamua ma e iai le faʻaauau. E laʻititi le pusi nai lo le tele o le vaega o totoe o le faila mulimuli:
Tu'u i luga o le PostgreSQL log mai le AWS ao

4) O le faila o se faʻaauau o le faila muamua ma o le mea mulimuli. O le lapo'a o le pusi e sili atu nai lo le tele o totoe o le faila mulimuli:
Tu'u i luga o le PostgreSQL log mai le AWS ao

O le galuega o le fa'apotopotoina o se tafa'afa pe ta'alo Tetris i se tulaga fou.
Tu'u i luga o le PostgreSQL log mai le AWS ao

Faʻafitauli e tulaʻi mai aʻo foia se faʻafitauli

1) Faapipii se manoa e 2 fasi vaega

Tu'u i luga o le PostgreSQL log mai le AWS ao
I se tulaga lautele, e leai ni faʻafitauli faʻapitoa. Ose fa'afitauli masani mai se a'oa'oga fa'apolokalame muamua.

Ole tele ole tautua

Ae e sili atu le manaia o lenei mea.
Ae paga lea, e leai se auala e faʻaaoga ai se offset pe a uma le igoa o le vaega amata:

E pei ona uma ona e iloa le filifiliga -faʻailoga amata e faʻaaoga e faʻamaonia ai le mea e amata ai le faʻailoga. O lenei filifiliga e mana'omia ai le String values ​​o lona uiga afai e te taumafai e fa'aopoopo se tau offset i luma o le Next Token string, o le filifiliga o le a le mafaufauina o se offset.

Ma o le mea lea, e tatau ona e faitau i ni pusi.
Afai e te faitau i vaega tetele, o le numera o faitauga o le a laʻititi, ae o le voluma o le a maualuga.
Afai e te faitau i ni vaega laiti, i se isi itu, o le numera o faitauga o le a maualuga, ae o le voluma o le a laʻititi.
O le mea lea, ina ia faʻaitiitia ai fefaʻatauaiga ma mo le matagofie atoa o le fofo, e tatau ona ou faia se ituaiga o fofo, lea, o le mea e leaga ai, e foliga mai e pei o se tootoo.

Mo se faʻataʻitaʻiga, seʻi o tatou mafaufau i le faʻagasologa o le laʻuina o se faila ogalaau i 2 faʻafaigofie tele. Ole numera ole faitau ile tulaga e lua e fa'alagolago ile tele ole vaega.

1) uta i ni vaega laiti:
Tu'u i luga o le PostgreSQL log mai le AWS ao

2) uta i vaega tetele:
Tu'u i luga o le PostgreSQL log mai le AWS ao

E pei ona masani ai, o le fofo sili ona lelei o loʻo i le ogatotonu.
O le tele o le tautua e laʻititi, ae i le faagasologa o le faitau, e mafai ona faʻateleina le tele e faʻaitiitia ai le numera o faitauga.

E tatau ona maitauina o le faʻafitauli o le filifilia o le tele sili ona lelei o le vaega faitau e leʻi foia ma e manaʻomia ai suʻesuʻega loloto ma auʻiliʻiliga. Atonu o se taimi mulimuli ane.

Fa'amatalaga lautele o le fa'atinoga

Fa'aaoga laulau tautua

CREATE TABLE endpoint
(
id SERIAL ,
host text 
);

TABLE database
(
id SERIAL , 
…
last_aws_log_time text ,
last_aws_nexttoken text ,
aws_max_item_size integer 
);
last_aws_log_time — временная метка последнего загруженного лог-файла в формате YYYY-MM-DD-HH24.
last_aws_nexttoken — текстовая метка последней загруженной порции.
aws_max_item_size- эмпирическим путем, подобранный начальный размер порции.

O tusitusiga atoa

download_aws_piece.sh

#!/bin/bash
#########################################################
# download_aws_piece.sh
# downloan piece of log from AWS
# version HABR
 let min_item_size=1024
 let max_item_size=1048576
 let growth_factor=3
 let growth_counter=1
 let growth_counter_max=3

 echo $(date +%Y%m%d%H%M)':    download_aws_piece.sh:''STARTED'
 
 AWS_LOG_TIME=$1
 echo $(date +%Y%m%d%H%M)':    download_aws_piece.sh:AWS_LOG_TIME='$AWS_LOG_TIME
  
 database_id=$2
 echo $(date +%Y%m%d%H%M)':    download_aws_piece.sh:database_id='$database_id
 RESULT_FILE=$3 
  
 endpoint=`psql -h MONITOR_ENDPOINT.rds.amazonaws.com -U USER -d MONITOR_DATABASE_DATABASE -A -t -c "select e.host from endpoint e join database d on e.id = d.endpoint_id where d.id = $database_id "`
 echo $(date +%Y%m%d%H%M)':    download_aws_piece.sh:endpoint='$endpoint
  
 db_instance=`echo $endpoint | awk -F"." '{print toupper($1)}'`
 
 echo $(date +%Y%m%d%H%M)':    download_aws_piece.sh:db_instance='$db_instance

 LOG_FILE=$RESULT_FILE'.tmp_log'
 TMP_FILE=$LOG_FILE'.tmp'
 TMP_MIDDLE=$LOG_FILE'.tmp_mid'  
 TMP_MIDDLE2=$LOG_FILE'.tmp_mid2'  
  
 current_aws_log_time=`psql -h MONITOR_ENDPOINT.rds.amazonaws.com -U USER -d MONITOR_DATABASE -A -t -c "select last_aws_log_time from database where id = $database_id "`

 echo $(date +%Y%m%d%H%M)':      download_aws_piece.sh:current_aws_log_time='$current_aws_log_time
  
  if [[ $current_aws_log_time != $AWS_LOG_TIME  ]];
  then
    is_new_log='1'
	if ! psql -h MONITOR_ENDPOINT.rds.amazonaws.com -U USER -d MONITOR_DATABASE -v ON_ERROR_STOP=1 -A -t -q -c "update database set last_aws_log_time = '$AWS_LOG_TIME' where id = $database_id "
	then
	  echo $(date +%Y%m%d%H%M)':    download_aws_piece.sh: FATAL_ERROR - update database set last_aws_log_time .'
	  exit 1
	fi
  else
    is_new_log='0'
  fi
  
  echo $(date +%Y%m%d%H%M)':    download_aws_piece.sh:is_new_log='$is_new_log
  
  let last_aws_max_item_size=`psql -h MONITOR_ENDPOINT.rds.amazonaws.com -U USER -d MONITOR_DATABASE -A -t -c "select aws_max_item_size from database where id = $database_id "`
  echo $(date +%Y%m%d%H%M)':    download_aws_piece.sh: last_aws_max_item_size='$last_aws_max_item_size
  
  let count=1
  if [[ $is_new_log == '1' ]];
  then    
	echo $(date +%Y%m%d%H%M)':    download_aws_piece.sh: START DOWNLOADING OF NEW AWS LOG'
	if ! aws rds download-db-log-file-portion 
		--max-items $last_aws_max_item_size 
		--region REGION 
		--db-instance-identifier  $db_instance 
		--log-file-name error/postgresql.log.$AWS_LOG_TIME > $LOG_FILE
	then
		echo $(date +%Y%m%d%H%M)':    download_aws_piece.sh: FATAL_ERROR - Could not get log from AWS .'
		exit 2
	fi  	
  else
    next_token=`psql -h MONITOR_ENDPOINT.rds.amazonaws.com -U USER -d MONITOR_DATABASE -v ON_ERROR_STOP=1 -A -t -c "select last_aws_nexttoken from database where id = $database_id "`
	
	if [[ $next_token == '' ]];
	then
	  next_token='0'	  
	fi
	
	echo $(date +%Y%m%d%H%M)':    download_aws_piece.sh: CONTINUE DOWNLOADING OF AWS LOG'
	if ! aws rds download-db-log-file-portion 
	    --max-items $last_aws_max_item_size 
		--starting-token $next_token 
		--region REGION 
		--db-instance-identifier  $db_instance 
		--log-file-name error/postgresql.log.$AWS_LOG_TIME > $LOG_FILE
	then
		echo $(date +%Y%m%d%H%M)':    download_aws_piece.sh: FATAL_ERROR - Could not get log from AWS .'
		exit 3
	fi       
	
	line_count=`cat  $LOG_FILE | wc -l`
	let lines=$line_count-1
	  
	tail -$lines $LOG_FILE > $TMP_MIDDLE 
	mv -f $TMP_MIDDLE $LOG_FILE
  fi
  
  next_token_str=`cat $LOG_FILE | grep NEXTTOKEN` 
  next_token=`echo $next_token_str | awk -F" " '{ print $2}' `
  
  grep -v NEXTTOKEN $LOG_FILE  > $TMP_FILE 
  
  if [[ $next_token == '' ]];
  then
	  cp $TMP_FILE $RESULT_FILE
	  
	  echo $(date +%Y%m%d%H%M)':    download_aws_piece.sh:  NEXTTOKEN NOT FOUND - FINISH '
	  rm $LOG_FILE 
	  rm $TMP_FILE
	  rm $TMP_MIDDLE
          rm $TMP_MIDDLE2	  
	  exit 0  
  else
	psql -h MONITOR_ENDPOINT.rds.amazonaws.com -U USER -d MONITOR_DATABASE -v ON_ERROR_STOP=1 -A -t -q -c "update database set last_aws_nexttoken = '$next_token' where id = $database_id "
  fi
  
  first_str=`tail -1 $TMP_FILE`
  
  line_count=`cat  $TMP_FILE | wc -l`
  let lines=$line_count-1    
  
  head -$lines $TMP_FILE  > $RESULT_FILE

###############################################
# MAIN CIRCLE
  let count=2
  while [[ $next_token != '' ]];
  do 
    echo $(date +%Y%m%d%H%M)':    download_aws_piece.sh: count='$count
	
	echo $(date +%Y%m%d%H%M)':    download_aws_piece.sh: START DOWNLOADING OF AWS LOG'
	if ! aws rds download-db-log-file-portion 
             --max-items $last_aws_max_item_size 
             --starting-token $next_token 
             --region REGION 
             --db-instance-identifier  $db_instance 
             --log-file-name error/postgresql.log.$AWS_LOG_TIME > $LOG_FILE
	then
		echo $(date +%Y%m%d%H%M)':    download_aws_piece.sh: FATAL_ERROR - Could not get log from AWS .'
		exit 4
	fi

	next_token_str=`cat $LOG_FILE | grep NEXTTOKEN` 
	next_token=`echo $next_token_str | awk -F" " '{ print $2}' `

	TMP_FILE=$LOG_FILE'.tmp'
	grep -v NEXTTOKEN $LOG_FILE  > $TMP_FILE  
	
	last_str=`head -1 $TMP_FILE`
  
    if [[ $next_token == '' ]];
	then
	  concat_str=$first_str$last_str
	  	  
	  echo $concat_str >> $RESULT_FILE
		 
	  line_count=`cat  $TMP_FILE | wc -l`
	  let lines=$line_count-1
	  
	  tail -$lines $TMP_FILE >> $RESULT_FILE
	  
	  echo $(date +%Y%m%d%H%M)':    download_aws_piece.sh:  NEXTTOKEN NOT FOUND - FINISH '
	  rm $LOG_FILE 
	  rm $TMP_FILE
	  rm $TMP_MIDDLE
          rm $TMP_MIDDLE2	  
	  exit 0  
	fi
	
    if [[ $next_token != '' ]];
	then
		let growth_counter=$growth_counter+1
		if [[ $growth_counter -gt $growth_counter_max ]];
		then
			let last_aws_max_item_size=$last_aws_max_item_size*$growth_factor
			let growth_counter=1
		fi
	
		if [[ $last_aws_max_item_size -gt $max_item_size ]]; 
		then
			let last_aws_max_item_size=$max_item_size
		fi 

	  psql -h MONITOR_ENDPOINT.rds.amazonaws.com -U USER -d MONITOR_DATABASE -A -t -q -c "update database set last_aws_nexttoken = '$next_token' where id = $database_id "
	  
	  concat_str=$first_str$last_str
	  	  
	  echo $concat_str >> $RESULT_FILE
		 
	  line_count=`cat  $TMP_FILE | wc -l`
	  let lines=$line_count-1
	  
	  #############################
	  #Get middle of file
	  head -$lines $TMP_FILE > $TMP_MIDDLE
	  
	  line_count=`cat  $TMP_MIDDLE | wc -l`
	  let lines=$line_count-1
	  tail -$lines $TMP_MIDDLE > $TMP_MIDDLE2
	  
	  cat $TMP_MIDDLE2 >> $RESULT_FILE	  
	  
	  first_str=`tail -1 $TMP_FILE`	  
	fi
	  
    let count=$count+1

  done
#
#################################################################

exit 0  

Vaega o tusitusiga ma nisi faʻamatalaga:

Fa'asologa o mea e fa'aoga ai tusitusiga:

  • Timestamp o le igoa faila ogalaau i le faatulagaga YYYY-MM-DD-HH24: AWS_LOG_TIME=$1
  • ID fa'amaumauga: database_id=$2
  • Igoa o le faila ogalaau ua aoina: RESULT_FILE=$3

Maua le faailoga taimi o le faila ogalaau mulimuli na utaina:

current_aws_log_time=`psql -h MONITOR_ENDPOINT.rds.amazonaws.com -U USER -d MONITOR_DATABASE -A -t -c "select last_aws_log_time from database where id = $database_id "`

Afai e le fetaui le fa'ailoga taimi o le faila ogalaau mulimuli na utaina ma le parakalafa fa'aoga, e utaina se faila ogalaau fou:

if [[ $current_aws_log_time != $AWS_LOG_TIME  ]];
  then
    is_new_log='1'
	if ! psql -h ENDPOINT.rds.amazonaws.com -U USER -d MONITOR_DATABASE -v ON_ERROR_STOP=1 -A -t -c "update database set last_aws_log_time = '$AWS_LOG_TIME' where id = $database_id "
	then
	  echo '***download_aws_piece.sh -FATAL_ERROR - update database set last_aws_log_time .'
	  exit 1
	fi
  else
    is_new_log='0'
  fi

Matou te maua le tau o le isi igoa mai le faila na sii mai:

  next_token_str=`cat $LOG_FILE | grep NEXTTOKEN` 
  next_token=`echo $next_token_str | awk -F" " '{ print $2}' `

O se isi fa'atauga gaogao e fai ma fa'ailoga o le fa'ai'uga o le la'uina.

I se matasele, matou te faitauina vaega o le faila, faʻapipiʻi laina i luga o le ala ma faʻateleina le tele o le vaega:
Mata'u autu

# MAIN CIRCLE
  let count=2
  while [[ $next_token != '' ]];
  do 
    echo $(date +%Y%m%d%H%M)':    download_aws_piece.sh: count='$count
	
	echo $(date +%Y%m%d%H%M)':    download_aws_piece.sh: START DOWNLOADING OF AWS LOG'
	if ! aws rds download-db-log-file-portion 
     --max-items $last_aws_max_item_size 
	 --starting-token $next_token 
     --region REGION 
     --db-instance-identifier  $db_instance 
     --log-file-name error/postgresql.log.$AWS_LOG_TIME > $LOG_FILE
	then
		echo $(date +%Y%m%d%H%M)':    download_aws_piece.sh: FATAL_ERROR - Could not get log from AWS .'
		exit 4
	fi

	next_token_str=`cat $LOG_FILE | grep NEXTTOKEN` 
	next_token=`echo $next_token_str | awk -F" " '{ print $2}' `

	TMP_FILE=$LOG_FILE'.tmp'
	grep -v NEXTTOKEN $LOG_FILE  > $TMP_FILE  
	
	last_str=`head -1 $TMP_FILE`
  
    if [[ $next_token == '' ]];
	then
	  concat_str=$first_str$last_str
	  	  
	  echo $concat_str >> $RESULT_FILE
		 
	  line_count=`cat  $TMP_FILE | wc -l`
	  let lines=$line_count-1
	  
	  tail -$lines $TMP_FILE >> $RESULT_FILE
	  
	  echo $(date +%Y%m%d%H%M)':    download_aws_piece.sh:  NEXTTOKEN NOT FOUND - FINISH '
	  rm $LOG_FILE 
	  rm $TMP_FILE
	  rm $TMP_MIDDLE
         rm $TMP_MIDDLE2	  
	  exit 0  
	fi
	
    if [[ $next_token != '' ]];
	then
		let growth_counter=$growth_counter+1
		if [[ $growth_counter -gt $growth_counter_max ]];
		then
			let last_aws_max_item_size=$last_aws_max_item_size*$growth_factor
			let growth_counter=1
		fi
	
		if [[ $last_aws_max_item_size -gt $max_item_size ]]; 
		then
			let last_aws_max_item_size=$max_item_size
		fi 

	  psql -h MONITOR_ENDPOINT.rds.amazonaws.com -U USER -d MONITOR_DATABASE -A -t -q -c "update database set last_aws_nexttoken = '$next_token' where id = $database_id "
	  
	  concat_str=$first_str$last_str
	  	  
	  echo $concat_str >> $RESULT_FILE
		 
	  line_count=`cat  $TMP_FILE | wc -l`
	  let lines=$line_count-1
	  
	  #############################
	  #Get middle of file
	  head -$lines $TMP_FILE > $TMP_MIDDLE
	  
	  line_count=`cat  $TMP_MIDDLE | wc -l`
	  let lines=$line_count-1
	  tail -$lines $TMP_MIDDLE > $TMP_MIDDLE2
	  
	  cat $TMP_MIDDLE2 >> $RESULT_FILE	  
	  
	  first_str=`tail -1 $TMP_FILE`	  
	fi
	  
    let count=$count+1

  done

O le a le isi mea?

O lea la, o le galuega muamua - "download se faila ogalaau mai le ao" ua foia. O le a le mea e fai i le ogalaau na sii mai?
Muamua, e tatau ona e faʻapipiʻi le faila ogalaau ma aveese mai talosaga moni mai ai.
E le faigata tele le galuega. O le tusitusiga bash sili ona faigofie e fai lelei le galuega.
upload_log_query.sh

#!/bin/bash
#########################################################
# upload_log_query.sh
# Upload table table from dowloaded aws file 
# version HABR
###########################################################  
echo 'TIMESTAMP:'$(date +%c)' Upload log_query table '
source_file=$1
echo 'source_file='$source_file
database_id=$2
echo 'database_id='$database_id

beginer=' '
first_line='1'
let "line_count=0"
sql_line=' '
sql_flag=' '    
space=' '
cat $source_file | while read line
do
  line="$space$line"

  if [[ $first_line == "1" ]]; then
    beginer=`echo $line | awk -F" " '{ print $1}' `
    first_line='0'
  fi

  current_beginer=`echo $line | awk -F" " '{ print $1}' `

  if [[ $current_beginer == $beginer ]]; then
    if [[ $sql_flag == '1' ]]; then
     sql_flag='0' 
     log_date=`echo $sql_line | awk -F" " '{ print $1}' `
     log_time=`echo $sql_line | awk -F" " '{ print $2}' `
     duration=`echo $sql_line | awk -F" " '{ print $5}' `

     #replace ' to ''
     sql_modline=`echo "$sql_line" | sed 's/'''/''''''/g'`
     sql_line=' '

	 ################
	 #PROCESSING OF THE SQL-SELECT IS HERE
     if ! psql -h ENDPOINT.rds.amazonaws.com -U USER -d DATABASE -v ON_ERROR_STOP=1 -A -t -c "select log_query('$ip_port',$database_id , '$log_date' , '$log_time' , '$duration' , '$sql_modline' )" 
     then
        echo 'FATAL_ERROR - log_query '
        exit 1
     fi
	 ################

    fi #if [[ $sql_flag == '1' ]]; then

    let "line_count=line_count+1"

    check=`echo $line | awk -F" " '{ print $8}' `
    check_sql=${check^^}    

    #echo 'check_sql='$check_sql
    
    if [[ $check_sql == 'SELECT' ]]; then
     sql_flag='1'    
     sql_line="$sql_line$line"
	 ip_port=`echo $sql_line | awk -F":" '{ print $4}' `
    fi
  else       

    if [[ $sql_flag == '1' ]]; then
      sql_line="$sql_line$line"
    fi   
    
  fi #if [[ $current_beginer == $beginer ]]; then

done

O lea e mafai ona e galue ma le talosaga na filifilia mai le faila ogalaau.

Ma e tele avanoa aoga e matala mai.

E mana'omia le teuina o fesili fa'asalalau i se mea. E fa'aogaina se laulau tautua mo lea log_query

CREATE TABLE log_query
(
   id SERIAL ,
   queryid bigint ,
   query_md5hash text not null ,
   database_id integer not null ,  
   timepoint timestamp without time zone not null,
   duration double precision not null ,
   query text not null ,
   explained_plan text[],
   plan_md5hash text  , 
   explained_plan_wo_costs text[],
   plan_hash_value text  ,
   baseline_id integer ,
   ip text ,
   port text 
);
ALTER TABLE log_query ADD PRIMARY KEY (id);
ALTER TABLE log_query ADD CONSTRAINT queryid_timepoint_unique_key UNIQUE (queryid, timepoint );
ALTER TABLE log_query ADD CONSTRAINT query_md5hash_timepoint_unique_key UNIQUE (query_md5hash, timepoint );

CREATE INDEX log_query_timepoint_idx ON log_query (timepoint);
CREATE INDEX log_query_queryid_idx ON log_query (queryid);
ALTER TABLE log_query ADD CONSTRAINT database_id_fk FOREIGN KEY (database_id) REFERENCES database (id) ON DELETE CASCADE ;

O lo'o fa'agasolo i totonu le talosaga na fa'avasegaina plpgsql galuega"log_query".
log_query.sql

--log_query.sql
--verison HABR
CREATE OR REPLACE FUNCTION log_query( ip_port text ,log_database_id integer , log_date text , log_time text , duration text , sql_line text   ) RETURNS boolean AS $$
DECLARE
  result boolean ;
  log_timepoint timestamp without time zone ;
  log_duration double precision ; 
  pos integer ;
  log_query text ;
  activity_string text ;
  log_md5hash text ;
  log_explain_plan text[] ;
  
  log_planhash text ;
  log_plan_wo_costs text[] ; 
  
  database_rec record ;
  
  pg_stat_query text ; 
  test_log_query text ;
  log_query_rec record;
  found_flag boolean;
  
  pg_stat_history_rec record ;
  port_start integer ;
  port_end integer ;
  client_ip text ;
  client_port text ;
  log_queryid bigint ;
  log_query_text text ;
  pg_stat_query_text text ; 
BEGIN
  result = TRUE ;

  RAISE NOTICE '***log_query';
  
  port_start = position('(' in ip_port);
  port_end = position(')' in ip_port);
  client_ip = substring( ip_port from 1 for port_start-1 );
  client_port = substring( ip_port from port_start+1 for port_end-port_start-1 );

  SELECT e.host , d.name , d.owner_pwd 
  INTO database_rec
  FROM database d JOIN endpoint e ON e.id = d.endpoint_id
  WHERE d.id = log_database_id ;
  
  log_timepoint = to_timestamp(log_date||' '||log_time,'YYYY-MM-DD HH24-MI-SS');
  log_duration = duration:: double precision; 

  
  pos = position ('SELECT' in UPPER(sql_line) );
  log_query = substring( sql_line from pos for LENGTH(sql_line));
  log_query = regexp_replace(log_query,' +',' ','g');
  log_query = regexp_replace(log_query,';+','','g');
  log_query = trim(trailing ' ' from log_query);
 

  log_md5hash = md5( log_query::text );
  
  --Explain execution plan--
  EXECUTE 'SELECT dblink_connect(''LINK1'',''host='||database_rec.host||' dbname='||database_rec.name||' user=DATABASE password='||database_rec.owner_pwd||' '')'; 
  
  log_explain_plan = ARRAY ( SELECT * FROM dblink('LINK1', 'EXPLAIN '||log_query ) AS t (plan text) );
  log_plan_wo_costs = ARRAY ( SELECT * FROM dblink('LINK1', 'EXPLAIN ( COSTS FALSE ) '||log_query ) AS t (plan text) );
    
  PERFORM dblink_disconnect('LINK1');
  --------------------------
  BEGIN
	INSERT INTO log_query
	(
		query_md5hash ,
		database_id , 
		timepoint ,
		duration ,
		query ,
		explained_plan ,
		plan_md5hash , 
		explained_plan_wo_costs , 
		plan_hash_value , 
		ip , 
		port
	) 
	VALUES 
	(
		log_md5hash ,
		log_database_id , 
		log_timepoint , 
		log_duration , 
		log_query ,
		log_explain_plan , 
		md5(log_explain_plan::text) ,
		log_plan_wo_costs , 
		md5(log_plan_wo_costs::text),
		client_ip , 
		client_port		
	);
	activity_string = 	'New query has logged '||
						' database_id = '|| log_database_id ||
						' query_md5hash='||log_md5hash||
						' , timepoint = '||to_char(log_timepoint,'YYYYMMDD HH24:MI:SS');
					
	RAISE NOTICE '%',activity_string;					
					 
	PERFORM pg_log( log_database_id , 'log_query' , activity_string);  

	EXCEPTION
	  WHEN unique_violation THEN
		RAISE NOTICE '*** unique_violation *** query already has logged';
	END;

	SELECT 	queryid
	INTO   	log_queryid
	FROM 	log_query 
	WHERE 	query_md5hash = log_md5hash AND
			timepoint = log_timepoint;

	IF log_queryid IS NOT NULL 
	THEN 
	  RAISE NOTICE 'log_query with query_md5hash = % and timepoint = % has already has a QUERYID = %',log_md5hash,log_timepoint , log_queryid ;
	  RETURN result;
	END IF;
	
	------------------------------------------------
	RAISE NOTICE 'Update queryid';	
	
	SELECT * 
	INTO log_query_rec
	FROM log_query
	WHERE query_md5hash = log_md5hash AND timepoint = log_timepoint ; 
	
	log_query_rec.query=regexp_replace(log_query_rec.query,';+','','g');
	
	FOR pg_stat_history_rec IN
	 SELECT 
         queryid ,
	  query 
	 FROM 
         pg_stat_db_queries 
     WHERE  
      database_id = log_database_id AND
       queryid is not null 
	LOOP
	  pg_stat_query = pg_stat_history_rec.query ; 
	  pg_stat_query=regexp_replace(pg_stat_query,'n+',' ','g');
	  pg_stat_query=regexp_replace(pg_stat_query,'t+',' ','g');
	  pg_stat_query=regexp_replace(pg_stat_query,' +',' ','g');
	  pg_stat_query=regexp_replace(pg_stat_query,'$.','%','g');
	
	  log_query_text = trim(trailing ' ' from log_query_rec.query);
	  pg_stat_query_text = pg_stat_query; 
	
	  
	  --SELECT log_query_rec.query like pg_stat_query INTO found_flag ; 
	  IF (log_query_text LIKE pg_stat_query_text) THEN
		found_flag = TRUE ;
	  ELSE
		found_flag = FALSE ;
	  END IF;	  
	  
	  
	  IF found_flag THEN
	    
		UPDATE log_query SET queryid = pg_stat_history_rec.queryid WHERE query_md5hash = log_md5hash AND timepoint = log_timepoint ;
		activity_string = 	' updated queryid = '||pg_stat_history_rec.queryid||
		                    ' for log_query with id = '||log_query_rec.id               
		   				    ;						
	    RAISE NOTICE '%',activity_string;	
		EXIT ;
	  END IF ;
	  
	END LOOP ;
	
  RETURN result ;
END
$$ LANGUAGE plpgsql;

O se laulau tautua e faʻaaogaina i le taimi o gaioiga pg_stat_db_queries, o lo'o i ai se ata o fesili o lo'o iai nei mai le laulau pg_stat_history (O le fa'aogaina o le laulau o lo'o fa'amatalaina iinei − Mata'ituina le faatinoga ole fesili ole PostgreSQL. Vaega 1 - lipoti)

TABLE pg_stat_db_queries
(
   database_id integer,  
   queryid bigint ,  
   query text , 
   max_time double precision 
);

TABLE pg_stat_history 
(
…
database_id integer ,
…
queryid bigint ,
…
max_time double precision	 , 	
…
);

O le galuega e mafai ai e oe ona faʻatinoina le tele o mea aoga mo le faʻatulagaina o talosaga mai se faila ogalaau. Fa'aigoa:

Avanoa #1 - Tala'aga o le fa'atinoina o fesili

E aoga tele mo le amataina e foia ai se faʻalavelave faʻatinoga. Muamua, faamasani i le tala faasolopito - o anafea na amata ai le faʻagesegese?
Ona, e tusa ai ma tala masani, vaavaai mo mafuaaga i fafo. Masalo o le uta o faʻamaumauga ua naʻo le faʻateleina ma le faʻapitoa e leai se mea e fai i ai.
Fa'aopoopo se tala fou ile laulau log_query

  port_start = position('(' in ip_port);
  port_end = position(')' in ip_port);
  client_ip = substring( ip_port from 1 for port_start-1 );
  client_port = substring( ip_port from port_start+1 for port_end-port_start-1 );

  SELECT e.host , d.name , d.owner_pwd 
  INTO database_rec
  FROM database d JOIN endpoint e ON e.id = d.endpoint_id
  WHERE d.id = log_database_id ;
  
  log_timepoint = to_timestamp(log_date||' '||log_time,'YYYY-MM-DD HH24-MI-SS');
  log_duration = to_number(duration,'99999999999999999999D9999999999'); 

  
  pos = position ('SELECT' in UPPER(sql_line) );
  log_query = substring( sql_line from pos for LENGTH(sql_line));
  log_query = regexp_replace(log_query,' +',' ','g');
  log_query = regexp_replace(log_query,';+','','g');
  log_query = trim(trailing ' ' from log_query);
 
  RAISE NOTICE 'log_query=%',log_query ;   

  log_md5hash = md5( log_query::text );
  
  --Explain execution plan--
  EXECUTE 'SELECT dblink_connect(''LINK1'',''host='||database_rec.host||' dbname='||database_rec.name||' user=DATABASE password='||database_rec.owner_pwd||' '')'; 
  
  log_explain_plan = ARRAY ( SELECT * FROM dblink('LINK1', 'EXPLAIN '||log_query ) AS t (plan text) );
  log_plan_wo_costs = ARRAY ( SELECT * FROM dblink('LINK1', 'EXPLAIN ( COSTS FALSE ) '||log_query ) AS t (plan text) );
    
  PERFORM dblink_disconnect('LINK1');
  --------------------------
  BEGIN
	INSERT INTO log_query
	(
		query_md5hash ,
		database_id , 
		timepoint ,
		duration ,
		query ,
		explained_plan ,
		plan_md5hash , 
		explained_plan_wo_costs , 
		plan_hash_value , 
		ip , 
		port
	) 
	VALUES 
	(
		log_md5hash ,
		log_database_id , 
		log_timepoint , 
		log_duration , 
		log_query ,
		log_explain_plan , 
		md5(log_explain_plan::text) ,
		log_plan_wo_costs , 
		md5(log_plan_wo_costs::text),
		client_ip , 
		client_port		
	);

Avanoa #2 - Fa'asaoina fuafuaga fa'atino

I le taimi nei e mafai ona tulaʻi mai se faʻamatalaga-faʻamanino-faʻamatalaga: "Ae ua uma ona autoexplain" Ioe, o loʻo i ai iina, ae o le a le uiga pe afai o le faʻatinoga o fuafuaga e teuina i totonu o le faila ogalaau e tasi ma ina ia mafai ona faʻasaoina mo se suʻesuʻega atili, e tatau ona e faʻasalalau le faila ogalaau?

O le mea na ou manaʻomia o le:
muamua: teu le fuafuaga faʻataunuʻu i le laulau tautua a le faʻamaumauga mataʻituina;
lona lua: ia mafai ona faʻatusatusa fuafuaga faʻataunuʻu ma le tasi ma le isi ina ia vave iloa ai ua suia le fuafuaga o le faʻatinoina o fesili.

O loʻo i ai se talosaga faʻatasi ai ma tapulaʻa faʻatinoina. O le mauaina ma le fa'asaoina o lana fuafuaga fa'atino e fa'aaoga ai le EXPLAIN ose galuega fa'avae.
E le gata i lea, i le faʻaaogaina o le faʻamatalaga (COSTS FALSE), e mafai ona e maua se auivi o le fuafuaga, lea o le a faʻaaogaina e maua ai le tau o le hash o le fuafuaga, lea o le a fesoasoani i le suʻesuʻeina mulimuli ane o le talafaasolopito o suiga i le fuafuaga faʻataunuʻu.
Maua le fa'ata'ita'iga o fuafuaga fa'atino

  --Explain execution plan--
  EXECUTE 'SELECT dblink_connect(''LINK1'',''host='||database_rec.host||' dbname='||database_rec.name||' user=DATABASE password='||database_rec.owner_pwd||' '')'; 
  
  log_explain_plan = ARRAY ( SELECT * FROM dblink('LINK1', 'EXPLAIN '||log_query ) AS t (plan text) );
  log_plan_wo_costs = ARRAY ( SELECT * FROM dblink('LINK1', 'EXPLAIN ( COSTS FALSE ) '||log_query ) AS t (plan text) );
    
  PERFORM dblink_disconnect('LINK1');

Avanoa #3 - Fa'aaogā le fa'amaumauga o fesili mo le mata'ituina

Talu ai e le faʻatulagaina fua faʻatinoga i luga o le tusi talosaga, ae i luga o lona ID, e tatau ona e faʻafesoʻotaʻi talosaga mai le faila ogalaau ma talosaga e faʻatulagaina fua faʻatinoga.
Ia, a itiiti mai ina ia maua le taimi tonu e tupu ai se faʻalavelave faʻatinoga.

I lenei auala, pe a tupu se faʻatinoga faʻapitoa mo se ID talosaga, o le ai ai se fesoʻotaʻiga i se talosaga faʻapitoa faʻatasi ai ma faʻamaumauga faʻapitoa ma le taimi tonu o le faʻatinoga ma le umi o le talosaga. Maua lenei fa'amatalaga e fa'aaoga ai na'o le va'aiga pg_stat_statements - e fa'asāina.
Su'e le fesili ole talosaga ma fa'afou le fa'ailoga ile log_query table

SELECT * 
	INTO log_query_rec
	FROM log_query
	WHERE query_md5hash = log_md5hash AND timepoint = log_timepoint ; 
	
	log_query_rec.query=regexp_replace(log_query_rec.query,';+','','g');
	
	FOR pg_stat_history_rec IN
	 SELECT 
      queryid ,
	  query 
	 FROM 
       pg_stat_db_queries 
     WHERE  
	   database_id = log_database_id AND
       queryid is not null 
	LOOP
	  pg_stat_query = pg_stat_history_rec.query ; 
	  pg_stat_query=regexp_replace(pg_stat_query,'n+',' ','g');
	  pg_stat_query=regexp_replace(pg_stat_query,'t+',' ','g');
	  pg_stat_query=regexp_replace(pg_stat_query,' +',' ','g');
	  pg_stat_query=regexp_replace(pg_stat_query,'$.','%','g');
	
	  log_query_text = trim(trailing ' ' from log_query_rec.query);
	  pg_stat_query_text = pg_stat_query; 
	  
	  --SELECT log_query_rec.query like pg_stat_query INTO found_flag ; 
	  IF (log_query_text LIKE pg_stat_query_text) THEN
		found_flag = TRUE ;
	  ELSE
		found_flag = FALSE ;
	  END IF;	  
	  
	  
	  IF found_flag THEN
	    
		UPDATE log_query SET queryid = pg_stat_history_rec.queryid WHERE query_md5hash = log_md5hash AND timepoint = log_timepoint ;
		activity_string = 	' updated queryid = '||pg_stat_history_rec.queryid||
		                    ' for log_query with id = '||log_query_rec.id		                    
		   				    ;						
					
	    RAISE NOTICE '%',activity_string;	
		EXIT ;
	  END IF ;
	  
	END LOOP ;

Taofi upu

O le metotia faʻamatalaina mulimuli ane maua le faʻaoga i totonu le atina'e PostgreSQL faiga mata'ituina galuega, fa'atagaina oe e maua nisi fa'amatalaga e su'esu'e pe a fo'ia fa'alavelave fa'aletonu o lo'o tula'i mai.

E ui lava, ioe, i loʻu lava manatu, e tatau ona galue atili i luga o le algorithm mo le filifilia ma le suia o le tele o le vaega na sii mai. E lei foia le faafitauli i le tulaga lautele. Atonu o le a manaia.

Ae o se tala e matua ese lava ...

puna: www.habr.com

Faaopoopo i ai se faamatalaga