Re: Command to prune archive at restartpoints - Mailing list pgsql-hackers

From Dimitri Fontaine
Subject Re: Command to prune archive at restartpoints
Date
Msg-id m2wru4ug7v.fsf@hi-media.com
Whole thread Raw
In response to Re: Command to prune archive at restartpoints  (Dimitri Fontaine <dfontaine@hi-media.com>)
Responses Re: Command to prune archive at restartpoints
List pgsql-hackers
Dimitri Fontaine <dfontaine@hi-media.com> writes:
> Also, should I try to send a patch implementing my proposal (internal
> command exposed as a function at the SQL level, and while at it, maybe
> the internal command "pg_archive_bypass" to mimic /usr/bin/true as an
> archive_command)?

I had to have a try at it, even if quick and dirty. I've not tried to
code the pg_archive_bypass internal command for lack of discussion, but
I still think it would be great to have it.

So here's a "see my idea in code" patch, that put the previous code by
Simon into a backend function. As the goal was not to adapt the existing
code intended as external to use the internal APIs, you'll find it quite
ugly I'm sure.

For example, this #define XLOG_DATA_FNAME_LEN has to go away, but that
won't help having the idea accepted or not, and as I'm only warming up,
I didn't tackle the problem. If you want me to do it, I'd appreciate
some guidance as how to, though.

It goes like this:

dim=# select pg_switch_xlog();
 pg_switch_xlog
----------------
 0/1000098
(1 row)

dim=# select pg_archive_cleanup('0/1000098');
DEBUG:  removing "pg_xlog/000000010000000000000000"
DEBUG:  removing "pg_xlog/000000010000000000000001"
 pg_archive_cleanup
--------------------
 t
(1 row)

I hope you too will find this way of interfacing is easier to deal with
for everybody (from code maintenance to user settings).

Regards,
--
dim

*** a/src/backend/access/transam/xlog.c
--- b/src/backend/access/transam/xlog.c
***************
*** 599,605 **** static bool read_backup_label(XLogRecPtr *checkPointLoc);
  static void rm_redo_error_callback(void *arg);
  static int    get_sync_bit(int method);

-
  /*
   * Insert an XLOG record having the specified RMID and info bytes,
   * with the body of the record being the data chunk(s) described by
--- 599,604 ----
***************
*** 3056,3062 **** not_available:
  }

  /*
!  * Attempt to execute an external shell command during recovery.
   *
   * 'command' is the shell command to be executed, 'commandName' is a
   * human-readable name describing the command emitted in the logs. If
--- 3055,3065 ----
  }

  /*
!  * Attempt to execute an external shell command during recovery, or an
!  * internal one if given one.
!  *
!  * There's only one supported internal commands today, which is named
!  * "pg_archive_cleanup".
   *
   * 'command' is the shell command to be executed, 'commandName' is a
   * human-readable name describing the command emitted in the logs. If
***************
*** 3094,3145 **** ExecuteRecoveryCommand(char *command, char *commandName, bool failOnSignal)
      LWLockRelease(ControlFileLock);

      /*
!      * construct the command to be executed
       */
!     dp = xlogRecoveryCmd;
!     endp = xlogRecoveryCmd + MAXPGPATH - 1;
!     *endp = '\0';

!     for (sp = command; *sp; sp++)
      {
!         if (*sp == '%')
          {
!             switch (sp[1])
              {
!                 case 'r':
!                     /* %r: filename of last restartpoint */
!                     sp++;
!                     StrNCpy(dp, lastRestartPointFname, endp - dp);
!                     dp += strlen(dp);
!                     break;
!                 case '%':
!                     /* convert %% to a single % */
!                     sp++;
!                     if (dp < endp)
!                         *dp++ = *sp;
!                     break;
!                 default:
!                     /* otherwise treat the % as not special */
!                     if (dp < endp)
!                         *dp++ = *sp;
!                     break;
              }
          }
!         else
!         {
!             if (dp < endp)
!                 *dp++ = *sp;
!         }
!     }
!     *dp = '\0';

!     ereport(DEBUG3,
!             (errmsg_internal("executing %s \"%s\"", commandName, command)));

-     /*
-      * execute the constructed command
-      */
-     rc = system(xlogRecoveryCmd);
      if (rc != 0)
      {
          /*
--- 3097,3164 ----
      LWLockRelease(ControlFileLock);

      /*
!      * if the command is internal, call the function
       */
!     if( strcmp(command, "pg_archive_cleanup") == 0 )
!     {
!         text *restart_filename = cstring_to_text(lastRestartPointFname);
!         rc = DatumGetBool(DirectFunctionCall1(pg_archive_cleanup,
!                                               restart_filename)) ? 0 : 1;

!         ereport(DEBUG3,
!                 (errmsg_internal("calling %s \"%s\"", commandName, command)));
!     }
!     else
      {
!         /*
!          * construct the command to be executed
!          */
!         dp = xlogRecoveryCmd;
!         endp = xlogRecoveryCmd + MAXPGPATH - 1;
!         *endp = '\0';
!
!         for (sp = command; *sp; sp++)
          {
!             if (*sp == '%')
              {
!                 switch (sp[1])
!                 {
!                     case 'r':
!                         /* %r: filename of last restartpoint */
!                         sp++;
!                         StrNCpy(dp, lastRestartPointFname, endp - dp);
!                         dp += strlen(dp);
!                         break;
!                     case '%':
!                         /* convert %% to a single % */
!                         sp++;
!                         if (dp < endp)
!                             *dp++ = *sp;
!                         break;
!                     default:
!                         /* otherwise treat the % as not special */
!                         if (dp < endp)
!                             *dp++ = *sp;
!                         break;
!                 }
!             }
!             else
!             {
!                 if (dp < endp)
!                     *dp++ = *sp;
              }
          }
!         *dp = '\0';

!         ereport(DEBUG3,
!                 (errmsg_internal("executing %s \"%s\"", commandName, command)));
!
!         /*
!          * execute the constructed command
!          */
!         rc = system(xlogRecoveryCmd);
!     }

      if (rc != 0)
      {
          /*
***************
*** 8916,8921 **** pg_xlogfile_name(PG_FUNCTION_ARGS)
--- 8935,9008 ----
  }

  /*
+  * Cleanup XLOGDIR given a specific WAL file name, which we keep. Typically
+  * used as an archive_cleanup_command.
+  */
+ Datum
+ pg_archive_cleanup(PG_FUNCTION_ARGS)
+ {
+     text       *location = PG_GETARG_TEXT_P(0);
+     char        *exclusiveCleanupFileName = text_to_cstring(location);
+     char        WALFilePath[MAXPGPATH]; /* the file path including archive */
+     int            rc = 0;                 /* nothing to do ain't an error */
+     DIR           *xldir;
+     struct dirent *xlde;
+
+     if ((xldir = opendir(XLOGDIR)) != NULL)
+     {
+         while ((xlde = readdir(xldir)) != NULL)
+         {
+             /*
+              * We ignore the timeline part of the XLOG segment identifiers
+              * in deciding whether a segment is still needed.  This
+              * ensures that we won't prematurely remove a segment from a
+              * parent timeline. We could probably be a little more
+              * proactive about removing segments of non-parent timelines,
+              * but that would be a whole lot more complicated.
+              *
+              * We use the alphanumeric sorting property of the filenames
+              * to decide which ones are earlier than the
+              * exclusiveCleanupFileName file. Note that this means files
+              * are not removed in the order they were originally written,
+              * in case this worries you.
+              */
+             #define XLOG_DATA_FNAME_LEN 24
+             if (strlen(xlde->d_name) == XLOG_DATA_FNAME_LEN &&
+                 strspn(xlde->d_name, "0123456789ABCDEF") == XLOG_DATA_FNAME_LEN &&
+                 strcmp(xlde->d_name + 8, exclusiveCleanupFileName + 8) < 0)
+             {
+ #ifdef WIN32
+                 snprintf(WALFilePath, MAXPGPATH, "%s\\%s", XLOGDIR, xlde->d_name);
+ #else
+                 snprintf(WALFilePath, MAXPGPATH, "%s/%s", XLOGDIR, xlde->d_name);
+ #endif
+
+                 elog(DEBUG1, "removing \"%s\"", WALFilePath);
+
+                 rc = unlink(WALFilePath);
+                 if (rc != 0)
+                 {
+                     elog(ERROR,
+                             "failed to remove \"%s\": %s",
+                             WALFilePath, strerror(errno));
+                     break;
+                 }
+             }
+         }
+     }
+     else
+         /*
+          * How much do we want to manage that? ereport?
+          */
+         elog(ERROR, "Can't open \"%s\"", XLOGDIR);
+
+     closedir(xldir);
+     fflush(stderr);
+
+     PG_RETURN_BOOL(rc == 0);
+ }
+
+ /*
   * read_backup_label: check to see if a backup_label file is present
   *
   * If we see a backup_label during recovery, we assume that we are recovering
*** a/src/include/access/xlog_internal.h
--- b/src/include/access/xlog_internal.h
***************
*** 273,278 **** extern Datum pg_last_xlog_receive_location(PG_FUNCTION_ARGS);
--- 273,279 ----
  extern Datum pg_last_xlog_replay_location(PG_FUNCTION_ARGS);
  extern Datum pg_xlogfile_name_offset(PG_FUNCTION_ARGS);
  extern Datum pg_xlogfile_name(PG_FUNCTION_ARGS);
+ extern Datum pg_archive_cleanup(PG_FUNCTION_ARGS);
  extern Datum pg_is_in_recovery(PG_FUNCTION_ARGS);

  #endif   /* XLOG_INTERNAL_H */
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
***************
*** 3314,3319 **** DATA(insert OID = 2850 ( pg_xlogfile_name_offset    PGNSP PGUID 12 1 0 0 f f f t f
--- 3314,3321 ----
  DESCR("xlog filename and byte offset, given an xlog location");
  DATA(insert OID = 2851 ( pg_xlogfile_name            PGNSP PGUID 12 1 0 0 f f f t f i 1 0 25 "25" _null_ _null_
_null__null_ pg_xlogfile_name _null_ _null_ _null_ )); 
  DESCR("xlog filename, given an xlog location");
+ DATA(insert OID = 3822 ( pg_archive_cleanup            PGNSP PGUID 12 1 0 0 f f f t f i 1 0 16 "25" _null_ _null_
_null__null_ pg_archive_cleanup _null_ _null_ _null_ )); 
+ DESCR("cleanup archive up to given xlog location");

  DATA(insert OID = 3810 (  pg_is_in_recovery        PGNSP PGUID 12 1 0 0 f f f t f v 0 0 16 "" _null_ _null_ _null_
_null_pg_is_in_recovery _null_ _null_ _null_ )); 
  DESCR("true if server is in recovery");

pgsql-hackers by date:

Previous
From: Dimitri Fontaine
Date:
Subject: Re: Command to prune archive at restartpoints
Next
From: Bruce Momjian
Date:
Subject: Re: [PERFORM] Large (almost 50%!) performance drop after upgrading to 8.4.4?