Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1n51Dx-0005Fa-Ah for pgsql-hackers@arkaria.postgresql.org; Wed, 05 Jan 2022 08:00:45 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.92) (envelope-from ) id 1n51Dw-0006TE-8t for pgsql-hackers@arkaria.postgresql.org; Wed, 05 Jan 2022 08:00:44 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1n51Dv-0006T5-T7 for pgsql-hackers@lists.postgresql.org; Wed, 05 Jan 2022 08:00:43 +0000 Received: from out3-smtp.messagingengine.com ([66.111.4.27]) by magus.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1n51Ds-0006rD-4c for pgsql-hackers@lists.postgresql.org; Wed, 05 Jan 2022 08:00:43 +0000 Received: from compute3.internal (compute3.nyi.internal [10.202.2.43]) by mailout.nyi.internal (Postfix) with ESMTP id 19EFE5C0163; Wed, 5 Jan 2022 03:00:38 -0500 (EST) Received: from mailfrontend1 ([10.202.2.162]) by compute3.internal (MEProxy); Wed, 05 Jan 2022 03:00:38 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=paquier.xyz; h= date:from:to:cc:subject:message-id:references:mime-version :content-type:in-reply-to; s=fm3; bh=QDQ37ANhCh4ebBfPDzKytQYL7vA WzCxAe8x882aqrjQ=; b=cdrNlMs2o0/T0yBKiT9b4xuaiHhZxDsuVtnYW95nx5p bkUEqwOh+303PEvYiGFezWMgNm7EhBm0FlAaDSmOqfYM99dzMqVaROSIvHBmwrD7 6OW2gLpGhQ3p1udRQ6QRxUDY0MfPAhLU2UBTM5OtlDoyhImcoVZ96BNogHED25ra LeFnaMqgA2txG/P6nC2xmfzHThZpnMv4fj4LZU5ZSmcekPg4pa0MvCKP8wZ0w9i6 rqpQqQ4EF22vNMV3OOoycvIcC8woLmTf1Fz4jJ1axR8rWEHHSYfXX5SUwUmItpQJ srVp6DUp6vu0Gc/DMnqo6fSVdfRtz6ZSdtKUBuRM5aA== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to:x-me-proxy :x-me-proxy:x-me-sender:x-me-sender:x-sasl-enc; s=fm1; bh=QDQ37A NhCh4ebBfPDzKytQYL7vAWzCxAe8x882aqrjQ=; b=C8bbzMF+pODg2l+6iX/5K8 ZJP/7o70dnHGmWsXhMj/71VmMiOHflcrkJ5xcZTCnPQ3WlrFfGhEEKRhBVX4XwjX RBqX+J+tnlyMj0bIoZUKmJdi2c8wVdIdbU4DtfeJ16hn3uS8ye4n0lva/PlOEVxI dEUG2E7LbCPQuBRltDklpmJBZhGaJJwTwIMxe6ChizplUHW82vFmOHKxlNtkyvjl 19ux15OQhG9MPRarY44iJ8Ytdj88A8vfSA1iZ9fVCEE1jN9tfCLUvgyBt517a6Lx J6qulOA3fD2xFagGRvxh2Anamnt7KIQdqDmJ7ir672ZD/uYed+UXW2GADrsTWupw == X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgedvuddrudefgedgudduudcutefuodetggdotefrod ftvfcurfhrohhfihhlvgemucfhrghsthforghilhdpqfgfvfdpuffrtefokffrpgfnqfgh necuuegrihhlohhuthemuceftddtnecusecvtfgvtghiphhivghnthhsucdlqddutddtmd enfghrlhcuvffnffculdefhedmnecujfgurhepfffhvffukfhfgggtuggjsehgtderredt tddvnecuhfhrohhmpefoihgthhgrvghlucfrrghquhhivghruceomhhitghhrggvlhesph grqhhuihgvrhdrgiihiieqnecuggftrfgrthhtvghrnhepvdegudeuhfdtueeltedtveej heehieevueeigeelteegleejleeiueeiheegvefhnecuvehluhhsthgvrhfuihiivgeptd enucfrrghrrghmpehmrghilhhfrhhomhepmhhitghhrggvlhesphgrqhhuihgvrhdrgiih ii X-ME-Proxy: Received: by mail.messagingengine.com (Postfix) with ESMTPA; Wed, 5 Jan 2022 03:00:36 -0500 (EST) Date: Wed, 5 Jan 2022 17:00:32 +0900 From: Michael Paquier To: gkokolatos@pm.me Cc: Robert Haas , Postgres hackers , Jeevan Ladhe Subject: Re: Refactoring of compression options in pg_basebackup Message-ID: References: MIME-Version: 1.0 Content-Type: multipart/signed; micalg=pgp-sha512; protocol="application/pgp-signature"; boundary="83dG70XdriP2CsyA" Content-Disposition: inline In-Reply-To: List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --83dG70XdriP2CsyA Content-Type: multipart/mixed; boundary="UZtdx9USRu6HfXcm" Content-Disposition: inline --UZtdx9USRu6HfXcm Content-Type: text/plain; charset=us-ascii Content-Disposition: inline (Added Jeevan in CC, for awareness) On Mon, Jan 03, 2022 at 03:35:57PM +0000, gkokolatos@pm.me wrote: > I propose to initialize streamer to NULL when declared at the top of > CreateBackupStreamer(). Yes, that may be noisy. Done this way. > You can see that after the check_pg_config() test, only 3 tests follow, > namely: > * $node->command_ok() > * is(scalar(), ...) > * is($gzip_is_valid, ...) Indeed. The CF bot was complaining about that, actually. Thinking more about this stuff, pg_basebackup --compress is an option that exists already for a couple of years, and that's independent of the backend-side compression that Robert and Jeevan are working on, so I'd like to move on this code cleanup. We can always figure out the LZ4 part for pg_basebackup after, if necessary. Attached is an updated patch. The CF bot should improve with that. -- Michael --UZtdx9USRu6HfXcm Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v2-0001-Refactor-options-of-pg_basebackup-for-compression.patch" Content-Transfer-Encoding: quoted-printable =46rom a216d5569fa6d963c2f846d9f5539d5ea7ddbd62 Mon Sep 17 00:00:00 2001 =46rom: Michael Paquier Date: Wed, 5 Jan 2022 16:58:00 +0900 Subject: [PATCH v2] Refactor options of pg_basebackup for compression level and methods --- src/bin/pg_basebackup/pg_basebackup.c | 81 +++++++++++++++----- src/bin/pg_basebackup/t/010_pg_basebackup.pl | 47 +++++++++++- src/bin/pg_basebackup/walmethods.c | 47 ++++++++---- doc/src/sgml/ref/pg_basebackup.sgml | 22 +++++- 4 files changed, 156 insertions(+), 41 deletions(-) diff --git a/src/bin/pg_basebackup/pg_basebackup.c b/src/bin/pg_basebackup/= pg_basebackup.c index 1739ac6382..7f29200832 100644 --- a/src/bin/pg_basebackup/pg_basebackup.c +++ b/src/bin/pg_basebackup/pg_basebackup.c @@ -113,6 +113,7 @@ static bool showprogress =3D false; static bool estimatesize =3D true; static int verbose =3D 0; static int compresslevel =3D 0; +static WalCompressionMethod compression_method =3D COMPRESSION_NONE; static IncludeWal includewal =3D STREAM_WAL; static bool fastcheckpoint =3D false; static bool writerecoveryconf =3D false; @@ -359,7 +360,9 @@ usage(void) printf(_(" -X, --wal-method=3Dnone|fetch|stream\n" " include required WAL files with specified me= thod\n")); printf(_(" -z, --gzip compress tar output\n")); - printf(_(" -Z, --compress=3D0-9 compress tar output with given compr= ession level\n")); + printf(_(" -Z, --compress=3D1-9 compress tar output with given compr= ession level\n")); + printf(_(" --compression-method=3DMETHOD\n" + " method to compress data\n")); printf(_("\nGeneral options:\n")); printf(_(" -c, --checkpoint=3Dfast|spread\n" " set fast or spread checkpointing\n")); @@ -524,7 +527,7 @@ LogStreamerMain(logstreamer_param *param) stream.do_sync); else stream.walmethod =3D CreateWalTarMethod(param->xlog, - COMPRESSION_NONE, /* ignored */ + compression_method, compresslevel, stream.do_sync); =20 @@ -975,7 +978,7 @@ CreateBackupStreamer(char *archive_name, char *spclocat= ion, bool is_recovery_guc_supported, bool expect_unterminated_tarfile) { - bbstreamer *streamer; + bbstreamer *streamer =3D NULL; bbstreamer *manifest_inject_streamer =3D NULL; bool inject_manifest; bool must_parse_archive; @@ -1034,19 +1037,22 @@ CreateBackupStreamer(char *archive_name, char *spcl= ocation, archive_file =3D NULL; } =20 + if (compression_method =3D=3D COMPRESSION_NONE) + streamer =3D bbstreamer_plain_writer_new(archive_filename, + archive_file); #ifdef HAVE_LIBZ - if (compresslevel !=3D 0) + else if (compression_method =3D=3D COMPRESSION_GZIP) { strlcat(archive_filename, ".gz", sizeof(archive_filename)); streamer =3D bbstreamer_gzip_writer_new(archive_filename, archive_file, compresslevel); } - else #endif - streamer =3D bbstreamer_plain_writer_new(archive_filename, - archive_file); - + else + { + Assert(false); /* not reachable */ + } =20 /* * If we need to parse the archive for whatever reason, then we'll @@ -1765,6 +1771,7 @@ main(int argc, char **argv) {"no-manifest", no_argument, NULL, 5}, {"manifest-force-encode", no_argument, NULL, 6}, {"manifest-checksums", required_argument, NULL, 7}, + {"compression-method", required_argument, NULL, 8}, {NULL, 0, NULL, 0} }; int c; @@ -1872,14 +1879,10 @@ main(int argc, char **argv) do_sync =3D false; break; case 'z': -#ifdef HAVE_LIBZ - compresslevel =3D Z_DEFAULT_COMPRESSION; -#else - compresslevel =3D 1; /* will be rejected below */ -#endif + compression_method =3D COMPRESSION_GZIP; break; case 'Z': - if (!option_parse_int(optarg, "-Z/--compress", 0, 9, + if (!option_parse_int(optarg, "-Z/--compress", 1, 9, &compresslevel)) exit(1); break; @@ -1941,6 +1944,18 @@ main(int argc, char **argv) case 7: manifest_checksums =3D pg_strdup(optarg); break; + case 8: + if (pg_strcasecmp(optarg, "gzip") =3D=3D 0) + compression_method =3D COMPRESSION_GZIP; + else if (pg_strcasecmp(optarg, "none") =3D=3D 0) + compression_method =3D COMPRESSION_NONE; + else + { + pg_log_error("invalid value \"%s\" for option %s", + optarg, "--compression-method"); + exit(1); + } + break; default: =20 /* @@ -1978,7 +1993,7 @@ main(int argc, char **argv) /* * Mutually exclusive arguments */ - if (format =3D=3D 'p' && compresslevel !=3D 0) + if (format =3D=3D 'p' && compression_method !=3D COMPRESSION_NONE) { pg_log_error("only tar mode backups can be compressed"); fprintf(stderr, _("Try \"%s --help\" for more information.\n"), @@ -2056,13 +2071,39 @@ main(int argc, char **argv) } } =20 -#ifndef HAVE_LIBZ - if (compresslevel !=3D 0) + /* + * Compression-related options. + */ + switch (compression_method) { - pg_log_error("this build does not support compression"); - exit(1); - } + case COMPRESSION_NONE: + if (compresslevel !=3D 0) + { + pg_log_error("cannot use --compress with --compression-method=3D%s", + "none"); + fprintf(stderr, _("Try \"%s --help\" for more information.\n"), + progname); + exit(1); + } + break; + case COMPRESSION_GZIP: +#ifdef HAVE_LIBZ + if (compresslevel =3D=3D 0) + { + pg_log_info("no value specified for --compress, switching to default"); + compresslevel =3D Z_DEFAULT_COMPRESSION; + } +#else + pg_log_error("this build does not support compression with %s", + "gzip"); + exit(1); #endif + break; + case COMPRESSION_LZ4: + /* option not supported */ + Assert(false); + break; + } =20 if (showprogress && !estimatesize) { diff --git a/src/bin/pg_basebackup/t/010_pg_basebackup.pl b/src/bin/pg_base= backup/t/010_pg_basebackup.pl index e56825382c..e5fec5f813 100644 --- a/src/bin/pg_basebackup/t/010_pg_basebackup.pl +++ b/src/bin/pg_basebackup/t/010_pg_basebackup.pl @@ -10,7 +10,7 @@ use File::Path qw(rmtree); use Fcntl qw(:seek); use PostgreSQL::Test::Cluster; use PostgreSQL::Test::Utils; -use Test::More tests =3D> 110; +use Test::More tests =3D> 115; =20 program_help_ok('pg_basebackup'); program_version_ok('pg_basebackup'); @@ -31,6 +31,17 @@ my $pgdata =3D $node->data_dir; $node->command_fails(['pg_basebackup'], 'pg_basebackup needs target directory specified'); =20 +# Sanity checks for options +$node->command_fails_like( + [ + 'pg_basebackup', '-D', + "$tempdir/backup", '--compression-method', + 'none', '--compress', + '1' + ], + qr/\Qpg_basebackup: error: cannot use --compress with --compression-metho= d=3Dnone/, + 'failure if --compress specified with --compression-method=3Dnone'); + # Some Windows ANSI code pages may reject this filename, in which case we # quietly proceed without this bit of test coverage. if (open my $badchars, '>>', "$tempdir/pgdata/FOO\xe0\xe0\xe0BAR") @@ -624,3 +635,37 @@ rmtree("$tempdir/backup_corrupt4"); =20 $node->safe_psql('postgres', "DROP TABLE corrupt1;"); $node->safe_psql('postgres', "DROP TABLE corrupt2;"); + +note "Testing pg_basebackup with compression methods"; + +# Check ZLIB compression if available. +SKIP: +{ + skip "postgres was not built with ZLIB support", 3 + if (!check_pg_config("#define HAVE_LIBZ 1")); + + $node->command_ok( + [ + 'pg_basebackup', '-D', + "$tempdir/backup_gzip", '--compression-method', + 'gzip', '--compress', '1', '--no-sync', '--format', 't' + ], + 'pg_basebackup with --compress and --compression-method=3Dgzip'); + + # Verify that the stored files are generated with their expected + # names. + my @zlib_files =3D glob "$tempdir/backup_gzip/*.tar.gz"; + is(scalar(@zlib_files), 2, + "two files created with gzip (base.tar.gz and pg_wal.tar.gz)"); + + # Check the integrity of the files generated. + my $gzip =3D $ENV{GZIP_PROGRAM}; + skip "program gzip is not found in your system", 1 + if ( !defined $gzip + || $gzip eq '' + || system_log($gzip, '--version') !=3D 0); + + my $gzip_is_valid =3D system_log($gzip, '--test', @zlib_files); + is($gzip_is_valid, 0, "gzip verified the integrity of compressed data"); + rmtree("$tempdir/backup_gzip"); +} diff --git a/src/bin/pg_basebackup/walmethods.c b/src/bin/pg_basebackup/wal= methods.c index affdc5055f..127bb5e013 100644 --- a/src/bin/pg_basebackup/walmethods.c +++ b/src/bin/pg_basebackup/walmethods.c @@ -749,7 +749,7 @@ tar_write(Walfile f, const void *buf, size_t count) tar_clear_error(); =20 /* Tarfile will always be positioned at the end */ - if (!tar_data->compression_level) + if (tar_data->compression_method =3D=3D COMPRESSION_NONE) { errno =3D 0; r =3D write(tar_data->fd, buf, count); @@ -833,7 +833,7 @@ tar_open_for_write(const char *pathname, const char *te= mp_suffix, size_t pad_to_ } =20 #ifdef HAVE_LIBZ - if (tar_data->compression_level) + if (tar_data->compression_method =3D=3D COMPRESSION_GZIP) { tar_data->zp =3D (z_streamp) pg_malloc(sizeof(z_stream)); tar_data->zp->zalloc =3D Z_NULL; @@ -884,7 +884,7 @@ tar_open_for_write(const char *pathname, const char *te= mp_suffix, size_t pad_to_ pg_free(tmppath); =20 #ifdef HAVE_LIBZ - if (tar_data->compression_level) + if (tar_data->compression_method =3D=3D COMPRESSION_GZIP) { /* Flush existing data */ if (!tar_write_compressed_data(NULL, 0, true)) @@ -909,7 +909,7 @@ tar_open_for_write(const char *pathname, const char *te= mp_suffix, size_t pad_to_ } tar_data->currentfile->currpos =3D 0; =20 - if (!tar_data->compression_level) + if (tar_data->compression_method =3D=3D COMPRESSION_NONE) { errno =3D 0; if (write(tar_data->fd, tar_data->currentfile->header, @@ -923,7 +923,7 @@ tar_open_for_write(const char *pathname, const char *te= mp_suffix, size_t pad_to_ } } #ifdef HAVE_LIBZ - else + else if (tar_data->compression_method =3D=3D COMPRESSION_GZIP) { /* Write header through the zlib APIs but with no compression */ if (!tar_write_compressed_data(tar_data->currentfile->header, @@ -938,6 +938,10 @@ tar_open_for_write(const char *pathname, const char *t= emp_suffix, size_t pad_to_ } } #endif + else + { + Assert(false); /* not reachable */ + } =20 tar_data->currentfile->pathname =3D pg_strdup(pathname); =20 @@ -948,7 +952,7 @@ tar_open_for_write(const char *pathname, const char *te= mp_suffix, size_t pad_to_ if (pad_to_size) { tar_data->currentfile->pad_to_size =3D pad_to_size; - if (!tar_data->compression_level) + if (tar_data->compression_method =3D=3D COMPRESSION_NONE) { /* Uncompressed, so pad now */ if (!tar_write_padding_data(tar_data->currentfile, pad_to_size)) @@ -1009,7 +1013,7 @@ tar_sync(Walfile f) * Always sync the whole tarfile, because that's all we can do. This makes * no sense on compressed files, so just ignore those. */ - if (tar_data->compression_level) + if (tar_data->compression_method !=3D COMPRESSION_NONE) return 0; =20 r =3D fsync(tar_data->fd); @@ -1030,7 +1034,7 @@ tar_close(Walfile f, WalCloseMethod method) =20 if (method =3D=3D CLOSE_UNLINK) { - if (tar_data->compression_level) + if (tar_data->compression_method !=3D COMPRESSION_NONE) { tar_set_error("unlink not supported with compression"); return -1; @@ -1061,7 +1065,7 @@ tar_close(Walfile f, WalCloseMethod method) */ if (tf->pad_to_size) { - if (tar_data->compression_level) + if (tar_data->compression_method =3D=3D COMPRESSION_GZIP) { /* * A compressed tarfile is padded on close since we cannot know @@ -1102,7 +1106,7 @@ tar_close(Walfile f, WalCloseMethod method) =20 =20 #ifdef HAVE_LIBZ - if (tar_data->compression_level) + if (tar_data->compression_method =3D=3D COMPRESSION_GZIP) { /* Flush the current buffer */ if (!tar_write_compressed_data(NULL, 0, true)) @@ -1131,7 +1135,7 @@ tar_close(Walfile f, WalCloseMethod method) tar_data->lasterrno =3D errno; return -1; } - if (!tar_data->compression_level) + if (tar_data->compression_method =3D=3D COMPRESSION_NONE) { errno =3D 0; if (write(tar_data->fd, tf->header, TAR_BLOCK_SIZE) !=3D TAR_BLOCK_SIZE) @@ -1142,7 +1146,7 @@ tar_close(Walfile f, WalCloseMethod method) } } #ifdef HAVE_LIBZ - else + else if (tar_data->compression_method =3D=3D COMPRESSION_GZIP) { /* Turn off compression */ if (deflateParams(tar_data->zp, 0, 0) !=3D Z_OK) @@ -1164,6 +1168,10 @@ tar_close(Walfile f, WalCloseMethod method) } } #endif + else + { + Assert(false); /* not reachable */ + } =20 /* Move file pointer back down to end, so we can write the next file */ if (lseek(tar_data->fd, 0, SEEK_END) < 0) @@ -1212,7 +1220,7 @@ tar_finish(void) =20 /* A tarfile always ends with two empty blocks */ MemSet(zerobuf, 0, sizeof(zerobuf)); - if (!tar_data->compression_level) + if (tar_data->compression_method =3D=3D COMPRESSION_NONE) { errno =3D 0; if (write(tar_data->fd, zerobuf, sizeof(zerobuf)) !=3D sizeof(zerobuf)) @@ -1223,7 +1231,7 @@ tar_finish(void) } } #ifdef HAVE_LIBZ - else + else if (tar_data->compression_method =3D=3D COMPRESSION_GZIP) { if (!tar_write_compressed_data(zerobuf, sizeof(zerobuf), false)) return false; @@ -1268,6 +1276,10 @@ tar_finish(void) } } #endif + else + { + Assert(false); /* not reachable */ + } =20 /* sync the empty blocks as well, since they're after the last file */ if (tar_data->sync) @@ -1312,7 +1324,8 @@ CreateWalTarMethod(const char *tarbase, int compression_level, bool sync) { WalWriteMethod *method; - const char *suffix =3D (compression_level !=3D 0) ? ".tar.gz" : ".tar"; + const char *suffix =3D (compression_method =3D=3D COMPRESSION_GZIP) ? + ".tar.gz" : ".tar"; =20 method =3D pg_malloc0(sizeof(WalWriteMethod)); method->open_for_write =3D tar_open_for_write; @@ -1335,7 +1348,7 @@ CreateWalTarMethod(const char *tarbase, tar_data->compression_level =3D compression_level; tar_data->sync =3D sync; #ifdef HAVE_LIBZ - if (compression_level) + if (compression_method =3D=3D COMPRESSION_GZIP) tar_data->zlibOut =3D (char *) pg_malloc(ZLIB_OUT_SIZE + 1); #endif =20 @@ -1347,7 +1360,7 @@ FreeWalTarMethod(void) { pg_free(tar_data->tarfilename); #ifdef HAVE_LIBZ - if (tar_data->compression_level) + if (tar_data->compression_method =3D=3D COMPRESSION_GZIP) pg_free(tar_data->zlibOut); #endif pg_free(tar_data); diff --git a/doc/src/sgml/ref/pg_basebackup.sgml b/doc/src/sgml/ref/pg_base= backup.sgml index 9e6807b457..9cbec844c8 100644 --- a/doc/src/sgml/ref/pg_basebackup.sgml +++ b/doc/src/sgml/ref/pg_basebackup.sgml @@ -372,9 +372,9 @@ PostgreSQL documentation Enables gzip compression of tar file output, and specifies the - compression level (0 through 9, 0 being no compression and 9 being= best - compression). Compression is only available when using the tar - format, and the suffix .gz will + compression level (1 through 9, 1 being worst compression and 9 + being best compression). Compression is only available when using + the tar format, and the suffix .gz will automatically be added to all tar filenames. @@ -567,6 +567,22 @@ PostgreSQL documentation =20 + + + + + Enables compression of backup data using the specified method. + Supported values gzip and none, + the default. + + + + The suffix .gz will automatically be added to + all filenames when using gzip. + + + + =20 --=20 2.34.1 --UZtdx9USRu6HfXcm-- --83dG70XdriP2CsyA Content-Type: application/pgp-signature; name="signature.asc" -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEG72nH6vTowiyblFKnvQgOdbyQH0FAmHVUCAACgkQnvQgOdby QH1cjRAAljrIfc4G3IhwGqfSAYZxSO2IkYscjN2BIW3pc2riN2YCzkQGhV/STHHe MoB3YvBStXYCvpPuRW/HN5e+Ygl8LM9+L4yVz8gKjr0y4hzhjkD7bJKqtIPLMONr lLNr/1sx3UTl0zqANyPUjhZh1IamtiGaDBN11VTpfyspBIANF+TIssQbh+GYGC3I j3cZsuOYCfhb5KR6ujrLTaShWOfr9W3Pq4QzLO1TB7B973h3qHEBh0IkKvgYfHHb ZKEzbrWkE+jxiZJgJvKplaJey5HpETG1tA/Gm0S6I0wZ9OQxpIoNCfwFsgWKz0M6 1ycH6o9d5NAWsRZPWZ28PxMJEjgnyCWsGtq1zJHJmgcQdHnjxmByH6zY0fUfQEeA F3raYD3touOO5KHIHeexClrIthxpL5tMpnMLQExTJaISLAetRgKJrVOFIOVJieNG 0h3S4dTaVUMaDqC4pIjUXoNpG9p8gtnglJZh9zkwCOJuYA9vTO9Gt9dNRaUOiyks fxo5eAsJuh4Ligwx9NiQhuWlacecYoPiBlUon0HMSqjhZ3cd0N2J82+O4w08ZbuQ c2TM/O6iryrj7Mjml+g4wGIVf3dPvMFUOxFMp+H74Cx9FrNYEYOEAnViDrTamJ1/ cxJxcJ+5c2GkOpy+RsWgW73OiAEs/w3KVoVZ0rhFlgkc9ISYN/U= =ZD5H -----END PGP SIGNATURE----- --83dG70XdriP2CsyA--